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"
38 #include "iversioncontrol.h"
39 #include "mimedatabase.h"
40 #include "tabpositionindicator.h"
41 #include "vcsmanager.h"
43 #include <coreplugin/editortoolbar.h>
44 #include <coreplugin/coreconstants.h>
45 #include <coreplugin/modemanager.h>
46 #include <coreplugin/actionmanager/actionmanager.h>
47 #include <coreplugin/actionmanager/actioncontainer.h>
48 #include <coreplugin/actionmanager/command.h>
49 #include <coreplugin/editormanager/ieditorfactory.h>
50 #include <coreplugin/editormanager/iexternaleditor.h>
51 #include <coreplugin/icorelistener.h>
52 #include <coreplugin/imode.h>
53 #include <coreplugin/settingsdatabase.h>
54 #include <coreplugin/variablemanager.h>
56 #include <extensionsystem/pluginmanager.h>
58 #include <utils/consoleprocess.h>
59 #include <utils/qtcassert.h>
61 #include <QtCore/QDebug>
62 #include <QtCore/QFileInfo>
63 #include <QtCore/QMap>
64 #include <QtCore/QProcess>
65 #include <QtCore/QSet>
66 #include <QtCore/QSettings>
68 #include <QtGui/QAction>
69 #include <QtGui/QShortcut>
70 #include <QtGui/QApplication>
71 #include <QtGui/QFileDialog>
72 #include <QtGui/QLayout>
73 #include <QtGui/QMainWindow>
74 #include <QtGui/QMenu>
75 #include <QtGui/QMessageBox>
76 #include <QtGui/QPushButton>
77 #include <QtGui/QSplitter>
78 #include <QtGui/QStackedLayout>
80 Q_DECLARE_METATYPE(Core::IEditor*)
82 enum { debugEditorManager=0 };
84 static inline ExtensionSystem::PluginManager *pluginManager()
86 return ExtensionSystem::PluginManager::instance();
89 //===================EditorClosingCoreListener======================
94 class EditorClosingCoreListener : public ICoreListener
97 EditorClosingCoreListener(EditorManager *em);
98 bool editorAboutToClose(IEditor *editor);
99 bool coreAboutToClose();
105 EditorClosingCoreListener::EditorClosingCoreListener(EditorManager *em)
110 bool EditorClosingCoreListener::editorAboutToClose(IEditor *)
115 bool EditorClosingCoreListener::coreAboutToClose()
117 // Do not ask for files to save.
118 // MainWindow::closeEvent has already done that.
119 return m_em->closeAllEditors(false);
122 } // namespace Internal
125 using namespace Core;
126 using namespace Core::Internal;
127 using namespace Utils;
129 //===================EditorManager=====================
131 EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0;
133 EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent)
134 : QWidget(parent), m_mode(mode)
136 setLayout(new QVBoxLayout);
137 layout()->setMargin(0);
138 connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)),
139 this, SLOT(currentModeChanged(Core::IMode *)));
141 currentModeChanged(Core::ModeManager::instance()->currentMode());
144 EditorManagerPlaceHolder::~EditorManagerPlaceHolder()
146 if (m_current == this) {
147 EditorManager::instance()->setParent(0);
148 EditorManager::instance()->hide();
152 void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode)
154 if (m_current == this) {
156 EditorManager::instance()->setParent(0);
157 EditorManager::instance()->hide();
159 if (m_mode == mode) {
161 layout()->addWidget(EditorManager::instance());
162 EditorManager::instance()->show();
166 EditorManagerPlaceHolder* EditorManagerPlaceHolder::current()
171 // ---------------- EditorManager
176 struct EditorManagerPrivate {
177 explicit EditorManagerPrivate(ICore *core, QWidget *parent);
178 ~EditorManagerPrivate();
179 Internal::EditorView *m_view;
180 Internal::SplitterOrView *m_splitter;
181 QPointer<IEditor> m_currentEditor;
182 QPointer<SplitterOrView> m_currentView;
188 QAction *m_revertToSavedAction;
189 QAction *m_saveAction;
190 QAction *m_saveAsAction;
191 QAction *m_closeCurrentEditorAction;
192 QAction *m_closeAllEditorsAction;
193 QAction *m_closeOtherEditorsAction;
194 QAction *m_gotoNextDocHistoryAction;
195 QAction *m_gotoPreviousDocHistoryAction;
196 QAction *m_goBackAction;
197 QAction *m_goForwardAction;
198 QAction *m_openInExternalEditorAction;
199 QAction *m_splitAction;
200 QAction *m_splitSideBySideAction;
201 QAction *m_removeCurrentSplitAction;
202 QAction *m_removeAllSplitsAction;
203 QAction *m_gotoOtherSplitAction;
205 Internal::OpenEditorsWindow *m_windowPopup;
206 Internal::EditorClosingCoreListener *m_coreListener;
208 QMap<QString, QVariant> m_editorStates;
209 Internal::OpenEditorsViewFactory *m_openEditorsFactory;
212 QString selectedFilter;
214 OpenEditorsModel *m_editorModel;
215 QString m_externalEditor;
217 IFile::ReloadSetting m_reloadSetting;
221 EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) :
225 m_revertToSavedAction(new QAction(EditorManager::tr("Revert to Saved"), parent)),
226 m_saveAction(new QAction(parent)),
227 m_saveAsAction(new QAction(parent)),
228 m_closeCurrentEditorAction(new QAction(EditorManager::tr("Close"), parent)),
229 m_closeAllEditorsAction(new QAction(EditorManager::tr("Close All"), parent)),
230 m_closeOtherEditorsAction(new QAction(EditorManager::tr("Close Others"), parent)),
231 m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Open Document in History"), parent)),
232 m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Open Document in History"), parent)),
233 m_goBackAction(new QAction(QIcon(QLatin1String(Constants::ICON_PREV)), EditorManager::tr("Go Back"), parent)),
234 m_goForwardAction(new QAction(QIcon(QLatin1String(Constants::ICON_NEXT)), EditorManager::tr("Go Forward"), parent)),
235 m_openInExternalEditorAction(new QAction(EditorManager::tr("Open in External Editor"), parent)),
238 m_reloadSetting(IFile::AlwaysAsk)
240 m_editorModel = new OpenEditorsModel(parent);
243 EditorManagerPrivate::~EditorManagerPrivate()
245 // clearNavigationHistory();
248 EditorManager *EditorManager::m_instance = 0;
250 static Command *createSeparator(ActionManager *am, QObject *parent,
252 const Context &context)
254 QAction *tmpaction = new QAction(parent);
255 tmpaction->setSeparator(true);
256 Command *cmd = am->registerAction(tmpaction, name, context);
260 EditorManager::EditorManager(ICore *core, QWidget *parent) :
262 m_d(new EditorManagerPrivate(core, parent))
266 connect(m_d->m_core, SIGNAL(contextAboutToChange(Core::IContext *)),
267 this, SLOT(handleContextChange(Core::IContext *)));
269 const Context editManagerContext(Constants::C_EDITORMANAGER);
270 // combined context for edit & design modes
271 const Context editDesignContext(Constants::C_EDITORMANAGER, Constants::C_DESIGN_MODE);
273 ActionManager *am = m_d->m_core->actionManager();
274 ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
277 m_d->m_revertToSavedAction->setIcon(QIcon::fromTheme(QLatin1String("document-revert")));
278 Command *cmd = am->registerAction(m_d->m_revertToSavedAction,
279 Constants::REVERTTOSAVED, editManagerContext);
280 cmd->setAttribute(Command::CA_UpdateText);
281 cmd->setDefaultText(tr("Revert File to Saved"));
282 mfile->addAction(cmd, Constants::G_FILE_SAVE);
283 connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved()));
286 am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
287 connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));
290 am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
291 connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs()));
294 ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
296 // Window menu separators
297 QAction *tmpaction = new QAction(this);
298 tmpaction->setSeparator(true);
299 cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Split"), editManagerContext);
300 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
302 tmpaction = new QAction(this);
303 tmpaction->setSeparator(true);
304 cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Navigate"), editManagerContext);
305 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
308 cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext);
309 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W")));
310 cmd->setAttribute(Core::Command::CA_UpdateText);
311 cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text());
312 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
313 connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor()));
316 // workaround for QTCREATORBUG-72
317 QShortcut *sc = new QShortcut(parent);
318 cmd = am->registerShortcut(sc, Constants::CLOSE_ALTERNATIVE, editManagerContext);
319 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F4")));
320 cmd->setDefaultText(EditorManager::tr("Close"));
321 connect(sc, SIGNAL(activated()), this, SLOT(closeEditor()));
325 cmd = am->registerAction(m_d->m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext);
326 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W")));
327 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
328 connect(m_d->m_closeAllEditorsAction, SIGNAL(triggered()), this, SLOT(closeAllEditors()));
330 // Close All Others Action
331 cmd = am->registerAction(m_d->m_closeOtherEditorsAction, Constants::CLOSEOTHERS, editManagerContext);
332 mfile->addAction(cmd, Constants::G_FILE_CLOSE);
333 cmd->setAttribute(Core::Command::CA_UpdateText);
334 connect(m_d->m_closeOtherEditorsAction, SIGNAL(triggered()), this, SLOT(closeOtherEditors()));
336 // Goto Previous In History Action
337 cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext);
339 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Tab")));
341 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab")));
343 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
344 connect(m_d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory()));
346 // Goto Next In History Action
347 cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editDesignContext);
349 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab")));
351 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab")));
353 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
354 connect(m_d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory()));
356 // Go back in navigation history
357 cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editDesignContext);
359 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Left")));
361 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left")));
363 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
364 connect(m_d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory()));
366 // Go forward in navigation history
367 cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editDesignContext);
369 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Right")));
371 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right")));
373 mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
374 connect(m_d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory()));
377 QString prefix = tr("Meta+E");
379 QString prefix = tr("Ctrl+E");
382 m_d->m_splitAction = new QAction(tr("Split"), this);
383 cmd = am->registerAction(m_d->m_splitAction, Constants::SPLIT, editManagerContext);
384 cmd->setDefaultKeySequence(QKeySequence(tr("%1,2").arg(prefix)));
385 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
386 connect(m_d->m_splitAction, SIGNAL(triggered()), this, SLOT(split()));
388 m_d->m_splitSideBySideAction = new QAction(tr("Split Side by Side"), this);
389 cmd = am->registerAction(m_d->m_splitSideBySideAction, Constants::SPLIT_SIDE_BY_SIDE, editManagerContext);
390 cmd->setDefaultKeySequence(QKeySequence(tr("%1,3").arg(prefix)));
391 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
392 connect(m_d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide()));
394 m_d->m_removeCurrentSplitAction = new QAction(tr("Remove Current Split"), this);
395 cmd = am->registerAction(m_d->m_removeCurrentSplitAction, Constants::REMOVE_CURRENT_SPLIT, editManagerContext);
396 cmd->setDefaultKeySequence(QKeySequence(tr("%1,0").arg(prefix)));
397 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
398 connect(m_d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
400 m_d->m_removeAllSplitsAction = new QAction(tr("Remove All Splits"), this);
401 cmd = am->registerAction(m_d->m_removeAllSplitsAction, Constants::REMOVE_ALL_SPLITS, editManagerContext);
402 cmd->setDefaultKeySequence(QKeySequence(tr("%1,1").arg(prefix)));
403 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
404 connect(m_d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
406 m_d->m_gotoOtherSplitAction = new QAction(tr("Go to Next Split"), this);
407 cmd = am->registerAction(m_d->m_gotoOtherSplitAction, Constants::GOTO_OTHER_SPLIT, editManagerContext);
408 cmd->setDefaultKeySequence(QKeySequence(tr("%1,o").arg(prefix)));
409 mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
410 connect(m_d->m_gotoOtherSplitAction, SIGNAL(triggered()), this, SLOT(gotoOtherSplit()));
412 ActionContainer *medit = am->actionContainer(Constants::M_EDIT);
413 ActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED);
414 medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED);
415 advancedMenu->menu()->setTitle(tr("&Advanced"));
416 advancedMenu->appendGroup(Constants::G_EDIT_FORMAT);
417 advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING);
418 advancedMenu->appendGroup(Constants::G_EDIT_BLOCKS);
419 advancedMenu->appendGroup(Constants::G_EDIT_FONT);
420 advancedMenu->appendGroup(Constants::G_EDIT_EDITOR);
422 // Advanced menu separators
423 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Collapsing"), editManagerContext);
424 advancedMenu->addAction(cmd, Constants::G_EDIT_COLLAPSING);
425 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Blocks"), editManagerContext);
426 advancedMenu->addAction(cmd, Constants::G_EDIT_BLOCKS);
427 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Font"), editManagerContext);
428 advancedMenu->addAction(cmd, Constants::G_EDIT_FONT);
429 cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Editor"), editManagerContext);
430 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
432 cmd = am->registerAction(m_d->m_openInExternalEditorAction, Constants::OPEN_IN_EXTERNAL_EDITOR, editManagerContext);
433 cmd->setDefaultKeySequence(QKeySequence(tr("Alt+V,Alt+I")));
434 advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
435 connect(m_d->m_openInExternalEditorAction, SIGNAL(triggered()), this, SLOT(openInExternalEditor()));
437 // Connect to VariableManager for CURRENT_DOCUMENT variable setting
438 VariableManager *vm = VariableManager::instance();
439 connect(this, SIGNAL(currentEditorChanged(Core::IEditor *)),
440 vm, SLOT(updateCurrentDocument(Core::IEditor *)));
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()
461 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
462 if (m_d->m_coreListener) {
463 pm->removeObject(m_d->m_coreListener);
464 delete m_d->m_coreListener;
466 pm->removeObject(m_d->m_openEditorsFactory);
467 delete m_d->m_openEditorsFactory;
472 void EditorManager::init()
474 m_d->m_coreListener = new EditorClosingCoreListener(this);
475 pluginManager()->addObject(m_d->m_coreListener);
477 m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
478 pluginManager()->addObject(m_d->m_openEditorsFactory);
482 EditorToolBar *EditorManager::createToolBar(QWidget *parent)
484 return new EditorToolBar(parent);
487 QString EditorManager::defaultExternalEditor() const
490 return ConsoleProcess::defaultTerminalEmulator() + QLatin1String(
494 " -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\"");
496 return QLatin1String("notepad %f");
500 void EditorManager::removeEditor(IEditor *editor)
502 bool isDuplicate = m_d->m_editorModel->isDuplicate(editor);
503 m_d->m_editorModel->removeEditor(editor);
505 m_d->m_core->fileManager()->removeFile(editor->file());
507 m_d->m_core->removeContextObject(editor);
510 void EditorManager::handleContextChange(Core::IContext *context)
512 if (debugEditorManager)
513 qDebug() << Q_FUNC_INFO;
514 IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0;
516 setCurrentEditor(editor);
522 void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
527 if (m_d->m_currentEditor == editor)
529 if (m_d->m_currentEditor && !ignoreNavigationHistory)
530 addCurrentPositionToNavigationHistory();
532 m_d->m_currentEditor = editor;
534 if (SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor))
535 splitterOrView->view()->setCurrentEditor(editor);
536 m_d->m_view->updateEditorHistory(editor); // the global view should have a complete history
539 emit currentEditorChanged(editor);
543 void EditorManager::setCurrentView(Core::Internal::SplitterOrView *view)
545 if (view == m_d->m_currentView)
548 SplitterOrView *old = m_d->m_currentView;
549 m_d->m_currentView = view;
556 if (view && !view->editor())
560 Core::Internal::SplitterOrView *EditorManager::currentSplitterOrView() const
562 SplitterOrView *view = m_d->m_currentView;
564 view = m_d->m_currentEditor?
565 m_d->m_splitter->findView(m_d->m_currentEditor):
566 m_d->m_splitter->findFirstView();
568 return m_d->m_splitter;
572 Core::Internal::EditorView *EditorManager::currentEditorView() const
574 return currentSplitterOrView()->view();
577 QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
579 QList<IEditor *> found;
580 QString fixedname = FileManager::fixFileName(filename);
581 foreach (IEditor *editor, openedEditors()) {
582 if (fixedname == FileManager::fixFileName(editor->file()->fileName()))
588 QList<IEditor *> EditorManager::editorsForFile(IFile *file) const
590 QList<IEditor *> found;
591 foreach (IEditor *editor, openedEditors()) {
592 if (editor->file() == file)
598 IEditor *EditorManager::currentEditor() const
600 return m_d->m_currentEditor;
603 void EditorManager::emptyView(Core::Internal::EditorView *view)
608 QList<IEditor *> editors = view->editors();
609 foreach (IEditor *editor, editors) {
610 if (!m_d->m_editorModel->isDuplicate(editor)) {
611 editors.removeAll(editor);
612 view->removeEditor(editor);
615 emit editorAboutToClose(editor);
616 removeEditor(editor);
617 view->removeEditor(editor);
619 emit editorsClosed(editors);
620 foreach (IEditor *editor, editors) {
625 void EditorManager::closeView(Core::Internal::EditorView *view)
630 if (view == m_d->m_view) {
631 if (IEditor *e = view->currentEditor())
632 closeEditors(QList<IEditor *>() << e);
636 if (IEditor *e = view->currentEditor()) {
638 when we are closing a view with an original editor which has
639 duplicates, then make one of the duplicates the original.
640 Otherwise the original would be kept around and the user might
641 experience jumping to a missleading position within the file when
642 visiting the file again. With the code below, the position within
643 the file will be the position of the first duplicate which is still
646 if (!m_d->m_editorModel->isDuplicate(e)) {
647 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(e);
648 if (!duplicates.isEmpty()) {
649 m_d->m_editorModel->makeOriginal(duplicates.first());
656 SplitterOrView *splitterOrView = m_d->m_splitter->findView(view);
657 Q_ASSERT(splitterOrView);
658 Q_ASSERT(splitterOrView->view() == view);
659 SplitterOrView *splitter = m_d->m_splitter->findSplitter(splitterOrView);
660 Q_ASSERT(splitterOrView->hasEditors() == false);
661 splitterOrView->hide();
662 delete splitterOrView;
666 SplitterOrView *newCurrent = splitter->findFirstView();
668 if (IEditor *e = newCurrent->editor()) {
669 activateEditor(newCurrent->view(), e);
671 setCurrentView(newCurrent);
677 EditorManager::editorsForFiles(QList<IFile*> files) const
679 const QList<IEditor *> editors = openedEditors();
680 QSet<IEditor *> found;
681 foreach (IFile *file, files) {
682 foreach (IEditor *editor, editors) {
683 if (editor->file() == file && !found.contains(editor)) {
688 return found.toList();
691 QList<IFile *> EditorManager::filesForEditors(QList<IEditor *> editors) const
693 QSet<IEditor *> handledEditors;
694 QList<IFile *> files;
695 foreach (IEditor *editor, editors) {
696 if (!handledEditors.contains(editor)) {
697 files << editor->file();
698 handledEditors.insert(editor);
704 bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
706 m_d->m_editorModel->removeAllRestoredEditors();
707 if (closeEditors(openedEditors(), askAboutModifiedEditors)) {
708 // m_d->clearNavigationHistory();
714 void EditorManager::closeOtherEditors(IEditor *editor)
716 m_d->m_editorModel->removeAllRestoredEditors();
717 QList<IEditor*> editors = openedEditors();
718 editors.removeAll(editor);
719 closeEditors(editors, true);
722 void EditorManager::closeOtherEditors()
724 IEditor *current = currentEditor();
725 QTC_ASSERT(current, return);
726 closeOtherEditors(current);
729 // SLOT connected to action
730 void EditorManager::closeEditor()
732 if (!m_d->m_currentEditor)
734 addCurrentPositionToNavigationHistory();
735 closeEditor(m_d->m_currentEditor);
738 void EditorManager::closeEditor(Core::IEditor *editor)
742 closeEditors(QList<IEditor *>() << editor);
745 void EditorManager::closeEditor(const QModelIndex &index)
747 IEditor *editor = index.data(Qt::UserRole).value<Core::IEditor*>();
751 m_d->m_editorModel->removeEditor(index);
754 bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
756 if (editorsToClose.isEmpty())
759 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
761 bool closingFailed = false;
762 QList<IEditor*> acceptedEditors;
763 //ask all core listeners to check whether the editor can be closed
764 const QList<ICoreListener *> listeners =
765 pluginManager()->getObjects<ICoreListener>();
766 foreach (IEditor *editor, editorsToClose) {
767 bool editorAccepted = true;
768 if (m_d->m_editorModel->isDuplicate(editor))
769 editor = m_d->m_editorModel->originalForDuplicate(editor);
770 foreach (ICoreListener *listener, listeners) {
771 if (!listener->editorAboutToClose(editor)) {
772 editorAccepted = false;
773 closingFailed = false;
778 acceptedEditors.append(editor);
780 if (acceptedEditors.isEmpty())
782 //ask whether to save modified files
783 if (askAboutModifiedEditors) {
784 bool cancelled = false;
785 QList<IFile*> list = m_d->m_core->fileManager()->
786 saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled);
789 if (!list.isEmpty()) {
790 closingFailed = true;
791 QSet<IEditor*> skipSet = editorsForFiles(list).toSet();
792 acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList();
795 if (acceptedEditors.isEmpty())
799 foreach(IEditor *editor, acceptedEditors)
800 acceptedEditors += m_d->m_editorModel->duplicatesFor(editor);
802 QList<EditorView*> closedViews;
804 // remove the editors
805 foreach (IEditor *editor, acceptedEditors) {
806 emit editorAboutToClose(editor);
807 if (!editor->file()->fileName().isEmpty()) {
808 QByteArray state = editor->saveState();
809 if (!state.isEmpty())
810 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
813 removeEditor(editor);
814 if (SplitterOrView *view = m_d->m_splitter->findView(editor)) {
815 if (editor == view->view()->currentEditor())
816 closedViews += view->view();
817 view->view()->removeEditor(editor);
821 foreach (EditorView *view, closedViews) {
822 IEditor *newCurrent = view->currentEditor();
824 newCurrent = pickUnusedEditor();
826 activateEditor(view, newCurrent, NoActivate);
828 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
830 activateEditor(idx, view, NoActivate);
834 emit editorsClosed(acceptedEditors);
836 foreach (IEditor *editor, acceptedEditors) {
840 if (currentSplitterOrView) {
841 if (IEditor *editor = currentSplitterOrView->editor())
842 activateEditor(currentSplitterOrView->view(), editor);
845 if (!currentEditor()) {
846 emit currentEditorChanged(0);
850 return !closingFailed;
853 void EditorManager::closeDuplicate(Core::IEditor *editor)
856 IEditor *original = editor;
857 if (m_d->m_editorModel->isDuplicate(editor))
858 original= m_d->m_editorModel->originalForDuplicate(editor);
859 QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(original);
861 if (duplicates.isEmpty()) {
866 if (original== editor)
867 m_d->m_editorModel->makeOriginal(duplicates.first());
869 SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
871 emit editorAboutToClose(editor);
873 if(m_d->m_splitter->findView(editor)) {
874 EditorView *view = m_d->m_splitter->findView(editor)->view();
875 removeEditor(editor);
876 view->removeEditor(editor);
878 IEditor *newCurrent = view->currentEditor();
880 newCurrent = pickUnusedEditor();
882 activateEditor(view, newCurrent, NoActivate);
884 QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
886 activateEditor(idx, view, NoActivate);
890 emit editorsClosed(QList<IEditor*>() << editor);
892 if (currentSplitterOrView) {
893 if (IEditor *currentEditor = currentSplitterOrView->editor())
894 activateEditor(currentSplitterOrView->view(), currentEditor);
898 Core::IEditor *EditorManager::pickUnusedEditor() const
900 foreach (IEditor *editor, openedEditors()) {
901 SplitterOrView *view = m_d->m_splitter->findView(editor);
902 if (!view || view->editor() != editor)
908 Core::IEditor *EditorManager::activateEditor(const QModelIndex &index, Internal::EditorView *view, OpenEditorFlags flags)
910 IEditor *editor = index.data(Qt::UserRole).value<IEditor*>();
912 return activateEditor(view, editor, flags);
915 QString fileName = index.data(Qt::UserRole + 1).toString();
916 QString id = index.data(Qt::UserRole + 2).toString();
917 return openEditor(view, fileName, id, flags);
920 Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)
922 Q_ASSERT(view && editor);
924 if (view->currentEditor() && view->currentEditor()->file() == editor->file())
925 editor = view->currentEditor();
927 if (!view->hasEditor(editor)) {
928 bool duplicateSupported = editor->duplicateSupported();
929 if (SplitterOrView *sourceView = m_d->m_splitter->findView(editor)) {
930 if (editor != sourceView->editor() || !duplicateSupported) {
931 sourceView->view()->removeEditor(editor);
932 view->addEditor(editor);
933 view->setCurrentEditor(editor);
934 if (!sourceView->editor()) {
935 if (IEditor *replacement = pickUnusedEditor()) {
936 sourceView->view()->addEditor(replacement);
940 } else if (duplicateSupported) {
941 editor = duplicateEditor(editor);
943 m_d->m_editorModel->makeOriginal(editor);
946 view->addEditor(editor);
951 Core::IEditor *EditorManager::activateEditor(Core::IEditor *editor, OpenEditorFlags flags)
953 return activateEditor(0, editor, flags);
956 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags)
959 view = currentEditorView();
964 if (!m_d->m_currentEditor)
965 setCurrentEditor(0, (flags & IgnoreNavigationHistory));
969 editor = placeEditor(view, editor);
971 if (!(flags & NoActivate)) {
972 setCurrentEditor(editor, (flags & IgnoreNavigationHistory));
973 if (!(flags & NoModeSwitch)) {
974 const QString preferredMode = editor->preferredMode();
975 if (preferredMode.isEmpty() || preferredMode == Core::Constants::MODE_EDIT) {
976 ensureEditorManagerVisible();
978 ModeManager::instance()->activateMode(preferredMode);
982 editor->widget()->setFocus();
987 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IFile *file, OpenEditorFlags flags)
989 const QList<IEditor*> editors = editorsForFile(file);
990 if (editors.isEmpty())
993 return activateEditor(view, editors.first(), flags);
996 /* For something that has a 'QStringList mimeTypes' (IEditorFactory
997 * or IExternalEditor), find the one best matching the mimetype passed in.
998 * Recurse over the parent classes of the mimetype to find them. */
999 template <class EditorFactoryLike>
1000 static void mimeTypeFactoryRecursion(const MimeDatabase *db,
1001 const MimeType &mimeType,
1002 const QList<EditorFactoryLike*> &allFactories,
1003 bool firstMatchOnly,
1004 QList<EditorFactoryLike*> *list)
1006 typedef typename QList<EditorFactoryLike*>::const_iterator EditorFactoryLikeListConstIterator;
1007 // Loop factories to find type
1008 const QString type = mimeType.type();
1009 const EditorFactoryLikeListConstIterator fcend = allFactories.constEnd();
1010 for (EditorFactoryLikeListConstIterator fit = allFactories.constBegin(); fit != fcend; ++fit) {
1011 // Exclude duplicates when recursing over xml or C++ -> C -> text.
1012 EditorFactoryLike *factory = *fit;
1013 if (!list->contains(factory) && factory->mimeTypes().contains(type)) {
1014 list->push_back(*fit);
1020 // Any parent mime type classes? -> recurse
1021 QStringList parentTypes = mimeType.subClassesOf();
1022 if (parentTypes.empty())
1024 const QStringList::const_iterator pcend = parentTypes .constEnd();
1025 for (QStringList::const_iterator pit = parentTypes .constBegin(); pit != pcend; ++pit) {
1026 if (const MimeType parent = db->findByType(*pit))
1027 mimeTypeFactoryRecursion(db, parent, allFactories, firstMatchOnly, list);
1031 EditorManager::EditorFactoryList
1032 EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const
1034 EditorFactoryList rc;
1035 const EditorFactoryList allFactories = pluginManager()->getObjects<IEditorFactory>();
1036 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allFactories, bestMatchOnly, &rc);
1037 if (debugEditorManager)
1038 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1042 EditorManager::ExternalEditorList
1043 EditorManager::externalEditors(const MimeType &mimeType, bool bestMatchOnly) const
1045 ExternalEditorList rc;
1046 const ExternalEditorList allEditors = pluginManager()->getObjects<IExternalEditor>();
1047 mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allEditors, bestMatchOnly, &rc);
1048 if (debugEditorManager)
1049 qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
1053 /* For something that has a 'QString id' (IEditorFactory
1054 * or IExternalEditor), find the one matching a id. */
1055 template <class EditorFactoryLike>
1056 inline EditorFactoryLike *findById(ExtensionSystem::PluginManager *pm,
1059 const QList<EditorFactoryLike *> factories = pm->template getObjects<EditorFactoryLike>();
1060 foreach(EditorFactoryLike *efl, factories)
1061 if (id == efl->id())
1066 IEditor *EditorManager::createEditor(const QString &editorId,
1067 const QString &fileName)
1069 if (debugEditorManager)
1070 qDebug() << Q_FUNC_INFO << editorId << fileName;
1072 EditorFactoryList factories;
1073 if (editorId.isEmpty()) {
1074 const QFileInfo fileInfo(fileName);
1075 // Find by mime type
1076 MimeType mimeType = m_d->m_core->mimeDatabase()->findByFile(fileInfo);
1078 qWarning("%s unable to determine mime type of %s/%s. Falling back to text/plain",
1079 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1080 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("text/plain"));
1082 // open text files > 48 MB in binary editor
1083 if (fileInfo.size() > maxTextFileSize() && mimeType.type().startsWith(QLatin1String("text")))
1084 mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("application/octet-stream"));
1085 factories = editorFactories(mimeType, true);
1087 // Find by editor id
1088 if (IEditorFactory *factory = findById<IEditorFactory>(pluginManager(), editorId))
1089 factories.push_back(factory);
1091 if (factories.empty()) {
1092 qWarning("%s: unable to find an editor factory for the file '%s', editor Id '%s'.",
1093 Q_FUNC_INFO, fileName.toUtf8().constData(), editorId.toUtf8().constData());
1097 IEditor *editor = factories.front()->createEditor(this);
1099 connect(editor, SIGNAL(changed()), this, SLOT(updateActions()));
1101 emit editorCreated(editor, fileName);
1105 void EditorManager::addEditor(IEditor *editor, bool isDuplicate)
1109 m_d->m_core->addContextObject(editor);
1111 m_d->m_editorModel->addEditor(editor, isDuplicate);
1113 const bool isTemporary = editor->isTemporary();
1114 const bool addWatcher = !isTemporary;
1115 m_d->m_core->fileManager()->addFile(editor->file(), addWatcher);
1117 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1119 emit editorOpened(editor);
1122 // Run the OpenWithDialog and return the editor id
1123 // selected by the user.
1124 QString EditorManager::getOpenWithEditorId(const QString &fileName,
1125 bool *isExternalEditor) const
1127 // Collect editors that can open the file
1128 const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName);
1131 QStringList allEditorIds;
1132 QStringList externalEditorIds;
1134 const EditorFactoryList editors = editorFactories(mt, false);
1135 const int size = editors.size();
1136 for (int i = 0; i < size; i++) {
1137 allEditorIds.push_back(editors.at(i)->id());
1140 const ExternalEditorList exEditors = externalEditors(mt, false);
1141 const int esize = exEditors.size();
1142 for (int i = 0; i < esize; i++) {
1143 externalEditorIds.push_back(exEditors.at(i)->id());
1144 allEditorIds.push_back(exEditors.at(i)->id());
1146 if (allEditorIds.empty())
1149 OpenWithDialog dialog(fileName, m_d->m_core->mainWindow());
1150 dialog.setEditors(allEditorIds);
1151 dialog.setCurrentEditor(0);
1152 if (dialog.exec() != QDialog::Accepted)
1154 const QString selectedId = dialog.editor();
1155 if (isExternalEditor)
1156 *isExternalEditor = externalEditorIds.contains(selectedId);
1160 static QString formatFileFilters(const Core::ICore *core, QString *selectedFilter)
1164 // Compile list of filter strings
1165 QStringList filters = core->mimeDatabase()->filterStrings();
1167 selectedFilter->clear();
1168 if (filters.empty())
1171 const QString filterSeparator = QLatin1String(";;");
1172 foreach (const QString &filterString, filters) {
1174 rc += filterSeparator;
1178 // prepend all files filter
1179 // prepending instead of appending to work around a bug in Qt/Mac
1180 QString allFilesFilter = EditorManager::tr("All Files (*)");
1182 allFilesFilter += filterSeparator;
1183 rc.prepend(allFilesFilter);
1184 *selectedFilter = allFilesFilter;
1189 IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorId,
1190 OpenEditorFlags flags, bool *newEditor)
1192 return openEditor(0, fileName, editorId, flags, newEditor);
1195 IEditor *EditorManager::openEditor(Core::Internal::EditorView *view, const QString &fileName,
1196 const QString &editorId, OpenEditorFlags flags, bool *newEditor)
1198 if (debugEditorManager)
1199 qDebug() << Q_FUNC_INFO << fileName << editorId;
1201 if (fileName.isEmpty())
1207 const QList<IEditor *> editors = editorsForFileName(fileName);
1208 if (!editors.isEmpty())
1209 return activateEditor(view, editors.first(), flags);
1211 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1212 IEditor *editor = createEditor(editorId, fileName);
1213 // If we could not open the file in the requested editor, fall
1214 // back to the default editor:
1216 editor = createEditor(QString(), fileName);
1217 if (!editor || !editor->open(fileName)) {
1218 QApplication::restoreOverrideCursor();
1219 QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), tr("Cannot open file %1!").arg(QDir::toNativeSeparators(fileName)));
1229 IEditor *result = activateEditor(view, editor, flags);
1230 if (editor == result)
1231 restoreEditorState(editor);
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 if (m_d->fileFilters.isEmpty())
1253 m_d->fileFilters = formatFileFilters(m_d->m_core, &m_d->selectedFilter);
1254 return ICore::instance()->fileManager()->getOpenFileNames(m_d->fileFilters,
1255 QString(), &m_d->selectedFilter);
1258 void EditorManager::ensureEditorManagerVisible()
1261 m_d->m_core->modeManager()->activateMode(Constants::MODE_EDIT);
1264 IEditor *EditorManager::openEditorWithContents(const QString &editorId,
1265 QString *titlePattern,
1266 const QString &contents)
1268 if (debugEditorManager)
1269 qDebug() << Q_FUNC_INFO << editorId << titlePattern << contents;
1271 if (editorId.isEmpty())
1274 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1275 IEditor *edt = createEditor(editorId);
1277 QApplication::restoreOverrideCursor();
1281 if (!edt->createNew(contents)) {
1282 QApplication::restoreOverrideCursor();
1288 QString title = edt->displayName();
1291 const QChar dollar = QLatin1Char('$');
1292 const QChar dot = QLatin1Char('.');
1294 QString base = *titlePattern;
1296 base = QLatin1String("unnamed$");
1297 if (base.contains(dollar)) {
1299 QSet<QString> docnames;
1300 foreach (IEditor *editor, openedEditors()) {
1301 QString name = editor->file()->fileName();
1302 if (name.isEmpty()) {
1303 name = editor->displayName();
1304 name.remove(QLatin1Char('*'));
1306 name = QFileInfo(name).completeBaseName();
1313 title.replace(QString(dollar), QString::number(i++));
1314 } while (docnames.contains(title));
1316 title = *titlePattern;
1318 *titlePattern = title;
1320 edt->setDisplayName(title);
1322 QApplication::restoreOverrideCursor();
1326 bool EditorManager::hasEditor(const QString &fileName) const
1328 return !editorsForFileName(fileName).isEmpty();
1331 void EditorManager::restoreEditorState(IEditor *editor)
1333 QTC_ASSERT(editor, return);
1334 QString fileName = editor->file()->fileName();
1335 editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray());
1338 bool EditorManager::saveEditor(IEditor *editor)
1340 return saveFile(editor);
1343 bool EditorManager::saveFile(IEditor *editor)
1346 editor = currentEditor();
1350 IFile *file = editor->file();
1351 file->checkPermissions();
1353 const QString &fileName = file->fileName();
1355 if (fileName.isEmpty())
1356 return saveFileAs(editor);
1358 bool success = false;
1360 // try saving, no matter what isReadOnly tells us
1361 m_d->m_core->fileManager()->blockFileChange(file);
1362 success = file->save(fileName);
1363 m_d->m_core->fileManager()->unblockFileChange(file);
1366 MakeWritableResult answer =
1367 makeEditorWritable(editor);
1368 if (answer == Failed)
1370 if (answer == SavedAs)
1373 file->checkPermissions();
1375 m_d->m_core->fileManager()->blockFileChange(file);
1376 success = file->save(fileName);
1377 m_d->m_core->fileManager()->unblockFileChange(file);
1380 if (success && !editor->isTemporary())
1381 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1386 EditorManager::ReadOnlyAction
1387 EditorManager::promptReadOnlyFile(const QString &fileName,
1388 const IVersionControl *versionControl,
1390 bool displaySaveAsButton)
1392 // Version Control: If automatic open is desired, open right away.
1393 bool promptVCS = false;
1394 if (versionControl && versionControl->supportsOperation(IVersionControl::OpenOperation)) {
1395 if (versionControl->settingsFlags() & IVersionControl::AutoOpen)
1400 // Create message box.
1401 QMessageBox msgBox(QMessageBox::Question, tr("File is Read Only"),
1402 tr("The file <i>%1</i> is read only.").arg(QDir::toNativeSeparators(fileName)),
1403 QMessageBox::Cancel, parent);
1405 QPushButton *vcsButton = 0;
1407 vcsButton = msgBox.addButton(tr("Open with VCS (%1)").arg(versionControl->displayName()), QMessageBox::AcceptRole);
1409 QPushButton *makeWritableButton = msgBox.addButton(tr("Make writable"), QMessageBox::AcceptRole);
1411 QPushButton *saveAsButton = 0;
1412 if (displaySaveAsButton)
1413 saveAsButton = msgBox.addButton(tr("Save as ..."), QMessageBox::ActionRole);
1415 msgBox.setDefaultButton(vcsButton ? vcsButton : makeWritableButton);
1418 QAbstractButton *clickedButton = msgBox.clickedButton();
1419 if (clickedButton == vcsButton)
1421 if (clickedButton == makeWritableButton)
1422 return RO_MakeWriteable;
1423 if (clickedButton == saveAsButton)
1430 EditorManager::makeEditorWritable(IEditor *editor)
1432 if (!editor || !editor->file())
1434 QString directory = QFileInfo(editor->file()->fileName()).absolutePath();
1435 IVersionControl *versionControl = m_d->m_core->vcsManager()->findVersionControlForDirectory(directory);
1436 IFile *file = editor->file();
1437 const QString &fileName = file->fileName();
1439 switch (promptReadOnlyFile(fileName, versionControl, m_d->m_core->mainWindow(), true)) {
1441 if (!versionControl->vcsOpen(fileName)) {
1442 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for editing with SCC."));
1445 file->checkPermissions();
1446 return OpenedWithVersionControl;
1447 case RO_MakeWriteable: {
1448 const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
1450 QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
1454 file->checkPermissions();
1455 return MadeWritable;
1457 return saveFileAs(editor) ? SavedAs : Failed;
1464 bool EditorManager::saveFileAs(IEditor *editor)
1467 editor = currentEditor();
1471 QString absoluteFilePath = m_d->m_core->fileManager()->getSaveAsFileName(editor->file());
1472 if (absoluteFilePath.isEmpty())
1474 if (absoluteFilePath != editor->file()->fileName()) {
1475 const QList<IEditor *> existList = editorsForFileName(absoluteFilePath);
1476 if (!existList.isEmpty()) {
1477 closeEditors(existList, false);
1481 m_d->m_core->fileManager()->blockFileChange(editor->file());
1482 const bool success = editor->file()->save(absoluteFilePath);
1483 m_d->m_core->fileManager()->unblockFileChange(editor->file());
1484 editor->file()->checkPermissions();
1486 if (success && !editor->isTemporary())
1487 m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1493 void EditorManager::gotoNextDocHistory()
1495 OpenEditorsWindow *dialog = windowPopup();
1496 if (dialog->isVisible()) {
1497 dialog->selectNextEditor();
1499 EditorView *view = currentEditorView();
1500 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1501 dialog->selectNextEditor();
1502 showPopupOrSelectDocument();
1506 void EditorManager::gotoPreviousDocHistory()
1508 OpenEditorsWindow *dialog = windowPopup();
1509 if (dialog->isVisible()) {
1510 dialog->selectPreviousEditor();
1512 EditorView *view = currentEditorView();
1513 dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1514 dialog->selectPreviousEditor();
1515 showPopupOrSelectDocument();
1519 void EditorManager::makeCurrentEditorWritable()
1521 if (IEditor* curEditor = currentEditor())
1522 makeEditorWritable(curEditor);
1525 void EditorManager::updateActions()
1528 IEditor *curEditor = currentEditor();
1529 int openedCount = openedEditors().count() + m_d->m_editorModel->restoredEditors().count();
1533 if (!curEditor->file()->fileName().isEmpty()) {
1534 QFileInfo fi(curEditor->file()->fileName());
1535 fName = fi.fileName();
1537 fName = curEditor->displayName();
1541 window()->setWindowModified(curEditor->file()->isModified());
1543 if (curEditor->file()->isModified() && curEditor->file()->isReadOnly()) {
1544 // we are about to change a read-only file, warn user
1545 showEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"),
1546 tr("<b>Warning:</b> You are changing a read-only file."),
1547 tr("Make writable"), this, SLOT(makeCurrentEditorWritable()));
1549 hideEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"));
1552 } else { // curEditor
1553 window()->setWindowModified(false);
1557 m_d->m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified());
1558 m_d->m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed());
1559 m_d->m_revertToSavedAction->setEnabled(curEditor != 0
1560 && !curEditor->file()->fileName().isEmpty() && curEditor->file()->isModified());
1563 if (!fName.isEmpty())
1564 quotedName = '"' + fName + '"';
1566 m_d->m_saveAsAction->setText(tr("Save %1 &As...").arg(quotedName));
1567 m_d->m_saveAction->setText(tr("&Save %1").arg(quotedName));
1568 m_d->m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName));
1570 m_d->m_closeCurrentEditorAction->setEnabled(curEditor != 0);
1571 m_d->m_closeCurrentEditorAction->setText(tr("Close %1").arg(quotedName));
1572 m_d->m_closeAllEditorsAction->setEnabled(openedCount > 0);
1573 m_d->m_closeOtherEditorsAction->setEnabled(openedCount > 1);
1574 m_d->m_closeOtherEditorsAction->setText((openedCount > 1 ? tr("Close All Except %1").arg(quotedName) : tr("Close Others")));
1576 m_d->m_gotoNextDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1577 m_d->m_gotoPreviousDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0);
1578 EditorView *view = currentEditorView();
1579 m_d->m_goBackAction->setEnabled(view ? view->canGoBack() : false);
1580 m_d->m_goForwardAction->setEnabled(view ? view->canGoForward() : false);
1582 bool hasSplitter = m_d->m_splitter->isSplitter();
1583 m_d->m_removeCurrentSplitAction->setEnabled(hasSplitter);
1584 m_d->m_removeAllSplitsAction->setEnabled(hasSplitter);
1585 m_d->m_gotoOtherSplitAction->setEnabled(hasSplitter);
1587 m_d->m_openInExternalEditorAction->setEnabled(curEditor != 0);
1590 QList<IEditor*> EditorManager::openedEditors() const
1592 return m_d->m_editorModel->editors();
1595 OpenEditorsModel *EditorManager::openedEditorsModel() const
1597 return m_d->m_editorModel;
1600 void EditorManager::addCurrentPositionToNavigationHistory(IEditor *editor, const QByteArray &saveState)
1602 currentEditorView()->addCurrentPositionToNavigationHistory(editor, saveState);
1606 void EditorManager::cutForwardNavigationHistory()
1608 currentEditorView()->cutForwardNavigationHistory();
1612 void EditorManager::goBackInNavigationHistory()
1614 currentEditorView()->goBackInNavigationHistory();
1616 ensureEditorManagerVisible();
1620 void EditorManager::goForwardInNavigationHistory()
1622 currentEditorView()->goForwardInNavigationHistory();
1624 ensureEditorManagerVisible();
1627 OpenEditorsWindow *EditorManager::windowPopup() const
1629 return m_d->m_windowPopup;
1632 void EditorManager::showPopupOrSelectDocument() const
1634 if (QApplication::keyboardModifiers() == Qt::NoModifier) {
1635 windowPopup()->selectAndHide();
1637 // EditorManager is invisible when invoked from Design Mode.
1638 const QPoint p = isVisible() ?
1639 mapToGlobal(QPoint(0, 0)) :
1640 m_d->m_core->mainWindow()->mapToGlobal(QPoint(0, 0));
1641 windowPopup()->move((width()-m_d->m_windowPopup->width())/2 + p.x(),
1642 (height()-m_d->m_windowPopup->height())/2 + p.y());
1643 windowPopup()->setVisible(true);
1647 QByteArray EditorManager::saveState() const
1650 QDataStream stream(&bytes, QIODevice::WriteOnly);
1652 stream << QByteArray("EditorManagerV4");
1654 QList<IEditor *> editors = openedEditors();
1655 foreach (IEditor *editor, editors) {
1656 if (!editor->file()->fileName().isEmpty()) {
1657 QByteArray state = editor->saveState();
1658 if (!state.isEmpty())
1659 m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
1663 stream << m_d->m_editorStates;
1665 QList<OpenEditorsModel::Entry> entries = m_d->m_editorModel->entries();
1666 stream << entries.count();
1668 foreach (const OpenEditorsModel::Entry &entry, entries) {
1669 stream << entry.fileName() << entry.displayName() << entry.id().toUtf8();
1672 stream << m_d->m_splitter->saveState();
1677 bool EditorManager::restoreState(const QByteArray &state)
1679 closeAllEditors(true);
1681 QDataStream stream(state);
1686 if (version != "EditorManagerV4")
1689 QMap<QString, QVariant> editorstates;
1691 QApplication::setOverrideCursor(Qt::WaitCursor);
1693 stream >> editorstates;
1695 QMapIterator<QString, QVariant> i(editorstates);
1696 while (i.hasNext()) {
1698 m_d->m_editorStates.insert(i.key(), i.value());
1701 int editorCount = 0;
1702 stream >> editorCount;
1703 while (--editorCount >= 0) {
1706 QString displayName;
1707 stream >> displayName;
1711 if (!fileName.isEmpty() && !displayName.isEmpty())
1712 m_d->m_editorModel->addRestoredEditor(fileName, displayName, QString::fromUtf8(id));
1715 QByteArray splitterstates;
1716 stream >> splitterstates;
1717 m_d->m_splitter->restoreState(splitterstates);
1719 // splitting and stuff results in focus trouble, that's why we set the focus again after restoration
1720 ensureEditorManagerVisible();
1721 if (m_d->m_currentEditor) {
1722 m_d->m_currentEditor->widget()->setFocus();
1723 } else if (Core::Internal::SplitterOrView *view = currentSplitterOrView()) {
1724 if (IEditor *e = view->editor())
1725 e->widget()->setFocus();
1726 else if (view->view())
1727 view->view()->setFocus();
1730 QApplication::restoreOverrideCursor();
1735 static const char * const documentStatesKey = "EditorManager/DocumentStates";
1736 static const char * const externalEditorKey = "EditorManager/ExternalEditorCommand";
1737 static const char * const reloadBehaviorKey = "EditorManager/ReloadBehavior";
1739 void EditorManager::saveSettings()
1741 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1742 settings->setValue(QLatin1String(documentStatesKey), m_d->m_editorStates);
1743 settings->setValue(QLatin1String(externalEditorKey), m_d->m_externalEditor);
1744 settings->setValue(QLatin1String(reloadBehaviorKey), m_d->m_reloadSetting);
1747 void EditorManager::readSettings()
1749 // Backward compatibility to old locations for these settings
1750 QSettings *qs = m_d->m_core->settings();
1751 if (qs->contains(QLatin1String(documentStatesKey))) {
1752 m_d->m_editorStates = qs->value(QLatin1String(documentStatesKey))
1753 .value<QMap<QString, QVariant> >();
1754 qs->remove(QLatin1String(documentStatesKey));
1756 if (qs->contains(QLatin1String(externalEditorKey))) {
1757 m_d->m_externalEditor = qs->value(QLatin1String(externalEditorKey)).toString();
1758 qs->remove(QLatin1String(externalEditorKey));
1761 SettingsDatabase *settings = m_d->m_core->settingsDatabase();
1762 if (settings->contains(QLatin1String(documentStatesKey)))
1763 m_d->m_editorStates = settings->value(QLatin1String(documentStatesKey))
1764 .value<QMap<QString, QVariant> >();
1765 if (settings->contains(QLatin1String(externalEditorKey)))
1766 m_d->m_externalEditor = settings->value(QLatin1String(externalEditorKey)).toString();
1768 if (settings->contains(QLatin1String(reloadBehaviorKey)))
1769 m_d->m_reloadSetting = (IFile::ReloadSetting)settings->value(QLatin1String(reloadBehaviorKey)).toInt();
1773 void EditorManager::revertToSaved()
1775 IEditor *currEditor = currentEditor();
1778 const QString fileName = currEditor->file()->fileName();
1779 if (fileName.isEmpty())
1781 if (currEditor->file()->isModified()) {
1782 QMessageBox msgBox(QMessageBox::Question, tr("Revert to Saved"),
1783 tr("You will lose your current changes if you proceed reverting %1.").arg(QDir::toNativeSeparators(fileName)),
1784 QMessageBox::Yes|QMessageBox::No, m_d->m_core->mainWindow());
1785 msgBox.button(QMessageBox::Yes)->setText(tr("Proceed"));
1786 msgBox.button(QMessageBox::No)->setText(tr("Cancel"));
1787 msgBox.setDefaultButton(QMessageBox::No);
1788 msgBox.setEscapeButton(QMessageBox::No);
1789 if (msgBox.exec() == QMessageBox::No)
1793 currEditor->file()->reload(IFile::FlagReload, IFile::TypeContents);
1796 void EditorManager::showEditorInfoBar(const QString &id,
1797 const QString &infoText,
1798 const QString &buttonText,
1799 QObject *object, const char *buttonPressMember,
1800 const char *cancelButtonPressMember)
1802 currentEditorView()->showEditorInfoBar(id, infoText, buttonText, object, buttonPressMember, cancelButtonPressMember);
1806 void EditorManager::hideEditorInfoBar(const QString &id)
1808 Core::Internal::EditorView *cev = currentEditorView();
1810 cev->hideEditorInfoBar(id);
1813 void EditorManager::showEditorStatusBar(const QString &id,
1814 const QString &infoText,
1815 const QString &buttonText,
1816 QObject *object, const char *member)
1819 currentEditorView()->showEditorStatusBar(id, infoText, buttonText, object, member);
1822 void EditorManager::hideEditorStatusBar(const QString &id)
1824 currentEditorView()->hideEditorStatusBar(id);
1827 QString EditorManager::externalEditorHelpText() const
1830 "<table border=1 cellspacing=0 cellpadding=3>"
1831 "<tr><th>Variable</th><th>Expands to</th></tr>"
1832 "<tr><td>%f</td><td>file name</td></tr>"
1833 "<tr><td>%l</td><td>current line number</td></tr>"
1834 "<tr><td>%c</td><td>current column number</td></tr>"
1835 "<tr><td>%x</td><td>editor's x position on screen</td></tr>"
1836 "<tr><td>%y</td><td>editor's y position on screen</td></tr>"
1837 "<tr><td>%w</td><td>editor's width in pixels</td></tr>"
1838 "<tr><td>%h</td><td>editor's height in pixels</td></tr>"
1839 "<tr><td>%W</td><td>editor's width in characters</td></tr>"
1840 "<tr><td>%H</td><td>editor's height in characters</td></tr>"
1841 "<tr><td>%%</td><td>%</td></tr>"
1846 void EditorManager::openInExternalEditor()
1848 QString command = m_d->m_externalEditor;
1849 if (command.isEmpty())
1850 command = defaultExternalEditor();
1852 if (command.isEmpty())
1855 IEditor *editor = currentEditor();
1858 if (editor->file()->isModified()) {
1859 bool cancelled = false;
1860 QList<IFile*> list = m_d->m_core->fileManager()->
1861 saveModifiedFiles(QList<IFile*>() << editor->file(), &cancelled);
1866 QRect rect = editor->widget()->rect();
1867 QFont font = editor->widget()->font();
1868 QFontMetrics fm(font);
1869 rect.moveTo(editor->widget()->mapToGlobal(QPoint(0,0)));
1871 QString pre = command;
1873 for (int i = 0; i < pre.size(); ++i) {
1874 QChar c = pre.at(i);
1875 if (c == QLatin1Char('%') && i < pre.size()-1) {
1878 if (c == QLatin1Char('f'))
1879 s = editor->file()->fileName();
1880 else if (c == QLatin1Char('l'))
1881 s = QString::number(editor->currentLine());
1882 else if (c == QLatin1Char('c'))
1883 s = QString::number(editor->currentColumn());
1884 else if (c == QLatin1Char('x'))
1885 s = QString::number(rect.x());
1886 else if (c == QLatin1Char('y'))
1887 s = QString::number(rect.y());
1888 else if (c == QLatin1Char('w'))
1889 s = QString::number(rect.width());
1890 else if (c == QLatin1Char('h'))
1891 s = QString::number(rect.height());
1892 else if (c == QLatin1Char('W'))
1893 s = QString::number(rect.width() / fm.width(QLatin1Char('x')));
1894 else if (c == QLatin1Char('H'))
1895 s = QString::number(rect.height() / fm.lineSpacing());
1896 else if (c == QLatin1Char('%'))
1899 s = QLatin1Char('%');
1909 QProcess::startDetached(cmd);
1912 void EditorManager::setExternalEditor(const QString &editor)
1914 if (editor.isEmpty() || editor == defaultExternalEditor())
1915 m_d->m_externalEditor = defaultExternalEditor();
1917 m_d->m_externalEditor = editor;
1920 QString EditorManager::externalEditor() const
1922 if (m_d->m_externalEditor.isEmpty())
1923 return defaultExternalEditor();
1924 return m_d->m_externalEditor;
1927 void EditorManager::setReloadSetting(IFile::ReloadSetting behavior)
1929 m_d->m_reloadSetting = behavior;
1932 IFile::ReloadSetting EditorManager::reloadSetting() const
1934 return m_d->m_reloadSetting;
1937 Core::IEditor *EditorManager::duplicateEditor(Core::IEditor *editor)
1939 if (!editor->duplicateSupported())
1942 IEditor *duplicate = editor->duplicate(0);
1943 duplicate->restoreState(editor->saveState());
1944 emit editorCreated(duplicate, duplicate->file()->fileName());
1945 addEditor(duplicate, true);
1949 void EditorManager::split(Qt::Orientation orientation)
1951 SplitterOrView *view = m_d->m_currentView;
1953 view = m_d->m_currentEditor ? m_d->m_splitter->findView(m_d->m_currentEditor)
1954 : m_d->m_splitter->findFirstView();
1955 if (view && !view->splitter()) {
1956 view->split(orientation);
1961 void EditorManager::split()
1963 split(Qt::Vertical);
1966 void EditorManager::splitSideBySide()
1968 split(Qt::Horizontal);
1971 void EditorManager::removeCurrentSplit()
1973 SplitterOrView *viewToClose = m_d->m_currentView;
1974 if (!viewToClose && m_d->m_currentEditor)
1975 viewToClose = m_d->m_splitter->findView(m_d->m_currentEditor);
1977 if (!viewToClose || viewToClose->isSplitter() || viewToClose == m_d->m_splitter)
1980 closeView(viewToClose->view());
1984 void EditorManager::removeAllSplits()
1986 if (!m_d->m_splitter->isSplitter())
1988 IEditor *editor = m_d->m_currentEditor;
1989 m_d->m_currentEditor = 0; // trigger update below
1990 if (editor && m_d->m_editorModel->isDuplicate(editor))
1991 m_d->m_editorModel->makeOriginal(editor);
1992 m_d->m_splitter->unsplitAll();
1994 editor = pickUnusedEditor();
1995 activateEditor(editor);
1998 void EditorManager::gotoOtherSplit()
2000 if (m_d->m_splitter->isSplitter()) {
2001 SplitterOrView *currentView = m_d->m_currentView;
2002 if (!currentView && m_d->m_currentEditor)
2003 currentView = m_d->m_splitter->findView(m_d->m_currentEditor);
2005 currentView = m_d->m_splitter->findFirstView();
2006 SplitterOrView *view = m_d->m_splitter->findNextView(currentView);
2008 view = m_d->m_splitter->findFirstView();
2010 if (IEditor *editor = view->editor()) {
2011 setCurrentEditor(editor, true);
2012 editor->widget()->setFocus();
2014 setCurrentView(view);
2020 qint64 EditorManager::maxTextFileSize()
2022 return (qint64(3) << 24);
2024 //===================EditorClosingCoreListener======================