editormanager.cpp 70.9 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
con's avatar
con committed
9
** No Commercial Usage
10
**
con's avatar
con committed
11 12 13 14
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
15
**
16
** GNU Lesser General Public License Usage
17
**
18 19 20 21 22 23
** 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.
24
**
con's avatar
con committed
25 26 27 28 29 30
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
con's avatar
con committed
31
**
32
**************************************************************************/
hjk's avatar
hjk committed
33

con's avatar
con committed
34
#include "editormanager.h"
35
#include "editorview.h"
con's avatar
con committed
36
#include "openeditorswindow.h"
mae's avatar
mae committed
37
#include "openeditorsview.h"
38
#include "openeditorsmodel.h"
con's avatar
con committed
39 40
#include "openwithdialog.h"
#include "filemanager.h"
41
#include "icore.h"
Friedemann Kleint's avatar
Friedemann Kleint committed
42
#include "ieditor.h"
con's avatar
con committed
43 44
#include "iversioncontrol.h"
#include "mimedatabase.h"
45 46
#include "tabpositionindicator.h"
#include "vcsmanager.h"
con's avatar
con committed
47

Lasse Holmstedt's avatar
Lasse Holmstedt committed
48
#include <coreplugin/editortoolbar.h>
con's avatar
con committed
49 50
#include <coreplugin/coreconstants.h>
#include <coreplugin/modemanager.h>
51
#include <coreplugin/actionmanager/actionmanager.h>
52 53
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
con's avatar
con committed
54
#include <coreplugin/editormanager/ieditorfactory.h>
55
#include <coreplugin/editormanager/iexternaleditor.h>
56
#include <coreplugin/icorelistener.h>
con's avatar
con committed
57
#include <coreplugin/imode.h>
58
#include <coreplugin/settingsdatabase.h>
59
#include <coreplugin/variablemanager.h>
hjk's avatar
hjk committed
60
#include <coreplugin/uniqueidmanager.h>
con's avatar
con committed
61

62 63
#include <extensionsystem/pluginmanager.h>

64
#include <utils/consoleprocess.h>
hjk's avatar
hjk committed
65 66 67
#include <utils/qtcassert.h>

#include <QtCore/QDebug>
con's avatar
con committed
68 69 70
#include <QtCore/QFileInfo>
#include <QtCore/QMap>
#include <QtCore/QProcess>
hjk's avatar
hjk committed
71 72
#include <QtCore/QSet>
#include <QtCore/QSettings>
73
#include <QtCore/QTextCodec>
con's avatar
con committed
74 75

#include <QtGui/QAction>
76
#include <QtGui/QShortcut>
con's avatar
con committed
77 78
#include <QtGui/QApplication>
#include <QtGui/QFileDialog>
hjk's avatar
hjk committed
79
#include <QtGui/QLayout>
80
#include <QtGui/QMainWindow>
con's avatar
con committed
81 82 83
#include <QtGui/QMenu>
#include <QtGui/QMessageBox>
#include <QtGui/QPushButton>
hjk's avatar
hjk committed
84
#include <QtGui/QSplitter>
85
#include <QtGui/QStackedLayout>
con's avatar
con committed
86 87 88

enum { debugEditorManager=0 };

89 90 91 92 93
static inline ExtensionSystem::PluginManager *pluginManager()
{
    return ExtensionSystem::PluginManager::instance();
}

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
//===================EditorClosingCoreListener======================

namespace Core {
namespace Internal {

class EditorClosingCoreListener : public ICoreListener
{
public:
    EditorClosingCoreListener(EditorManager *em);
    bool editorAboutToClose(IEditor *editor);
    bool coreAboutToClose();

private:
    EditorManager *m_em;
};

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);
}

} // namespace Internal
} // namespace Core

using namespace Core;
using namespace Core::Internal;
using namespace Utils;

con's avatar
con committed
134 135 136 137 138 139 140 141 142 143 144
//===================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 *)));
145 146

    currentModeChanged(Core::ModeManager::instance()->currentMode());
con's avatar
con committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
}

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

178
namespace Core {
179 180


181
struct EditorManagerPrivate {
con's avatar
con committed
182 183
    explicit EditorManagerPrivate(ICore *core, QWidget *parent);
    ~EditorManagerPrivate();
184
    Internal::EditorView *m_view;
mae's avatar
mae committed
185
    Internal::SplitterOrView *m_splitter;
mae's avatar
mae committed
186
    QPointer<IEditor> m_currentEditor;
mae's avatar
mae committed
187
    QPointer<SplitterOrView> m_currentView;
188

con's avatar
con committed
189 190 191 192 193 194 195 196 197
    ICore *m_core;


    // actions
    QAction *m_revertToSavedAction;
    QAction *m_saveAction;
    QAction *m_saveAsAction;
    QAction *m_closeCurrentEditorAction;
    QAction *m_closeAllEditorsAction;
con's avatar
con committed
198
    QAction *m_closeOtherEditorsAction;
con's avatar
con committed
199 200 201 202 203
    QAction *m_gotoNextDocHistoryAction;
    QAction *m_gotoPreviousDocHistoryAction;
    QAction *m_goBackAction;
    QAction *m_goForwardAction;
    QAction *m_openInExternalEditorAction;
204 205
    QAction *m_splitAction;
    QAction *m_splitSideBySideAction;
206 207 208
    QAction *m_removeCurrentSplitAction;
    QAction *m_removeAllSplitsAction;
    QAction *m_gotoOtherSplitAction;
con's avatar
con committed
209 210 211 212 213 214 215

    Internal::OpenEditorsWindow *m_windowPopup;
    Internal::EditorClosingCoreListener *m_coreListener;

    QMap<QString, QVariant> m_editorStates;
    Internal::OpenEditorsViewFactory *m_openEditorsFactory;

216
    OpenEditorsModel *m_editorModel;
con's avatar
con committed
217
    QString m_externalEditor;
218

219
    IFile::ReloadSetting m_reloadSetting;
Takumi ASAKI's avatar
Takumi ASAKI committed
220
    IFile::Utf8BomSetting m_utf8BomSetting;
221 222

    QString m_titleAddition;
con's avatar
con committed
223
};
224
}
con's avatar
con committed
225 226

EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) :
227
    m_view(0),
228
    m_splitter(0),
con's avatar
con committed
229 230 231 232 233 234
    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)),
con's avatar
con committed
235
    m_closeOtherEditorsAction(new QAction(EditorManager::tr("Close Others"), parent)),
mae's avatar
mae committed
236 237
    m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Open Document in History"), parent)),
    m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Open Document in History"), parent)),
238 239
    m_goBackAction(new QAction(QIcon(QLatin1String(Constants::ICON_PREV)), EditorManager::tr("Go Back"), parent)),
    m_goForwardAction(new QAction(QIcon(QLatin1String(Constants::ICON_NEXT)), EditorManager::tr("Go Forward"), parent)),
con's avatar
con committed
240 241
    m_openInExternalEditorAction(new QAction(EditorManager::tr("Open in External Editor"), parent)),
    m_windowPopup(0),
242
    m_coreListener(0),
Takumi ASAKI's avatar
Takumi ASAKI committed
243 244
    m_reloadSetting(IFile::AlwaysAsk),
    m_utf8BomSetting(IFile::OnlyKeep)
con's avatar
con committed
245
{
246
    m_editorModel = new OpenEditorsModel(parent);
con's avatar
con committed
247 248 249 250
}

EditorManagerPrivate::~EditorManagerPrivate()
{
251
//    clearNavigationHistory();
con's avatar
con committed
252 253 254 255
}

EditorManager *EditorManager::m_instance = 0;

256 257
static Command *createSeparator(ActionManager *am, QObject *parent,
                                const QString &name,
258
                                const Context &context)
259 260 261 262 263 264 265
{
    QAction *tmpaction = new QAction(parent);
    tmpaction->setSeparator(true);
    Command *cmd = am->registerAction(tmpaction, name, context);
    return cmd;
}

con's avatar
con committed
266 267 268 269 270 271 272
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 *)),
mae's avatar
mae committed
273
            this, SLOT(handleContextChange(Core::IContext *)));
con's avatar
con committed
274

275
    const Context editManagerContext(Constants::C_EDITORMANAGER);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
276
    // combined context for edit & design modes
277
    const Context editDesignContext(Constants::C_EDITORMANAGER, Constants::C_DESIGN_MODE);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
278

279
    ActionManager *am = m_d->m_core->actionManager();
280
    ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
con's avatar
con committed
281

282
    // Revert to saved
283
    m_d->m_revertToSavedAction->setIcon(QIcon::fromTheme(QLatin1String("document-revert")));
con's avatar
con committed
284
    Command *cmd = am->registerAction(m_d->m_revertToSavedAction,
con's avatar
con committed
285
                                       Constants::REVERTTOSAVED, editManagerContext);
con's avatar
con committed
286
    cmd->setAttribute(Command::CA_UpdateText);
con's avatar
con committed
287 288 289 290
    cmd->setDefaultText(tr("Revert File to Saved"));
    mfile->addAction(cmd, Constants::G_FILE_SAVE);
    connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved()));

291
    // Save Action
con's avatar
con committed
292 293 294
    am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
    connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));

295
    // Save As Action
con's avatar
con committed
296 297 298
    am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
    connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs()));

299
    // Window Menu
300
    ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
con's avatar
con committed
301

302
    // Window menu separators
con's avatar
con committed
303 304
    QAction *tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
hjk's avatar
hjk committed
305
    cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Split", editManagerContext);
con's avatar
con committed
306 307 308 309
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);

    tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
hjk's avatar
hjk committed
310
    cmd = am->registerAction(tmpaction, "QtCreator.Window.Sep.Navigate", editManagerContext);
con's avatar
con committed
311 312
    mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);

313
    // Close Action
314
    cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext, true);
con's avatar
con committed
315
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W")));
con's avatar
con committed
316
    cmd->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
317 318 319 320
    cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text());
    mfile->addAction(cmd, Constants::G_FILE_CLOSE);
    connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor()));

321 322 323 324 325 326 327 328 329
#ifdef Q_WS_WIN
    // workaround for QTCREATORBUG-72
    QShortcut *sc = new QShortcut(parent);
    cmd = am->registerShortcut(sc, Constants::CLOSE_ALTERNATIVE, editManagerContext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F4")));
    cmd->setDefaultText(EditorManager::tr("Close"));
    connect(sc, SIGNAL(activated()), this, SLOT(closeEditor()));
#endif

330
    // Close All Action
331
    cmd = am->registerAction(m_d->m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext, true);
con's avatar
con committed
332 333 334 335
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W")));
    mfile->addAction(cmd, Constants::G_FILE_CLOSE);
    connect(m_d->m_closeAllEditorsAction, SIGNAL(triggered()), this, SLOT(closeAllEditors()));

336
    // Close All Others Action
337
    cmd = am->registerAction(m_d->m_closeOtherEditorsAction, Constants::CLOSEOTHERS, editManagerContext, true);
con's avatar
con committed
338 339 340 341
    mfile->addAction(cmd, Constants::G_FILE_CLOSE);
    cmd->setAttribute(Core::Command::CA_UpdateText);
    connect(m_d->m_closeOtherEditorsAction, SIGNAL(triggered()), this, SLOT(closeOtherEditors()));

con's avatar
con committed
342
    // Goto Previous In History Action
Lasse Holmstedt's avatar
Lasse Holmstedt committed
343
    cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext);
con's avatar
con committed
344 345 346 347 348 349 350 351 352
#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
Lasse Holmstedt's avatar
Lasse Holmstedt committed
353
    cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editDesignContext);
con's avatar
con committed
354 355 356 357 358 359 360 361 362
#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
Lasse Holmstedt's avatar
Lasse Holmstedt committed
363
    cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editDesignContext);
con's avatar
con committed
364 365 366 367 368 369 370 371 372
#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
Lasse Holmstedt's avatar
Lasse Holmstedt committed
373
    cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editDesignContext);
con's avatar
con committed
374 375 376 377 378 379 380 381
#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()));

con's avatar
con committed
382 383 384 385 386 387
#ifdef Q_WS_MAC
    QString prefix = tr("Meta+E");
#else
    QString prefix = tr("Ctrl+E");
#endif

388 389
    m_d->m_splitAction = new QAction(tr("Split"), this);
    cmd = am->registerAction(m_d->m_splitAction, Constants::SPLIT, editManagerContext);
con's avatar
con committed
390
    cmd->setDefaultKeySequence(QKeySequence(tr("%1,2").arg(prefix)));
391 392 393 394 395
    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);
con's avatar
con committed
396
    cmd->setDefaultKeySequence(QKeySequence(tr("%1,3").arg(prefix)));
397 398 399
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
    connect(m_d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide()));

400 401
    m_d->m_removeCurrentSplitAction = new QAction(tr("Remove Current Split"), this);
    cmd = am->registerAction(m_d->m_removeCurrentSplitAction, Constants::REMOVE_CURRENT_SPLIT, editManagerContext);
con's avatar
con committed
402
    cmd->setDefaultKeySequence(QKeySequence(tr("%1,0").arg(prefix)));
403
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
404
    connect(m_d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
405

406 407
    m_d->m_removeAllSplitsAction = new QAction(tr("Remove All Splits"), this);
    cmd = am->registerAction(m_d->m_removeAllSplitsAction, Constants::REMOVE_ALL_SPLITS, editManagerContext);
con's avatar
con committed
408
    cmd->setDefaultKeySequence(QKeySequence(tr("%1,1").arg(prefix)));
409
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
410
    connect(m_d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
411

con's avatar
con committed
412
    m_d->m_gotoOtherSplitAction = new QAction(tr("Go to Next Split"), this);
413
    cmd = am->registerAction(m_d->m_gotoOtherSplitAction, Constants::GOTO_OTHER_SPLIT, editManagerContext);
con's avatar
con committed
414
    cmd->setDefaultKeySequence(QKeySequence(tr("%1,o").arg(prefix)));
415
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
416
    connect(m_d->m_gotoOtherSplitAction, SIGNAL(triggered()), this, SLOT(gotoOtherSplit()));
417

418 419
    ActionContainer *medit = am->actionContainer(Constants::M_EDIT);
    ActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED);
420
    medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED);
con's avatar
con committed
421
    advancedMenu->menu()->setTitle(tr("&Advanced"));
422 423
    advancedMenu->appendGroup(Constants::G_EDIT_FORMAT);
    advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING);
mae's avatar
mae committed
424
    advancedMenu->appendGroup(Constants::G_EDIT_BLOCKS);
425 426 427 428 429 430
    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);
mae's avatar
mae committed
431 432
    cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Blocks"), editManagerContext);
    advancedMenu->addAction(cmd, Constants::G_EDIT_BLOCKS);
433 434 435 436
    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);
hjk's avatar
hjk committed
437

con's avatar
con committed
438 439
    cmd = am->registerAction(m_d->m_openInExternalEditorAction, Constants::OPEN_IN_EXTERNAL_EDITOR, editManagerContext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Alt+V,Alt+I")));
440
    advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
con's avatar
con committed
441
    connect(m_d->m_openInExternalEditorAction, SIGNAL(triggered()), this, SLOT(openInExternalEditor()));
442

443
    // Connect to VariableManager for CURRENT_DOCUMENT variable setting
Friedemann Kleint's avatar
Friedemann Kleint committed
444
    VariableManager::initEditorManagerConnections();
con's avatar
con committed
445
    // other setup
mae's avatar
mae committed
446 447 448
    m_d->m_splitter = new SplitterOrView(m_d->m_editorModel);
    m_d->m_view = m_d->m_splitter->view();

con's avatar
con committed
449

450 451 452 453
    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->addWidget(m_d->m_splitter);
con's avatar
con committed
454 455 456 457 458 459 460 461

    updateActions();

    m_d->m_windowPopup = new OpenEditorsWindow(this);
}

EditorManager::~EditorManager()
{
462
    m_instance = 0;
con's avatar
con committed
463
    if (m_d->m_core) {
464
        ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
con's avatar
con committed
465
        if (m_d->m_coreListener) {
466
            pm->removeObject(m_d->m_coreListener);
con's avatar
con committed
467 468
            delete m_d->m_coreListener;
        }
469
        pm->removeObject(m_d->m_openEditorsFactory);
con's avatar
con committed
470 471 472 473 474 475 476 477
        delete m_d->m_openEditorsFactory;
    }
    delete m_d;
}

void EditorManager::init()
{
    m_d->m_coreListener = new EditorClosingCoreListener(this);
478
    pluginManager()->addObject(m_d->m_coreListener);
con's avatar
con committed
479 480

    m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
481
    pluginManager()->addObject(m_d->m_openEditorsFactory);
con's avatar
con committed
482 483
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
484

Lasse Holmstedt's avatar
Lasse Holmstedt committed
485
EditorToolBar *EditorManager::createToolBar(QWidget *parent)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
486
{
Lasse Holmstedt's avatar
Lasse Holmstedt committed
487
    return new EditorToolBar(parent);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
488 489
}

Roberto Raggi's avatar
Roberto Raggi committed
490 491
QString EditorManager::defaultExternalEditor() const
{
492 493 494 495 496 497
#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|\"");
Roberto Raggi's avatar
Roberto Raggi committed
498
#else
499
    return QLatin1String("notepad %f");
Roberto Raggi's avatar
Roberto Raggi committed
500 501 502
#endif
}

mae's avatar
mae committed
503
void EditorManager::removeEditor(IEditor *editor)
con's avatar
con committed
504
{
mae's avatar
mae committed
505
    bool isDuplicate = m_d->m_editorModel->isDuplicate(editor);
mae's avatar
mae committed
506
    m_d->m_editorModel->removeEditor(editor);
mae's avatar
mae committed
507 508 509
    if (!isDuplicate) {
        m_d->m_core->fileManager()->removeFile(editor->file());
    }
mae's avatar
mae committed
510
    m_d->m_core->removeContextObject(editor);
con's avatar
con committed
511 512
}

513
void EditorManager::handleContextChange(Core::IContext *context)
con's avatar
con committed
514 515 516 517
{
    if (debugEditorManager)
        qDebug() << Q_FUNC_INFO;
    IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0;
518 519 520 521 522
    if (editor) {
        setCurrentEditor(editor);
    } else {
        updateActions();
    }
523 524
}

con's avatar
con committed
525 526
void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
{
527 528 529
    if (editor)
        setCurrentView(0);

mae's avatar
mae committed
530
    if (m_d->m_currentEditor == editor)
con's avatar
con committed
531
        return;
532 533
    if (m_d->m_currentEditor && !ignoreNavigationHistory)
        addCurrentPositionToNavigationHistory();
mae's avatar
mae committed
534

mae's avatar
mae committed
535
    m_d->m_currentEditor = editor;
con's avatar
con committed
536
    if (editor) {
537 538
        if (SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor))
            splitterOrView->view()->setCurrentEditor(editor);
mae's avatar
mae committed
539
        m_d->m_view->updateEditorHistory(editor); // the global view should have a complete history
con's avatar
con committed
540
    }
mae's avatar
mae committed
541
    updateActions();
542
    updateWindowTitle();
mae's avatar
mae committed
543
    emit currentEditorChanged(editor);
con's avatar
con committed
544 545
}

mae's avatar
mae committed
546 547 548 549 550 551 552 553 554 555 556 557 558

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();
559 560 561

    if (view && !view->editor())
        view->setFocus();
mae's avatar
mae committed
562 563
}

mae's avatar
mae committed
564
Core::Internal::SplitterOrView *EditorManager::currentSplitterOrView() const
mae's avatar
mae committed
565
{
566 567
    SplitterOrView *view = m_d->m_currentView;
    if (!view)
568 569 570
        view = m_d->m_currentEditor?
               m_d->m_splitter->findView(m_d->m_currentEditor):
               m_d->m_splitter->findFirstView();
mae's avatar
mae committed
571 572
    if (!view)
        return m_d->m_splitter;
573
    return view;
mae's avatar
mae committed
574 575
}

mae's avatar
mae committed
576 577 578 579 580
Core::Internal::EditorView *EditorManager::currentEditorView() const
{
    return currentSplitterOrView()->view();
}

con's avatar
con committed
581 582 583
QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
{
    QList<IEditor *> found;
584
    QString fixedname = FileManager::fixFileName(filename, FileManager::KeepLinks);
con's avatar
con committed
585
    foreach (IEditor *editor, openedEditors()) {
586
        if (fixedname == FileManager::fixFileName(editor->file()->fileName(), FileManager::KeepLinks))
con's avatar
con committed
587 588 589 590 591
            found << editor;
    }
    return found;
}

592 593 594 595 596 597 598 599 600 601
QList<IEditor *> EditorManager::editorsForFile(IFile *file) const
{
    QList<IEditor *> found;
    foreach (IEditor *editor, openedEditors()) {
        if (editor->file() == file)
            found << editor;
    }
    return found;
}

con's avatar
con committed
602 603
IEditor *EditorManager::currentEditor() const
{
mae's avatar
mae committed
604
    return m_d->m_currentEditor;
con's avatar
con committed
605 606
}

mae's avatar
mae committed
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
void EditorManager::emptyView(Core::Internal::EditorView *view)
{
    if (!view)
        return;

    QList<IEditor *> 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;
    }
}

mae's avatar
mae committed
629
void EditorManager::closeView(Core::Internal::EditorView *view)
con's avatar
con committed
630
{
mae's avatar
mae committed
631
    if (!view)
con's avatar
con committed
632
        return;
mae's avatar
mae committed
633

634
    if (view == m_d->m_view) {
635 636
        if (IEditor *e = view->currentEditor())
            closeEditors(QList<IEditor *>() << e);
mae's avatar
mae committed
637 638 639
        return;
    }

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
    if (IEditor *e = view->currentEditor()) {
        /*
           when we are closing a view with an original editor which has
           duplicates, then make one of the duplicates the original.
           Otherwise the original would be kept around and the user might
           experience jumping to a missleading position within the file when
           visiting the file again. With the code below, the position within
           the file will be the position of the first duplicate which is still
           around.
        */
        if (!m_d->m_editorModel->isDuplicate(e)) {
            QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(e);
            if (!duplicates.isEmpty()) {
                m_d->m_editorModel->makeOriginal(duplicates.first());
            }
        }
    }

mae's avatar
mae committed
658 659 660
    emptyView(view);

    SplitterOrView *splitterOrView = m_d->m_splitter->findView(view);
661
    Q_ASSERT(splitterOrView);
mae's avatar
mae committed
662 663 664
    Q_ASSERT(splitterOrView->view() == view);
    SplitterOrView *splitter = m_d->m_splitter->findSplitter(splitterOrView);
    Q_ASSERT(splitterOrView->hasEditors() == false);
665
    splitterOrView->hide();
mae's avatar
mae committed
666 667 668 669 670 671
    delete splitterOrView;

    splitter->unsplit();

    SplitterOrView *newCurrent = splitter->findFirstView();
    if (newCurrent) {
672 673 674
        if (IEditor *e = newCurrent->editor()) {
            activateEditor(newCurrent->view(), e);
        } else {
mae's avatar
mae committed
675
            setCurrentView(newCurrent);
676
        }
mae's avatar
mae committed
677
    }
mae's avatar
mae committed
678
}
mae's avatar
mae committed
679

con's avatar
con committed
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
QList<IEditor*>
    EditorManager::editorsForFiles(QList<IFile*> files) const
{
    const QList<IEditor *> editors = openedEditors();
    QSet<IEditor *> found;
    foreach (IFile *file, files) {
        foreach (IEditor *editor, editors) {
            if (editor->file() == file && !found.contains(editor)) {
                    found << editor;
            }
        }
    }
    return found.toList();
}

695
QList<IFile *> EditorManager::filesForEditors(QList<IEditor *> editors) const
con's avatar
con committed
696 697 698 699 700 701
{
    QSet<IEditor *> handledEditors;
    QList<IFile *> files;
    foreach (IEditor *editor, editors) {
        if (!handledEditors.contains(editor)) {
            files << editor->file();
702
            handledEditors.insert(editor);
con's avatar
con committed
703 704 705 706 707 708 709
        }
    }
    return files;
}

bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
{
710
    m_d->m_editorModel->removeAllRestoredEditors();
711
    if (closeEditors(openedEditors(), askAboutModifiedEditors)) {
712
//        m_d->clearNavigationHistory();
713 714 715
        return true;
    }
    return false;
con's avatar
con committed
716 717
}

718
void EditorManager::closeOtherEditors(IEditor *editor)
con's avatar
con committed
719 720 721
{
    m_d->m_editorModel->removeAllRestoredEditors();
    QList<IEditor*> editors = openedEditors();
722
    editors.removeAll(editor);
con's avatar
con committed
723 724 725
    closeEditors(editors, true);
}

726 727 728 729 730 731 732
void EditorManager::closeOtherEditors()
{
    IEditor *current = currentEditor();
    QTC_ASSERT(current, return);
    closeOtherEditors(current);
}

733 734 735
// SLOT connected to action
void EditorManager::closeEditor()
{
736 737
    if (!m_d->m_currentEditor)
        return;
738
    addCurrentPositionToNavigationHistory();
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
    closeEditor(m_d->m_currentEditor);
}

void EditorManager::closeEditor(Core::IEditor *editor)
{
    if (!editor)
        return;
    closeEditors(QList<IEditor *>() << editor);
}

void EditorManager::closeEditor(const QModelIndex &index)
{
    IEditor *editor = index.data(Qt::UserRole).value<Core::IEditor*>();
    if (editor)
        closeEditor(editor);
    else
        m_d->m_editorModel->removeEditor(index);
}

758
bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
con's avatar
con committed
759 760 761
{
    if (editorsToClose.isEmpty())
        return true;
762

mae's avatar
mae committed
763
    SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
764

con's avatar
con committed
765 766 767 768
    bool closingFailed = false;
    QList<IEditor*> acceptedEditors;
    //ask all core listeners to check whether the editor can be closed
    const QList<ICoreListener *> listeners =
769
        pluginManager()->getObjects<ICoreListener>();
con's avatar
con committed
770 771
    foreach (IEditor *editor, editorsToClose) {
        bool editorAccepted = true;
mae's avatar
mae committed
772 773
        if (m_d->m_editorModel->isDuplicate(editor))
            editor = m_d->m_editorModel->originalForDuplicate(editor);
con's avatar
con committed
774 775 776
        foreach (ICoreListener *listener, listeners) {
            if (!listener->editorAboutToClose(editor)) {
                editorAccepted = false;
777
                closingFailed = true;
con's avatar
con committed
778 779 780 781 782 783 784 785 786 787 788
                break;
            }
        }
        if (editorAccepted)
            acceptedEditors.append(editor);
    }
    if (acceptedEditors.isEmpty())
        return false;
    //ask whether to save modified files
    if (askAboutModifiedEditors) {
        bool cancelled = false;
789
        QList<IFile*> list = m_d->m_core->fileManager()->
con's avatar
con committed
790 791 792 793
            saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled);
        if (cancelled)
            return false;
        if (!list.isEmpty()) {
794
            closingFailed = true;
con's avatar
con committed
795 796 797 798 799 800
            QSet<IEditor*> skipSet = editorsForFiles(list).toSet();
            acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList();
        }
    }
    if (acceptedEditors.isEmpty())
        return false;
mae's avatar
mae committed
801 802 803 804 805

    // add duplicates
    foreach(IEditor *editor, acceptedEditors)
        acceptedEditors += m_d->m_editorModel->duplicatesFor(editor);

806
    QList<EditorView*> closedViews;
mae's avatar
mae committed
807

con's avatar
con committed
808 809 810
    // remove the editors
    foreach (IEditor *editor, acceptedEditors) {
        emit editorAboutToClose(editor);
811 812
        if (!editor->file()->fileName().isEmpty()
                && !editor->isTemporary()) {
con's avatar
con committed
813 814 815 816
            QByteArray state = editor->saveState();
            if (!state.isEmpty())
                m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
        }
mae's avatar
mae committed
817

mae's avatar
mae committed
818
        removeEditor(editor);
mae's avatar
mae committed
819
        if (SplitterOrView *view = m_d->m_splitter->findView(editor)) {
820 821
            if (editor == view->view()->currentEditor())
                closedViews += view->view();
mae's avatar
mae committed
822
            view->view()->removeEditor(editor);
mae's avatar
mae committed
823
        }
con's avatar
con committed
824
    }
mae's avatar
mae committed
825

826
    foreach (EditorView *view, closedViews) {
mae's avatar
mae committed
827 828 829
        IEditor *newCurrent = view->currentEditor();
        if (!newCurrent)
            newCurrent = pickUnusedEditor();
830
        if (newCurrent) {
mae's avatar
mae committed
831
            activateEditor(view, newCurrent, NoActivate);
832 833 834
        } else {
            QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
            if (idx.isValid())
835
                activateEditorForIndex(view, idx, NoActivate);
836
        }
mae's avatar
mae committed
837 838
    }

con's avatar
con committed
839
    emit editorsClosed(acceptedEditors);
840

con's avatar
con committed
841 842 843
    foreach (IEditor *editor, acceptedEditors) {
        delete editor;
    }
mae's avatar
mae committed
844

845 846 847
    if (currentSplitterOrView) {
        if (IEditor *editor = currentSplitterOrView->editor())
            activateEditor(currentSplitterOrView->view(), editor);
848
    }
mae's avatar
mae committed
849

850
    if (!currentEditor()) {
851
        emit currentEditorChanged(0);
852
        updateActions();
853
        updateWindowTitle();
854
    }
855

con's avatar
con committed
856 857 858
    return !closingFailed;
}

859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
void EditorManager::closeDuplicate(Core::IEditor *editor)
{

    IEditor *original = editor;
    if (m_d->m_editorModel->isDuplicate(editor))
        original= m_d->m_editorModel->originalForDuplicate(editor);
    QList<IEditor *> duplicates = m_d->m_editorModel->duplicatesFor(original);

    if (duplicates.isEmpty()) {
        closeEditor(editor);
        return;
    }

    if (original== editor)
        m_d->m_editorModel->makeOriginal(duplicates.first());

mae's avatar
mae committed
875
    SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
876 877 878

    emit editorAboutToClose(editor);

Bill King's avatar
Bill King committed
879 880 881 882
    if(m_d->m_splitter->findView(editor)) {
        EditorView *view = m_d->m_splitter->findView(editor)->view();
        removeEditor(editor);
        view->removeEditor(editor);
883

Bill King's avatar
Bill King committed
884 885 886 887 888 889 890 891
        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())
892
                activateEditorForIndex(view, idx, NoActivate);
Bill King's avatar
Bill King committed
893
        }
894 895 896 897 898 899 900 901 902 903
    }

    emit editorsClosed(QList<IEditor*>() << editor);
    delete editor;
    if (currentSplitterOrView) {
        if (IEditor *currentEditor = currentSplitterOrView->editor())
            activateEditor(currentSplitterOrView->view(), currentEditor);
    }
}

904
Core::IEditor *EditorManager::pickUnusedEditor() const
mae's avatar
mae committed
905
{
mae's avatar
mae committed
906
    foreach (IEditor *editor, openedEditors()) {
mae's avatar
mae committed
907 908 909 910 911 912
        SplitterOrView *view = m_d->m_splitter->findView(editor);
        if (!view || view->editor() != editor)
            return editor;
    }
    return 0;
}
con's avatar
con committed
913

914
void EditorManager::activateEditorForIndex(const QModelIndex &index, OpenEditorFlags flags)
915
{
916 917 918 919 920 921
    activateEditorForIndex(currentEditorView(), index, flags);
}

void EditorManager::activateEditorForIndex(Internal::EditorView *view, const QModelIndex &index, OpenEditorFlags flags)
{
    Q_ASSERT(view);
922 923
    IEditor *editor = index.data(Qt::UserRole).value<IEditor*>();
    if (editor)  {
924 925
        activateEditor(view, editor, flags);
        return;
926 927 928
    }

    QString fileName = index.data(Qt::UserRole + 1).toString();
929
    QString id = index.data(Qt::UserRole + 2).toString();
930
    openEditor(view, fileName, id, flags);
mae's avatar
mae committed
931 932 933 934
}

Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)
{
mae's avatar
mae committed
935 936 937 938 939
    Q_ASSERT(view && editor);

    if (view->currentEditor() && view->currentEditor()->file() == editor->file())
        editor = view->currentEditor();

mae's avatar
mae committed
940
    if (!view->hasEditor(editor)) {
mae's avatar
mae committed
941 942 943 944
        bool duplicateSupported = editor->duplicateSupported();
        if (SplitterOrView *sourceView = m_d->m_splitter->findView(editor)) {
            if (editor != sourceView->editor() || !duplicateSupported) {
                sourceView->view()->removeEditor(editor);
mae's avatar
mae committed
945 946 947 948 949 950 951 952
                view->addEditor(editor);
                view->setCurrentEditor(editor);
                if (!sourceView->editor()) {
                    if (IEditor *replacement = pickUnusedEditor()) {
                        sourceView->view()->addEditor(replacement);
                    }
                }
                return editor;
mae's avatar
mae committed
953 954 955
            } else if (duplicateSupported) {
                editor = duplicateEditor(editor);
                Q_ASSERT(editor);
956
                m_d->m_editorModel->makeOriginal(editor);
mae's avatar
mae committed
957 958
            }
        }
mae's avatar
mae committed
959 960
        view->addEditor(editor);
    }
mae's avatar
mae committed
961 962
    return editor;
}
mae's avatar
mae committed
963

964
void EditorManager::activateEditor(Core::IEditor *editor, OpenEditorFlags flags)
965
{
con's avatar
con committed
966 967
    SplitterOrView *splitterOrView = m_d->m_splitter->findView(editor);
    EditorView *view = (splitterOrView ? splitterOrView->view() : 0);
968 969 970 971
    // TODO an IEditor doesn't have to belong to a view, which makes this method a bit funny
    if (!view)
        view = currentEditorView();
    activateEditor(view, editor, flags);
972 973
}

974
Core::IEditor *EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags)
mae's avatar
mae committed
975
{
976
    Q_ASSERT(view);
mae's avatar
mae committed
977

978 979 980 981
    if (!editor) {
        if (!m_d->m_currentEditor)
            setCurrentEditor(0, (flags & IgnoreNavigationHistory));
        return 0;
mae's avatar
mae committed
982 983
    }

mae's avatar
mae committed
984 985 986 987
    editor = placeEditor(view, editor);

    if (!(flags & NoActivate)) {
        setCurrentEditor(editor, (flags & IgnoreNavigationHistory));
988
        if (flags & ModeSwitch) {
989
            switchToPreferedMode();
990
        }
991 992
        if (isVisible())
            editor->widget()->setFocus();
mae's avatar
mae committed
993
    }
994
    return editor;
mae's avatar
mae committed
995 996
}

997
Core::IEditor *EditorManager::activateEditorForFile(Core::Internal::EditorView *view, Core::IFile *file, OpenEditorFlags flags)
998
{
999
    Q_ASSERT(view);
1000 1001 1002 1003
    const QList<IEditor*> editors = editorsForFile(file);
    if (editors.isEmpty())
        return 0;

1004 1005
    activateEditor(view, editors.first(), flags);
    return editors.first();
mae's avatar