1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "editormanager.h"
31 #include "editorview.h"
32 #include "openeditorswindow.h"
33 #include "openeditorsview.h"
34 #include "openeditorsmodel.h"
35 #include "openwithdialog.h"
36 #include "filemanager.h"
39 #include "iversioncontrol.h"
40 #include "mimedatabase.h"
41 #include "tabpositionindicator.h"
42 #include "vcsmanager.h"
44 #include <coreplugin/editortoolbar.h>
45 #include <coreplugin/coreconstants.h>
46 #include <coreplugin/modemanager.h>
47 #include <coreplugin/actionmanager/actionmanager.h>
48 #include <coreplugin/actionmanager/actioncontainer.h>
49 #include <coreplugin/actionmanager/command.h>
50 #include <coreplugin/editormanager/ieditorfactory.h>
51 #include <coreplugin/editormanager/iexternaleditor.h>
52 #include <coreplugin/icorelistener.h>
53 #include <coreplugin/imode.h>
54 #include <coreplugin/settingsdatabase.h>
55 #include <coreplugin/variablemanager.h>
56 #include <coreplugin/uniqueidmanager.h>
58 #include <extensionsystem/pluginmanager.h>
60 #include <utils/consoleprocess.h>
61 #include <utils/qtcassert.h>
63 #include <QtCore/QDebug>
64 #include <QtCore/QFileInfo>
65 #include <QtCore/QMap>
66 #include <QtCore/QProcess>
67 #include <QtCore/QSet>
68 #include <QtCore/QSettings>
69 #include <QtCore/QTextCodec>
71 #include <QtGui/QAction>
72 #include <QtGui/QShortcut>
73 #include <QtGui/QApplication>
74 #include <QtGui/QFileDialog>
75 #include <QtGui/QLayout>
76 #include <QtGui/QMainWindow>
77 #include <QtGui/QMenu>
78 #include <QtGui/QMessageBox>
79 #include <QtGui/QPushButton>
80 #include <QtGui/QSplitter>
81 #include <QtGui/QStackedLayout>
83 Q_DECLARE_METATYPE(Core::IEditor*)
85 enum { debugEditorManager=0 };
87 static inline ExtensionSystem::PluginManager *pluginManager()
89 return ExtensionSystem::PluginManager::instance();
92 //===================EditorClosingCoreListener======================
97 class EditorClosingCoreListener : public ICoreListener
100 EditorClosingCoreListener(EditorManager *em);
101 bool editorAboutToClose(IEditor *editor);
102 bool coreAboutToClose();
108 EditorClosingCoreListener::EditorClosingCoreListener(EditorManager *em)
113 bool EditorClosingCoreListener::editorAboutToClose(IEditor *)
118 bool EditorClosingCoreListener::coreAboutToClose()
120 // Do not ask for files to save.
121 // MainWindow::closeEvent has already done that.
122 return m_em->closeAllEditors(false);
125 } // namespace Internal
128 using namespace Core;
129 using namespace Core::Internal;
130 using namespace Utils;
132 //===================EditorManager=====================
134 EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0;
136 EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent)
137 : QWidget(parent), m_mode(mode)
139 setLayout(new QVBoxLayout);
140 layout()->setMargin(0);
141 connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)),
142 this, SLOT(currentModeChanged(Core::IMode *)));
144 currentModeChanged(Core::ModeManager::instance()->currentMode());
147 EditorManagerPlaceHolder::~EditorManagerPlaceHolder()
149 if (m_current == this) {
150 EditorManager::instance()->setParent(0);
151 EditorManager::instance()->hide();
155 void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode)
157 if (m_current == this) {
159 EditorManager::instance()->setParent(0);
160 EditorManager::instance()->hide();
162 if (m_mode == mode) {
164 layout()->addWidget(EditorManager::instance());
165 EditorManager::instance()->show();
169 EditorManagerPlaceHolder* EditorManagerPlaceHolder::current()
174 // ---------------- EditorManager
179 struct EditorManagerPrivate {
180 explicit EditorManagerPrivate(ICore *core, QWidget *parent);
181 ~EditorManagerPrivate();
182 Internal::EditorView *m_view;
183 Internal::SplitterOrView *m_splitter;
184 QPointer<IEditor> m_currentEditor;
185 QPointer<SplitterOrView> m_currentView;
191 QAction *m_revertToSavedAction;
192 QAction *m_saveAction;
193 QAction *m_saveAsAction;
194 QAction *m_closeCurrentEditorAction;
195 QAction *m_closeAllEditorsAction;
196 QAction *m_closeOtherEditorsAction;
197 QAction *m_gotoNextDocHistoryAction;
198 QAction *m_gotoPreviousDocHistoryAction;
199 QAction *m_goBackAction;
200 QAction *m_goForwardAction;
201 QAction *m_openInExternalEditorAction;
202 QAction *m_splitAction;
203 QAction *m_splitSideBySideAction;
204 QAction *m_removeCurrentSplitAction;
205 QAction *m_removeAllSplitsAction;
206 QAction *m_gotoOtherSplitAction;
208 Internal::OpenEditorsWindow *m_windowPopup;
209 Internal::EditorClosingCoreListener *m_coreListener;
211 QMap<QString, QVariant> m_editorStates;
212 Internal::OpenEditorsViewFactory *m_openEditorsFactory;
214 OpenEditorsModel *m_editorModel;
215 QString m_externalEditor;
217 IFile::ReloadSetting m_reloadSetting;
218 IFile::Utf8BomSetting m_utf8BomSetting;
220 QString m_titleAddition;
224 EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) :
228 m_revertToSavedAction(new QAction(EditorManager::tr("Revert to Saved"), parent)),
229 m_saveAction(new QAction(parent)),
230 m_saveAsAction(new QAction(parent)),
231 m_closeCurrentEditorAction(new QAction(EditorManager::tr("Close"), parent)),
232 m_closeAllEditorsAction(new QAction(EditorManager::tr("Close All"), parent)),
233 m_closeOtherEditorsAction(new QAction(EditorManager::tr("Close Others"), parent)),
234 m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Open Document in History"), parent)),
235 m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Open Document in History"), parent)),
236 m_goBackAction(new QAction(QIcon(QLatin1String(Constants::ICON_PREV)), EditorManager::tr("Go Back"), parent)),
237 m_goForwardAction(new QAction(QIcon(QLatin1String(Constants::ICON_NEXT)), EditorManager::tr("Go Forward"), parent)),
238 m_openInExternalEditorAction(new QAction(EditorManager::tr("Open in External Editor"), parent)),
241 m_reloadSetting(IFile::AlwaysAsk),
242 m_utf8BomSetting(IFile::OnlyKeep)
244 m_editorModel = new OpenEditorsModel(parent);
247 EditorManagerPrivate::~EditorManagerPrivate()
249 // clearNavigationHistory();
252 EditorManager *EditorManager::m_instance = 0;
254 static Command *createSeparator(ActionManager *am, QObject *parent,
256 const Context &context)
258 QAction *tmpaction = new QAction(parent);
259 tmpaction->setSeparator(true);
260 Command *cmd = am->registerAction(tmpaction, name, context);
264 EditorManager::EditorManager(ICore *core, QWidget *parent) :
266 m_d(new EditorManagerPrivate(core, parent))
270 connect(m_d->m_core, SIGNAL(contextAboutToChange(Core::IContext *)),
271 this, SLOT(handleContextChange(Core::IContext *)));
273 const Context editManagerContext(Constants::C_EDITORMANAGER);
274 // combined context for edit & design modes
275 const Context editDesignContext(Constants::C_EDITORMANAGER, Constants::C_DESIGN_MODE);
277 ActionManager *am = m_d->m_core->actionManager();
278 ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
281 m_d->m_revertToSavedAction->setIcon(QIcon::fromTheme(QLatin1String("document-revert")));
282 Command *cmd = am->registerAction(m_d->m_revertToSavedAction,
283 Constants::REVERTTOSAVED, editManagerContext);
284 cmd->setAttribute(Command::CA_UpdateText);
285 cmd->setDefaultText(tr("Revert File to Saved"));
286 mfile->addAction(cmd, Constants::G_FILE_SAVE);
287 connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved()));
290 am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
291 connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));
294 am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
295 connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs()));
298 ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
300 // Window menu separators
301 QAction *tmpaction = new QAction(this);
302 tmpaction->setSeparator(true);
303 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Split", editManagerContext);
304 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
306 tmpaction = new QAction(this);
307 tmpaction->setSeparator(true);
308 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Navigate", editManagerContext);
309 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
312 cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext);
313 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W")));
314 cmd->setAttribute(Core::Command::CA_UpdateText);
315 cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text());
316 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
317 connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor()));
320 // workaround for QTCREATORBUG-72
321 QShortcut *sc = new QShortcut(parent);
322 cmd = am->registerShortcut(sc, Constants::CLOSE_ALTERNATIVE, editManagerContext);
323 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F4")));
324 cmd->setDefaultText(EditorManager::tr("Close"));
325 connect(sc, SIGNAL(activated()), this, SLOT(closeEditor()));
329 cmd = am->registerAction(m_d->m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext);
330 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W")));
331 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
332 connect(m_d->m_closeAllEditorsAction, SIGNAL(triggered()), this, SLOT(closeAllEditors()));
334 // Close All Others Action
335 cmd = am->registerAction(m_d->m_closeOtherEditorsAction, Constants::CLOSEOTHERS, editManagerContext);
336 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
337 cmd->setAttribute(Core::Command::CA_UpdateText);
338 connect(m_d->m_closeOtherEditorsAction, SIGNAL(triggered()), this, SLOT(closeOtherEditors()));
340 // Goto Previous In History Action
341 cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext);
343 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Tab")));
345 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab")));
347 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
348 connect(m_d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory()));
350 // Goto Next In History Action
351 cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editDesignContext);
353 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab")));
355 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab")));
357 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
358 connect(m_d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory()));
360 // Go back in navigation history
361 cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editDesignContext);
363 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Left")));
365 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left")));
367 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
368 connect(m_d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory()));
370 // Go forward in navigation history
371 cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editDesignContext);
373 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Right")));
375 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right")));
377 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
378 connect(m_d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory()));
381 QString prefix = tr("Meta+E");
383 QString prefix = tr("Ctrl+E");
386 m_d->m_splitAction = new QAction(tr("Split"), this);
387 cmd = am->registerAction(m_d->m_splitAction, Constants::SPLIT, editManagerContext);
388 cmd->setDefaultKeySequence(QKeySequence(tr("%1,2").arg(prefix)));
389 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
390 connect(m_d->m_splitAction, SIGNAL(triggered()), this, SLOT(split()));
392 m_d->m_splitSideBySideAction = new QAction(tr("Split Side by Side"), this);
393 cmd = am->registerAction(m_d->m_splitSideBySideAction, Constants::SPLIT_SIDE_BY_SIDE, editManagerContext);
394 cmd->setDefaultKeySequence(QKeySequence(tr("%1,3").arg(prefix)));
395 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
396 connect(m_d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide()));
398 m_d->m_removeCurrentSplitAction = new QAction(tr("Remove Current Split"), this);
399 cmd = am->registerAction(m_d->m_removeCurrentSplitAction, Constants::REMOVE_CURRENT_SPLIT, editManagerContext);
400 cmd->setDefaultKeySequence(QKeySequence(tr("%1,0").arg(prefix)));
401 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
402 connect(m_d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
404 m_d->m_removeAllSplitsAction = new QAction(tr("Remove All Splits"), this);
405 cmd = am->registerAction(m_d->m_removeAllSplitsAction, Constants::REMOVE_ALL_SPLITS, editManagerContext);
406 cmd->setDefaultKeySequence(QKeySequence(tr("%1,1").arg(prefix)));
407 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
408 connect(m_d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
410 m_d->m_gotoOtherSplitAction = new QAction(tr("Go to Next Split"), this);
411 cmd = am->registerAction(m_d->m_gotoOtherSplitAction, Constants::GOTO_OTHER_SPLIT, editManagerContext);
412 cmd->setDefaultKeySequence(QKeySequence(tr("%1,o").arg(prefix)));
413 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
414 connect(m_d->m_gotoOtherSplitAction, SIGNAL(triggered()), this, SLOT(gotoOtherSplit()));
416 ActionContainer *medit = am->actionContainer(Constants::M_EDIT);
417 ActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED);
418 medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED);
419 advancedMenu->menu()->setTitle(tr("&Advanced"));
420 advancedMenu->appendGroup(Constants::G_EDIT_FORMAT);
421 advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING);
422 advancedMenu->appendGroup(Constants::G_EDIT_BLOCKS);
423 advancedMenu->appendGroup(Constants::G_EDIT_FONT);
424 advancedMenu->appendGroup(Constants::G_EDIT_EDITOR);
426 // Advanced menu separators
427 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Collapsing"), editManagerContext);
428 advancedMenu->addAction(cmd, Constants::G_EDIT_COLLAPSING);
429 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Blocks"), editManagerContext);
430 advancedMenu->addAction(cmd, Constants::G_EDIT_BLOCKS);
431 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Font"), editManagerContext);
432 advancedMenu->addAction(cmd, Constants::G_EDIT_FONT);
433 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Editor"), editManagerContext);
434 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
436 cmd = am->registerAction(m_d->m_openInExternalEditorAction, Constants::OPEN_IN_EXTERNAL_EDITOR, editManagerContext);
437 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+V,Alt+I")));
438 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
439 connect(m_d->m_openInExternalEditorAction, SIGNAL(triggered()), this, SLOT(openInExternalEditor()));
441 // Connect to VariableManager for CURRENT_DOCUMENT variable setting
442 VariableManager::initEditorManagerConnections();
444 m_d->m_splitter = new SplitterOrView(m_d->m_editorModel);
445 m_d->m_view = m_d->m_splitter->view();
448 QHBoxLayout *layout = new QHBoxLayout(this);
449 layout->setMargin(0);
450 layout->setSpacing(0);
451 layout->addWidget(m_d->m_splitter);
455 m_d->m_windowPopup = new OpenEditorsWindow(this);
458 EditorManager::~EditorManager()
462 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
463 if (m_d->m_coreListener) {
464 pm->removeObject(m_d->m_coreListener);
465 delete m_d->m_coreListener;
467 pm->removeObject(m_d->m_openEditorsFactory);
468 delete m_d->m_openEditorsFactory;
473 void EditorManager::init()
475 m_d->m_coreListener = new EditorClosingCoreListener(this);
476 pluginManager()->addObject(m_d->m_coreListener);
478 m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
479 pluginManager()->addObject(m_d->m_openEditorsFactory);
483 EditorToolBar *EditorManager::createToolBar(QWidget *parent)
485 return new EditorToolBar(parent);
488 QString EditorManager::defaultExternalEditor() const
491 return ConsoleProcess::defaultTerminalEmulator() + QLatin1String(
495 " -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\"");
497 return QLatin1String("notepad %f");
501 void EditorManager::removeEditor(IEditor *editor)
503 bool isDuplicate = m_d->m_editorModel->isDuplicate(editor);
504 m_d->m_editorModel->removeEditor(editor);
506 m_d->m_core->fileManager()->removeFile(editor->file());
508 m_d->m_core->removeContextObject(editor);
511 void EditorManager::handleContextChange(Core::IContext *context)
513 if (debugEditorManager)
514 qDebug() << Q_FUNC_INFO;
515 IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0;
517 setCurrentEditor(editor);
523 void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
528 if (m_d->m_currentEditor == editor)
530 if (m_d->m_currentEditor && !ignoreNavigationHistory)
531 addCurrentPositionToNavigationHistory();
533 m_d->m_currentEditor = editor;
535 if (SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor))
536 splitterOrView->view()->setCurrentEditor(editor);
537 m_d->m_view->updateEditorHistory(editor); // the global view should have a complete history
541 emit currentEditorChanged(editor);
545 void EditorManager::setCurrentView(Core::Internal::SplitterOrView *view)
547 if (view == m_d->m_currentView)
550 SplitterOrView *old = m_d->m_currentView;
551 m_d->m_currentView = view;
558 if (view && !view->editor())
562 Core::Internal::SplitterOrView *EditorManager::currentSplitterOrView() const
564 SplitterOrView *view = m_d->m_currentView;
566 view = m_d->m_currentEditor?
567 m_d->m_splitter->findView(m_d->m_currentEditor):
568 m_d->m_splitter->findFirstView();
570 return m_d->m_splitter;
574 Core::Internal::EditorView *EditorManager::currentEditorView() const
576 return currentSplitterOrView()->view();
579 QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
581 QList<IEditor *> found;
582 QString fixedname = FileManager::fixFileName(filename, FileManager::KeepLinks);
583 foreach (IEditor *editor, openedEditors()) {
584 if (fixedname == FileManager::fixFileName(editor->file()->fileName(), FileManager::KeepLinks))
590 QList<IEditor *> EditorManager::editorsForFile(IFile *file) const
592 QList<IEditor *> found;
593 foreach (IEditor *editor, openedEditors()) {
594 if (editor->file() == file)
600 IEditor *EditorManager::currentEditor() const
602 return m_d->m_currentEditor;
605 void EditorManager::emptyView(Core::Internal::EditorView *view)
610 QList<IEditor *> editors = view->editors();
611 foreach (IEditor *editor, editors) {
612 if (!m_d->m_editorModel->isDuplicate(editor)) {
613 editors.removeAll(editor);
614 view->removeEditor(editor);
617 emit editorAboutToClose(editor);
618 removeEditor(editor);
619 view->removeEditor(editor);
621 emit editorsClosed(editors);
622 foreach (IEditor *editor, editors) {
627 void EditorManager::closeView(Core::Internal::EditorView *view)
632 if (view == m_d->m_view) {
633 if (IEditor *e = view->currentEditor())
634 closeEditors(QList<IEditor *>() << e);
638 if (IEditor *e = view->currentEditor()) {
640 when we are closing a view with an original editor which has
641 duplicates, then make one of the duplicates the original.
642 Otherwise the original would be kept around and the user might
643 experience jumping to a missleading position within the file when
644 visiting the file again. With the code below, the position within
645 the file will be the position of the first duplicate which is still
648 if (!m_d->m_editorModel->isDuplicate(e)) {
649 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(e);
650 if (!duplicates.isEmpty()) {
651 m_d->m_editorModel->makeOriginal(duplicates.first());
658 SplitterOrView *splitterOrView = m_d->m_splitter->findView(view);
659 Q_ASSERT(splitterOrView);
660 Q_ASSERT(splitterOrView->view() == view);
661 SplitterOrView *splitter = m_d->m_splitter->findSplitter(splitterOrView);
662 Q_ASSERT(splitterOrView->hasEditors() == false);
663 splitterOrView->hide();
664 delete splitterOrView;
668 SplitterOrView *newCurrent = splitter->findFirstView();
670 if (IEditor *e = newCurrent->editor()) {
671 activateEditor(newCurrent->view(), e);
673 setCurrentView(newCurrent);
679 EditorManager::editorsForFiles(QList<IFile*> files) const
681 const QList<IEditor *> editors = openedEditors();
682 QSet<IEditor *> found;
683 foreach (IFile *file, files) {
684 foreach (IEditor *editor, editors) {
685 if (editor->file() == file && !found.contains(editor)) {
690 return found.toList();
693 QList<IFile *> EditorManager::filesForEditors(QList<IEditor *> editors) const
695 QSet<IEditor *> handledEditors;
696 QList<IFile *> files;
697 foreach (IEditor *editor, editors) {
698 if (!handledEditors.contains(editor)) {
699 files << editor->file();
700 handledEditors.insert(editor);
706 bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
708 m_d->m_editorModel->removeAllRestoredEditors();
709 if (closeEditors(openedEditors(), askAboutModifiedEditors)) {
710 // m_d->clearNavigationHistory();
716 void EditorManager::closeOtherEditors(IEditor *editor)
718 m_d->m_editorModel->removeAllRestoredEditors();
719 QList<IEditor*> editors = openedEditors();
720 editors.removeAll(editor);
721 closeEditors(editors, true);
724 void EditorManager::closeOtherEditors()
726 IEditor *current = currentEditor();
727 QTC_ASSERT(current, return);
728 closeOtherEditors(current);
731 // SLOT connected to action
732 void EditorManager::closeEditor()
734 if (!m_d->m_currentEditor)
736 addCurrentPositionToNavigationHistory();
737 closeEditor(m_d->m_currentEditor);
740 void EditorManager::closeEditor(Core::IEditor *editor)
744 closeEditors(QList<IEditor *>() << editor);
747 void EditorManager::closeEditor(const QModelIndex &index)
749 IEditor *editor = index.data(Qt::UserRole).value<Core::IEditor*>();
753 m_d->m_editorModel->removeEditor(index);
756 bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
758 if (editorsToClose.isEmpty())
761 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
763 bool closingFailed = false;
764 QList<IEditor*> acceptedEditors;
765 //ask all core listeners to check whether the editor can be closed
766 const QList<ICoreListener *> listeners =
767 pluginManager()->getObjects<ICoreListener>();
768 foreach (IEditor *editor, editorsToClose) {
769 bool editorAccepted = true;
770 if (m_d->m_editorModel->isDuplicate(editor))
771 editor = m_d->m_editorModel->originalForDuplicate(editor);
772 foreach (ICoreListener *listener, listeners) {
773 if (!listener->editorAboutToClose(editor)) {
774 editorAccepted = false;
775 closingFailed = true;
780 acceptedEditors.append(editor);
782 if (acceptedEditors.isEmpty())
784 //ask whether to save modified files
785 if (askAboutModifiedEditors) {
786 bool cancelled = false;
787 QList<IFile*> list = m_d->m_core->fileManager()->
788 saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled);
791 if (!list.isEmpty()) {
792 closingFailed = true;
793 QSet<IEditor*> skipSet = editorsForFiles(list).toSet();
794 acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList();
797 if (acceptedEditors.isEmpty())
801 foreach(IEditor *editor, acceptedEditors)
802 acceptedEditors += m_d->m_editorModel->duplicatesFor(editor);
804 QList<EditorView*> closedViews;
806 // remove the editors
807 foreach (IEditor *editor, acceptedEditors) {
808 emit editorAboutToClose(editor);
809 if (!editor->file()->fileName().isEmpty()
810 && !editor->isTemporary()) {
811 QByteArray state = editor->saveState();
812 if (!state.isEmpty())
813 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
816 removeEditor(editor);
817 if (SplitterOrView *view = m_d->m_splitter->findView(editor)) {
818 if (editor == view->view()->currentEditor())
819 closedViews += view->view();
820 view->view()->removeEditor(editor);
824 foreach (EditorView *view, closedViews) {
825 IEditor *newCurrent = view->currentEditor();
827 newCurrent = pickUnusedEditor();
829 activateEditor(view, newCurrent, NoActivate);
831 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
833 activateEditor(idx, view, NoActivate);
837 emit editorsClosed(acceptedEditors);
839 foreach (IEditor *editor, acceptedEditors) {
843 if (currentSplitterOrView) {
844 if (IEditor *editor = currentSplitterOrView->editor())
845 activateEditor(currentSplitterOrView->view(), editor);
848 if (!currentEditor()) {
849 emit currentEditorChanged(0);
854 return !closingFailed;
857 void EditorManager::closeDuplicate(Core::IEditor *editor)
860 IEditor *original = editor;
861 if (m_d->m_editorModel->isDuplicate(editor))
862 original= m_d->m_editorModel->originalForDuplicate(editor);
863 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(original);
865 if (duplicates.isEmpty()) {
870 if (original== editor)
871 m_d->m_editorModel->makeOriginal(duplicates.first());
873 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
875 emit editorAboutToClose(editor);
877 if(m_d->m_splitter->findView(editor)) {
878 EditorView *view = m_d->m_splitter->findView(editor)->view();
879 removeEditor(editor);
880 view->removeEditor(editor);
882 IEditor *newCurrent = view->currentEditor();
884 newCurrent = pickUnusedEditor();
886 activateEditor(view, newCurrent, NoActivate);
888 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
890 activateEditor(idx, view, NoActivate);
894 emit editorsClosed(QList<IEditor*>() << editor);
896 if (currentSplitterOrView) {
897 if (IEditor *currentEditor = currentSplitterOrView->editor())
898 activateEditor(currentSplitterOrView->view(), currentEditor);
902 Core::IEditor *EditorManager::pickUnusedEditor() const
904 foreach (IEditor *editor, openedEditors()) {
905 SplitterOrView *view = m_d->m_splitter->findView(editor);
906 if (!view || view->editor() != editor)
912 Core::IEditor *EditorManager::activateEditor(const QModelIndex &index, Internal::EditorView *view, OpenEditorFlags flags)
914 IEditor *editor = index.data(Qt::UserRole).value<IEditor*>();
916 return activateEditor(view, editor, flags);
919 QString fileName = index.data(Qt::UserRole + 1).toString();
920 QString id = index.data(Qt::UserRole + 2).toString();
921 return 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 Core::IEditor *EditorManager::activateEditor(Core::IEditor *editor, OpenEditorFlags flags)
957 return activateEditor(0, editor, flags);
960 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags)
963 view = currentEditorView();
968 if (!m_d->m_currentEditor)
969 setCurrentEditor(0, (flags & IgnoreNavigationHistory));
973 editor = placeEditor(view, editor);
975 if (!(flags & NoActivate)) {
976 setCurrentEditor(editor, (flags & IgnoreNavigationHistory));
977 if (flags & ModeSwitch) {
978 switchToPreferedMode();
981 editor->widget()->setFocus();
986 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IFile *file, OpenEditorFlags flags)
988 const QList<IEditor*> editors = editorsForFile(file);
989 if (editors.isEmpty())
992 return activateEditor(view, editors.first(), flags);
995 /* For something that has a 'QStringList mimeTypes' (IEditorFactory
996 * or IExternalEditor), find the one best matching the mimetype passed in.
997 * Recurse over the parent classes of the mimetype to find them. */
998 template <class EditorFactoryLike>
999 static void mimeTypeFactoryRecursion(const MimeDatabase *db,
1000 const MimeType &mimeType,
1001 const QList<EditorFactoryLike*> &allFactories,
1002 bool firstMatchOnly,
1003 QList<EditorFactoryLike*> *list)
1005 typedef typename QList<EditorFactoryLike*>::const_iterator EditorFactoryLikeListConstIterator;
1006 // Loop factories to find type
1007 const QString type = mimeType.type();
1008 const EditorFactoryLikeListConstIterator fcend = allFactories.constEnd();
1009 for (EditorFactoryLikeListConstIterator fit = allFactories.constBegin(); fit != fcend; ++fit) {
1010 // Exclude duplicates when recursing over xml or C++ -> C -> text.
1011 EditorFactoryLike *factory = *fit;
1012 if (!list->contains(factory) && factory->mimeTypes().contains(type)) {
1013 list->push_back(*fit);
1019 // Any parent mime type classes? -> recurse
1020 QStringList parentTypes = mimeType.subClassesOf();
1021 if (parentTypes.empty())
1023 const QStringList::const_iterator pcend = parentTypes .constEnd();
1024 for (QStringList::const_iterator pit = parentTypes .constBegin(); pit != pcend; ++pit) {
1025 if (const MimeType parent = db->findByType(*pit))
1026 mimeTypeFactoryRecursion(db, parent, allFactories, firstMatchOnly, list);
1030 EditorManager::EditorFactoryList
1031 EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const
1033 EditorFactoryList rc;
1034 const EditorFactoryList allFactories = pluginManager()->getObjects<IEditorFactory>();
1035 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allFactories, bestMatchOnly, &rc);
1036 if (debugEditorManager)
1037 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1041 EditorManager::ExternalEditorList
1042 EditorManager::externalEditors(const MimeType &mimeType, bool bestMatchOnly) const
1044 ExternalEditorList rc;
1045 const ExternalEditorList allEditors = pluginManager()->getObjects<IExternalEditor>();
1046 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allEditors, bestMatchOnly, &rc);
1047 if (debugEditorManager)
1048 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1052 /* For something that has a 'QString id' (IEditorFactory
1053 * or IExternalEditor), find the one matching a id. */
1054 template <class EditorFactoryLike>
1055 inline EditorFactoryLike *findById(ExtensionSystem::PluginManager *pm,
1058 const QList<EditorFactoryLike *> factories = pm->template getObjects<EditorFactoryLike>();
1059 foreach(EditorFactoryLike *efl, factories)
1060 if (id == efl->id())
1065 IEditor *EditorManager::createEditor(const QString &editorId,
1066 const QString &fileName)
1068 if (debugEditorManager)
1069 qDebug() << Q_FUNC_INFO << editorId << fileName;
1071 EditorFactoryList factories;
1072 if (editorId.isEmpty()) {
1073 const QFileInfo fileInfo(fileName);
1074 // Find by mime type
1075 MimeType mimeType = m_d->m_core->mimeDatabase()->findByFile(fileInfo);
1077 qWarning("%s unable to determine mime type of %s/%s. Falling back to text/plain",
1078 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1079 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("text/plain"));
1081 // open text files > 48 MB in binary editor
1082 if (fileInfo.size() > maxTextFileSize() && mimeType.type().startsWith(QLatin1String("text")))
1083 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("application/octet-stream"));
1084 factories = editorFactories(mimeType, true);
1086 // Find by editor id
1087 if (IEditorFactory *factory = findById<IEditorFactory>(pluginManager(), editorId))
1088 factories.push_back(factory);
1090 if (factories.empty()) {
1091 qWarning("%s: unable to find an editor factory for the file '%s', editor Id '%s'.",
1092 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1096 IEditor *editor = factories.front()->createEditor(this);
1098 connect(editor, SIGNAL(changed()), this, SLOT(handleEditorStateChange()));
1100 emit editorCreated(editor, fileName);
1104 void EditorManager::addEditor(IEditor *editor, bool isDuplicate)
1108 m_d->m_core->addContextObject(editor);
1110 m_d->m_editorModel->addEditor(editor, isDuplicate);
1112 const bool isTemporary = editor->isTemporary();
1113 const bool addWatcher = !isTemporary;
1114 m_d->m_core->fileManager()->addFile(editor->file(), addWatcher);
1116 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1118 emit editorOpened(editor);
1121 // Run the OpenWithDialog and return the editor id
1122 // selected by the user.
1123 QString EditorManager::getOpenWithEditorId(const QString &fileName,
1124 bool *isExternalEditor) const
1126 // Collect editors that can open the file
1127 const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName);
1130 QStringList allEditorIds;
1131 QStringList externalEditorIds;
1133 const EditorFactoryList editors = editorFactories(mt, false);
1134 const int size = editors.size();
1135 for (int i = 0; i < size; i++) {
1136 allEditorIds.push_back(editors.at(i)->id());
1139 const ExternalEditorList exEditors = externalEditors(mt, false);
1140 const int esize = exEditors.size();
1141 for (int i = 0; i < esize; i++) {
1142 externalEditorIds.push_back(exEditors.at(i)->id());
1143 allEditorIds.push_back(exEditors.at(i)->id());
1145 if (allEditorIds.empty())
1148 OpenWithDialog dialog(fileName, m_d->m_core->mainWindow());
1149 dialog.setEditors(allEditorIds);
1150 dialog.setCurrentEditor(0);
1151 if (dialog.exec() != QDialog::Accepted)
1153 const QString selectedId = dialog.editor();
1154 if (isExternalEditor)
1155 *isExternalEditor = externalEditorIds.contains(selectedId);
1159 IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorId,
1160 OpenEditorFlags flags, bool *newEditor)
1162 return openEditor(0, fileName, editorId, flags, newEditor);
1165 int extractLineNumber(QString *fileName)
1167 int i = fileName->length() - 1;
1168 for (; i >= 0; --i) {
1169 if (!fileName->at(i).isNumber())
1174 if (fileName->at(i) == ':' || fileName->at(i) == '+') {
1175 int result = fileName->mid(i+1).toInt();
1176 *fileName = fileName->left(i);
1182 IEditor *EditorManager::openEditor(Core::Internal::EditorView *view, const QString &fileName,
1183 const QString &editorId, OpenEditorFlags flags, bool *newEditor)
1185 if (debugEditorManager)
1186 qDebug() << Q_FUNC_INFO << fileName << editorId;
1188 QString fn = fileName;
1189 int lineNumber = -1;
1190 if (flags && EditorManager::CanContainLineNumber)
1191 lineNumber = extractLineNumber(&fn);
1199 const QList<IEditor *> editors = editorsForFileName(fn);
1200 if (!editors.isEmpty()) {
1201 IEditor *editor = editors.first();
1202 if (flags && EditorManager::CanContainLineNumber)
1203 editor->gotoLine(lineNumber, -1);
1204 return activateEditor(view, editor, flags);
1207 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1208 IEditor *editor = createEditor(editorId, fn);
1209 // If we could not open the file in the requested editor, fall
1210 // back to the default editor:
1212 editor = createEditor(QString(), fn);
1213 if (!editor || !editor->open(fn)) {
1214 QApplication::restoreOverrideCursor();
1215 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), tr("Cannot open file %1!").arg(QDir::toNativeSeparators(fn)));
1225 IEditor *result = activateEditor(view, editor, flags);
1226 if (editor == result)
1227 restoreEditorState(editor);
1229 if (flags && EditorManager::CanContainLineNumber)
1230 editor->gotoLine(lineNumber, -1);
1232 QApplication::restoreOverrideCursor();
1236 bool EditorManager::openExternalEditor(const QString &fileName, const QString &editorId)
1238 IExternalEditor *ee = findById<IExternalEditor>(pluginManager(), editorId);
1241 QString errorMessage;
1242 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1243 const bool ok = ee->startEditor(fileName, &errorMessage);
1244 QApplication::restoreOverrideCursor();
1246 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), errorMessage);
1250 QStringList EditorManager::getOpenFileNames() const
1252 QString selectedFilter;
1253 const QString &fileFilters = m_d->m_core->mimeDatabase()->allFiltersString(&selectedFilter);
1254 return ICore::instance()->fileManager()->getOpenFileNames(fileFilters,
1255 QString(), &selectedFilter);
1259 /// Empty mode == figure out the correct mode from the editor
1260 /// forcePrefered = true, switch to the mode even if the editor is visible in another mode
1261 /// forcePrefered = false, only switch if it is not visible
1262 void EditorManager::switchToPreferedMode()
1264 QString preferedMode;
1265 // Figure out preferred mode for editor
1266 if (m_d->m_currentEditor)
1267 preferedMode = m_d->m_currentEditor->preferredModeType();
1269 if (preferedMode.isEmpty())
1270 preferedMode = Constants::MODE_EDIT_TYPE;
1272 m_d->m_core->modeManager()->activateModeType(preferedMode);
1275 IEditor *EditorManager::openEditorWithContents(const QString &editorId,
1276 QString *titlePattern,
1277 const QString &contents)
1279 if (debugEditorManager)
1280 qDebug() << Q_FUNC_INFO << editorId << titlePattern << contents;
1282 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1286 const QChar dollar = QLatin1Char('$');
1288 QString base = *titlePattern;
1290 base = QLatin1String("unnamed$");
1291 if (base.contains(dollar)) {
1293 QSet<QString> docnames;
1294 foreach (IEditor *editor, openedEditors()) {
1295 QString name = editor->file()->fileName();
1296 if (name.isEmpty()) {
1297 name = editor->displayName();
1299 name = QFileInfo(name).completeBaseName();
1306 title.replace(QString(dollar), QString::number(i++));
1307 } while (docnames.contains(title));
1309 title = *titlePattern;
1311 *titlePattern = title;
1314 IEditor *edt = createEditor(editorId, title);
1316 QApplication::restoreOverrideCursor();
1320 if (!edt->createNew(contents)) {
1321 QApplication::restoreOverrideCursor();
1327 if (title.isEmpty())
1328 title = edt->displayName();
1330 edt->setDisplayName(title);
1332 QApplication::restoreOverrideCursor();
1336 bool EditorManager::hasEditor(const QString &fileName) const
1338 return !editorsForFileName(fileName).isEmpty();
1341 void EditorManager::restoreEditorState(IEditor *editor)
1343 QTC_ASSERT(editor, return);
1344 QString fileName = editor->file()->fileName();
1345 editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray());
1348 bool EditorManager::saveEditor(IEditor *editor)
1350 return saveFile(editor->file());
1353 bool EditorManager::saveFile(IFile *fileParam)
1355 IFile *file = fileParam;
1356 if (!file && currentEditor())
1357 file = currentEditor()->file();
1361 file->checkPermissions();
1363 const QString &fileName = file->fileName();
1365 if (fileName.isEmpty())
1366 return saveFileAs(file);
1368 bool success = false;
1370 // try saving, no matter what isReadOnly tells us
1371 m_d->m_core->fileManager()->blockFileChange(file);
1372 success = file->save(fileName);
1373 m_d->m_core->fileManager()->unblockFileChange(file);
1376 MakeWritableResult answer =
1377 makeFileWritable(file);
1378 if (answer == Failed)
1380 if (answer == SavedAs)
1383 file->checkPermissions();
1385 m_d->m_core->fileManager()->blockFileChange(file);
1386 success = file->save(fileName);
1387 m_d->m_core->fileManager()->unblockFileChange(file);
1391 addFileToRecentFiles(file);
1398 EditorManager::makeFileWritable(IFile *file)
1402 QString directory = QFileInfo(file->fileName()).absolutePath();
1403 IVersionControl *versionControl = m_d->m_core->vcsManager()->findVersionControlForDirectory(directory);
1404 const QString &fileName = file->fileName();
1406 switch (FileManager::promptReadOnlyFile(fileName, versionControl, m_d->m_core->mainWindow(), file->isSaveAsAllowed())) {
1407 case FileManager::RO_OpenVCS:
1408 if (!versionControl->vcsOpen(fileName)) {
1409 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for editing with SCC."));
1412 file->checkPermissions();
1413 return OpenedWithVersionControl;
1414 case FileManager::RO_MakeWriteable: {
1415 const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
1417 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
1421 file->checkPermissions();
1422 return MadeWritable;
1423 case FileManager::RO_SaveAs :
1424 return saveFileAs(file) ? SavedAs : Failed;
1425 case FileManager::RO_Cancel:
1431 bool EditorManager::saveFileAs(IFile *fileParam)
1433 IFile *file = fileParam;
1434 if (!file && currentEditor())
1435 file = currentEditor()->file();
1439 const QString &filter = m_d->m_core->mimeDatabase()->allFiltersString();
1440 QString selectedFilter =
1441 m_d->m_core->mimeDatabase()->findByFile(QFileInfo(file->fileName())).filterString();
1442 const QString &absoluteFilePath =
1443 m_d->m_core->fileManager()->getSaveAsFileName(file, filter, &selectedFilter);
1445 if (absoluteFilePath.isEmpty())
1448 if (absoluteFilePath != file->fileName()) {
1449 // close existing editors for the new file name
1450 const QList<IEditor *> existList = editorsForFileName(absoluteFilePath);
1451 if (!existList.isEmpty()) {
1452 closeEditors(existList, false);
1456 m_d->m_core->fileManager()->blockFileChange(file);
1457 const bool success = file->save(absoluteFilePath);
1458 m_d->m_core->fileManager()->unblockFileChange(file);
1459 file->checkPermissions();
1461 // @todo: There is an issue to be treated here. The new file might be of a different mime
1462 // type than the original and thus require a different editor. An alternative strategy
1463 // would be to close the current editor and open a new appropriate one, but this is not
1464 // a good way out either (also the undo stack would be lost). Perhaps the best is to
1465 // re-think part of the editors design.
1468 addFileToRecentFiles(file);
1474 /* Adds the file name to the recent files if there is at least one non-temporary editor for it */
1475 void EditorManager::addFileToRecentFiles(IFile *file)
1477 bool isTemporary = true;
1478 QList<IEditor *> editors = editorsForFile(file);
1479 foreach (IEditor *editor, editors) {
1480 if (!editor->isTemporary()) {
1481 isTemporary = false;
1486 m_d->m_core->fileManager()->addToRecentFiles(file->fileName());
1489 void EditorManager::gotoNextDocHistory()
1491 OpenEditorsWindow *dialog = windowPopup();
1492 if (dialog->isVisible()) {
1493 dialog->selectNextEditor();
1495 EditorView *view = currentEditorView();
1496 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1497 dialog->selectNextEditor();
1498 showPopupOrSelectDocument();
1502 void EditorManager::gotoPreviousDocHistory()
1504 OpenEditorsWindow *dialog = windowPopup();
1505 if (dialog->isVisible()) {
1506 dialog->selectPreviousEditor();
1508 EditorView *view = currentEditorView();
1509 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1510 dialog->selectPreviousEditor();
1511 showPopupOrSelectDocument();
1515 void EditorManager::makeCurrentEditorWritable()
1517 if (IEditor* curEditor = currentEditor())
1518 makeFileWritable(curEditor->file());
1521 void EditorManager::updateWindowTitle()
1523 QString windowTitle = tr("Qt Creator");
1524 if (!m_d->m_titleAddition.isEmpty()) {
1525 windowTitle.prepend(m_d->m_titleAddition + " - ");
1527 IEditor *curEditor = currentEditor();
1529 QString editorName = curEditor->displayName();
1530 if (!editorName.isEmpty())
1531 windowTitle.prepend(editorName + " - ");
1532 QString filePath = QFileInfo(curEditor->file()->fileName()).absoluteFilePath();
1533 if (!filePath.isEmpty())
1534 m_d->m_core->mainWindow()->setWindowFilePath(filePath);
1536 m_d->m_core->mainWindow()->setWindowFilePath(QString());
1538 m_d->m_core->mainWindow()->setWindowTitle(windowTitle);
1541 void EditorManager::handleEditorStateChange()
1544 IEditor *currEditor = currentEditor();
1545 if (qobject_cast<IEditor *>(sender()) == currEditor) {
1546 updateWindowTitle();
1547 emit currentEditorStateChanged(currEditor);
1551 void EditorManager::updateActions()
1554 IEditor *curEditor = currentEditor();
1555 int openedCount = openedEditors().count() + m_d->m_editorModel->restoredEditors().count();
1559 if (!curEditor->file()->fileName().isEmpty()) {
1560 QFileInfo fi(curEditor->file()->fileName());
1561 fName = fi.fileName();
1563 fName = curEditor->displayName();
1567 window()->setWindowModified(curEditor->file()->isModified());
1569 if (curEditor->file()->isModified() && curEditor->file()->isReadOnly()) {
1570 // we are about to change a read-only file, warn user
1571 showEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"),
1572 tr("<b>Warning:</b> You are changing a read-only file."),
1573 tr("Make writable"), this, SLOT(makeCurrentEditorWritable()));
1575 hideEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"));
1578 } else { // curEditor
1579 window()->setWindowModified(false);
1583 m_d->m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified());
1584 m_d->m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed());
1585 m_d->m_revertToSavedAction->setEnabled(curEditor != 0
1586 && !curEditor->file()->fileName().isEmpty() && curEditor->file()->isModified());
1589 if (!fName.isEmpty())
1590 quotedName = '"' + fName + '"';
1592 m_d->m_saveAsAction->setText(tr("Save %1 &As...").arg(quotedName));
1593 m_d->m_saveAction->setText(tr("&Save %1").arg(quotedName));
1594 m_d->m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName));
1596 m_d->m_closeCurrentEditorAction->setEnabled(curEditor != 0);
1597 m_d->m_closeCurrentEditorAction->setText(tr("Close %1").arg(quotedName));
1598 m_d->m_closeAllEditorsAction->setEnabled(openedCount > 0);
1599 m_d->m_closeOtherEditorsAction->setEnabled(openedCount > 1);
1600 m_d->m_closeOtherEditorsAction->setText((openedCount > 1 ? tr("Close All Except %1").arg(quotedName) : tr("Close Others")));
1602 m_d->m_gotoNextDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1603 m_d->m_gotoPreviousDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1604 EditorView *view = currentEditorView();
1605 m_d->m_goBackAction->setEnabled(view ? view->canGoBack() : false);
1606 m_d->m_goForwardAction->setEnabled(view ? view->canGoForward() : false);
1608 bool hasSplitter = m_d->m_splitter->isSplitter();
1609 m_d->m_removeCurrentSplitAction->setEnabled(hasSplitter);
1610 m_d->m_removeAllSplitsAction->setEnabled(hasSplitter);
1611 m_d->m_gotoOtherSplitAction->setEnabled(hasSplitter);
1613 m_d->m_openInExternalEditorAction->setEnabled(curEditor != 0);
1616 bool EditorManager::hasSplitter() const
1618 return m_d->m_splitter->isSplitter();
1621 QList<IEditor*> EditorManager::openedEditors() const
1623 return m_d->m_editorModel->editors();
1626 OpenEditorsModel *EditorManager::openedEditorsModel() const
1628 return m_d->m_editorModel;
1631 void EditorManager::addCurrentPositionToNavigationHistory(IEditor *editor, const QByteArray &saveState)
1633 currentEditorView()->addCurrentPositionToNavigationHistory(editor, saveState);
1637 void EditorManager::cutForwardNavigationHistory()
1639 currentEditorView()->cutForwardNavigationHistory();
1643 void EditorManager::goBackInNavigationHistory()
1645 currentEditorView()->goBackInNavigationHistory();
1650 void EditorManager::goForwardInNavigationHistory()
1652 currentEditorView()->goForwardInNavigationHistory();
1656 OpenEditorsWindow *EditorManager::windowPopup() const
1658 return m_d->m_windowPopup;
1661 void EditorManager::showPopupOrSelectDocument() const
1663 if (QApplication::keyboardModifiers() == Qt::NoModifier) {
1664 windowPopup()->selectAndHide();
1666 // EditorManager is invisible when invoked from Design Mode.
1667 const QPoint p = isVisible() ?
1668 mapToGlobal(QPoint(0, 0)) :
1669 m_d->m_core->mainWindow()->mapToGlobal(QPoint(0, 0));
1670 windowPopup()->move((width()-m_d->m_windowPopup->width())/2 + p.x(),
1671 (height()-m_d->m_windowPopup->height())/2 + p.y());
1672 windowPopup()->setVisible(true);
1676 // Save state of all non-teporary editors.
1677 QByteArray EditorManager::saveState() const
1680 QDataStream stream(&bytes, QIODevice::WriteOnly);
1682 stream << QByteArray("EditorManagerV4");
1684 QList<IEditor *> editors = openedEditors();
1685 foreach (IEditor *editor, editors) {
1686 if (!editor->file()->fileName().isEmpty()
1687 && !editor->isTemporary()) {
1688 QByteArray state = editor->saveState();
1689 if (!state.isEmpty())
1690 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
1694 stream << m_d->m_editorStates;
1696 QList<OpenEditorsModel::Entry> entries = m_d->m_editorModel->entries();
1697 int entriesCount = 0;
1698 foreach (const OpenEditorsModel::Entry &entry, entries) {
1699 // The editor may be 0 if it was not loaded yet: In that case it is not temporary
1700 if (!entry.editor || !entry.editor->isTemporary())
1704 stream << entriesCount;
1706 foreach (const OpenEditorsModel::Entry &entry, entries) {
1707 if (!entry.editor || !entry.editor->isTemporary())
1708 stream << entry.fileName() << entry.displayName() << entry.id().toUtf8();
1711 stream << m_d->m_splitter->saveState();
1716 bool EditorManager::restoreState(const QByteArray &state)
1718 closeAllEditors(true);
1720 QDataStream stream(state);
1725 if (version != "EditorManagerV4")
1728 QMap<QString, QVariant> editorstates;
1730 QApplication::setOverrideCursor(Qt::WaitCursor);
1732 stream >> editorstates;
1734 QMapIterator<QString, QVariant> i(editorstates);
1735 while (i.hasNext()) {
1737 m_d->m_editorStates.insert(i.key(), i.value());
1740 int editorCount = 0;
1741 stream >> editorCount;
1742 while (--editorCount >= 0) {
1745 QString displayName;
1746 stream >> displayName;
1750 if (!fileName.isEmpty() && !displayName.isEmpty())
1751 m_d->m_editorModel->addRestoredEditor(fileName, displayName, QString::fromUtf8(id));
1754 QByteArray splitterstates;
1755 stream >> splitterstates;
1756 m_d->m_splitter->restoreState(splitterstates);
1758 // splitting and stuff results in focus trouble, that's why we set the focus again after restoration
1759 if (m_d->m_currentEditor) {
1760 m_d->m_currentEditor->widget()->setFocus();
1761 } else if (Core::Internal::SplitterOrView *view = currentSplitterOrView()) {
1762 if (IEditor *e = view->editor())
1763 e->widget()->setFocus();
1764 else if (view->view())
1765 view->view()->setFocus();
1768 QApplication::restoreOverrideCursor();
1773 static const char * const documentStatesKey = "EditorManager/DocumentStates";
1774 static const char * const externalEditorKey = "EditorManager/ExternalEditorCommand";
1775 static const char * const reloadBehaviorKey = "EditorManager/ReloadBehavior";
1776 static const char * const utf8BomBehaviorKey = "EditorManager/Utf8BomBehavior";
1778 void EditorManager::saveSettings()
1780 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1781 settings->setValue(QLatin1String(documentStatesKey), m_d->m_editorStates);
1782 settings->setValue(QLatin1String(externalEditorKey), m_d->m_externalEditor);
1783 settings->setValue(QLatin1String(reloadBehaviorKey), m_d->m_reloadSetting);
1784 settings->setValue(QLatin1String(utf8BomBehaviorKey), m_d->m_utf8BomSetting);
1787 void EditorManager::readSettings()
1789 // Backward compatibility to old locations for these settings
1790 QSettings *qs = m_d->m_core->settings();
1791 if (qs->contains(QLatin1String(documentStatesKey))) {
1792 m_d->m_editorStates = qs->value(QLatin1String(documentStatesKey))
1793 .value<QMap<QString, QVariant> >();
1794 qs->remove(QLatin1String(documentStatesKey));
1796 if (qs->contains(QLatin1String(externalEditorKey))) {
1797 m_d->m_externalEditor = qs->value(QLatin1String(externalEditorKey)).toString();
1798 qs->remove(QLatin1String(externalEditorKey));
1801 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1802 if (settings->contains(QLatin1String(documentStatesKey)))
1803 m_d->m_editorStates = settings->value(QLatin1String(documentStatesKey))
1804 .value<QMap<QString, QVariant> >();
1805 if (settings->contains(QLatin1String(externalEditorKey)))
1806 m_d->m_externalEditor = settings->value(QLatin1String(externalEditorKey)).toString();
1808 if (settings->contains(QLatin1String(reloadBehaviorKey)))
1809 m_d->m_reloadSetting = (IFile::ReloadSetting)settings->value(QLatin1String(reloadBehaviorKey)).toInt();
1811 if (settings->contains(QLatin1String(utf8BomBehaviorKey)))
1812 m_d->m_utf8BomSetting = (IFile::Utf8BomSetting)settings->value(QLatin1String(utf8BomBehaviorKey)).toInt();
1816 void EditorManager::revertToSaved()
1818 IEditor *currEditor = currentEditor();
1821 const QString fileName = currEditor->file()->fileName();
1822 if (fileName.isEmpty())
1824 if (currEditor->file()->isModified()) {
1825 QMessageBox msgBox(QMessageBox::Question, tr("Revert to Saved"),
1826 tr("You will lose your current changes if you proceed reverting %1.").arg(QDir::toNativeSeparators(fileName)),
1827 QMessageBox::Yes|QMessageBox::No, m_d->m_core->mainWindow());
1828 msgBox.button(QMessageBox::Yes)->setText(tr("Proceed"));
1829 msgBox.button(QMessageBox::No)->setText(tr("Cancel"));
1830 msgBox.setDefaultButton(QMessageBox::No);
1831 msgBox.setEscapeButton(QMessageBox::No);
1832 if (msgBox.exec() == QMessageBox::No)
1836 currEditor->file()->reload(IFile::FlagReload, IFile::TypeContents);
1839 void EditorManager::showEditorInfoBar(const QString &id,
1840 const QString &infoText,
1841 const QString &buttonText,
1842 QObject *object, const char *buttonPressMember,
1843 const char *cancelButtonPressMember)
1845 currentEditorView()->showEditorInfoBar(id, infoText, buttonText, object, buttonPressMember, cancelButtonPressMember);
1849 void EditorManager::hideEditorInfoBar(const QString &id)
1851 Core::Internal::EditorView *cev = currentEditorView();
1853 cev->hideEditorInfoBar(id);
1856 void EditorManager::showEditorStatusBar(const QString &id,
1857 const QString &infoText,
1858 const QString &buttonText,
1859 QObject *object, const char *member)
1862 currentEditorView()->showEditorStatusBar(id, infoText, buttonText, object, member);
1865 void EditorManager::hideEditorStatusBar(const QString &id)
1867 currentEditorView()->hideEditorStatusBar(id);
1870 QString EditorManager::externalEditorHelpText() const
1873 "<table border=1 cellspacing=0 cellpadding=3>"
1874 "<tr><th>Variable</th><th>Expands to</th></tr>"
1875 "<tr><td>%f</td><td>file name</td></tr>"
1876 "<tr><td>%l</td><td>current line number</td></tr>"
1877 "<tr><td>%c</td><td>current column number</td></tr>"
1878 "<tr><td>%x</td><td>editor's x position on screen</td></tr>"
1879 "<tr><td>%y</td><td>editor's y position on screen</td></tr>"
1880 "<tr><td>%w</td><td>editor's width in pixels</td></tr>"
1881 "<tr><td>%h</td><td>editor's height in pixels</td></tr>"
1882 "<tr><td>%W</td><td>editor's width in characters</td></tr>"
1883 "<tr><td>%H</td><td>editor's height in characters</td></tr>"
1884 "<tr><td>%%</td><td>%</td></tr>"
1889 void EditorManager::openInExternalEditor()
1891 QString command = m_d->m_externalEditor;
1892 if (command.isEmpty())
1893 command = defaultExternalEditor();
1895 if (command.isEmpty())
1898 IEditor *editor = currentEditor();
1901 if (editor->file()->isModified()) {
1902 bool cancelled = false;
1903 QList<IFile*> list = m_d->m_core->fileManager()->
1904 saveModifiedFiles(QList<IFile*>() << editor->file(), &cancelled);
1909 QRect rect = editor->widget()->rect();
1910 QFont font = editor->widget()->font();
1911 QFontMetrics fm(font);
1912 rect.moveTo(editor->widget()->mapToGlobal(QPoint(0,0)));
1914 QString pre = command;
1916 for (int i = 0; i < pre.size(); ++i) {
1917 QChar c = pre.at(i);
1918 if (c == QLatin1Char('%') && i < pre.size()-1) {
1921 if (c == QLatin1Char('f'))
1922 s = editor->file()->fileName();
1923 else if (c == QLatin1Char('l'))
1924 s = QString::number(editor->currentLine());
1925 else if (c == QLatin1Char('c'))
1926 s = QString::number(editor->currentColumn());
1927 else if (c == QLatin1Char('x'))
1928 s = QString::number(rect.x());
1929 else if (c == QLatin1Char('y'))
1930 s = QString::number(rect.y());
1931 else if (c == QLatin1Char('w'))
1932 s = QString::number(rect.width());
1933 else if (c == QLatin1Char('h'))
1934 s = QString::number(rect.height());
1935 else if (c == QLatin1Char('W'))
1936 s = QString::number(rect.width() / fm.width(QLatin1Char('x')));
1937 else if (c == QLatin1Char('H'))
1938 s = QString::number(rect.height() / fm.lineSpacing());
1939 else if (c == QLatin1Char('%'))
1942 s = QLatin1Char('%');
1952 QProcess::startDetached(cmd);
1955 void EditorManager::setExternalEditor(const QString &editor)
1957 if (editor.isEmpty() || editor == defaultExternalEditor())
1958 m_d->m_externalEditor = defaultExternalEditor();
1960 m_d->m_externalEditor = editor;
1963 QString EditorManager::externalEditor() const
1965 if (m_d->m_externalEditor.isEmpty())
1966 return defaultExternalEditor();
1967 return m_d->m_externalEditor;
1970 void EditorManager::setReloadSetting(IFile::ReloadSetting behavior)
1972 m_d->m_reloadSetting = behavior;
1975 IFile::ReloadSetting EditorManager::reloadSetting() const
1977 return m_d->m_reloadSetting;
1980 void EditorManager::setUtf8BomSetting(IFile::Utf8BomSetting behavior)
1982 m_d->m_utf8BomSetting = behavior;
1985 IFile::Utf8BomSetting EditorManager::utf8BomSetting() const
1987 return m_d->m_utf8BomSetting;
1990 QTextCodec *EditorManager::defaultTextEncoding() const
1992 QSettings *settings = Core::ICore::instance()->settings();
1993 if (QTextCodec *candidate = QTextCodec::codecForName(
1994 settings->value(QLatin1String(Constants::SETTINGS_DEFAULTTEXTENCODING)).toByteArray()))
1996 return QTextCodec::codecForLocale();
1999 Core::IEditor *EditorManager::duplicateEditor(Core::IEditor *editor)
2001 if (!editor->duplicateSupported())
2004 IEditor *duplicate = editor->duplicate(0);
2005 duplicate->restoreState(editor->saveState());
2006 connect(duplicate, SIGNAL(changed()), this, SLOT(handleEditorStateChange()));
2007 emit editorCreated(duplicate, duplicate->file()->fileName());
2008 addEditor(duplicate, true);
2012 void EditorManager::split(Qt::Orientation orientation)
2014 SplitterOrView *view = m_d->m_currentView;
2016 view = m_d->m_currentEditor ? m_d->m_splitter->findView(m_d->m_currentEditor)
2017 : m_d->m_splitter->findFirstView();
2018 if (view && !view->splitter()) {
2019 view->split(orientation);
2024 void EditorManager::split()
2026 split(Qt::Vertical);
2029 void EditorManager::splitSideBySide()
2031 split(Qt::Horizontal);
2034 void EditorManager::removeCurrentSplit()
2036 SplitterOrView *viewToClose = m_d->m_currentView;
2037 if (!viewToClose && m_d->m_currentEditor)
2038 viewToClose = m_d->m_splitter->findView(m_d->m_currentEditor);
2040 if (!viewToClose || viewToClose->isSplitter() || viewToClose == m_d->m_splitter)
2043 closeView(viewToClose->view());
2047 void EditorManager::removeAllSplits()
2049 if (!m_d->m_splitter->isSplitter())
2051 IEditor *editor = m_d->m_currentEditor;
2052 // trigger update below
2053 m_d->m_currentEditor = 0;
2054 if (editor && m_d->m_editorModel->isDuplicate(editor))
2055 m_d->m_editorModel->makeOriginal(editor);
2056 m_d->m_splitter->unsplitAll();
2058 editor = pickUnusedEditor();
2059 activateEditor(editor);
2062 void EditorManager::gotoOtherSplit()
2064 if (m_d->m_splitter->isSplitter()) {
2065 SplitterOrView *currentView = m_d->m_currentView;
2066 if (!currentView && m_d->m_currentEditor)
2067 currentView = m_d->m_splitter->findView(m_d->m_currentEditor);
2069 currentView = m_d->m_splitter->findFirstView();
2070 SplitterOrView *view = m_d->m_splitter->findNextView(currentView);
2072 view = m_d->m_splitter->findFirstView();
2074 if (IEditor *editor = view->editor()) {
2075 setCurrentEditor(editor, true);
2076 editor->widget()->setFocus();
2078 setCurrentView(view);
2084 qint64 EditorManager::maxTextFileSize()
2086 return (qint64(3) << 24);
2089 void EditorManager::setWindowTitleAddition(const QString &addition)
2091 m_d->m_titleAddition = addition;
2092 updateWindowTitle();
2095 QString EditorManager::windowTitleAddition() const
2097 return m_d->m_titleAddition;