editormanager.cpp 72.6 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 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

con's avatar
con committed
443
    // other setup
mae's avatar
mae committed
444 445 446
    m_d->m_splitter = new SplitterOrView(m_d->m_editorModel);
    m_d->m_view = m_d->m_splitter->view();

con's avatar
con committed
447

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

    updateActions();

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

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

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

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

    connect(VariableManager::instance(), SIGNAL(variableUpdateRequested(QString)),
            this, SLOT(updateVariable(QString)));
con's avatar
con committed
483 484
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
485

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

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

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

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

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

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

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

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

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

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

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

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

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

593 594 595 596 597 598 599 600 601 602
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
603 604
IEditor *EditorManager::currentEditor() const
{
mae's avatar
mae committed
605
    return m_d->m_currentEditor;
con's avatar
con committed
606 607
}

mae's avatar
mae committed
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
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
630
void EditorManager::closeView(Core::Internal::EditorView *view)
con's avatar
con committed
631
{
mae's avatar
mae committed
632
    if (!view)
con's avatar
con committed
633
        return;
mae's avatar
mae committed
634

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

641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
    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
659 660 661
    emptyView(view);

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

    splitter->unsplit();

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

con's avatar
con committed
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
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();
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
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
876
    SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
877 878 879

    emit editorAboutToClose(editor);

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

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

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

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

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

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

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

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

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

mae's avatar
mae committed
941
    if (!view->hasEditor(editor)) {
mae's avatar
mae committed
942 943 944 945
        bool duplicateSupported = editor->duplicateSupported();
        if (SplitterOrView *sourceView = m_d->m_splitter->findView(editor)) {
            if (editor != sourceView->editor() || !duplicateSupported) {