OSDN Git Service

Merge remote branch 'origin/2.0'
[qt-creator-jp/qt-creator-jp.git] / src / plugins / coreplugin / editormanager / editormanager.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
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.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
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"
37 #include "icore.h"
38 #include "iversioncontrol.h"
39 #include "mimedatabase.h"
40 #include "tabpositionindicator.h"
41 #include "vcsmanager.h"
42
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>
55
56 #include <extensionsystem/pluginmanager.h>
57
58 #include <utils/consoleprocess.h>
59 #include <utils/qtcassert.h>
60
61 #include <QtCore/QDebug>
62 #include <QtCore/QFileInfo>
63 #include <QtCore/QMap>
64 #include <QtCore/QProcess>
65 #include <QtCore/QSet>
66 #include <QtCore/QSettings>
67
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>
79
80 Q_DECLARE_METATYPE(Core::IEditor*)
81
82 enum { debugEditorManager=0 };
83
84 static inline ExtensionSystem::PluginManager *pluginManager()
85 {
86     return ExtensionSystem::PluginManager::instance();
87 }
88
89 //===================EditorClosingCoreListener======================
90
91 namespace Core {
92 namespace Internal {
93
94 class EditorClosingCoreListener : public ICoreListener
95 {
96 public:
97     EditorClosingCoreListener(EditorManager *em);
98     bool editorAboutToClose(IEditor *editor);
99     bool coreAboutToClose();
100
101 private:
102     EditorManager *m_em;
103 };
104
105 EditorClosingCoreListener::EditorClosingCoreListener(EditorManager *em)
106         : m_em(em)
107 {
108 }
109
110 bool EditorClosingCoreListener::editorAboutToClose(IEditor *)
111 {
112     return true;
113 }
114
115 bool EditorClosingCoreListener::coreAboutToClose()
116 {
117     // Do not ask for files to save.
118     // MainWindow::closeEvent has already done that.
119     return m_em->closeAllEditors(false);
120 }
121
122 } // namespace Internal
123 } // namespace Core
124
125 using namespace Core;
126 using namespace Core::Internal;
127 using namespace Utils;
128
129 //===================EditorManager=====================
130
131 EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0;
132
133 EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent)
134     : QWidget(parent), m_mode(mode)
135 {
136     setLayout(new QVBoxLayout);
137     layout()->setMargin(0);
138     connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)),
139             this, SLOT(currentModeChanged(Core::IMode *)));
140
141     currentModeChanged(Core::ModeManager::instance()->currentMode());
142 }
143
144 EditorManagerPlaceHolder::~EditorManagerPlaceHolder()
145 {
146     if (m_current == this) {
147         EditorManager::instance()->setParent(0);
148         EditorManager::instance()->hide();
149     }
150 }
151
152 void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode)
153 {
154     if (m_current == this) {
155         m_current = 0;
156         EditorManager::instance()->setParent(0);
157         EditorManager::instance()->hide();
158     }
159     if (m_mode == mode) {
160         m_current = this;
161         layout()->addWidget(EditorManager::instance());
162         EditorManager::instance()->show();
163     }
164 }
165
166 EditorManagerPlaceHolder* EditorManagerPlaceHolder::current()
167 {
168     return m_current;
169 }
170
171 // ---------------- EditorManager
172
173 namespace Core {
174
175
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;
183
184     ICore *m_core;
185
186
187     // actions
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;
204
205     Internal::OpenEditorsWindow *m_windowPopup;
206     Internal::EditorClosingCoreListener *m_coreListener;
207
208     QMap<QString, QVariant> m_editorStates;
209     Internal::OpenEditorsViewFactory *m_openEditorsFactory;
210
211     QString fileFilters;
212     QString selectedFilter;
213
214     OpenEditorsModel *m_editorModel;
215     QString m_externalEditor;
216
217     IFile::ReloadSetting m_reloadSetting;
218 };
219 }
220
221 EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) :
222     m_view(0),
223     m_splitter(0),
224     m_core(core),
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)),
236     m_windowPopup(0),
237     m_coreListener(0),
238     m_reloadSetting(IFile::AlwaysAsk)
239 {
240     m_editorModel = new OpenEditorsModel(parent);
241 }
242
243 EditorManagerPrivate::~EditorManagerPrivate()
244 {
245 //    clearNavigationHistory();
246 }
247
248 EditorManager *EditorManager::m_instance = 0;
249
250 static Command *createSeparator(ActionManager *am, QObject *parent,
251                                 const QString &name,
252                                 const Context &context)
253 {
254     QAction *tmpaction = new QAction(parent);
255     tmpaction->setSeparator(true);
256     Command *cmd = am->registerAction(tmpaction, name, context);
257     return cmd;
258 }
259
260 EditorManager::EditorManager(ICore *core, QWidget *parent) :
261     QWidget(parent),
262     m_d(new EditorManagerPrivate(core, parent))
263 {
264     m_instance = this;
265
266     connect(m_d->m_core, SIGNAL(contextAboutToChange(Core::IContext *)),
267             this, SLOT(handleContextChange(Core::IContext *)));
268
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);
272
273     ActionManager *am = m_d->m_core->actionManager();
274     ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
275
276     // Revert to saved
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()));
284
285     // Save Action
286     am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
287     connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));
288
289     // Save As Action
290     am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
291     connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs()));
292
293     // Window Menu
294     ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
295
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);
301
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);
306
307     // Close Action
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()));
314
315 #ifdef Q_WS_WIN
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()));
322 #endif
323
324     // Close All Action
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()));
329
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()));
335
336     // Goto Previous In History Action
337     cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext);
338 #ifdef Q_WS_MAC
339     cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Tab")));
340 #else
341     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab")));
342 #endif
343     mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
344     connect(m_d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory()));
345
346     // Goto Next In History Action
347     cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editDesignContext);
348 #ifdef Q_WS_MAC
349     cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab")));
350 #else
351     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab")));
352 #endif
353     mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
354     connect(m_d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory()));
355
356     // Go back in navigation history
357     cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editDesignContext);
358 #ifdef Q_WS_MAC
359     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Left")));
360 #else
361     cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left")));
362 #endif
363     mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
364     connect(m_d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory()));
365
366     // Go forward in navigation history
367     cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editDesignContext);
368 #ifdef Q_WS_MAC
369     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Right")));
370 #else
371     cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right")));
372 #endif
373     mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
374     connect(m_d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory()));
375
376 #ifdef Q_WS_MAC
377     QString prefix = tr("Meta+E");
378 #else
379     QString prefix = tr("Ctrl+E");
380 #endif
381
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()));
387
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()));
393
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()));
399
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()));
405
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()));
411
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);
421
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);
431
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()));
436
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 *)));
441
442
443     // other setup
444     m_d->m_splitter = new SplitterOrView(m_d->m_editorModel);
445     m_d->m_view = m_d->m_splitter->view();
446
447
448     QHBoxLayout *layout = new QHBoxLayout(this);
449     layout->setMargin(0);
450     layout->setSpacing(0);
451     layout->addWidget(m_d->m_splitter);
452
453     updateActions();
454
455     m_d->m_windowPopup = new OpenEditorsWindow(this);
456 }
457
458 EditorManager::~EditorManager()
459 {
460     if (m_d->m_core) {
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;
465         }
466         pm->removeObject(m_d->m_openEditorsFactory);
467         delete m_d->m_openEditorsFactory;
468     }
469     delete m_d;
470 }
471
472 void EditorManager::init()
473 {
474     m_d->m_coreListener = new EditorClosingCoreListener(this);
475     pluginManager()->addObject(m_d->m_coreListener);
476
477     m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
478     pluginManager()->addObject(m_d->m_openEditorsFactory);
479 }
480
481
482 EditorToolBar *EditorManager::createToolBar(QWidget *parent)
483 {
484     return new EditorToolBar(parent);
485 }
486
487 QString EditorManager::defaultExternalEditor() const
488 {
489 #ifdef Q_OS_UNIX
490     return ConsoleProcess::defaultTerminalEmulator() + QLatin1String(
491 # ifdef Q_OS_MAC
492             " -async"
493 # endif
494             " -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\"");
495 #else
496     return QLatin1String("notepad %f");
497 #endif
498 }
499
500 void EditorManager::removeEditor(IEditor *editor)
501 {
502     bool isDuplicate = m_d->m_editorModel->isDuplicate(editor);
503     m_d->m_editorModel->removeEditor(editor);
504     if (!isDuplicate) {
505         m_d->m_core->fileManager()->removeFile(editor->file());
506     }
507     m_d->m_core->removeContextObject(editor);
508 }
509
510 void EditorManager::handleContextChange(Core::IContext *context)
511 {
512     if (debugEditorManager)
513         qDebug() << Q_FUNC_INFO;
514     IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0;
515     if (editor) {
516         setCurrentEditor(editor);
517     } else {
518         updateActions();
519     }
520 }
521
522 void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
523 {
524     if (editor)
525         setCurrentView(0);
526
527     if (m_d->m_currentEditor == editor)
528         return;
529     if (m_d->m_currentEditor && !ignoreNavigationHistory)
530         addCurrentPositionToNavigationHistory();
531
532     m_d->m_currentEditor = editor;
533     if (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
537     }
538     updateActions();
539     emit currentEditorChanged(editor);
540 }
541
542
543 void EditorManager::setCurrentView(Core::Internal::SplitterOrView *view)
544 {
545     if (view == m_d->m_currentView)
546         return;
547
548     SplitterOrView *old = m_d->m_currentView;
549     m_d->m_currentView = view;
550
551     if (old)
552         old->update();
553     if (view)
554         view->update();
555
556     if (view && !view->editor())
557         view->setFocus();
558 }
559
560 Core::Internal::SplitterOrView *EditorManager::currentSplitterOrView() const
561 {
562     SplitterOrView *view = m_d->m_currentView;
563     if (!view)
564         view = m_d->m_currentEditor?
565                m_d->m_splitter->findView(m_d->m_currentEditor):
566                m_d->m_splitter->findFirstView();
567     if (!view)
568         return m_d->m_splitter;
569     return view;
570 }
571
572 Core::Internal::EditorView *EditorManager::currentEditorView() const
573 {
574     return currentSplitterOrView()->view();
575 }
576
577 QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
578 {
579     QList<IEditor *> found;
580     QString fixedname = FileManager::fixFileName(filename);
581     foreach (IEditor *editor, openedEditors()) {
582         if (fixedname == FileManager::fixFileName(editor->file()->fileName()))
583             found << editor;
584     }
585     return found;
586 }
587
588 QList<IEditor *> EditorManager::editorsForFile(IFile *file) const
589 {
590     QList<IEditor *> found;
591     foreach (IEditor *editor, openedEditors()) {
592         if (editor->file() == file)
593             found << editor;
594     }
595     return found;
596 }
597
598 IEditor *EditorManager::currentEditor() const
599 {
600     return m_d->m_currentEditor;
601 }
602
603 void EditorManager::emptyView(Core::Internal::EditorView *view)
604 {
605     if (!view)
606         return;
607
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);
613             continue;
614         }
615         emit editorAboutToClose(editor);
616         removeEditor(editor);
617         view->removeEditor(editor);
618     }
619     emit editorsClosed(editors);
620     foreach (IEditor *editor, editors) {
621         delete editor;
622     }
623 }
624
625 void EditorManager::closeView(Core::Internal::EditorView *view)
626 {
627     if (!view)
628         return;
629
630     if (view == m_d->m_view) {
631         if (IEditor *e = view->currentEditor())
632             closeEditors(QList<IEditor *>() << e);
633         return;
634     }
635
636     if (IEditor *e = view->currentEditor()) {
637         /*
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
644            around.
645         */
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());
650             }
651         }
652     }
653
654     emptyView(view);
655
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;
663
664     splitter->unsplit();
665
666     SplitterOrView *newCurrent = splitter->findFirstView();
667     if (newCurrent) {
668         if (IEditor *e = newCurrent->editor()) {
669             activateEditor(newCurrent->view(), e);
670         } else {
671             setCurrentView(newCurrent);
672         }
673     }
674 }
675
676 QList<IEditor*>
677     EditorManager::editorsForFiles(QList<IFile*> files) const
678 {
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)) {
684                     found << editor;
685             }
686         }
687     }
688     return found.toList();
689 }
690
691 QList<IFile *> EditorManager::filesForEditors(QList<IEditor *> editors) const
692 {
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);
699         }
700     }
701     return files;
702 }
703
704 bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
705 {
706     m_d->m_editorModel->removeAllRestoredEditors();
707     if (closeEditors(openedEditors(), askAboutModifiedEditors)) {
708 //        m_d->clearNavigationHistory();
709         return true;
710     }
711     return false;
712 }
713
714 void EditorManager::closeOtherEditors(IEditor *editor)
715 {
716     m_d->m_editorModel->removeAllRestoredEditors();
717     QList<IEditor*> editors = openedEditors();
718     editors.removeAll(editor);
719     closeEditors(editors, true);
720 }
721
722 void EditorManager::closeOtherEditors()
723 {
724     IEditor *current = currentEditor();
725     QTC_ASSERT(current, return);
726     closeOtherEditors(current);
727 }
728
729 // SLOT connected to action
730 void EditorManager::closeEditor()
731 {
732     if (!m_d->m_currentEditor)
733         return;
734     addCurrentPositionToNavigationHistory();
735     closeEditor(m_d->m_currentEditor);
736 }
737
738 void EditorManager::closeEditor(Core::IEditor *editor)
739 {
740     if (!editor)
741         return;
742     closeEditors(QList<IEditor *>() << editor);
743 }
744
745 void EditorManager::closeEditor(const QModelIndex &index)
746 {
747     IEditor *editor = index.data(Qt::UserRole).value<Core::IEditor*>();
748     if (editor)
749         closeEditor(editor);
750     else
751         m_d->m_editorModel->removeEditor(index);
752 }
753
754 bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
755 {
756     if (editorsToClose.isEmpty())
757         return true;
758
759     SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
760
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;
774                 break;
775             }
776         }
777         if (editorAccepted)
778             acceptedEditors.append(editor);
779     }
780     if (acceptedEditors.isEmpty())
781         return false;
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);
787         if (cancelled)
788             return false;
789         if (!list.isEmpty()) {
790             closingFailed = true;
791             QSet<IEditor*> skipSet = editorsForFiles(list).toSet();
792             acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList();
793         }
794     }
795     if (acceptedEditors.isEmpty())
796         return false;
797
798     // add duplicates
799     foreach(IEditor *editor, acceptedEditors)
800         acceptedEditors += m_d->m_editorModel->duplicatesFor(editor);
801
802     QList<EditorView*> closedViews;
803
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));
811         }
812
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);
818         }
819     }
820
821     foreach (EditorView *view, closedViews) {
822         IEditor *newCurrent = view->currentEditor();
823         if (!newCurrent)
824             newCurrent = pickUnusedEditor();
825         if (newCurrent) {
826             activateEditor(view, newCurrent, NoActivate);
827         } else {
828             QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
829             if (idx.isValid())
830                 activateEditor(idx, view, NoActivate);
831         }
832     }
833
834     emit editorsClosed(acceptedEditors);
835
836     foreach (IEditor *editor, acceptedEditors) {
837         delete editor;
838     }
839
840     if (currentSplitterOrView) {
841         if (IEditor *editor = currentSplitterOrView->editor())
842             activateEditor(currentSplitterOrView->view(), editor);
843     }
844
845     if (!currentEditor()) {
846         emit currentEditorChanged(0);
847         updateActions();
848     }
849
850     return !closingFailed;
851 }
852
853 void EditorManager::closeDuplicate(Core::IEditor *editor)
854 {
855
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);
860
861     if (duplicates.isEmpty()) {
862         closeEditor(editor);
863         return;
864     }
865
866     if (original== editor)
867         m_d->m_editorModel->makeOriginal(duplicates.first());
868
869     SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
870
871     emit editorAboutToClose(editor);
872
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);
877
878         IEditor *newCurrent = view->currentEditor();
879         if (!newCurrent)
880             newCurrent = pickUnusedEditor();
881         if (newCurrent) {
882             activateEditor(view, newCurrent, NoActivate);
883         } else {
884             QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
885             if (idx.isValid())
886                 activateEditor(idx, view, NoActivate);
887         }
888     }
889
890     emit editorsClosed(QList<IEditor*>() << editor);
891     delete editor;
892     if (currentSplitterOrView) {
893         if (IEditor *currentEditor = currentSplitterOrView->editor())
894             activateEditor(currentSplitterOrView->view(), currentEditor);
895     }
896 }
897
898 Core::IEditor *EditorManager::pickUnusedEditor() const
899 {
900     foreach (IEditor *editor, openedEditors()) {
901         SplitterOrView *view = m_d->m_splitter->findView(editor);
902         if (!view || view->editor() != editor)
903             return editor;
904     }
905     return 0;
906 }
907
908 Core::IEditor *EditorManager::activateEditor(const QModelIndex &index, Internal::EditorView *view, OpenEditorFlags flags)
909 {
910     IEditor *editor = index.data(Qt::UserRole).value<IEditor*>();
911     if (editor)  {
912         return activateEditor(view, editor, flags);
913     }
914
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);
918 }
919
920 Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)
921 {
922     Q_ASSERT(view && editor);
923
924     if (view->currentEditor() && view->currentEditor()->file() == editor->file())
925         editor = view->currentEditor();
926
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);
937                     }
938                 }
939                 return editor;
940             } else if (duplicateSupported) {
941                 editor = duplicateEditor(editor);
942                 Q_ASSERT(editor);
943                 m_d->m_editorModel->makeOriginal(editor);
944             }
945         }
946         view->addEditor(editor);
947     }
948     return editor;
949 }
950
951 Core::IEditor *EditorManager::activateEditor(Core::IEditor *editor, OpenEditorFlags flags)
952 {
953     return activateEditor(0, editor, flags);
954 }
955
956 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags)
957 {
958     if (!view)
959         view = currentEditorView();
960
961     Q_ASSERT(view);
962
963     if (!editor) {
964         if (!m_d->m_currentEditor)
965             setCurrentEditor(0, (flags & IgnoreNavigationHistory));
966         return 0;
967     }
968
969     editor = placeEditor(view, editor);
970
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();
977             } else {
978                 ModeManager::instance()->activateMode(preferredMode);
979             }
980         }
981         if (isVisible())
982             editor->widget()->setFocus();
983     }
984     return editor;
985 }
986
987 Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IFile *file, OpenEditorFlags flags)
988 {
989     const QList<IEditor*> editors = editorsForFile(file);
990     if (editors.isEmpty())
991         return 0;
992
993     return activateEditor(view, editors.first(), flags);
994 }
995
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)
1005 {
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);
1015             if (firstMatchOnly)
1016                 return;
1017             break;
1018         }
1019     }
1020     // Any parent mime type classes? -> recurse
1021     QStringList parentTypes = mimeType.subClassesOf();
1022     if (parentTypes.empty())
1023         return;
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);
1028     }
1029 }
1030
1031 EditorManager::EditorFactoryList
1032     EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const
1033 {
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;
1039     return rc;
1040 }
1041
1042 EditorManager::ExternalEditorList
1043         EditorManager::externalEditors(const MimeType &mimeType, bool bestMatchOnly) const
1044 {
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;
1050     return rc;
1051 }
1052
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,
1057                                              const QString &id)
1058 {
1059     const QList<EditorFactoryLike *> factories = pm->template getObjects<EditorFactoryLike>();
1060     foreach(EditorFactoryLike *efl, factories)
1061         if (id == efl->id())
1062             return efl;
1063     return 0;
1064 }
1065
1066 IEditor *EditorManager::createEditor(const QString &editorId,
1067                                      const QString &fileName)
1068 {
1069     if (debugEditorManager)
1070         qDebug() << Q_FUNC_INFO << editorId << fileName;
1071
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);
1077         if (!mimeType) {
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"));
1081         }
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);
1086     } else {
1087         // Find by editor id
1088         if (IEditorFactory *factory = findById<IEditorFactory>(pluginManager(), editorId))
1089             factories.push_back(factory);
1090     }
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());
1094         return 0;
1095     }
1096
1097     IEditor *editor = factories.front()->createEditor(this);
1098     if (editor)
1099         connect(editor, SIGNAL(changed()), this, SLOT(updateActions()));
1100     if (editor)
1101         emit editorCreated(editor, fileName);
1102     return editor;
1103 }
1104
1105 void EditorManager::addEditor(IEditor *editor, bool isDuplicate)
1106 {
1107     if (!editor)
1108         return;
1109     m_d->m_core->addContextObject(editor);
1110
1111     m_d->m_editorModel->addEditor(editor, isDuplicate);
1112     if (!isDuplicate) {
1113         const bool isTemporary = editor->isTemporary();
1114         const bool addWatcher = !isTemporary;
1115         m_d->m_core->fileManager()->addFile(editor->file(), addWatcher);
1116         if (!isTemporary)
1117             m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1118     }
1119     emit editorOpened(editor);
1120 }
1121
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
1126 {
1127     // Collect editors that can open the file
1128     const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName);
1129     if (!mt)
1130         return QString();
1131     QStringList allEditorIds;
1132     QStringList externalEditorIds;
1133     // Built-in
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());
1138     }
1139     // External editors
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());
1145     }
1146     if (allEditorIds.empty())
1147         return QString();
1148     // Run dialog.
1149     OpenWithDialog dialog(fileName, m_d->m_core->mainWindow());
1150     dialog.setEditors(allEditorIds);
1151     dialog.setCurrentEditor(0);
1152     if (dialog.exec() != QDialog::Accepted)
1153         return QString();
1154     const QString selectedId = dialog.editor();
1155     if (isExternalEditor)
1156         *isExternalEditor = externalEditorIds.contains(selectedId);
1157     return selectedId;
1158 }
1159
1160 static QString formatFileFilters(const Core::ICore *core, QString *selectedFilter)
1161 {
1162     QString rc;
1163
1164     // Compile list of filter strings
1165     QStringList filters = core->mimeDatabase()->filterStrings();
1166     filters.sort();
1167     selectedFilter->clear();
1168     if (filters.empty())
1169         return rc;
1170
1171     const QString filterSeparator = QLatin1String(";;");
1172     foreach (const QString &filterString, filters) {
1173         if (!rc.isEmpty())
1174             rc += filterSeparator;
1175         rc += filterString;
1176     }
1177
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 (*)");
1181     if (!rc.isEmpty())
1182         allFilesFilter += filterSeparator;
1183     rc.prepend(allFilesFilter);
1184     *selectedFilter = allFilesFilter;
1185
1186     return rc;
1187 }
1188
1189 IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorId,
1190                                    OpenEditorFlags flags, bool *newEditor)
1191 {
1192     return openEditor(0, fileName, editorId, flags, newEditor);
1193 }
1194
1195 IEditor *EditorManager::openEditor(Core::Internal::EditorView *view, const QString &fileName,
1196                         const QString &editorId, OpenEditorFlags flags, bool *newEditor)
1197 {
1198     if (debugEditorManager)
1199         qDebug() << Q_FUNC_INFO << fileName << editorId;
1200
1201     if (fileName.isEmpty())
1202         return 0;
1203
1204     if (newEditor)
1205         *newEditor = false;
1206
1207     const QList<IEditor *> editors = editorsForFileName(fileName);
1208     if (!editors.isEmpty())
1209         return activateEditor(view, editors.first(), flags);
1210
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:
1215     if (!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)));
1220         delete editor;
1221         editor = 0;
1222         return 0;
1223     }
1224     addEditor(editor);
1225
1226     if (newEditor)
1227         *newEditor = true;
1228
1229     IEditor *result = activateEditor(view, editor, flags);
1230     if (editor == result)
1231         restoreEditorState(editor);
1232     QApplication::restoreOverrideCursor();
1233     return result;
1234 }
1235
1236 bool EditorManager::openExternalEditor(const QString &fileName, const QString &editorId)
1237 {
1238     IExternalEditor *ee = findById<IExternalEditor>(pluginManager(), editorId);
1239     if (!ee)
1240         return false;
1241     QString errorMessage;
1242     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1243     const bool ok = ee->startEditor(fileName, &errorMessage);
1244     QApplication::restoreOverrideCursor();
1245     if (!ok)
1246         QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), errorMessage);
1247     return ok;
1248 }
1249
1250 QStringList EditorManager::getOpenFileNames() const
1251 {
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);
1256 }
1257
1258 void EditorManager::ensureEditorManagerVisible()
1259 {
1260     if (!isVisible())
1261         m_d->m_core->modeManager()->activateMode(Constants::MODE_EDIT);
1262 }
1263
1264 IEditor *EditorManager::openEditorWithContents(const QString &editorId,
1265                                         QString *titlePattern,
1266                                         const QString &contents)
1267 {
1268     if (debugEditorManager)
1269         qDebug() << Q_FUNC_INFO << editorId << titlePattern << contents;
1270
1271     if (editorId.isEmpty())
1272         return 0;
1273
1274     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1275     IEditor *edt = createEditor(editorId);
1276     if (!edt) {
1277         QApplication::restoreOverrideCursor();
1278         return 0;
1279     }
1280
1281     if (!edt->createNew(contents)) {
1282         QApplication::restoreOverrideCursor();
1283         delete edt;
1284         edt = 0;
1285         return 0;
1286     }
1287
1288     QString title = edt->displayName();
1289
1290     if (titlePattern) {
1291         const QChar dollar = QLatin1Char('$');
1292         const QChar dot = QLatin1Char('.');
1293
1294         QString base = *titlePattern;
1295         if (base.isEmpty())
1296             base = QLatin1String("unnamed$");
1297         if (base.contains(dollar)) {
1298             int i = 1;
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('*'));
1305                 } else {
1306                     name = QFileInfo(name).completeBaseName();
1307                 }
1308                 docnames << name;
1309             }
1310
1311             do {
1312                 title = base;
1313                 title.replace(QString(dollar), QString::number(i++));
1314             } while (docnames.contains(title));
1315         } else {
1316             title = *titlePattern;
1317         }
1318         *titlePattern = title;
1319     }
1320     edt->setDisplayName(title);
1321     addEditor(edt);
1322     QApplication::restoreOverrideCursor();
1323     return edt;
1324 }
1325
1326 bool EditorManager::hasEditor(const QString &fileName) const
1327 {
1328     return !editorsForFileName(fileName).isEmpty();
1329 }
1330
1331 void EditorManager::restoreEditorState(IEditor *editor)
1332 {
1333     QTC_ASSERT(editor, return);
1334     QString fileName = editor->file()->fileName();
1335     editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray());
1336 }
1337
1338 bool EditorManager::saveEditor(IEditor *editor)
1339 {
1340     return saveFile(editor);
1341 }
1342
1343 bool EditorManager::saveFile(IEditor *editor)
1344 {
1345     if (!editor)
1346         editor = currentEditor();
1347     if (!editor)
1348         return false;
1349
1350     IFile *file = editor->file();
1351     file->checkPermissions();
1352
1353     const QString &fileName = file->fileName();
1354
1355     if (fileName.isEmpty())
1356         return saveFileAs(editor);
1357
1358     bool success = false;
1359
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);
1364
1365     if (!success) {
1366         MakeWritableResult answer =
1367                 makeEditorWritable(editor);
1368         if (answer == Failed)
1369             return false;
1370         if (answer == SavedAs)
1371             return true;
1372
1373         file->checkPermissions();
1374
1375         m_d->m_core->fileManager()->blockFileChange(file);
1376         success = file->save(fileName);
1377         m_d->m_core->fileManager()->unblockFileChange(file);
1378     }
1379
1380     if (success && !editor->isTemporary())
1381         m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1382
1383     return success;
1384 }
1385
1386 EditorManager::ReadOnlyAction
1387     EditorManager::promptReadOnlyFile(const QString &fileName,
1388                                       const IVersionControl *versionControl,
1389                                       QWidget *parent,
1390                                       bool displaySaveAsButton)
1391 {
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)
1396             return RO_OpenVCS;
1397         promptVCS = true;
1398     }
1399
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);
1404
1405     QPushButton *vcsButton = 0;
1406     if (promptVCS)
1407         vcsButton = msgBox.addButton(tr("Open with VCS (%1)").arg(versionControl->displayName()), QMessageBox::AcceptRole);
1408
1409     QPushButton *makeWritableButton =  msgBox.addButton(tr("Make writable"), QMessageBox::AcceptRole);
1410
1411     QPushButton *saveAsButton = 0;
1412     if (displaySaveAsButton)
1413         saveAsButton = msgBox.addButton(tr("Save as ..."), QMessageBox::ActionRole);
1414
1415     msgBox.setDefaultButton(vcsButton ? vcsButton : makeWritableButton);
1416     msgBox.exec();
1417
1418     QAbstractButton *clickedButton = msgBox.clickedButton();
1419     if (clickedButton == vcsButton)
1420         return RO_OpenVCS;
1421     if (clickedButton == makeWritableButton)
1422         return RO_MakeWriteable;
1423     if (clickedButton == saveAsButton)
1424         return RO_SaveAs;
1425     return  RO_Cancel;
1426 }
1427
1428
1429 MakeWritableResult
1430 EditorManager::makeEditorWritable(IEditor *editor)
1431 {
1432     if (!editor || !editor->file())
1433         return Failed;
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();
1438
1439     switch (promptReadOnlyFile(fileName, versionControl, m_d->m_core->mainWindow(), true)) {
1440     case RO_OpenVCS:
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."));
1443             return Failed;
1444         }
1445         file->checkPermissions();
1446         return OpenedWithVersionControl;
1447     case RO_MakeWriteable: {
1448         const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser);
1449         if (!permsOk) {
1450             QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"),  tr("Could not set permissions to writable."));
1451             return Failed;
1452         }
1453     }
1454         file->checkPermissions();
1455         return MadeWritable;
1456     case RO_SaveAs :
1457         return saveFileAs(editor) ? SavedAs : Failed;
1458     case RO_Cancel:
1459         break;
1460     }
1461     return Failed;
1462 }
1463
1464 bool EditorManager::saveFileAs(IEditor *editor)
1465 {
1466     if (!editor)
1467         editor = currentEditor();
1468     if (!editor)
1469         return false;
1470
1471     QString absoluteFilePath = m_d->m_core->fileManager()->getSaveAsFileName(editor->file());
1472     if (absoluteFilePath.isEmpty())
1473         return false;
1474     if (absoluteFilePath != editor->file()->fileName()) {
1475         const QList<IEditor *> existList = editorsForFileName(absoluteFilePath);
1476         if (!existList.isEmpty()) {
1477             closeEditors(existList, false);
1478         }
1479     }
1480
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();
1485
1486     if (success && !editor->isTemporary())
1487         m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
1488
1489     updateActions();
1490     return success;
1491 }
1492
1493 void EditorManager::gotoNextDocHistory()
1494 {
1495     OpenEditorsWindow *dialog = windowPopup();
1496     if (dialog->isVisible()) {
1497         dialog->selectNextEditor();
1498     } else {
1499         EditorView *view = currentEditorView();
1500         dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1501         dialog->selectNextEditor();
1502         showPopupOrSelectDocument();
1503     }
1504 }
1505
1506 void EditorManager::gotoPreviousDocHistory()
1507 {
1508     OpenEditorsWindow *dialog = windowPopup();
1509     if (dialog->isVisible()) {
1510         dialog->selectPreviousEditor();
1511     } else {
1512         EditorView *view = currentEditorView();
1513         dialog->setEditors(m_d->m_view, view, m_d->m_editorModel);
1514         dialog->selectPreviousEditor();
1515         showPopupOrSelectDocument();
1516     }
1517 }
1518
1519 void EditorManager::makeCurrentEditorWritable()
1520 {
1521     if (IEditor* curEditor = currentEditor())
1522         makeEditorWritable(curEditor);
1523 }
1524
1525 void EditorManager::updateActions()
1526 {
1527     QString fName;
1528     IEditor *curEditor = currentEditor();
1529     int openedCount = openedEditors().count() + m_d->m_editorModel->restoredEditors().count();
1530
1531     if (curEditor) {
1532
1533         if (!curEditor->file()->fileName().isEmpty()) {
1534             QFileInfo fi(curEditor->file()->fileName());
1535             fName = fi.fileName();
1536         } else {
1537             fName = curEditor->displayName();
1538         }
1539
1540 #ifdef Q_WS_MAC
1541         window()->setWindowModified(curEditor->file()->isModified());
1542 #endif
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()));
1548         } else {
1549             hideEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"));
1550         }
1551 #ifdef Q_WS_MAC
1552     } else { // curEditor
1553         window()->setWindowModified(false);
1554 #endif
1555     }
1556
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());
1561
1562     QString quotedName;
1563     if (!fName.isEmpty())
1564         quotedName = '"' + fName + '"';
1565
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));
1569
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")));
1575
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);
1581
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);
1586
1587     m_d->m_openInExternalEditorAction->setEnabled(curEditor != 0);
1588 }
1589
1590 QList<IEditor*> EditorManager::openedEditors() const
1591 {
1592     return m_d->m_editorModel->editors();
1593 }
1594
1595 OpenEditorsModel *EditorManager::openedEditorsModel() const
1596 {
1597     return m_d->m_editorModel;
1598 }
1599
1600 void EditorManager::addCurrentPositionToNavigationHistory(IEditor *editor, const QByteArray &saveState)
1601 {
1602     currentEditorView()->addCurrentPositionToNavigationHistory(editor, saveState);
1603     updateActions();
1604 }
1605
1606 void EditorManager::cutForwardNavigationHistory()
1607 {
1608     currentEditorView()->cutForwardNavigationHistory();
1609     updateActions();
1610 }
1611
1612 void EditorManager::goBackInNavigationHistory()
1613 {
1614     currentEditorView()->goBackInNavigationHistory();
1615     updateActions();
1616     ensureEditorManagerVisible();
1617     return;
1618 }
1619
1620 void EditorManager::goForwardInNavigationHistory()
1621 {
1622     currentEditorView()->goForwardInNavigationHistory();
1623     updateActions();
1624     ensureEditorManagerVisible();
1625 }
1626
1627 OpenEditorsWindow *EditorManager::windowPopup() const
1628 {
1629     return m_d->m_windowPopup;
1630 }
1631
1632 void EditorManager::showPopupOrSelectDocument() const
1633 {
1634     if (QApplication::keyboardModifiers() == Qt::NoModifier) {
1635         windowPopup()->selectAndHide();
1636     } else {
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);
1644     }
1645 }
1646
1647 QByteArray EditorManager::saveState() const
1648 {
1649     QByteArray bytes;
1650     QDataStream stream(&bytes, QIODevice::WriteOnly);
1651
1652     stream << QByteArray("EditorManagerV4");
1653
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));
1660         }
1661     }
1662
1663     stream << m_d->m_editorStates;
1664
1665     QList<OpenEditorsModel::Entry> entries = m_d->m_editorModel->entries();
1666     stream << entries.count();
1667
1668     foreach (const OpenEditorsModel::Entry &entry, entries) {
1669         stream << entry.fileName() << entry.displayName() << entry.id().toUtf8();
1670     }
1671
1672     stream << m_d->m_splitter->saveState();
1673
1674     return bytes;
1675 }
1676
1677 bool EditorManager::restoreState(const QByteArray &state)
1678 {
1679     closeAllEditors(true);
1680     removeAllSplits();
1681     QDataStream stream(state);
1682
1683     QByteArray version;
1684     stream >> version;
1685
1686     if (version != "EditorManagerV4")
1687         return false;
1688
1689     QMap<QString, QVariant> editorstates;
1690
1691     QApplication::setOverrideCursor(Qt::WaitCursor);
1692
1693     stream >> editorstates;
1694
1695     QMapIterator<QString, QVariant> i(editorstates);
1696     while (i.hasNext()) {
1697         i.next();
1698         m_d->m_editorStates.insert(i.key(), i.value());
1699     }
1700
1701     int editorCount = 0;
1702     stream >> editorCount;
1703     while (--editorCount >= 0) {
1704         QString fileName;
1705         stream >> fileName;
1706         QString displayName;
1707         stream >> displayName;
1708         QByteArray id;
1709         stream >> id;
1710
1711         if (!fileName.isEmpty() && !displayName.isEmpty())
1712             m_d->m_editorModel->addRestoredEditor(fileName, displayName, QString::fromUtf8(id));
1713     }
1714
1715     QByteArray splitterstates;
1716     stream >> splitterstates;
1717     m_d->m_splitter->restoreState(splitterstates);
1718
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();
1728     }
1729
1730     QApplication::restoreOverrideCursor();
1731
1732     return true;
1733 }
1734
1735 static const char * const documentStatesKey = "EditorManager/DocumentStates";
1736 static const char * const externalEditorKey = "EditorManager/ExternalEditorCommand";
1737 static const char * const reloadBehaviorKey = "EditorManager/ReloadBehavior";
1738
1739 void EditorManager::saveSettings()
1740 {
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);
1745 }
1746
1747 void EditorManager::readSettings()
1748 {
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));
1755     }
1756     if (qs->contains(QLatin1String(externalEditorKey))) {
1757         m_d->m_externalEditor = qs->value(QLatin1String(externalEditorKey)).toString();
1758         qs->remove(QLatin1String(externalEditorKey));
1759     }
1760
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();
1767
1768     if (settings->contains(QLatin1String(reloadBehaviorKey)))
1769         m_d->m_reloadSetting = (IFile::ReloadSetting)settings->value(QLatin1String(reloadBehaviorKey)).toInt();
1770 }
1771
1772
1773 void EditorManager::revertToSaved()
1774 {
1775     IEditor *currEditor = currentEditor();
1776     if (!currEditor)
1777         return;
1778     const QString fileName =  currEditor->file()->fileName();
1779     if (fileName.isEmpty())
1780         return;
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)
1790             return;
1791
1792     }
1793     currEditor->file()->reload(IFile::FlagReload, IFile::TypeContents);
1794 }
1795
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)
1801 {
1802     currentEditorView()->showEditorInfoBar(id, infoText, buttonText, object, buttonPressMember, cancelButtonPressMember);
1803 }
1804
1805
1806 void EditorManager::hideEditorInfoBar(const QString &id)
1807 {
1808     Core::Internal::EditorView *cev = currentEditorView();
1809     if (cev)
1810         cev->hideEditorInfoBar(id);
1811 }
1812
1813 void EditorManager::showEditorStatusBar(const QString &id,
1814                                       const QString &infoText,
1815                                       const QString &buttonText,
1816                                       QObject *object, const char *member)
1817 {
1818
1819     currentEditorView()->showEditorStatusBar(id, infoText, buttonText, object, member);
1820 }
1821
1822 void EditorManager::hideEditorStatusBar(const QString &id)
1823 {
1824     currentEditorView()->hideEditorStatusBar(id);
1825 }
1826
1827 QString EditorManager::externalEditorHelpText() const
1828 {
1829     QString help = tr(
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>"
1842             "</table>");
1843     return help;
1844 }
1845
1846 void EditorManager::openInExternalEditor()
1847 {
1848     QString command = m_d->m_externalEditor;
1849     if (command.isEmpty())
1850         command = defaultExternalEditor();
1851
1852     if (command.isEmpty())
1853         return;
1854
1855     IEditor *editor = currentEditor();
1856     if (!editor)
1857         return;
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);
1862         if (cancelled)
1863             return;
1864     }
1865
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)));
1870
1871     QString pre = command;
1872     QString cmd;
1873     for (int i = 0; i < pre.size(); ++i) {
1874         QChar c = pre.at(i);
1875         if (c == QLatin1Char('%') && i < pre.size()-1) {
1876             c = pre.at(++i);
1877             QString s;
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('%'))
1897                 s = c;
1898             else {
1899                 s = QLatin1Char('%');
1900                 s += c;
1901             }
1902             cmd += s;
1903             continue;
1904
1905         }
1906         cmd += c;
1907     }
1908
1909     QProcess::startDetached(cmd);
1910 }
1911
1912 void EditorManager::setExternalEditor(const QString &editor)
1913 {
1914     if (editor.isEmpty() || editor == defaultExternalEditor())
1915         m_d->m_externalEditor = defaultExternalEditor();
1916     else
1917         m_d->m_externalEditor = editor;
1918 }
1919
1920 QString EditorManager::externalEditor() const
1921 {
1922     if (m_d->m_externalEditor.isEmpty())
1923         return defaultExternalEditor();
1924     return m_d->m_externalEditor;
1925 }
1926
1927 void EditorManager::setReloadSetting(IFile::ReloadSetting behavior)
1928 {
1929     m_d->m_reloadSetting = behavior;
1930 }
1931
1932 IFile::ReloadSetting EditorManager::reloadSetting() const
1933 {
1934     return m_d->m_reloadSetting;
1935 }
1936
1937 Core::IEditor *EditorManager::duplicateEditor(Core::IEditor *editor)
1938 {
1939     if (!editor->duplicateSupported())
1940         return 0;
1941
1942     IEditor *duplicate = editor->duplicate(0);
1943     duplicate->restoreState(editor->saveState());
1944     emit editorCreated(duplicate, duplicate->file()->fileName());
1945     addEditor(duplicate, true);
1946     return duplicate;
1947 }
1948
1949 void EditorManager::split(Qt::Orientation orientation)
1950 {
1951     SplitterOrView *view = m_d->m_currentView;
1952     if (!view)
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);
1957     }
1958     updateActions();
1959 }
1960
1961 void EditorManager::split()
1962 {
1963     split(Qt::Vertical);
1964 }
1965
1966 void EditorManager::splitSideBySide()
1967 {
1968     split(Qt::Horizontal);
1969 }
1970
1971 void EditorManager::removeCurrentSplit()
1972 {
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);
1976
1977     if (!viewToClose || viewToClose->isSplitter() || viewToClose == m_d->m_splitter)
1978         return;
1979
1980     closeView(viewToClose->view());
1981     updateActions();
1982 }
1983
1984 void EditorManager::removeAllSplits()
1985 {
1986     if (!m_d->m_splitter->isSplitter())
1987         return;
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();
1993     if (!editor)
1994         editor = pickUnusedEditor();
1995     activateEditor(editor);
1996 }
1997
1998 void EditorManager::gotoOtherSplit()
1999 {
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);
2004         if (!currentView)
2005             currentView = m_d->m_splitter->findFirstView();
2006         SplitterOrView *view = m_d->m_splitter->findNextView(currentView);
2007         if (!view)
2008             view = m_d->m_splitter->findFirstView();
2009         if (view) {
2010             if (IEditor *editor = view->editor()) {
2011                 setCurrentEditor(editor, true);
2012                 editor->widget()->setFocus();
2013             } else {
2014                 setCurrentView(view);
2015             }
2016         }
2017     }
2018 }
2019
2020 qint64 EditorManager::maxTextFileSize()
2021 {
2022     return (qint64(3) << 24);
2023 }
2024 //===================EditorClosingCoreListener======================
2025