1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "editormanager.h"
31 #include "editorview.h"
32 #include "openeditorswindow.h"
33 #include "openeditorsview.h"
34 #include "openeditorsmodel.h"
35 #include "openwithdialog.h"
36 #include "filemanager.h"
39 #include "iversioncontrol.h"
40 #include "mimedatabase.h"
41 #include "tabpositionindicator.h"
42 #include "vcsmanager.h"
44 #include <coreplugin/editortoolbar.h>
45 #include <coreplugin/coreconstants.h>
46 #include <coreplugin/modemanager.h>
47 #include <coreplugin/actionmanager/actionmanager.h>
48 #include <coreplugin/actionmanager/actioncontainer.h>
49 #include <coreplugin/actionmanager/command.h>
50 #include <coreplugin/editormanager/ieditorfactory.h>
51 #include <coreplugin/editormanager/iexternaleditor.h>
52 #include <coreplugin/icorelistener.h>
53 #include <coreplugin/imode.h>
54 #include <coreplugin/settingsdatabase.h>
55 #include <coreplugin/variablemanager.h>
56 #include <coreplugin/uniqueidmanager.h>
58 #include <extensionsystem/pluginmanager.h>
60 #include <utils/consoleprocess.h>
61 #include <utils/qtcassert.h>
63 #include <QtCore/QDebug>
64 #include <QtCore/QFileInfo>
65 #include <QtCore/QMap>
66 #include <QtCore/QProcess>
67 #include <QtCore/QSet>
68 #include <QtCore/QSettings>
69 #include <QtCore/QTextCodec>
71 #include <QtGui/QAction>
72 #include <QtGui/QShortcut>
73 #include <QtGui/QApplication>
74 #include <QtGui/QFileDialog>
75 #include <QtGui/QLayout>
76 #include <QtGui/QMainWindow>
77 #include <QtGui/QMenu>
78 #include <QtGui/QMessageBox>
79 #include <QtGui/QPushButton>
80 #include <QtGui/QSplitter>
81 #include <QtGui/QStackedLayout>
85 Q_DECLARE_METATYPE(Core::IEditor*)
87 enum { debugEditorManager=0 };
89 static inline ExtensionSystem::PluginManager *pluginManager()
91 return ExtensionSystem::PluginManager::instance();
94 //===================EditorClosingCoreListener======================
99 class EditorClosingCoreListener : public ICoreListener
102 EditorClosingCoreListener(EditorManager *em);
103 bool editorAboutToClose(IEditor *editor);
104 bool coreAboutToClose();
110 EditorClosingCoreListener::EditorClosingCoreListener(EditorManager *em)
115 bool EditorClosingCoreListener::editorAboutToClose(IEditor *)
120 bool EditorClosingCoreListener::coreAboutToClose()
122 // Do not ask for files to save.
123 // MainWindow::closeEvent has already done that.
124 return m_em->closeAllEditors(false);
127 } // namespace Internal
130 using namespace Core;
131 using namespace Core::Internal;
132 using namespace Utils;
134 //===================EditorManager=====================
136 EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0;
138 EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent)
139 : QWidget(parent), m_mode(mode)
141 setLayout(new QVBoxLayout);
142 layout()->setMargin(0);
143 connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)),
144 this, SLOT(currentModeChanged(Core::IMode *)));
146 currentModeChanged(Core::ModeManager::instance()->currentMode());
149 EditorManagerPlaceHolder::~EditorManagerPlaceHolder()
151 if (m_current == this) {
152 EditorManager::instance()->setParent(0);
153 EditorManager::instance()->hide();
157 void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode)
159 if (m_current == this) {
161 EditorManager::instance()->setParent(0);
162 EditorManager::instance()->hide();
164 if (m_mode == mode) {
166 layout()->addWidget(EditorManager::instance());
167 EditorManager::instance()->show();
171 EditorManagerPlaceHolder* EditorManagerPlaceHolder::current()
176 // ---------------- EditorManager
181 struct EditorManagerPrivate {
182 explicit EditorManagerPrivate(ICore *core, QWidget *parent);
183 ~EditorManagerPrivate();
184 Internal::EditorView *m_view;
185 Internal::SplitterOrView *m_splitter;
186 QPointer<IEditor> m_currentEditor;
187 QPointer<SplitterOrView> m_currentView;
193 QAction *m_revertToSavedAction;
194 QAction *m_saveAction;
195 QAction *m_saveAsAction;
196 QAction *m_closeCurrentEditorAction;
197 QAction *m_closeAllEditorsAction;
198 QAction *m_closeOtherEditorsAction;
199 QAction *m_gotoNextDocHistoryAction;
200 QAction *m_gotoPreviousDocHistoryAction;
201 QAction *m_goBackAction;
202 QAction *m_goForwardAction;
203 QAction *m_openInExternalEditorAction;
204 QAction *m_splitAction;
205 QAction *m_splitSideBySideAction;
206 QAction *m_removeCurrentSplitAction;
207 QAction *m_removeAllSplitsAction;
208 QAction *m_gotoOtherSplitAction;
210 Internal::OpenEditorsWindow *m_windowPopup;
211 Internal::EditorClosingCoreListener *m_coreListener;
213 QMap<QString, QVariant> m_editorStates;
214 Internal::OpenEditorsViewFactory *m_openEditorsFactory;
216 OpenEditorsModel *m_editorModel;
217 QString m_externalEditor;
219 IFile::ReloadSetting m_reloadSetting;
223 EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) :
227 m_revertToSavedAction(new QAction(EditorManager::tr("Revert to Saved"), parent)),
228 m_saveAction(new QAction(parent)),
229 m_saveAsAction(new QAction(parent)),
230 m_closeCurrentEditorAction(new QAction(EditorManager::tr("Close"), parent)),
231 m_closeAllEditorsAction(new QAction(EditorManager::tr("Close All"), parent)),
232 m_closeOtherEditorsAction(new QAction(EditorManager::tr("Close Others"), parent)),
233 m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Open Document in History"), parent)),
234 m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Open Document in History"), parent)),
235 m_goBackAction(new QAction(QIcon(QLatin1String(Constants::ICON_PREV)), EditorManager::tr("Go Back"), parent)),
236 m_goForwardAction(new QAction(QIcon(QLatin1String(Constants::ICON_NEXT)), EditorManager::tr("Go Forward"), parent)),
237 m_openInExternalEditorAction(new QAction(EditorManager::tr("Open in External Editor"), parent)),
240 m_reloadSetting(IFile::AlwaysAsk)
242 m_editorModel = new OpenEditorsModel(parent);
245 EditorManagerPrivate::~EditorManagerPrivate()
247 // clearNavigationHistory();
250 EditorManager *EditorManager::m_instance = 0;
252 static Command *createSeparator(ActionManager *am, QObject *parent,
254 const Context &context)
256 QAction *tmpaction = new QAction(parent);
257 tmpaction->setSeparator(true);
258 Command *cmd = am->registerAction(tmpaction, name, context);
262 EditorManager::EditorManager(ICore *core, QWidget *parent) :
264 m_d(new EditorManagerPrivate(core, parent))
268 connect(m_d->m_core, SIGNAL(contextAboutToChange(Core::IContext *)),
269 this, SLOT(handleContextChange(Core::IContext *)));
271 const Context editManagerContext(Constants::C_EDITORMANAGER);
272 // combined context for edit & design modes
273 const Context editDesignContext(Constants::C_EDITORMANAGER, Constants::C_DESIGN_MODE);
275 ActionManager *am = m_d->m_core->actionManager();
276 ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
279 m_d->m_revertToSavedAction->setIcon(QIcon::fromTheme(QLatin1String("document-revert")));
280 Command *cmd = am->registerAction(m_d->m_revertToSavedAction,
281 Constants::REVERTTOSAVED, editManagerContext);
282 cmd->setAttribute(Command::CA_UpdateText);
283 cmd->setDefaultText(tr("Revert File to Saved"));
284 mfile->addAction(cmd, Constants::G_FILE_SAVE);
285 connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved()));
288 am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
289 connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));
292 am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
293 connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs()));
296 ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
298 // Window menu separators
299 QAction *tmpaction = new QAction(this);
300 tmpaction->setSeparator(true);
301 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Split", editManagerContext);
302 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
304 tmpaction = new QAction(this);
305 tmpaction->setSeparator(true);
306 cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Navigate", editManagerContext);
307 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
310 cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext);
311 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W")));
312 cmd->setAttribute(Core::Command::CA_UpdateText);
313 cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text());
314 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
315 connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor()));
318 // workaround for QTCREATORBUG-72
319 QShortcut *sc = new QShortcut(parent);
320 cmd = am->registerShortcut(sc, Constants::CLOSE_ALTERNATIVE, editManagerContext);
321 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F4")));
322 cmd->setDefaultText(EditorManager::tr("Close"));
323 connect(sc, SIGNAL(activated()), this, SLOT(closeEditor()));
327 cmd = am->registerAction(m_d->m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext);
328 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W")));
329 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
330 connect(m_d->m_closeAllEditorsAction, SIGNAL(triggered()), this, SLOT(closeAllEditors()));
332 // Close All Others Action
333 cmd = am->registerAction(m_d->m_closeOtherEditorsAction, Constants::CLOSEOTHERS, editManagerContext);
334 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
335 cmd->setAttribute(Core::Command::CA_UpdateText);
336 connect(m_d->m_closeOtherEditorsAction, SIGNAL(triggered()), this, SLOT(closeOtherEditors()));
338 // Goto Previous In History Action
339 cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext);
341 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Tab")));
343 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab")));
345 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
346 connect(m_d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory()));
348 // Goto Next In History Action
349 cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editDesignContext);
351 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab")));
353 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab")));
355 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
356 connect(m_d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory()));
358 // Go back in navigation history
359 cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editDesignContext);
361 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Left")));
363 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left")));
365 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
366 connect(m_d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory()));
368 // Go forward in navigation history
369 cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editDesignContext);
371 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Right")));
373 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right")));
375 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
376 connect(m_d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory()));
379 QString prefix = tr("Meta+E");
381 QString prefix = tr("Ctrl+E");
384 m_d->m_splitAction = new QAction(tr("Split"), this);
385 cmd = am->registerAction(m_d->m_splitAction, Constants::SPLIT, editManagerContext);
386 cmd->setDefaultKeySequence(QKeySequence(tr("%1,2").arg(prefix)));
387 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
388 connect(m_d->m_splitAction, SIGNAL(triggered()), this, SLOT(split()));
390 m_d->m_splitSideBySideAction = new QAction(tr("Split Side by Side"), this);
391 cmd = am->registerAction(m_d->m_splitSideBySideAction, Constants::SPLIT_SIDE_BY_SIDE, editManagerContext);
392 cmd->setDefaultKeySequence(QKeySequence(tr("%1,3").arg(prefix)));
393 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
394 connect(m_d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide()));
396 m_d->m_removeCurrentSplitAction = new QAction(tr("Remove Current Split"), this);
397 cmd = am->registerAction(m_d->m_removeCurrentSplitAction, Constants::REMOVE_CURRENT_SPLIT, editManagerContext);
398 cmd->setDefaultKeySequence(QKeySequence(tr("%1,0").arg(prefix)));
399 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
400 connect(m_d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
402 m_d->m_removeAllSplitsAction = new QAction(tr("Remove All Splits"), this);
403 cmd = am->registerAction(m_d->m_removeAllSplitsAction, Constants::REMOVE_ALL_SPLITS, editManagerContext);
404 cmd->setDefaultKeySequence(QKeySequence(tr("%1,1").arg(prefix)));
405 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
406 connect(m_d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
408 m_d->m_gotoOtherSplitAction = new QAction(tr("Go to Next Split"), this);
409 cmd = am->registerAction(m_d->m_gotoOtherSplitAction, Constants::GOTO_OTHER_SPLIT, editManagerContext);
410 cmd->setDefaultKeySequence(QKeySequence(tr("%1,o").arg(prefix)));
411 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
412 connect(m_d->m_gotoOtherSplitAction, SIGNAL(triggered()), this, SLOT(gotoOtherSplit()));
414 ActionContainer *medit = am->actionContainer(Constants::M_EDIT);
415 ActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED);
416 medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED);
417 advancedMenu->menu()->setTitle(tr("&Advanced"));
418 advancedMenu->appendGroup(Constants::G_EDIT_FORMAT);
419 advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING);
420 advancedMenu->appendGroup(Constants::G_EDIT_BLOCKS);
421 advancedMenu->appendGroup(Constants::G_EDIT_FONT);
422 advancedMenu->appendGroup(Constants::G_EDIT_EDITOR);
424 // Advanced menu separators
425 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Collapsing"), editManagerContext);
426 advancedMenu->addAction(cmd, Constants::G_EDIT_COLLAPSING);
427 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Blocks"), editManagerContext);
428 advancedMenu->addAction(cmd, Constants::G_EDIT_BLOCKS);
429 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Font"), editManagerContext);
430 advancedMenu->addAction(cmd, Constants::G_EDIT_FONT);
431 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Editor"), editManagerContext);
432 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
434 cmd = am->registerAction(m_d->m_openInExternalEditorAction, Constants::OPEN_IN_EXTERNAL_EDITOR, editManagerContext);
435 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+V,Alt+I")));
436 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
437 connect(m_d->m_openInExternalEditorAction, SIGNAL(triggered()), this, SLOT(openInExternalEditor()));
439 // Connect to VariableManager for CURRENT_DOCUMENT variable setting
440 VariableManager::initEditorManagerConnections();
442 m_d->m_splitter = new SplitterOrView(m_d->m_editorModel);
443 m_d->m_view = m_d->m_splitter->view();
446 QHBoxLayout *layout = new QHBoxLayout(this);
447 layout->setMargin(0);
448 layout->setSpacing(0);
449 layout->addWidget(m_d->m_splitter);
453 m_d->m_windowPopup = new OpenEditorsWindow(this);
456 EditorManager::~EditorManager()
459 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
460 if (m_d->m_coreListener) {
461 pm->removeObject(m_d->m_coreListener);
462 delete m_d->m_coreListener;
464 pm->removeObject(m_d->m_openEditorsFactory);
465 delete m_d->m_openEditorsFactory;
470 void EditorManager::init()
472 m_d->m_coreListener = new EditorClosingCoreListener(this);
473 pluginManager()->addObject(m_d->m_coreListener);
475 m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
476 pluginManager()->addObject(m_d->m_openEditorsFactory);
480 EditorToolBar *EditorManager::createToolBar(QWidget *parent)
482 return new EditorToolBar(parent);
485 QString EditorManager::defaultExternalEditor() const
488 return ConsoleProcess::defaultTerminalEmulator() + QLatin1String(
492 " -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\"");
494 return QLatin1String("notepad %f");
498 void EditorManager::removeEditor(IEditor *editor)
500 bool isDuplicate = m_d->m_editorModel->isDuplicate(editor);
501 m_d->m_editorModel->removeEditor(editor);
503 m_d->m_core->fileManager()->removeFile(editor->file());
505 m_d->m_core->removeContextObject(editor);
508 void EditorManager::handleContextChange(Core::IContext *context)
510 if (debugEditorManager)
511 qDebug() << Q_FUNC_INFO;
512 IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0;
514 setCurrentEditor(editor);
520 void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
525 if (m_d->m_currentEditor == editor)
527 if (m_d->m_currentEditor && !ignoreNavigationHistory)
528 addCurrentPositionToNavigationHistory();
530 m_d->m_currentEditor = editor;
532 if (SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor))
533 splitterOrView->view()->setCurrentEditor(editor);
534 m_d->m_view->updateEditorHistory(editor); // the global view should have a complete history
537 emit currentEditorChanged(editor);
541 void EditorManager::setCurrentView(Core::Internal::SplitterOrView *view)
543 if (view == m_d->m_currentView)
546 SplitterOrView *old = m_d->m_currentView;
547 m_d->m_currentView = view;
554 if (view && !view->editor())
558 Core::Internal::SplitterOrView *EditorManager::currentSplitterOrView() const
560 SplitterOrView *view = m_d->m_currentView;
562 view = m_d->m_currentEditor?
563 m_d->m_splitter->findView(m_d->m_currentEditor):
564 m_d->m_splitter->findFirstView();
566 return m_d->m_splitter;
570 Core::Internal::EditorView *EditorManager::currentEditorView() const
572 return currentSplitterOrView()->view();
575 QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
577 QList<IEditor *> found;
578 QString fixedname = FileManager::fixFileName(filename);
579 foreach (IEditor *editor, openedEditors()) {
580 if (fixedname == FileManager::fixFileName(editor->file()->fileName()))
586 QList<IEditor *> EditorManager::editorsForFile(IFile *file) const
588 QList<IEditor *> found;
589 foreach (IEditor *editor, openedEditors()) {
590 if (editor->file() == file)
596 IEditor *EditorManager::currentEditor() const
598 return m_d->m_currentEditor;
601 void EditorManager::emptyView(Core::Internal::EditorView *view)
606 QList<IEditor *> editors = view->editors();
607 foreach (IEditor *editor, editors) {
608 if (!m_d->m_editorModel->isDuplicate(editor)) {
609 editors.removeAll(editor);
610 view->removeEditor(editor);
613 emit editorAboutToClose(editor);
614 removeEditor(editor);
615 view->removeEditor(editor);
617 emit editorsClosed(editors);
618 foreach (IEditor *editor, editors) {
623 void EditorManager::closeView(Core::Internal::EditorView *view)
628 if (view == m_d->m_view) {
629 if (IEditor *e = view->currentEditor())
630 closeEditors(QList<IEditor *>() << e);
634 if (IEditor *e = view->currentEditor()) {
636 when we are closing a view with an original editor which has
637 duplicates, then make one of the duplicates the original.
638 Otherwise the original would be kept around and the user might
639 experience jumping to a missleading position within the file when
640 visiting the file again. With the code below, the position within
641 the file will be the position of the first duplicate which is still
644 if (!m_d->m_editorModel->isDuplicate(e)) {
645 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(e);
646 if (!duplicates.isEmpty()) {
647 m_d->m_editorModel->makeOriginal(duplicates.first());
654 SplitterOrView *splitterOrView = m_d->m_splitter->findView(view);
655 Q_ASSERT(splitterOrView);
656 Q_ASSERT(splitterOrView->view() == view);
657 SplitterOrView *splitter = m_d->m_splitter->findSplitter(splitterOrView);
658 Q_ASSERT(splitterOrView->hasEditors() == false);
659 splitterOrView->hide();
660 delete splitterOrView;
664 SplitterOrView *newCurrent = splitter->findFirstView();
666 if (IEditor *e = newCurrent->editor()) {
667 activateEditor(newCurrent->view(), e);
669 setCurrentView(newCurrent);
675 EditorManager::editorsForFiles(QList<IFile*> files) const
677 const QList<IEditor *> editors = openedEditors();
678 QSet<IEditor *> found;
679 foreach (IFile *file, files) {
680 foreach (IEditor *editor, editors) {
681 if (editor->file() == file && !found.contains(editor)) {
686 return found.toList();
689 QList<IFile *> EditorManager::filesForEditors(QList<IEditor *> editors) const
691 QSet<IEditor *> handledEditors;
692 QList<IFile *> files;
693 foreach (IEditor *editor, editors) {
694 if (!handledEditors.contains(editor)) {
695 files << editor->file();
696 handledEditors.insert(editor);
702 bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
704 m_d->m_editorModel->removeAllRestoredEditors();
705 if (closeEditors(openedEditors(), askAboutModifiedEditors)) {
706 // m_d->clearNavigationHistory();
712 void EditorManager::closeOtherEditors(IEditor *editor)
714 m_d->m_editorModel->removeAllRestoredEditors();
715 QList<IEditor*> editors = openedEditors();
716 editors.removeAll(editor);
717 closeEditors(editors, true);
720 void EditorManager::closeOtherEditors()
722 IEditor *current = currentEditor();
723 QTC_ASSERT(current, return);
724 closeOtherEditors(current);
727 // SLOT connected to action
728 void EditorManager::closeEditor()
730 if (!m_d->m_currentEditor)
732 addCurrentPositionToNavigationHistory();
733 closeEditor(m_d->m_currentEditor);
736 void EditorManager::closeEditor(Core::IEditor *editor)
740 closeEditors(QList<IEditor *>() << editor);
743 void EditorManager::closeEditor(const QModelIndex &index)
745 IEditor *editor = index.data(Qt::UserRole).value<Core::IEditor*>();
749 m_d->m_editorModel->removeEditor(index);
752 bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
754 if (editorsToClose.isEmpty())
757 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
759 bool closingFailed = false;
760 QList<IEditor*> acceptedEditors;
761 //ask all core listeners to check whether the editor can be closed
762 const QList<ICoreListener *> listeners =
763 pluginManager()->getObjects<ICoreListener>();
764 foreach (IEditor *editor, editorsToClose) {
765 bool editorAccepted = true;
766 if (m_d->m_editorModel->isDuplicate(editor))
767 editor = m_d->m_editorModel->originalForDuplicate(editor);
768 foreach (ICoreListener *listener, listeners) {
769 if (!listener->editorAboutToClose(editor)) {
770 editorAccepted = false;
771 closingFailed = false;
776 acceptedEditors.append(editor);
778 if (acceptedEditors.isEmpty())
780 //ask whether to save modified files
781 if (askAboutModifiedEditors) {
782 bool cancelled = false;
783 QList<IFile*> list = m_d->m_core->fileManager()->
784 saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled);
787 if (!list.isEmpty()) {
788 closingFailed = true;
789 QSet<IEditor*> skipSet = editorsForFiles(list).toSet();
790 acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList();
793 if (acceptedEditors.isEmpty())
797 foreach(IEditor *editor, acceptedEditors)
798 acceptedEditors += m_d->m_editorModel->duplicatesFor(editor);
800 QList<EditorView*> closedViews;
802 // remove the editors
803 foreach (IEditor *editor, acceptedEditors) {
804 emit editorAboutToClose(editor);
805 if (!editor->file()->fileName().isEmpty()) {
806 QByteArray state = editor->saveState();
807 if (!state.isEmpty())
808 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
811 removeEditor(editor);
812 if (SplitterOrView *view = m_d->m_splitter->findView(editor)) {
813 if (editor == view->view()->currentEditor())
814 closedViews += view->view();
815 view->view()->removeEditor(editor);
819 foreach (EditorView *view, closedViews) {
820 IEditor *newCurrent = view->currentEditor();
822 newCurrent = pickUnusedEditor();
824 activateEditor(view, newCurrent, NoActivate);
826 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
828 activateEditor(idx, view, NoActivate);
832 emit editorsClosed(acceptedEditors);
834 foreach (IEditor *editor, acceptedEditors) {
838 if (currentSplitterOrView) {
839 if (IEditor *editor = currentSplitterOrView->editor())
840 activateEditor(currentSplitterOrView->view(), editor);
843 if (!currentEditor()) {
844 emit currentEditorChanged(0);
848 return !closingFailed;
851 void EditorManager::closeDuplicate(Core::IEditor *editor)
854 IEditor *original = editor;
855 if (m_d->m_editorModel->isDuplicate(editor))
856 original= m_d->m_editorModel->originalForDuplicate(editor);
857 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(original);
859 if (duplicates.isEmpty()) {
864 if (original== editor)
865 m_d->m_editorModel->makeOriginal(duplicates.first());
867 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
869 emit editorAboutToClose(editor);
871 if(m_d->m_splitter->findView(editor)) {
872 EditorView *view = m_d->m_splitter->findView(editor)->view();
873 removeEditor(editor);
874 view->removeEditor(editor);
876 IEditor *newCurrent = view->currentEditor();
878 newCurrent = pickUnusedEditor();
880 activateEditor(view, newCurrent, NoActivate);
882 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
884 activateEditor(idx, view, NoActivate);
888 emit editorsClosed(QList<IEditor*>() << editor);
890 if (currentSplitterOrView) {
891 if (IEditor *currentEditor = currentSplitterOrView->editor())
892 activateEditor(currentSplitterOrView->view(), currentEditor);
896 Core::IEditor *EditorManager::pickUnusedEditor() const
898 foreach (IEditor *editor, openedEditors()) {
899 SplitterOrView *view = m_d->m_splitter->findView(editor);
900 if (!view || view->editor() != editor)
906 Core::IEditor *EditorManager::activateEditor(const QModelIndex &index, Internal::EditorView *view, OpenEditorFlags flags)
908 IEditor *editor = index.data(Qt::UserRole).value<IEditor*>();
910 return activateEditor(view, editor, flags);
913 QString fileName = index.data(Qt::UserRole + 1).toString();
914 QString id = index.data(Qt::UserRole + 2).toString();
915 return openEditor(view, fileName, id, flags);
918 Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)
920 Q_ASSERT(view && editor);
922 if (view->currentEditor() && view->currentEditor()->file() == editor->file())
923 editor = view->currentEditor();
925 if (!view->hasEditor(editor)) {
926 bool duplicateSupported = editor->duplicateSupported();
927 if (SplitterOrView *sourceView = m_d->m_splitter->findView(editor)) {
928 if (editor != sourceView->editor() || !duplicateSupported) {
929 sourceView->view()->removeEditor(editor);
930 view->addEditor(editor);
931 view->setCurrentEditor(editor);
932 if (!sourceView->editor()) {
933 if (IEditor *replacement = pickUnusedEditor()) {
934 sourceView->view()->addEditor(replacement);
938 } else if (duplicateSupported) {
939 editor = duplicateEditor(editor);
941 m_d->m_editorModel->makeOriginal(editor);
944 view->addEditor(editor);
949 Core::IEditor *EditorManager::activateEditor(Core::IEditor *editor, OpenEditorFlags flags)
951 return activateEditor(0, editor, flags);
954 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags)
957 view = currentEditorView();
962 if (!m_d->m_currentEditor)
963 setCurrentEditor(0, (flags & IgnoreNavigationHistory));
967 editor = placeEditor(view, editor);
969 if (!(flags & NoActivate)) {
970 setCurrentEditor(editor, (flags & IgnoreNavigationHistory));
971 if (flags & ModeSwitch) {
972 switchToPreferedMode();
975 editor->widget()->setFocus();
980 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IFile *file, OpenEditorFlags flags)
982 const QList<IEditor*> editors = editorsForFile(file);
983 if (editors.isEmpty())
986 return activateEditor(view, editors.first(), flags);
989 /* For something that has a 'QStringList mimeTypes' (IEditorFactory
990 * or IExternalEditor), find the one best matching the mimetype passed in.
991 * Recurse over the parent classes of the mimetype to find them. */
992 template <class EditorFactoryLike>
993 static void mimeTypeFactoryRecursion(const MimeDatabase *db,
994 const MimeType &mimeType,
995 const QList<EditorFactoryLike*> &allFactories,
997 QList<EditorFactoryLike*> *list)
999 typedef typename QList<EditorFactoryLike*>::const_iterator EditorFactoryLikeListConstIterator;
1000 // Loop factories to find type
1001 const QString type = mimeType.type();
1002 const EditorFactoryLikeListConstIterator fcend = allFactories.constEnd();
1003 for (EditorFactoryLikeListConstIterator fit = allFactories.constBegin(); fit != fcend; ++fit) {
1004 // Exclude duplicates when recursing over xml or C++ -> C -> text.
1005 EditorFactoryLike *factory = *fit;
1006 if (!list->contains(factory) && factory->mimeTypes().contains(type)) {
1007 list->push_back(*fit);
1013 // Any parent mime type classes? -> recurse
1014 QStringList parentTypes = mimeType.subClassesOf();
1015 if (parentTypes.empty())
1017 const QStringList::const_iterator pcend = parentTypes .constEnd();
1018 for (QStringList::const_iterator pit = parentTypes .constBegin(); pit != pcend; ++pit) {
1019 if (const MimeType parent = db->findByType(*pit))
1020 mimeTypeFactoryRecursion(db, parent, allFactories, firstMatchOnly, list);
1024 EditorManager::EditorFactoryList
1025 EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const
1027 EditorFactoryList rc;
1028 const EditorFactoryList allFactories = pluginManager()->getObjects<IEditorFactory>();
1029 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allFactories, bestMatchOnly, &rc);
1030 if (debugEditorManager)
1031 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1035 EditorManager::ExternalEditorList
1036 EditorManager::externalEditors(const MimeType &mimeType, bool bestMatchOnly) const
1038 ExternalEditorList rc;
1039 const ExternalEditorList allEditors = pluginManager()->getObjects<IExternalEditor>();
1040 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allEditors, bestMatchOnly, &rc);
1041 if (debugEditorManager)
1042 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1046 /* For something that has a 'QString id' (IEditorFactory
1047 * or IExternalEditor), find the one matching a id. */
1048 template <class EditorFactoryLike>
1049 inline EditorFactoryLike *findById(ExtensionSystem::PluginManager *pm,
1052 const QList<EditorFactoryLike *> factories = pm->template getObjects<EditorFactoryLike>();
1053 foreach(EditorFactoryLike *efl, factories)
1054 if (id == efl->id())
1059 IEditor *EditorManager::createEditor(const QString &editorId,
1060 const QString &fileName)
1062 if (debugEditorManager)
1063 qDebug() << Q_FUNC_INFO << editorId << fileName;
1065 EditorFactoryList factories;
1066 if (editorId.isEmpty()) {
1067 const QFileInfo fileInfo(fileName);
1068 // Find by mime type
1069 MimeType mimeType = m_d->m_core->mimeDatabase()->findByFile(fileInfo);
1071 qWarning("%s unable to determine mime type of %s/%s. Falling back to text/plain",
1072 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1073 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("text/plain"));
1075 // open text files > 48 MB in binary editor
1076 if (fileInfo.size() > maxTextFileSize() && mimeType.type().startsWith(QLatin1String("text")))
1077 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("application/octet-stream"));
1078 factories = editorFactories(mimeType, true);
1080 // Find by editor id
1081 if (IEditorFactory *factory = findById<IEditorFactory>(pluginManager(), editorId))
1082 factories.push_back(factory);
1084 if (factories.empty()) {
1085 qWarning("%s: unable to find an editor factory for the file '%s', editor Id '%s'.",
1086 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1090 IEditor *editor = factories.front()->createEditor(this);
1092 connect(editor, SIGNAL(changed()), this, SLOT(updateActions()));
1094 emit editorCreated(editor, fileName);
1098 void EditorManager::addEditor(IEditor *editor, bool isDuplicate)
1102 m_d->m_core->addContextObject(editor);
1104 m_d->m_editorModel->addEditor(editor, isDuplicate);
1106 const bool isTemporary = editor->isTemporary();
1107 const bool addWatcher = !isTemporary;
1108 m_d->m_core->fileManager()->addFile(editor->file(), addWatcher);
1110 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1112 emit editorOpened(editor);
1115 // Run the OpenWithDialog and return the editor id
1116 // selected by the user.
1117 QString EditorManager::getOpenWithEditorId(const QString &fileName,
1118 bool *isExternalEditor) const
1120 // Collect editors that can open the file
1121 const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName);
1124 QStringList allEditorIds;
1125 QStringList externalEditorIds;
1127 const EditorFactoryList editors = editorFactories(mt, false);
1128 const int size = editors.size();
1129 for (int i = 0; i < size; i++) {
1130 allEditorIds.push_back(editors.at(i)->id());
1133 const ExternalEditorList exEditors = externalEditors(mt, false);
1134 const int esize = exEditors.size();
1135 for (int i = 0; i < esize; i++) {
1136 externalEditorIds.push_back(exEditors.at(i)->id());
1137 allEditorIds.push_back(exEditors.at(i)->id());
1139 if (allEditorIds.empty())
1142 OpenWithDialog dialog(fileName, m_d->m_core->mainWindow());
1143 dialog.setEditors(allEditorIds);
1144 dialog.setCurrentEditor(0);
1145 if (dialog.exec() != QDialog::Accepted)
1147 const QString selectedId = dialog.editor();
1148 if (isExternalEditor)
1149 *isExternalEditor = externalEditorIds.contains(selectedId);
1153 static QString formatFileFilters(const Core::ICore *core, QString *selectedFilter = 0)
1156 selectedFilter->clear();
1158 // Compile list of filter strings, sort, and remove duplicates (different mime types might
1159 // generate the same filter).
1160 QStringList filters = core->mimeDatabase()->filterStrings();
1161 if (filters.empty())
1164 filters.erase(std::unique(filters.begin(), filters.end()), filters.end());
1166 static const QString allFilesFilter =
1167 QCoreApplication::translate("Core", Constants::ALL_FILES_FILTER);
1169 *selectedFilter = allFilesFilter;
1171 // Prepend all files filter (instead of appending to work around a bug in Qt/Mac).
1172 filters.prepend(allFilesFilter);
1174 return filters.join(QLatin1String(";;"));
1177 IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorId,
1178 OpenEditorFlags flags, bool *newEditor)
1180 return openEditor(0, fileName, editorId, flags, newEditor);
1183 int extractLineNumber(QString *fileName)
1185 int i = fileName->length() - 1;
1186 for (; i >= 0; --i) {
1187 if (!fileName->at(i).isNumber())
1192 if (fileName->at(i) == ':' || fileName->at(i) == '+') {
1193 int result = fileName->mid(i+1).toInt();
1194 *fileName = fileName->left(i);
1200 IEditor *EditorManager::openEditor(Core::Internal::EditorView *view, const QString &fileName,
1201 const QString &editorId, OpenEditorFlags flags, bool *newEditor)
1203 if (debugEditorManager)
1204 qDebug() << Q_FUNC_INFO << fileName << editorId;
1206 QString fn = fileName;
1207 int lineNumber = -1;
1208 if (flags && EditorManager::CanContainLineNumber)
1209 lineNumber = extractLineNumber(&fn);
1217 const QList<IEditor *> editors = editorsForFileName(fn);
1218 if (!editors.isEmpty())
1219 return activateEditor(view, editors.first(), flags);
1221 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1222 IEditor *editor = createEditor(editorId, fn);
1223 // If we could not open the file in the requested editor, fall
1224 // back to the default editor:
1226 editor = createEditor(QString(), fn);
1227 if (!editor || !editor->open(fn)) {
1228 QApplication::restoreOverrideCursor();
1229 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), tr("Cannot open file %1!").arg(QDir::toNativeSeparators(fn)));
1239 IEditor *result = activateEditor(view, editor, flags);
1240 if (editor == result)
1241 restoreEditorState(editor);
1243 if (flags && EditorManager::CanContainLineNumber)
1244 editor->gotoLine(lineNumber, -1);
1246 QApplication::restoreOverrideCursor();
1250 bool EditorManager::openExternalEditor(const QString &fileName, const QString &editorId)
1252 IExternalEditor *ee = findById<IExternalEditor>(pluginManager(), editorId);
1255 QString errorMessage;
1256 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1257 const bool ok = ee->startEditor(fileName, &errorMessage);
1258 QApplication::restoreOverrideCursor();
1260 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), errorMessage);
1264 QStringList EditorManager::getOpenFileNames() const
1266 QString selectedFilter;
1267 const QString &fileFilters = formatFileFilters(m_d->m_core, &selectedFilter);
1268 return ICore::instance()->fileManager()->getOpenFileNames(fileFilters,
1269 QString(), &selectedFilter);
1273 /// Empty mode == figure out the correct mode from the editor
1274 /// forcePrefered = true, switch to the mode even if the editor is visible in another mode
1275 /// forcePrefered = false, only switch if it is not visible
1276 void EditorManager::switchToPreferedMode()
1278 QString preferedMode;
1279 // Figure out prefered mode for editor
1280 if (m_d->m_currentEditor)
1281 preferedMode = m_d->m_currentEditor->preferredModeType();
1283 if (preferedMode.isEmpty())
1284 preferedMode = Constants::MODE_EDIT_TYPE;
1286 if (m_d->m_core->modeManager()->currentMode()->type() != preferedMode) {
1287 m_d->m_core->modeManager()->activateModeType(preferedMode);
1291 IEditor *EditorManager::openEditorWithContents(const QString &editorId,
1292 QString *titlePattern,
1293 const QString &contents)
1295 if (debugEditorManager)
1296 qDebug() << Q_FUNC_INFO << editorId << titlePattern << contents;
1298 if (editorId.isEmpty())
1301 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1302 IEditor *edt = createEditor(editorId);
1304 QApplication::restoreOverrideCursor();
1308 if (!edt->createNew(contents)) {
1309 QApplication::restoreOverrideCursor();
1315 QString title = edt->displayName();
1318 const QChar dollar = QLatin1Char('$');
1319 const QChar dot = QLatin1Char('.');
1321 QString base = *titlePattern;
1323 base = QLatin1String("unnamed$");
1324 if (base.contains(dollar)) {
1326 QSet<QString> docnames;
1327 foreach (IEditor *editor, openedEditors()) {
1328 QString name = editor->file()->fileName();
1329 if (name.isEmpty()) {
1330 name = editor->displayName();
1331 name.remove(QLatin1Char('*'));
1333 name = QFileInfo(name).completeBaseName();
1340 title.replace(QString(dollar), QString::number(i++));
1341 } while (docnames.contains(title));
1343 title = *titlePattern;
1345 *titlePattern = title;
1347 edt->setDisplayName(title);
1349 QApplication::restoreOverrideCursor();
1353 bool EditorManager::hasEditor(const QString &fileName) const
1355 return !editorsForFileName(fileName).isEmpty();
1358 void EditorManager::restoreEditorState(IEditor *editor)
1360 QTC_ASSERT(editor, return);
1361 QString fileName = editor->file()->fileName();
1362 editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray());
1365 bool EditorManager::saveEditor(IEditor *editor)
1367 return saveFile(editor);
1370 bool EditorManager::saveFile(IEditor *editor)
1373 editor = currentEditor();
1377 IFile *file = editor->file();
1378 file->checkPermissions();
1380 const QString &fileName = file->fileName();
1382 if (fileName.isEmpty())
1383 return saveFileAs(editor);
1385 bool success = false;
1387 // try saving, no matter what isReadOnly tells us
1388 m_d->m_core->fileManager()->blockFileChange(file);
1389 success = file->save(fileName);
1390 m_d->m_core->fileManager()->unblockFileChange(file);
1393 MakeWritableResult answer =
1394 makeEditorWritable(editor);
1395 if (answer == Failed)
1397 if (answer == SavedAs)
1400 file->checkPermissions();
1402 m_d->m_core->fileManager()->blockFileChange(file);
1403 success = file->save(fileName);
1404 m_d->m_core->fileManager()->unblockFileChange(file);
1407 if (success && !editor->isTemporary())
1408 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1413 EditorManager::ReadOnlyAction
1414 EditorManager::promptReadOnlyFile(const QString &fileName,
1415 const IVersionControl *versionControl,
1417 bool displaySaveAsButton)
1419 // Version Control: If automatic open is desired, open right away.
1420 bool promptVCS = false;
1421 if (versionControl && versionControl->supportsOperation(IVersionControl::OpenOperation)) {
1422 if (versionControl->settingsFlags() & IVersionControl::AutoOpen)
1427 // Create message box.
1428 QMessageBox msgBox(QMessageBox::Question, tr("File is Read Only"),
1429 tr("The file <i>%1</i> is read only.").arg(QDir::toNativeSeparators(fileName)),
1430 QMessageBox::Cancel, parent);
1432 QPushButton *vcsButton = 0;
1434 vcsButton = msgBox.addButton(tr("Open with VCS (%1)").arg(versionControl->displayName()), QMessageBox::AcceptRole);
1436 QPushButton *makeWritableButton = msgBox.addButton(tr("Make writable"), QMessageBox::AcceptRole);
1438 QPushButton *saveAsButton = 0;
1439 if (displaySaveAsButton)
1440 saveAsButton = msgBox.addButton(tr("Save as ..."), QMessageBox::ActionRole);
1442 msgBox.setDefaultButton(vcsButton ? vcsButton : makeWritableButton);
1445 QAbstractButton *clickedButton = msgBox.clickedButton();
1446 if (clickedButton == vcsButton)
1448 if (clickedButton == makeWritableButton)
1449 return RO_MakeWriteable;
1450 if (clickedButton == saveAsButton)
1457 EditorManager::makeEditorWritable(IEditor *editor)
1459 if (!editor || !editor->file())
1461 QString directory = QFileInfo(editor->file()->fileName()).absolutePath();
1462 IVersionControl *versionControl = m_d->m_core->vcsManager()->findVersionControlForDirectory(directory);
1463 IFile *file = editor->file();
1464 const QString &fileName = file->fileName();
1466 switch (promptReadOnlyFile(fileName, versionControl, m_d->m_core->mainWindow(), true)) {
1468 if (!versionControl->vcsOpen(fileName)) {
1469 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for editing with SCC."));
1472 file->checkPermissions();
1473 return OpenedWithVersionControl;
1474 case RO_MakeWriteable: {
1475 const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
1477 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
1481 file->checkPermissions();
1482 return MadeWritable;
1484 return saveFileAs(editor) ? SavedAs : Failed;
1491 bool EditorManager::saveFileAs(IEditor *editor)
1494 editor = currentEditor();
1498 IFile *file = editor->file();
1499 const QString &filter = formatFileFilters(m_d->m_core);
1500 QString selectedFilter =
1501 m_d->m_core->mimeDatabase()->findByFile(QFileInfo(file->fileName())).filterString();
1502 const QString &absoluteFilePath =
1503 m_d->m_core->fileManager()->getSaveAsFileName(file, filter, &selectedFilter);
1505 if (absoluteFilePath.isEmpty())
1507 if (absoluteFilePath != file->fileName()) {
1508 const QList<IEditor *> existList = editorsForFileName(absoluteFilePath);
1509 if (!existList.isEmpty()) {
1510 closeEditors(existList, false);
1514 m_d->m_core->fileManager()->blockFileChange(file);
1515 const bool success = file->save(absoluteFilePath);
1516 m_d->m_core->fileManager()->unblockFileChange(file);
1517 file->checkPermissions();
1519 // @todo: There is an issue to be treated here. The new file might be of a different mime
1520 // type than the original and thus require a different editor. An alternative strategy
1521 // would be to close the current editor and open a new appropriate one, but this is not
1522 // a good way out either (also the undo stack would be lost). Perhaps the best is to
1523 // re-think part of the editors design.
1525 if (success && !editor->isTemporary())
1526 m_d->m_core->fileManager()->addToRecentFiles(file->fileName());
1532 void EditorManager::gotoNextDocHistory()
1534 OpenEditorsWindow *dialog = windowPopup();
1535 if (dialog->isVisible()) {
1536 dialog->selectNextEditor();
1538 EditorView *view = currentEditorView();
1539 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1540 dialog->selectNextEditor();
1541 showPopupOrSelectDocument();
1545 void EditorManager::gotoPreviousDocHistory()
1547 OpenEditorsWindow *dialog = windowPopup();
1548 if (dialog->isVisible()) {
1549 dialog->selectPreviousEditor();
1551 EditorView *view = currentEditorView();
1552 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1553 dialog->selectPreviousEditor();
1554 showPopupOrSelectDocument();
1558 void EditorManager::makeCurrentEditorWritable()
1560 if (IEditor* curEditor = currentEditor())
1561 makeEditorWritable(curEditor);
1564 void EditorManager::updateActions()
1567 IEditor *curEditor = currentEditor();
1568 int openedCount = openedEditors().count() + m_d->m_editorModel->restoredEditors().count();
1572 if (!curEditor->file()->fileName().isEmpty()) {
1573 QFileInfo fi(curEditor->file()->fileName());
1574 fName = fi.fileName();
1576 fName = curEditor->displayName();
1580 window()->setWindowModified(curEditor->file()->isModified());
1582 if (curEditor->file()->isModified() && curEditor->file()->isReadOnly()) {
1583 // we are about to change a read-only file, warn user
1584 showEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"),
1585 tr("<b>Warning:</b> You are changing a read-only file."),
1586 tr("Make writable"), this, SLOT(makeCurrentEditorWritable()));
1588 hideEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"));
1591 } else { // curEditor
1592 window()->setWindowModified(false);
1596 m_d->m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified());
1597 m_d->m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed());
1598 m_d->m_revertToSavedAction->setEnabled(curEditor != 0
1599 && !curEditor->file()->fileName().isEmpty() && curEditor->file()->isModified());
1602 if (!fName.isEmpty())
1603 quotedName = '"' + fName + '"';
1605 m_d->m_saveAsAction->setText(tr("Save %1 &As...").arg(quotedName));
1606 m_d->m_saveAction->setText(tr("&Save %1").arg(quotedName));
1607 m_d->m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName));
1609 m_d->m_closeCurrentEditorAction->setEnabled(curEditor != 0);
1610 m_d->m_closeCurrentEditorAction->setText(tr("Close %1").arg(quotedName));
1611 m_d->m_closeAllEditorsAction->setEnabled(openedCount > 0);
1612 m_d->m_closeOtherEditorsAction->setEnabled(openedCount > 1);
1613 m_d->m_closeOtherEditorsAction->setText((openedCount > 1 ? tr("Close All Except %1").arg(quotedName) : tr("Close Others")));
1615 m_d->m_gotoNextDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1616 m_d->m_gotoPreviousDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1617 EditorView *view = currentEditorView();
1618 m_d->m_goBackAction->setEnabled(view ? view->canGoBack() : false);
1619 m_d->m_goForwardAction->setEnabled(view ? view->canGoForward() : false);
1621 bool hasSplitter = m_d->m_splitter->isSplitter();
1622 m_d->m_removeCurrentSplitAction->setEnabled(hasSplitter);
1623 m_d->m_removeAllSplitsAction->setEnabled(hasSplitter);
1624 m_d->m_gotoOtherSplitAction->setEnabled(hasSplitter);
1626 m_d->m_openInExternalEditorAction->setEnabled(curEditor != 0);
1629 bool EditorManager::hasSplitter() const
1631 return m_d->m_splitter->isSplitter();
1634 QList<IEditor*> EditorManager::openedEditors() const
1636 return m_d->m_editorModel->editors();
1639 OpenEditorsModel *EditorManager::openedEditorsModel() const
1641 return m_d->m_editorModel;
1644 void EditorManager::addCurrentPositionToNavigationHistory(IEditor *editor, const QByteArray &saveState)
1646 currentEditorView()->addCurrentPositionToNavigationHistory(editor, saveState);
1650 void EditorManager::cutForwardNavigationHistory()
1652 currentEditorView()->cutForwardNavigationHistory();
1656 void EditorManager::goBackInNavigationHistory()
1658 currentEditorView()->goBackInNavigationHistory();
1663 void EditorManager::goForwardInNavigationHistory()
1665 currentEditorView()->goForwardInNavigationHistory();
1669 OpenEditorsWindow *EditorManager::windowPopup() const
1671 return m_d->m_windowPopup;
1674 void EditorManager::showPopupOrSelectDocument() const
1676 if (QApplication::keyboardModifiers() == Qt::NoModifier) {
1677 windowPopup()->selectAndHide();
1679 // EditorManager is invisible when invoked from Design Mode.
1680 const QPoint p = isVisible() ?
1681 mapToGlobal(QPoint(0, 0)) :
1682 m_d->m_core->mainWindow()->mapToGlobal(QPoint(0, 0));
1683 windowPopup()->move((width()-m_d->m_windowPopup->width())/2 + p.x(),
1684 (height()-m_d->m_windowPopup->height())/2 + p.y());
1685 windowPopup()->setVisible(true);
1689 // Save state of all non-teporary editors.
1690 QByteArray EditorManager::saveState() const
1693 QDataStream stream(&bytes, QIODevice::WriteOnly);
1695 stream << QByteArray("EditorManagerV4");
1697 QList<IEditor *> editors = openedEditors();
1698 foreach (IEditor *editor, editors) {
1699 if (!editor->file()->fileName().isEmpty()
1700 && !editor->isTemporary()) {
1701 QByteArray state = editor->saveState();
1702 if (!state.isEmpty())
1703 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
1707 stream << m_d->m_editorStates;
1709 QList<OpenEditorsModel::Entry> entries = m_d->m_editorModel->entries();
1710 int entriesCount = 0;
1711 foreach (const OpenEditorsModel::Entry &entry, entries) {
1712 // The editor may be 0 if it was not loaded yet: In that case it is not temporary
1713 if (!entry.editor || !entry.editor->isTemporary())
1717 stream << entriesCount;
1719 foreach (const OpenEditorsModel::Entry &entry, entries) {
1720 if (!entry.editor || !entry.editor->isTemporary())
1721 stream << entry.fileName() << entry.displayName() << entry.id().toUtf8();
1724 stream << m_d->m_splitter->saveState();
1729 bool EditorManager::restoreState(const QByteArray &state)
1731 closeAllEditors(true);
1733 QDataStream stream(state);
1738 if (version != "EditorManagerV4")
1741 QMap<QString, QVariant> editorstates;
1743 QApplication::setOverrideCursor(Qt::WaitCursor);
1745 stream >> editorstates;
1747 QMapIterator<QString, QVariant> i(editorstates);
1748 while (i.hasNext()) {
1750 m_d->m_editorStates.insert(i.key(), i.value());
1753 int editorCount = 0;
1754 stream >> editorCount;
1755 while (--editorCount >= 0) {
1758 QString displayName;
1759 stream >> displayName;
1763 if (!fileName.isEmpty() && !displayName.isEmpty())
1764 m_d->m_editorModel->addRestoredEditor(fileName, displayName, QString::fromUtf8(id));
1767 QByteArray splitterstates;
1768 stream >> splitterstates;
1769 m_d->m_splitter->restoreState(splitterstates);
1771 // splitting and stuff results in focus trouble, that's why we set the focus again after restoration
1772 if (m_d->m_currentEditor) {
1773 m_d->m_currentEditor->widget()->setFocus();
1774 } else if (Core::Internal::SplitterOrView *view = currentSplitterOrView()) {
1775 if (IEditor *e = view->editor())
1776 e->widget()->setFocus();
1777 else if (view->view())
1778 view->view()->setFocus();
1781 QApplication::restoreOverrideCursor();
1786 static const char * const documentStatesKey = "EditorManager/DocumentStates";
1787 static const char * const externalEditorKey = "EditorManager/ExternalEditorCommand";
1788 static const char * const reloadBehaviorKey = "EditorManager/ReloadBehavior";
1790 void EditorManager::saveSettings()
1792 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1793 settings->setValue(QLatin1String(documentStatesKey), m_d->m_editorStates);
1794 settings->setValue(QLatin1String(externalEditorKey), m_d->m_externalEditor);
1795 settings->setValue(QLatin1String(reloadBehaviorKey), m_d->m_reloadSetting);
1798 void EditorManager::readSettings()
1800 // Backward compatibility to old locations for these settings
1801 QSettings *qs = m_d->m_core->settings();
1802 if (qs->contains(QLatin1String(documentStatesKey))) {
1803 m_d->m_editorStates = qs->value(QLatin1String(documentStatesKey))
1804 .value<QMap<QString, QVariant> >();
1805 qs->remove(QLatin1String(documentStatesKey));
1807 if (qs->contains(QLatin1String(externalEditorKey))) {
1808 m_d->m_externalEditor = qs->value(QLatin1String(externalEditorKey)).toString();
1809 qs->remove(QLatin1String(externalEditorKey));
1812 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1813 if (settings->contains(QLatin1String(documentStatesKey)))
1814 m_d->m_editorStates = settings->value(QLatin1String(documentStatesKey))
1815 .value<QMap<QString, QVariant> >();
1816 if (settings->contains(QLatin1String(externalEditorKey)))
1817 m_d->m_externalEditor = settings->value(QLatin1String(externalEditorKey)).toString();
1819 if (settings->contains(QLatin1String(reloadBehaviorKey)))
1820 m_d->m_reloadSetting = (IFile::ReloadSetting)settings->value(QLatin1String(reloadBehaviorKey)).toInt();
1824 void EditorManager::revertToSaved()
1826 IEditor *currEditor = currentEditor();
1829 const QString fileName = currEditor->file()->fileName();
1830 if (fileName.isEmpty())
1832 if (currEditor->file()->isModified()) {
1833 QMessageBox msgBox(QMessageBox::Question, tr("Revert to Saved"),
1834 tr("You will lose your current changes if you proceed reverting %1.").arg(QDir::toNativeSeparators(fileName)),
1835 QMessageBox::Yes|QMessageBox::No, m_d->m_core->mainWindow());
1836 msgBox.button(QMessageBox::Yes)->setText(tr("Proceed"));
1837 msgBox.button(QMessageBox::No)->setText(tr("Cancel"));
1838 msgBox.setDefaultButton(QMessageBox::No);
1839 msgBox.setEscapeButton(QMessageBox::No);
1840 if (msgBox.exec() == QMessageBox::No)
1844 currEditor->file()->reload(IFile::FlagReload, IFile::TypeContents);
1847 void EditorManager::showEditorInfoBar(const QString &id,
1848 const QString &infoText,
1849 const QString &buttonText,
1850 QObject *object, const char *buttonPressMember,
1851 const char *cancelButtonPressMember)
1853 currentEditorView()->showEditorInfoBar(id, infoText, buttonText, object, buttonPressMember, cancelButtonPressMember);
1857 void EditorManager::hideEditorInfoBar(const QString &id)
1859 Core::Internal::EditorView *cev = currentEditorView();
1861 cev->hideEditorInfoBar(id);
1864 void EditorManager::showEditorStatusBar(const QString &id,
1865 const QString &infoText,
1866 const QString &buttonText,
1867 QObject *object, const char *member)
1870 currentEditorView()->showEditorStatusBar(id, infoText, buttonText, object, member);
1873 void EditorManager::hideEditorStatusBar(const QString &id)
1875 currentEditorView()->hideEditorStatusBar(id);
1878 QString EditorManager::externalEditorHelpText() const
1881 "<table border=1 cellspacing=0 cellpadding=3>"
1882 "<tr><th>Variable</th><th>Expands to</th></tr>"
1883 "<tr><td>%f</td><td>file name</td></tr>"
1884 "<tr><td>%l</td><td>current line number</td></tr>"
1885 "<tr><td>%c</td><td>current column number</td></tr>"
1886 "<tr><td>%x</td><td>editor's x position on screen</td></tr>"
1887 "<tr><td>%y</td><td>editor's y position on screen</td></tr>"
1888 "<tr><td>%w</td><td>editor's width in pixels</td></tr>"
1889 "<tr><td>%h</td><td>editor's height in pixels</td></tr>"
1890 "<tr><td>%W</td><td>editor's width in characters</td></tr>"
1891 "<tr><td>%H</td><td>editor's height in characters</td></tr>"
1892 "<tr><td>%%</td><td>%</td></tr>"
1897 void EditorManager::openInExternalEditor()
1899 QString command = m_d->m_externalEditor;
1900 if (command.isEmpty())
1901 command = defaultExternalEditor();
1903 if (command.isEmpty())
1906 IEditor *editor = currentEditor();
1909 if (editor->file()->isModified()) {
1910 bool cancelled = false;
1911 QList<IFile*> list = m_d->m_core->fileManager()->
1912 saveModifiedFiles(QList<IFile*>() << editor->file(), &cancelled);
1917 QRect rect = editor->widget()->rect();
1918 QFont font = editor->widget()->font();
1919 QFontMetrics fm(font);
1920 rect.moveTo(editor->widget()->mapToGlobal(QPoint(0,0)));
1922 QString pre = command;
1924 for (int i = 0; i < pre.size(); ++i) {
1925 QChar c = pre.at(i);
1926 if (c == QLatin1Char('%') && i < pre.size()-1) {
1929 if (c == QLatin1Char('f'))
1930 s = editor->file()->fileName();
1931 else if (c == QLatin1Char('l'))
1932 s = QString::number(editor->currentLine());
1933 else if (c == QLatin1Char('c'))
1934 s = QString::number(editor->currentColumn());
1935 else if (c == QLatin1Char('x'))
1936 s = QString::number(rect.x());
1937 else if (c == QLatin1Char('y'))
1938 s = QString::number(rect.y());
1939 else if (c == QLatin1Char('w'))
1940 s = QString::number(rect.width());
1941 else if (c == QLatin1Char('h'))
1942 s = QString::number(rect.height());
1943 else if (c == QLatin1Char('W'))
1944 s = QString::number(rect.width() / fm.width(QLatin1Char('x')));
1945 else if (c == QLatin1Char('H'))
1946 s = QString::number(rect.height() / fm.lineSpacing());
1947 else if (c == QLatin1Char('%'))
1950 s = QLatin1Char('%');
1960 QProcess::startDetached(cmd);
1963 void EditorManager::setExternalEditor(const QString &editor)
1965 if (editor.isEmpty() || editor == defaultExternalEditor())
1966 m_d->m_externalEditor = defaultExternalEditor();
1968 m_d->m_externalEditor = editor;
1971 QString EditorManager::externalEditor() const
1973 if (m_d->m_externalEditor.isEmpty())
1974 return defaultExternalEditor();
1975 return m_d->m_externalEditor;
1978 void EditorManager::setReloadSetting(IFile::ReloadSetting behavior)
1980 m_d->m_reloadSetting = behavior;
1983 IFile::ReloadSetting EditorManager::reloadSetting() const
1985 return m_d->m_reloadSetting;
1988 QTextCodec *EditorManager::defaultTextEncoding() const
1990 QSettings *settings = Core::ICore::instance()->settings();
1991 if (QTextCodec *candidate = QTextCodec::codecForName(
1992 settings->value(QLatin1String(Constants::SETTINGS_DEFAULTTEXTENCODING)).toByteArray()))
1994 return QTextCodec::codecForLocale();
1997 Core::IEditor *EditorManager::duplicateEditor(Core::IEditor *editor)
1999 if (!editor->duplicateSupported())
2002 IEditor *duplicate = editor->duplicate(0);
2003 duplicate->restoreState(editor->saveState());
2004 emit editorCreated(duplicate, duplicate->file()->fileName());
2005 addEditor(duplicate, true);
2009 void EditorManager::split(Qt::Orientation orientation)
2011 SplitterOrView *view = m_d->m_currentView;
2013 view = m_d->m_currentEditor ? m_d->m_splitter->findView(m_d->m_currentEditor)
2014 : m_d->m_splitter->findFirstView();
2015 if (view && !view->splitter()) {
2016 view->split(orientation);
2021 void EditorManager::split()
2023 split(Qt::Vertical);
2026 void EditorManager::splitSideBySide()
2028 split(Qt::Horizontal);
2031 void EditorManager::removeCurrentSplit()
2033 SplitterOrView *viewToClose = m_d->m_currentView;
2034 if (!viewToClose && m_d->m_currentEditor)
2035 viewToClose = m_d->m_splitter->findView(m_d->m_currentEditor);
2037 if (!viewToClose || viewToClose->isSplitter() || viewToClose == m_d->m_splitter)
2040 closeView(viewToClose->view());
2044 void EditorManager::removeAllSplits()
2046 if (!m_d->m_splitter->isSplitter())
2048 IEditor *editor = m_d->m_currentEditor;
2049 m_d->m_currentEditor = 0; // trigger update below
2050 if (editor && m_d->m_editorModel->isDuplicate(editor))
2051 m_d->m_editorModel->makeOriginal(editor);
2052 m_d->m_splitter->unsplitAll();
2054 editor = pickUnusedEditor();
2055 activateEditor(editor);
2058 void EditorManager::gotoOtherSplit()
2060 if (m_d->m_splitter->isSplitter()) {
2061 SplitterOrView *currentView = m_d->m_currentView;
2062 if (!currentView && m_d->m_currentEditor)
2063 currentView = m_d->m_splitter->findView(m_d->m_currentEditor);
2065 currentView = m_d->m_splitter->findFirstView();
2066 SplitterOrView *view = m_d->m_splitter->findNextView(currentView);
2068 view = m_d->m_splitter->findFirstView();
2070 if (IEditor *editor = view->editor()) {
2071 setCurrentEditor(editor, true);
2072 editor->widget()->setFocus();
2074 setCurrentView(view);
2080 qint64 EditorManager::maxTextFileSize()
2082 return (qint64(3) << 24);
2084 //===================EditorClosingCoreListener======================