/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** Commercial Usage ** ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** **************************************************************************/ #include "editormanager.h" #include "editorview.h" #include "openeditorswindow.h" #include "openeditorsview.h" #include "openeditorsmodel.h" #include "openwithdialog.h" #include "filemanager.h" #include "icore.h" #include "iversioncontrol.h" #include "mimedatabase.h" #include "tabpositionindicator.h" #include "vcsmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(Core::IEditor*) using namespace Core; using namespace Core::Internal; using namespace Utils; enum { debugEditorManager=0 }; static inline ExtensionSystem::PluginManager *pluginManager() { return ExtensionSystem::PluginManager::instance(); } //===================EditorManager===================== EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0; EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent) : QWidget(parent), m_mode(mode) { setLayout(new QVBoxLayout); layout()->setMargin(0); connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)), this, SLOT(currentModeChanged(Core::IMode *))); currentModeChanged(Core::ModeManager::instance()->currentMode()); } EditorManagerPlaceHolder::~EditorManagerPlaceHolder() { if (m_current == this) { EditorManager::instance()->setParent(0); EditorManager::instance()->hide(); } } void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode) { if (m_current == this) { m_current = 0; EditorManager::instance()->setParent(0); EditorManager::instance()->hide(); } if (m_mode == mode) { m_current = this; layout()->addWidget(EditorManager::instance()); EditorManager::instance()->show(); } } EditorManagerPlaceHolder* EditorManagerPlaceHolder::current() { return m_current; } // ---------------- EditorManager namespace Core { struct EditorManagerPrivate { explicit EditorManagerPrivate(ICore *core, QWidget *parent); ~EditorManagerPrivate(); Internal::EditorView *m_view; Internal::SplitterOrView *m_splitter; QPointer m_currentEditor; QPointer m_currentView; ICore *m_core; // actions QAction *m_revertToSavedAction; QAction *m_saveAction; QAction *m_saveAsAction; QAction *m_closeCurrentEditorAction; QAction *m_closeAllEditorsAction; QAction *m_closeOtherEditorsAction; QAction *m_gotoNextDocHistoryAction; QAction *m_gotoPreviousDocHistoryAction; QAction *m_goBackAction; QAction *m_goForwardAction; QAction *m_openInExternalEditorAction; QAction *m_splitAction; QAction *m_splitSideBySideAction; QAction *m_removeCurrentSplitAction; QAction *m_removeAllSplitsAction; QAction *m_gotoOtherSplitAction; Internal::OpenEditorsWindow *m_windowPopup; Core::BaseView *m_openEditorsView; Internal::EditorClosingCoreListener *m_coreListener; QMap m_editorStates; Internal::OpenEditorsViewFactory *m_openEditorsFactory; QString fileFilters; QString selectedFilter; OpenEditorsModel *m_editorModel; QString m_externalEditor; IFile::ReloadBehavior m_reloadBehavior; }; } EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) : m_view(0), m_splitter(0), m_core(core), m_revertToSavedAction(new QAction(EditorManager::tr("Revert to Saved"), parent)), m_saveAction(new QAction(parent)), m_saveAsAction(new QAction(parent)), m_closeCurrentEditorAction(new QAction(EditorManager::tr("Close"), parent)), m_closeAllEditorsAction(new QAction(EditorManager::tr("Close All"), parent)), m_closeOtherEditorsAction(new QAction(EditorManager::tr("Close Others"), parent)), m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Open Document in History"), parent)), m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Open Document in History"), parent)), m_goBackAction(new QAction(QIcon(QLatin1String(":/help/images/previous.png")), EditorManager::tr("Go Back"), parent)), m_goForwardAction(new QAction(QIcon(QLatin1String(":/help/images/next.png")), EditorManager::tr("Go Forward"), parent)), m_openInExternalEditorAction(new QAction(EditorManager::tr("Open in External Editor"), parent)), m_windowPopup(0), m_coreListener(0), m_reloadBehavior(IFile::AskForReload) { m_editorModel = new OpenEditorsModel(parent); } EditorManagerPrivate::~EditorManagerPrivate() { // clearNavigationHistory(); } EditorManager *EditorManager::m_instance = 0; static Command *createSeparator(ActionManager *am, QObject *parent, const QString &name, const QList &context) { QAction *tmpaction = new QAction(parent); tmpaction->setSeparator(true); Command *cmd = am->registerAction(tmpaction, name, context); return cmd; } EditorManager::EditorManager(ICore *core, QWidget *parent) : QWidget(parent), m_d(new EditorManagerPrivate(core, parent)) { m_instance = this; connect(m_d->m_core, SIGNAL(contextAboutToChange(Core::IContext *)), this, SLOT(handleContextChange(Core::IContext *))); const QList gc = QList() << Constants::C_GLOBAL_ID; const QList editManagerContext = QList() << m_d->m_core->uniqueIDManager()->uniqueIdentifier(Constants::C_EDITORMANAGER); ActionManager *am = m_d->m_core->actionManager(); ActionContainer *mfile = am->actionContainer(Constants::M_FILE); //Revert to saved Command *cmd = am->registerAction(m_d->m_revertToSavedAction, Constants::REVERTTOSAVED, editManagerContext); cmd->setAttribute(Command::CA_UpdateText); cmd->setDefaultText(tr("Revert File to Saved")); mfile->addAction(cmd, Constants::G_FILE_SAVE); connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved())); //Save Action am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext); connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile())); //Save As Action am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext); connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs())); //Window Menu ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW); //Window menu separators QAction *tmpaction = new QAction(this); tmpaction->setSeparator(true); cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Split"), editManagerContext); mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); tmpaction = new QAction(this); tmpaction->setSeparator(true); cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Navigate"), editManagerContext); mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); //Close Action cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext); cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W"))); cmd->setAttribute(Core::Command::CA_UpdateText); cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text()); mfile->addAction(cmd, Constants::G_FILE_CLOSE); connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor())); //Close All Action cmd = am->registerAction(m_d->m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext); cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W"))); mfile->addAction(cmd, Constants::G_FILE_CLOSE); connect(m_d->m_closeAllEditorsAction, SIGNAL(triggered()), this, SLOT(closeAllEditors())); //Close All Others Action cmd = am->registerAction(m_d->m_closeOtherEditorsAction, Constants::CLOSEOTHERS, editManagerContext); mfile->addAction(cmd, Constants::G_FILE_CLOSE); cmd->setAttribute(Core::Command::CA_UpdateText); connect(m_d->m_closeOtherEditorsAction, SIGNAL(triggered()), this, SLOT(closeOtherEditors())); // Goto Previous In History Action cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editManagerContext); #ifdef Q_WS_MAC cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Tab"))); #else cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab"))); #endif mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); connect(m_d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory())); // Goto Next In History Action cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editManagerContext); #ifdef Q_WS_MAC cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab"))); #else cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab"))); #endif mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); connect(m_d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory())); // Go back in navigation history cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editManagerContext); #ifdef Q_WS_MAC cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Left"))); #else cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left"))); #endif mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); connect(m_d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory())); // Go forward in navigation history cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editManagerContext); #ifdef Q_WS_MAC cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Right"))); #else cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right"))); #endif mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); connect(m_d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory())); #ifdef Q_WS_MAC QString prefix = tr("Meta+E"); #else QString prefix = tr("Ctrl+E"); #endif m_d->m_splitAction = new QAction(tr("Split"), this); cmd = am->registerAction(m_d->m_splitAction, Constants::SPLIT, editManagerContext); cmd->setDefaultKeySequence(QKeySequence(tr("%1,2").arg(prefix))); mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); connect(m_d->m_splitAction, SIGNAL(triggered()), this, SLOT(split())); m_d->m_splitSideBySideAction = new QAction(tr("Split Side by Side"), this); cmd = am->registerAction(m_d->m_splitSideBySideAction, Constants::SPLIT_SIDE_BY_SIDE, editManagerContext); cmd->setDefaultKeySequence(QKeySequence(tr("%1,3").arg(prefix))); mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); connect(m_d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide())); m_d->m_removeCurrentSplitAction = new QAction(tr("Remove Current Split"), this); cmd = am->registerAction(m_d->m_removeCurrentSplitAction, Constants::REMOVE_CURRENT_SPLIT, editManagerContext); cmd->setDefaultKeySequence(QKeySequence(tr("%1,0").arg(prefix))); mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); connect(m_d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit())); m_d->m_removeAllSplitsAction = new QAction(tr("Remove All Splits"), this); cmd = am->registerAction(m_d->m_removeAllSplitsAction, Constants::REMOVE_ALL_SPLITS, editManagerContext); cmd->setDefaultKeySequence(QKeySequence(tr("%1,1").arg(prefix))); mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); connect(m_d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits())); m_d->m_gotoOtherSplitAction = new QAction(tr("Goto Other Split"), this); cmd = am->registerAction(m_d->m_gotoOtherSplitAction, Constants::GOTO_OTHER_SPLIT, editManagerContext); cmd->setDefaultKeySequence(QKeySequence(tr("%1,o").arg(prefix))); mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); connect(m_d->m_gotoOtherSplitAction, SIGNAL(triggered()), this, SLOT(gotoOtherSplit())); ActionContainer *medit = am->actionContainer(Constants::M_EDIT); ActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED); medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED); advancedMenu->menu()->setTitle(tr("&Advanced")); advancedMenu->appendGroup(Constants::G_EDIT_FORMAT); advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING); advancedMenu->appendGroup(Constants::G_EDIT_BLOCKS); advancedMenu->appendGroup(Constants::G_EDIT_FONT); advancedMenu->appendGroup(Constants::G_EDIT_EDITOR); // Advanced menu separators cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Collapsing"), editManagerContext); advancedMenu->addAction(cmd, Constants::G_EDIT_COLLAPSING); cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Blocks"), editManagerContext); advancedMenu->addAction(cmd, Constants::G_EDIT_BLOCKS); cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Font"), editManagerContext); advancedMenu->addAction(cmd, Constants::G_EDIT_FONT); cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Editor"), editManagerContext); advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR); cmd = am->registerAction(m_d->m_openInExternalEditorAction, Constants::OPEN_IN_EXTERNAL_EDITOR, editManagerContext); cmd->setDefaultKeySequence(QKeySequence(tr("Alt+V,Alt+I"))); advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR); connect(m_d->m_openInExternalEditorAction, SIGNAL(triggered()), this, SLOT(openInExternalEditor())); // Connect to VariableManager for CURRENT_DOCUMENT variable setting VariableManager *vm = VariableManager::instance(); connect(this, SIGNAL(currentEditorChanged(Core::IEditor *)), vm, SLOT(updateCurrentDocument(Core::IEditor *))); // other setup m_d->m_splitter = new SplitterOrView(m_d->m_editorModel); m_d->m_view = m_d->m_splitter->view(); QHBoxLayout *layout = new QHBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(m_d->m_splitter); updateActions(); m_d->m_windowPopup = new OpenEditorsWindow(this); } EditorManager::~EditorManager() { if (m_d->m_core) { ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); if (m_d->m_coreListener) { pm->removeObject(m_d->m_coreListener); delete m_d->m_coreListener; } pm->removeObject(m_d->m_openEditorsFactory); delete m_d->m_openEditorsFactory; } delete m_d; } void EditorManager::init() { QList context; context << m_d->m_core->uniqueIDManager()->uniqueIdentifier("QtCreator.OpenDocumentsView"); m_d->m_coreListener = new EditorClosingCoreListener(this); pluginManager()->addObject(m_d->m_coreListener); m_d->m_openEditorsFactory = new OpenEditorsViewFactory(); pluginManager()->addObject(m_d->m_openEditorsFactory); } QString EditorManager::defaultExternalEditor() const { #ifdef Q_OS_UNIX return ConsoleProcess::defaultTerminalEmulator() + QLatin1String( # ifdef Q_OS_MAC " -async" # endif " -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\""); #else return QLatin1String("notepad %f"); #endif } void EditorManager::removeEditor(IEditor *editor) { bool isDuplicate = m_d->m_editorModel->isDuplicate(editor); m_d->m_editorModel->removeEditor(editor); if (!isDuplicate) { m_d->m_core->fileManager()->removeFile(editor->file()); } m_d->m_core->removeContextObject(editor); } void EditorManager::handleContextChange(Core::IContext *context) { if (debugEditorManager) qDebug() << Q_FUNC_INFO; IEditor *editor = context ? qobject_cast(context) : 0; if (editor) { setCurrentEditor(editor); } else { updateActions(); } } void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory) { if (editor) setCurrentView(0); if (m_d->m_currentEditor == editor) return; if (m_d->m_currentEditor && !ignoreNavigationHistory) addCurrentPositionToNavigationHistory(); m_d->m_currentEditor = editor; if (editor) { if (SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor)) splitterOrView->view()->setCurrentEditor(editor); m_d->m_view->updateEditorHistory(editor); // the global view should have a complete history } updateActions(); emit currentEditorChanged(editor); } void EditorManager::setCurrentView(Core::Internal::SplitterOrView *view) { if (view == m_d->m_currentView) return; SplitterOrView *old = m_d->m_currentView; m_d->m_currentView = view; if (old) old->update(); if (view) view->update(); if (view && !view->editor()) view->setFocus(); } Core::Internal::SplitterOrView *EditorManager::currentSplitterOrView() const { SplitterOrView *view = m_d->m_currentView; if (!view) view = m_d->m_currentEditor? m_d->m_splitter->findView(m_d->m_currentEditor): m_d->m_splitter->findFirstView(); if (!view) return m_d->m_splitter; return view; } Core::Internal::EditorView *EditorManager::currentEditorView() const { return currentSplitterOrView()->view(); } QList EditorManager::editorsForFileName(const QString &filename) const { QList found; QString fixedname = FileManager::fixFileName(filename); foreach (IEditor *editor, openedEditors()) { if (fixedname == FileManager::fixFileName(editor->file()->fileName())) found << editor; } return found; } QList EditorManager::editorsForFile(IFile *file) const { QList found; foreach (IEditor *editor, openedEditors()) { if (editor->file() == file) found << editor; } return found; } IEditor *EditorManager::currentEditor() const { return m_d->m_currentEditor; } void EditorManager::emptyView(Core::Internal::EditorView *view) { if (!view) return; QList editors = view->editors(); foreach (IEditor *editor, editors) { if (!m_d->m_editorModel->isDuplicate(editor)) { editors.removeAll(editor); view->removeEditor(editor); continue; } emit editorAboutToClose(editor); removeEditor(editor); view->removeEditor(editor); } emit editorsClosed(editors); foreach (IEditor *editor, editors) { delete editor; } } void EditorManager::closeView(Core::Internal::EditorView *view) { if (!view) return; if (view == m_d->m_view) { if (IEditor *e = view->currentEditor()) closeEditors(QList() << e); return; } emptyView(view); SplitterOrView *splitterOrView = m_d->m_splitter->findView(view); Q_ASSERT(splitterOrView); Q_ASSERT(splitterOrView->view() == view); SplitterOrView *splitter = m_d->m_splitter->findSplitter(splitterOrView); Q_ASSERT(splitterOrView->hasEditors() == false); splitterOrView->hide(); delete splitterOrView; splitter->unsplit(); SplitterOrView *newCurrent = splitter->findFirstView(); if (newCurrent) { if (newCurrent->editor()) activateEditor(newCurrent->view(), newCurrent->editor()); else setCurrentView(newCurrent); } } QList EditorManager::editorsForFiles(QList files) const { const QList editors = openedEditors(); QSet found; foreach (IFile *file, files) { foreach (IEditor *editor, editors) { if (editor->file() == file && !found.contains(editor)) { found << editor; } } } return found.toList(); } QList EditorManager::filesForEditors(QList editors) const { QSet handledEditors; QList files; foreach (IEditor *editor, editors) { if (!handledEditors.contains(editor)) { files << editor->file(); handledEditors.insert(editor); } } return files; } bool EditorManager::closeAllEditors(bool askAboutModifiedEditors) { m_d->m_editorModel->removeAllRestoredEditors(); if (closeEditors(openedEditors(), askAboutModifiedEditors)) { // m_d->clearNavigationHistory(); return true; } return false; } void EditorManager::closeOtherEditors(IEditor *editor) { m_d->m_editorModel->removeAllRestoredEditors(); QList editors = openedEditors(); editors.removeAll(editor); closeEditors(editors, true); } void EditorManager::closeOtherEditors() { IEditor *current = currentEditor(); QTC_ASSERT(current, return); closeOtherEditors(current); } // SLOT connected to action // since this is potentially called in the event handler of the editor // we simply postpone it with a single shot timer void EditorManager::closeEditor() { closeEditor(m_d->m_currentEditor); } void EditorManager::closeEditor(Core::IEditor *editor) { if (!editor) return; closeEditors(QList() << editor); } void EditorManager::closeEditor(const QModelIndex &index) { IEditor *editor = index.data(Qt::UserRole).value(); if (editor) closeEditor(editor); else m_d->m_editorModel->removeEditor(index); } bool EditorManager::closeEditors(const QList editorsToClose, bool askAboutModifiedEditors) { if (editorsToClose.isEmpty()) return true; SplitterOrView *currentSplitterOrView = this->currentSplitterOrView(); bool closingFailed = false; QList acceptedEditors; //ask all core listeners to check whether the editor can be closed const QList listeners = pluginManager()->getObjects(); foreach (IEditor *editor, editorsToClose) { bool editorAccepted = true; if (m_d->m_editorModel->isDuplicate(editor)) editor = m_d->m_editorModel->originalForDuplicate(editor); foreach (ICoreListener *listener, listeners) { if (!listener->editorAboutToClose(editor)) { editorAccepted = false; closingFailed = false; break; } } if (editorAccepted) acceptedEditors.append(editor); } if (acceptedEditors.isEmpty()) return false; //ask whether to save modified files if (askAboutModifiedEditors) { bool cancelled = false; QList list = m_d->m_core->fileManager()-> saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled); if (cancelled) return false; if (!list.isEmpty()) { closingFailed = true; QSet skipSet = editorsForFiles(list).toSet(); acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList(); } } if (acceptedEditors.isEmpty()) return false; // add duplicates foreach(IEditor *editor, acceptedEditors) acceptedEditors += m_d->m_editorModel->duplicatesFor(editor); QList closedViews; // remove the editors foreach (IEditor *editor, acceptedEditors) { emit editorAboutToClose(editor); if (!editor->file()->fileName().isEmpty()) { QByteArray state = editor->saveState(); if (!state.isEmpty()) m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state)); } removeEditor(editor); if (SplitterOrView *view = m_d->m_splitter->findView(editor)) { if (editor == view->view()->currentEditor()) closedViews += view->view(); view->view()->removeEditor(editor); } } foreach (EditorView *view, closedViews) { IEditor *newCurrent = view->currentEditor(); if (!newCurrent) newCurrent = pickUnusedEditor(); if (newCurrent) { activateEditor(view, newCurrent, NoActivate); } else { QModelIndex idx = m_d->m_editorModel->firstRestoredEditor(); if (idx.isValid()) activateEditor(idx, view, NoActivate); } } emit editorsClosed(acceptedEditors); foreach (IEditor *editor, acceptedEditors) { delete editor; } if (currentSplitterOrView) { if (IEditor *editor = currentSplitterOrView->editor()) activateEditor(currentSplitterOrView->view(), editor); } if (!currentEditor()) emit currentEditorChanged(0); return !closingFailed; } void EditorManager::closeDuplicate(Core::IEditor *editor) { IEditor *original = editor; if (m_d->m_editorModel->isDuplicate(editor)) original= m_d->m_editorModel->originalForDuplicate(editor); QList duplicates = m_d->m_editorModel->duplicatesFor(original); if (duplicates.isEmpty()) { closeEditor(editor); return; } if (original== editor) m_d->m_editorModel->makeOriginal(duplicates.first()); SplitterOrView *currentSplitterOrView = this->currentSplitterOrView(); emit editorAboutToClose(editor); EditorView *view = m_d->m_splitter->findView(editor)->view(); removeEditor(editor); view->removeEditor(editor); IEditor *newCurrent = view->currentEditor(); if (!newCurrent) newCurrent = pickUnusedEditor(); if (newCurrent) { activateEditor(view, newCurrent, NoActivate); } else { QModelIndex idx = m_d->m_editorModel->firstRestoredEditor(); if (idx.isValid()) activateEditor(idx, view, NoActivate); } emit editorsClosed(QList() << editor); delete editor; if (currentSplitterOrView) { if (IEditor *currentEditor = currentSplitterOrView->editor()) activateEditor(currentSplitterOrView->view(), currentEditor); } } Core::IEditor *EditorManager::pickUnusedEditor() const { foreach (IEditor *editor, openedEditors()) { SplitterOrView *view = m_d->m_splitter->findView(editor); if (!view || view->editor() != editor) return editor; } return 0; } Core::IEditor *EditorManager::activateEditor(const QModelIndex &index, Internal::EditorView *view, OpenEditorFlags flags) { IEditor *editor = index.data(Qt::UserRole).value(); if (editor) { return activateEditor(view, editor, flags); } QString fileName = index.data(Qt::UserRole + 1).toString(); QByteArray kind = index.data(Qt::UserRole + 2).toByteArray(); return openEditor(view, fileName, kind, flags); } Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor) { Q_ASSERT(view && editor); if (view->currentEditor() && view->currentEditor()->file() == editor->file()) editor = view->currentEditor(); if (!view->hasEditor(editor)) { bool duplicateSupported = editor->duplicateSupported(); if (SplitterOrView *sourceView = m_d->m_splitter->findView(editor)) { if (editor != sourceView->editor() || !duplicateSupported) { sourceView->view()->removeEditor(editor); view->addEditor(editor); view->setCurrentEditor(editor); if (!sourceView->editor()) { if (IEditor *replacement = pickUnusedEditor()) { sourceView->view()->addEditor(replacement); } } return editor; } else if (duplicateSupported) { editor = duplicateEditor(editor); Q_ASSERT(editor); m_d->m_editorModel->makeOriginal(editor); } } view->addEditor(editor); } return editor; } Core::IEditor *EditorManager::activateEditor(Core::IEditor *editor, OpenEditorFlags flags) { return activateEditor(0, editor, flags); } Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags) { if (!view) view = currentEditorView(); Q_ASSERT(view); if (!editor) { if (!m_d->m_currentEditor) setCurrentEditor(0, (flags & IgnoreNavigationHistory)); return 0; } editor = placeEditor(view, editor); if (!(flags & NoActivate)) { setCurrentEditor(editor, (flags & IgnoreNavigationHistory)); if (!(flags & NoModeSwitch)) ensureEditorManagerVisible(); if (isVisible()) editor->widget()->setFocus(); } return editor; } Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IFile *file, OpenEditorFlags flags) { QList editors = editorsForFile(file); Q_ASSERT(!editors.isEmpty()); return activateEditor(view, editors.first(), flags); } /* For something that has a 'QStringList mimeTypes' (IEditorFactory * or IExternalEditor), find the one best matching the mimetype passed in. * Recurse over the parent classes of the mimetype to find them. */ template static void mimeTypeFactoryRecursion(const MimeDatabase *db, const MimeType &mimeType, const QList &allFactories, bool firstMatchOnly, QList *list) { typedef typename QList::const_iterator EditorFactoryLikeListConstIterator; // Loop factories to find type const QString type = mimeType.type(); const EditorFactoryLikeListConstIterator fcend = allFactories.constEnd(); for (EditorFactoryLikeListConstIterator fit = allFactories.constBegin(); fit != fcend; ++fit) { // Exclude duplicates when recursing over xml or C++ -> C -> text. EditorFactoryLike *factory = *fit; if (!list->contains(factory) && factory->mimeTypes().contains(type)) { list->push_back(*fit); if (firstMatchOnly) return; break; } } // Any parent mime type classes? -> recurse QStringList parentTypes = mimeType.subClassesOf(); if (parentTypes.empty()) return; const QStringList::const_iterator pcend = parentTypes .constEnd(); for (QStringList::const_iterator pit = parentTypes .constBegin(); pit != pcend; ++pit) { if (const MimeType parent = db->findByType(*pit)) mimeTypeFactoryRecursion(db, parent, allFactories, firstMatchOnly, list); } } EditorManager::EditorFactoryList EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const { EditorFactoryList rc; const EditorFactoryList allFactories = pluginManager()->getObjects(); mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allFactories, bestMatchOnly, &rc); if (debugEditorManager) qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc; return rc; } EditorManager::ExternalEditorList EditorManager::externalEditors(const MimeType &mimeType, bool bestMatchOnly) const { ExternalEditorList rc; const ExternalEditorList allEditors = pluginManager()->getObjects(); mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allEditors, bestMatchOnly, &rc); if (debugEditorManager) qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc; return rc; } /* For something that has a 'QString kind' (IEditorFactory * or IExternalEditor), find the one matching a kind. */ template inline EditorFactoryLike *findByKind(ExtensionSystem::PluginManager *pm, const QString &kind) { const QList factories = pm->template getObjects(); foreach(EditorFactoryLike *efl, factories) if (kind == efl->kind()) return efl; return 0; } IEditor *EditorManager::createEditor(const QString &editorKind, const QString &fileName) { typedef QList FactoryList; if (debugEditorManager) qDebug() << Q_FUNC_INFO << editorKind << fileName; EditorFactoryList factories; if (editorKind.isEmpty()) { // Find by mime type MimeType mimeType = m_d->m_core->mimeDatabase()->findByFile(QFileInfo(fileName)); if (!mimeType) { qWarning("%s unable to determine mime type of %s/%s. Falling back to text/plain", Q_FUNC_INFO, fileName.toUtf8().constData(), editorKind.toUtf8().constData()); mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("text/plain")); } factories = editorFactories(mimeType, true); } else { // Find by editor kind if (IEditorFactory *factory = findByKind(pluginManager(), editorKind)) factories.push_back(factory); } if (factories.empty()) { qWarning("%s: unable to find an editor factory for the file '%s', editor kind '%s'.", Q_FUNC_INFO, fileName.toUtf8().constData(), editorKind.toUtf8().constData()); return 0; } IEditor *editor = factories.front()->createEditor(this); if (editor) connect(editor, SIGNAL(changed()), this, SLOT(updateActions())); if (editor) emit editorCreated(editor, fileName); return editor; } void EditorManager::addEditor(IEditor *editor, bool isDuplicate) { if (!editor) return; m_d->m_core->addContextObject(editor); m_d->m_editorModel->addEditor(editor, isDuplicate); if (!isDuplicate) { m_d->m_core->fileManager()->addFile(editor->file()); if (!editor->isTemporary()) { m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName()); } } emit editorOpened(editor); } // Run the OpenWithDialog and return the editor kind // selected by the user. QString EditorManager::getOpenWithEditorKind(const QString &fileName, bool *isExternalEditor) const { // Collect editors that can open the file const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName); if (!mt) return QString(); QStringList allEditorKinds; QStringList externalEditorKinds; // Built-in const EditorFactoryList editors = editorFactories(mt, false); const int size = editors.size(); for (int i = 0; i < size; i++) { allEditorKinds.push_back(editors.at(i)->kind()); } // External editors const ExternalEditorList exEditors = externalEditors(mt, false); const int esize = exEditors.size(); for (int i = 0; i < esize; i++) { externalEditorKinds.push_back(exEditors.at(i)->kind()); allEditorKinds.push_back(exEditors.at(i)->kind()); } if (allEditorKinds.empty()) return QString(); // Run dialog. OpenWithDialog dialog(fileName, m_d->m_core->mainWindow()); dialog.setEditors(allEditorKinds); dialog.setCurrentEditor(0); if (dialog.exec() != QDialog::Accepted) return QString(); const QString selectedKind = dialog.editor(); if (isExternalEditor) *isExternalEditor = externalEditorKinds.contains(selectedKind); return selectedKind; } static QString formatFileFilters(const Core::ICore *core, QString *selectedFilter) { QString rc; // Compile list of filter strings. If we find a glob matching all files, // put it last and set it as default selectedFilter. QStringList filters = core->mimeDatabase()->filterStrings(); filters.sort(); selectedFilter->clear(); if (filters.empty()) return rc; const QString filterSeparator = QLatin1String(";;"); bool hasAllFilter = false; const int size = filters.size(); for (int i = 0; i < size; i++) { const QString &filterString = filters.at(i); if (filterString.isEmpty()) { // binary editor hasAllFilter = true; } else { if (!rc.isEmpty()) rc += filterSeparator; rc += filterString; } } if (hasAllFilter) { // prepend all files filter // prepending instead of appending to work around a but in Qt/Mac QString allFilesFilter = EditorManager::tr("All Files (*)"); if (!rc.isEmpty()) allFilesFilter += filterSeparator; rc.prepend(allFilesFilter); *selectedFilter = allFilesFilter; } else { *selectedFilter = filters.front(); } return rc; } IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorKind, EditorManager::OpenEditorFlags flags) { return openEditor(0, fileName, editorKind, flags); } IEditor *EditorManager::openEditor(Core::Internal::EditorView *view, const QString &fileName, const QString &editorKind, EditorManager::OpenEditorFlags flags) { if (debugEditorManager) qDebug() << Q_FUNC_INFO << fileName << editorKind; if (fileName.isEmpty()) return 0; const QList editors = editorsForFileName(fileName); if (!editors.isEmpty()) { return activateEditor(view, editors.first(), flags); } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); IEditor *editor = createEditor(editorKind, fileName); if (!editor || !editor->open(fileName)) { QApplication::restoreOverrideCursor(); QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), tr("Cannot open file %1!").arg(QDir::toNativeSeparators(fileName))); delete editor; editor = 0; return 0; } addEditor(editor); IEditor *result= activateEditor(view, editor, flags); if (editor == result) restoreEditorState(editor); QApplication::restoreOverrideCursor(); return result; } bool EditorManager::openExternalEditor(const QString &fileName, const QString &editorKind) { IExternalEditor *ee = findByKind(pluginManager(), editorKind); if (!ee) return false; QString errorMessage; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); const bool ok = ee->startEditor(fileName, &errorMessage); QApplication::restoreOverrideCursor(); if (!ok) QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), errorMessage); return ok; } QStringList EditorManager::getOpenFileNames() const { static QString dir = QDir::homePath(); if (m_d->fileFilters.isEmpty()) m_d->fileFilters = formatFileFilters(m_d->m_core, &m_d->selectedFilter); QString currentFile = ICore::instance()->fileManager()->currentFile(); if (!currentFile.isEmpty()) { const QFileInfo fi(currentFile); dir = fi.absolutePath(); } QStringList files = QFileDialog::getOpenFileNames(m_d->m_core->mainWindow(), tr("Open File"), dir, m_d->fileFilters, &m_d->selectedFilter); if (!files.isEmpty()) dir = QFileInfo(files.at(0)).absolutePath(); return files; } void EditorManager::ensureEditorManagerVisible() { if (!isVisible()) m_d->m_core->modeManager()->activateMode(Constants::MODE_EDIT); } IEditor *EditorManager::openEditorWithContents(const QString &editorKind, QString *titlePattern, const QString &contents) { if (debugEditorManager) qDebug() << Q_FUNC_INFO << editorKind << titlePattern << contents; if (editorKind.isEmpty()) return 0; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); IEditor *edt = createEditor(editorKind); if (!edt) return 0; if (!edt || !edt->createNew(contents)) { QApplication::restoreOverrideCursor(); delete edt; edt = 0; return 0; } QString title = edt->displayName(); if (title.isEmpty() && titlePattern) { const QChar dollar = QLatin1Char('$'); const QChar dot = QLatin1Char('.'); QString base = *titlePattern; if (base.isEmpty()) base = QLatin1String("unnamed$"); if (base.contains(dollar)) { int i = 1; QSet docnames; foreach (IEditor *editor, openedEditors()) { QString name = editor->file()->fileName(); if (name.isEmpty()) { name = editor->displayName(); name.remove(QLatin1Char('*')); } else { name = QFileInfo(name).completeBaseName(); } docnames << name; } do { title = base; title.replace(QString(dollar), QString::number(i++)); } while (docnames.contains(title)); } else { title = *titlePattern; } } *titlePattern = title; edt->setDisplayName(title); addEditor(edt); QApplication::restoreOverrideCursor(); return edt; } bool EditorManager::hasEditor(const QString &fileName) const { return !editorsForFileName(fileName).isEmpty(); } void EditorManager::restoreEditorState(IEditor *editor) { QTC_ASSERT(editor, return); QString fileName = editor->file()->fileName(); if (m_d->m_editorStates.contains(fileName)) editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray()); } bool EditorManager::saveEditor(IEditor *editor) { return saveFile(editor); } bool EditorManager::saveFile(IEditor *editor) { if (!editor) editor = currentEditor(); if (!editor) return false; IFile *file = editor->file(); file->checkPermissions(); const QString &fileName = file->fileName(); if (fileName.isEmpty()) return saveFileAs(editor); bool success = false; // try saving, no matter what isReadOnly tells us m_d->m_core->fileManager()->blockFileChange(file); success = file->save(fileName); m_d->m_core->fileManager()->unblockFileChange(file); if (!success) { MakeWritableResult answer = makeEditorWritable(editor); if (answer == Failed) return false; if (answer == SavedAs) return true; file->checkPermissions(); m_d->m_core->fileManager()->blockFileChange(file); success = file->save(fileName); m_d->m_core->fileManager()->unblockFileChange(file); } if (success && !editor->isTemporary()) m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName()); return success; } EditorManager::ReadOnlyAction EditorManager::promptReadOnlyFile(const QString &fileName, const IVersionControl *versionControl, QWidget *parent, bool displaySaveAsButton) { QMessageBox msgBox(QMessageBox::Question, tr("File is Read Only"), tr("The file %1 is read only.").arg(QDir::toNativeSeparators(fileName)), QMessageBox::Cancel, parent); QPushButton *sccButton = 0; if (versionControl && versionControl->supportsOperation(IVersionControl::OpenOperation)) sccButton = msgBox.addButton(tr("Open with VCS (%1)").arg(versionControl->name()), QMessageBox::AcceptRole); QPushButton *makeWritableButton = msgBox.addButton(tr("Make writable"), QMessageBox::AcceptRole); QPushButton *saveAsButton = 0; if (displaySaveAsButton) saveAsButton = msgBox.addButton(tr("Save as ..."), QMessageBox::ActionRole); msgBox.setDefaultButton(sccButton ? sccButton : makeWritableButton); msgBox.exec(); QAbstractButton *clickedButton = msgBox.clickedButton(); if (clickedButton == sccButton) return RO_OpenVCS; if (clickedButton == makeWritableButton) return RO_MakeWriteable; if (clickedButton == saveAsButton) return RO_SaveAs; return RO_Cancel; } MakeWritableResult EditorManager::makeEditorWritable(IEditor *editor) { QString directory = QFileInfo(editor->file()->fileName()).absolutePath(); IVersionControl *versionControl = m_d->m_core->vcsManager()->findVersionControlForDirectory(directory); IFile *file = editor->file(); const QString &fileName = file->fileName(); switch (promptReadOnlyFile(fileName, versionControl, m_d->m_core->mainWindow(), true)) { case RO_OpenVCS: if (!versionControl->vcsOpen(fileName)) { QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for editing with SCC.")); return Failed; } file->checkPermissions(); return OpenedWithVersionControl; case RO_MakeWriteable: { const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser); if (!permsOk) { QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable.")); return Failed; } } file->checkPermissions(); return MadeWritable; case RO_SaveAs : return saveFileAs(editor) ? SavedAs : Failed; case RO_Cancel: break; } return Failed; } bool EditorManager::saveFileAs(IEditor *editor) { if (!editor) editor = currentEditor(); if (!editor) return false; QString absoluteFilePath = m_d->m_core->fileManager()->getSaveAsFileName(editor->file()); if (absoluteFilePath.isEmpty()) return false; if (absoluteFilePath != editor->file()->fileName()) { const QList existList = editorsForFileName(absoluteFilePath); if (!existList.isEmpty()) { closeEditors(existList, false); } } m_d->m_core->fileManager()->blockFileChange(editor->file()); const bool success = editor->file()->save(absoluteFilePath); m_d->m_core->fileManager()->unblockFileChange(editor->file()); editor->file()->checkPermissions(); if (success && !editor->isTemporary()) m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName()); updateActions(); return success; } void EditorManager::gotoNextDocHistory() { OpenEditorsWindow *dialog = windowPopup(); if (dialog->isVisible()) { dialog->selectNextEditor(); } else { EditorView *view = currentEditorView(); dialog->setEditors(m_d->m_view, view, m_d->m_editorModel); dialog->selectNextEditor(); showWindowPopup(); } } void EditorManager::gotoPreviousDocHistory() { OpenEditorsWindow *dialog = windowPopup(); if (dialog->isVisible()) { dialog->selectPreviousEditor(); } else { EditorView *view = currentEditorView(); dialog->setEditors(m_d->m_view, view, m_d->m_editorModel); dialog->selectPreviousEditor(); showWindowPopup(); } } void EditorManager::makeCurrentEditorWritable() { if (IEditor* curEditor = currentEditor()) makeEditorWritable(curEditor); } void EditorManager::updateActions() { QString fName; IEditor *curEditor = currentEditor(); int openedCount = openedEditors().count() + m_d->m_editorModel->restoredEditorCount(); if (curEditor) { if (!curEditor->file()->fileName().isEmpty()) { QFileInfo fi(curEditor->file()->fileName()); fName = fi.fileName(); } else { fName = curEditor->displayName(); } if (curEditor->file()->isModified() && curEditor->file()->isReadOnly()) { // we are about to change a read-only file, warn user showEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"), tr("Warning: You are changing a read-only file."), tr("Make writable"), this, SLOT(makeCurrentEditorWritable())); } else { hideEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable")); } } m_d->m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified()); m_d->m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed()); m_d->m_revertToSavedAction->setEnabled(curEditor != 0 && !curEditor->file()->fileName().isEmpty() && curEditor->file()->isModified()); QString quotedName; if (!fName.isEmpty()) quotedName = '"' + fName + '"'; m_d->m_saveAsAction->setText(tr("Save %1 As...").arg(quotedName)); m_d->m_saveAction->setText(tr("&Save %1").arg(quotedName)); m_d->m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(quotedName)); m_d->m_closeCurrentEditorAction->setEnabled(curEditor != 0); m_d->m_closeCurrentEditorAction->setText(tr("Close %1").arg(quotedName)); m_d->m_closeAllEditorsAction->setEnabled(openedCount > 0); m_d->m_closeOtherEditorsAction->setEnabled(openedCount > 1); m_d->m_closeOtherEditorsAction->setText((openedCount > 1 ? tr("Close All Except %1").arg(quotedName) : tr("Close Others"))); m_d->m_gotoNextDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0); m_d->m_gotoPreviousDocHistoryAction->setEnabled(m_d->m_editorModel->rowCount() != 0); EditorView *view = currentEditorView(); m_d->m_goBackAction->setEnabled(view ? view->canGoBack() : false); m_d->m_goForwardAction->setEnabled(view ? view->canGoForward() : false); bool hasSplitter = m_d->m_splitter->isSplitter(); m_d->m_removeCurrentSplitAction->setEnabled(hasSplitter); m_d->m_removeAllSplitsAction->setEnabled(hasSplitter); m_d->m_gotoOtherSplitAction->setEnabled(hasSplitter); m_d->m_openInExternalEditorAction->setEnabled(curEditor != 0); } QList EditorManager::openedEditors() const { return m_d->m_editorModel->editors(); } OpenEditorsModel *EditorManager::openedEditorsModel() const { return m_d->m_editorModel; } void EditorManager::addCurrentPositionToNavigationHistory(IEditor *editor, const QByteArray &saveState) { currentEditorView()->addCurrentPositionToNavigationHistory(editor, saveState); updateActions(); } void EditorManager::goBackInNavigationHistory() { currentEditorView()->goBackInNavigationHistory(); updateActions(); ensureEditorManagerVisible(); return; } void EditorManager::goForwardInNavigationHistory() { currentEditorView()->goForwardInNavigationHistory(); updateActions(); ensureEditorManagerVisible(); } OpenEditorsWindow *EditorManager::windowPopup() const { return m_d->m_windowPopup; } void EditorManager::showWindowPopup() const { const QPoint p(mapToGlobal(QPoint(0, 0))); m_d->m_windowPopup->move((width()-m_d->m_windowPopup->width())/2 + p.x(), (height()-m_d->m_windowPopup->height())/2 + p.y()); m_d->m_windowPopup->setVisible(true); } QByteArray EditorManager::saveState() const { QByteArray bytes; QDataStream stream(&bytes, QIODevice::WriteOnly); stream << QByteArray("EditorManagerV4"); QList editors = openedEditors(); foreach (IEditor *editor, editors) { if (!editor->file()->fileName().isEmpty()) { QByteArray state = editor->saveState(); if (!state.isEmpty()) m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state)); } } stream << m_d->m_editorStates; QList entries = m_d->m_editorModel->entries(); stream << entries.count(); foreach (OpenEditorsModel::Entry entry, entries) { stream << entry.fileName() << entry.displayName() << entry.kind(); } stream << m_d->m_splitter->saveState(); return bytes; } bool EditorManager::restoreState(const QByteArray &state) { closeAllEditors(true); removeAllSplits(); QDataStream stream(state); QByteArray version; stream >> version; if (version != "EditorManagerV4") return false; QMap editorstates; QApplication::setOverrideCursor(Qt::WaitCursor); stream >> editorstates; QMapIterator i(editorstates); while (i.hasNext()) { i.next(); m_d->m_editorStates.insert(i.key(), i.value()); } int editorCount = 0; stream >> editorCount; while (--editorCount >= 0) { QString fileName; stream >> fileName; QString displayName; stream >> displayName; QByteArray kind; stream >> kind; if (!fileName.isEmpty() && !displayName.isEmpty()){ m_d->m_editorModel->addRestoredEditor(fileName, displayName, kind); } } QByteArray splitterstates; stream >> splitterstates; m_d->m_splitter->restoreState(splitterstates); // splitting and stuff results in focus trouble, that's why we set the focus again after restoration ensureEditorManagerVisible(); if (m_d->m_currentEditor) { m_d->m_currentEditor->widget()->setFocus(); } else if (Core::Internal::SplitterOrView *view = currentSplitterOrView()) { if (IEditor *e = view->editor()) e->widget()->setFocus(); else if (view->view()) view->view()->setFocus(); } QApplication::restoreOverrideCursor(); return true; } static const char * const documentStatesKey = "EditorManager/DocumentStates"; static const char * const externalEditorKey = "EditorManager/ExternalEditorCommand"; static const char * const reloadBehaviorKey = "EditorManager/ReloadBehavior"; void EditorManager::saveSettings() { SettingsDatabase *settings = m_d->m_core->settingsDatabase(); settings->setValue(QLatin1String(documentStatesKey), m_d->m_editorStates); settings->setValue(QLatin1String(externalEditorKey), m_d->m_externalEditor); settings->setValue(QLatin1String(reloadBehaviorKey), m_d->m_reloadBehavior); } void EditorManager::readSettings() { // Backward compatibility to old locations for these settings QSettings *qs = m_d->m_core->settings(); if (qs->contains(QLatin1String(documentStatesKey))) { m_d->m_editorStates = qs->value(QLatin1String(documentStatesKey)) .value >(); qs->remove(QLatin1String(documentStatesKey)); } if (qs->contains(QLatin1String(externalEditorKey))) { m_d->m_externalEditor = qs->value(QLatin1String(externalEditorKey)).toString(); qs->remove(QLatin1String(externalEditorKey)); } SettingsDatabase *settings = m_d->m_core->settingsDatabase(); if (settings->contains(QLatin1String(documentStatesKey))) m_d->m_editorStates = settings->value(QLatin1String(documentStatesKey)) .value >(); if (settings->contains(QLatin1String(externalEditorKey))) m_d->m_externalEditor = settings->value(QLatin1String(externalEditorKey)).toString(); if (settings->contains(QLatin1String(reloadBehaviorKey))) m_d->m_reloadBehavior = (IFile::ReloadBehavior)settings->value(QLatin1String(reloadBehaviorKey)).toInt(); } void EditorManager::revertToSaved() { IEditor *currEditor = currentEditor(); if (!currEditor) return; const QString fileName = currEditor->file()->fileName(); if (fileName.isEmpty()) return; if (currEditor->file()->isModified()) { QMessageBox msgBox(QMessageBox::Question, tr("Revert to Saved"), tr("You will lose your current changes if you proceed reverting %1.").arg(QDir::toNativeSeparators(fileName)), QMessageBox::Yes|QMessageBox::No, m_d->m_core->mainWindow()); msgBox.button(QMessageBox::Yes)->setText(tr("Proceed")); msgBox.button(QMessageBox::No)->setText(tr("Cancel")); msgBox.setDefaultButton(QMessageBox::No); msgBox.setEscapeButton(QMessageBox::No); if (msgBox.exec() == QMessageBox::No) return; } IFile::ReloadBehavior temp = IFile::ReloadAll; currEditor->file()->modified(&temp); } void EditorManager::showEditorInfoBar(const QString &kind, const QString &infoText, const QString &buttonText, QObject *object, const char *member) { currentEditorView()->showEditorInfoBar(kind, infoText, buttonText, object, member); } void EditorManager::hideEditorInfoBar(const QString &kind) { Core::Internal::EditorView *cev = currentEditorView(); if (cev) cev->hideEditorInfoBar(kind); } void EditorManager::showEditorStatusBar(const QString &kind, const QString &infoText, const QString &buttonText, QObject *object, const char *member) { currentEditorView()->showEditorStatusBar(kind, infoText, buttonText, object, member); } void EditorManager::hideEditorStatusBar(const QString &kind) { currentEditorView()->hideEditorStatusBar(kind); } QString EditorManager::externalEditorHelpText() const { QString help = tr( "" "" "" "" "" "" "" "" "" "" "" "" "
VariableExpands to
%ffile name
%lcurrent line number
%ccurrent column number
%xeditor's x position on screen
%yeditor's y position on screen
%weditor's width in pixels
%heditor's height in pixels
%Weditor's width in characters
%Heditor's height in characters
%%%
"); return help; } void EditorManager::openInExternalEditor() { QString command = m_d->m_externalEditor; if (command.isEmpty()) command = defaultExternalEditor(); if (command.isEmpty()) return; IEditor *editor = currentEditor(); if (!editor) return; if (editor->file()->isModified()) { bool cancelled = false; QList list = m_d->m_core->fileManager()-> saveModifiedFiles(QList() << editor->file(), &cancelled); if (cancelled) return; } QRect rect = editor->widget()->rect(); QFont font = editor->widget()->font(); QFontMetrics fm(font); rect.moveTo(editor->widget()->mapToGlobal(QPoint(0,0))); QString pre = command; QString cmd; for (int i = 0; i < pre.size(); ++i) { QChar c = pre.at(i); if (c == QLatin1Char('%') && i < pre.size()-1) { c = pre.at(++i); QString s; if (c == QLatin1Char('f')) s = editor->file()->fileName(); else if (c == QLatin1Char('l')) s = QString::number(editor->currentLine()); else if (c == QLatin1Char('c')) s = QString::number(editor->currentColumn()); else if (c == QLatin1Char('x')) s = QString::number(rect.x()); else if (c == QLatin1Char('y')) s = QString::number(rect.y()); else if (c == QLatin1Char('w')) s = QString::number(rect.width()); else if (c == QLatin1Char('h')) s = QString::number(rect.height()); else if (c == QLatin1Char('W')) s = QString::number(rect.width() / fm.width(QLatin1Char('x'))); else if (c == QLatin1Char('H')) s = QString::number(rect.height() / fm.lineSpacing()); else if (c == QLatin1Char('%')) s = c; else { s = QLatin1Char('%'); cmd += c; } cmd += s; continue; } cmd += c; } QProcess::startDetached(cmd); } void EditorManager::setExternalEditor(const QString &editor) { if (editor.isEmpty() || editor == defaultExternalEditor()) m_d->m_externalEditor = defaultExternalEditor(); else m_d->m_externalEditor = editor; } QString EditorManager::externalEditor() const { if (m_d->m_externalEditor.isEmpty()) return defaultExternalEditor(); return m_d->m_externalEditor; } void EditorManager::setReloadBehavior(IFile::ReloadBehavior behavior) { m_d->m_reloadBehavior = behavior; } IFile::ReloadBehavior EditorManager::reloadBehavior() const { return m_d->m_reloadBehavior; } Core::IEditor *EditorManager::duplicateEditor(Core::IEditor *editor) { if (!editor->duplicateSupported()) return 0; IEditor *duplicate = editor->duplicate(0); duplicate->restoreState(editor->saveState()); emit editorCreated(duplicate, duplicate->file()->fileName()); addEditor(duplicate, true); return duplicate; } void EditorManager::split(Qt::Orientation orientation) { SplitterOrView *view = m_d->m_currentView; if (!view) view = m_d->m_currentEditor ? m_d->m_splitter->findView(m_d->m_currentEditor) : m_d->m_splitter->findFirstView(); if (view && !view->splitter()) { view->split(orientation); } updateActions(); } void EditorManager::split() { split(Qt::Vertical); } void EditorManager::splitSideBySide() { split(Qt::Horizontal); } void EditorManager::removeCurrentSplit() { SplitterOrView *viewToClose = m_d->m_currentView; if (!viewToClose && m_d->m_currentEditor) viewToClose = m_d->m_splitter->findView(m_d->m_currentEditor); if (!viewToClose || viewToClose->isSplitter() || viewToClose == m_d->m_splitter) return; closeView(viewToClose->view()); updateActions(); } void EditorManager::removeAllSplits() { if (!m_d->m_splitter->isSplitter()) return; IEditor *editor = m_d->m_currentEditor; m_d->m_currentEditor = 0; // trigger update below if (editor && m_d->m_editorModel->isDuplicate(editor)) editor = m_d->m_editorModel->originalForDuplicate(editor); m_d->m_splitter->unsplitAll(); if (!editor) editor = pickUnusedEditor(); activateEditor(editor); } void EditorManager::gotoOtherSplit() { if (m_d->m_splitter->isSplitter()) { SplitterOrView *currentView = m_d->m_currentView; if (!currentView && m_d->m_currentEditor) currentView = m_d->m_splitter->findView(m_d->m_currentEditor); if (!currentView) currentView = m_d->m_splitter->findFirstView(); SplitterOrView *view = m_d->m_splitter->findNextView(currentView); if (!view) view = m_d->m_splitter->findFirstView(); if (view) { if (IEditor *editor = view->editor()) { setCurrentEditor(editor, true); editor->widget()->setFocus(); } else { setCurrentView(view); } } } } //===================EditorClosingCoreListener====================== EditorClosingCoreListener::EditorClosingCoreListener(EditorManager *em) : m_em(em) { } bool EditorClosingCoreListener::editorAboutToClose(IEditor *) { return true; } bool EditorClosingCoreListener::coreAboutToClose() { // Do not ask for files to save. // MainWindow::closeEvent has already done that. return m_em->closeAllEditors(false); }