editormanager.cpp 69.5 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
**
9
** Commercial Usage
10
**
11 12 13 14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
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
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

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

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

58 59
#include <extensionsystem/pluginmanager.h>

60
#include <utils/consoleprocess.h>
hjk's avatar
hjk committed
61 62 63
#include <utils/qtcassert.h>

#include <QtCore/QDebug>
con's avatar
con committed
64 65 66
#include <QtCore/QFileInfo>
#include <QtCore/QMap>
#include <QtCore/QProcess>
hjk's avatar
hjk committed
67 68
#include <QtCore/QSet>
#include <QtCore/QSettings>
69
#include <QtCore/QTextCodec>
con's avatar
con committed
70 71

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

enum { debugEditorManager=0 };

85 86 87 88 89
static inline ExtensionSystem::PluginManager *pluginManager()
{
    return ExtensionSystem::PluginManager::instance();
}

90 91 92 93 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
//===================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
130 131 132 133 134 135 136 137 138 139 140
//===================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 *)));
141 142

    currentModeChanged(Core::ModeManager::instance()->currentMode());
con's avatar
con committed
143 144 145 146 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
}

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

174
namespace Core {
175 176


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

con's avatar
con committed
185 186 187 188 189 190 191 192 193
    ICore *m_core;


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

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

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

212
    OpenEditorsModel *m_editorModel;
con's avatar
con committed
213
    QString m_externalEditor;
214

215
    IFile::ReloadSetting m_reloadSetting;
Takumi ASAKI's avatar
Takumi ASAKI committed
216
    IFile::Utf8BomSetting m_utf8BomSetting;
217 218

    QString m_titleAddition;
con's avatar
con committed
219
};
220
}
con's avatar
con committed
221 222

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

EditorManagerPrivate::~EditorManagerPrivate()
{
247
//    clearNavigationHistory();
con's avatar
con committed
248 249 250 251
}

EditorManager *EditorManager::m_instance = 0;

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

con's avatar
con committed
262 263 264 265 266 267 268
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
269
            this, SLOT(handleContextChange(Core::IContext *)));
con's avatar
con committed
270

271
    const Context editManagerContext(Constants::C_EDITORMANAGER);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
272
    // combined context for edit & design modes
273
    const Context editDesignContext(Constants::C_EDITORMANAGER, Constants::C_DESIGN_MODE);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
274

275
    ActionManager *am = m_d->m_core->actionManager();
276
    ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
con's avatar
con committed
277

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

287
    // Save Action
con's avatar
con committed
288 289 290
    am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
    connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));

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

295
    // Window Menu
296
    ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
con's avatar
con committed
297

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

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

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

317 318 319 320 321 322 323 324 325
#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

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

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

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

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

396 397
    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
398
    cmd->setDefaultKeySequence(QKeySequence(tr("%1,0").arg(prefix)));
399
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
400
    connect(m_d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
401

402 403
    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
404
    cmd->setDefaultKeySequence(QKeySequence(tr("%1,1").arg(prefix)));
405
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
406
    connect(m_d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
407

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

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

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

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

con's avatar
con committed
445

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

    updateActions();

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

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

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

    m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
477
    pluginManager()->addObject(m_d->m_openEditorsFactory);
con's avatar
con committed
478 479
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
480

Lasse Holmstedt's avatar
Lasse Holmstedt committed
481
EditorToolBar *EditorManager::createToolBar(QWidget *parent)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
482
{
Lasse Holmstedt's avatar
Lasse Holmstedt committed
483
    return new EditorToolBar(parent);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
484 485
}

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

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

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

con's avatar
con committed
521 522
void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
{
523 524 525
    if (editor)
        setCurrentView(0);

mae's avatar
mae committed
526
    if (m_d->m_currentEditor == editor)
con's avatar
con committed
527
        return;
528 529
    if (m_d->m_currentEditor && !ignoreNavigationHistory)
        addCurrentPositionToNavigationHistory();
mae's avatar
mae committed
530

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

mae's avatar
mae committed
542 543 544 545 546 547 548 549 550 551 552 553 554

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();
555 556 557

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

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

mae's avatar
mae committed
572 573 574 575 576
Core::Internal::EditorView *EditorManager::currentEditorView() const
{
    return currentSplitterOrView()->view();
}

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

588 589 590 591 592 593 594 595 596 597
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
598 599
IEditor *EditorManager::currentEditor() const
{
mae's avatar
mae committed
600
    return m_d->m_currentEditor;
con's avatar
con committed
601 602
}

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

630
    if (view == m_d->m_view) {
631 632
        if (IEditor *e = view->currentEditor())
            closeEditors(QList<IEditor *>() << e);
mae's avatar
mae committed
633 634 635
        return;
    }

636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
    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
654 655 656
    emptyView(view);

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

    splitter->unsplit();

    SplitterOrView *newCurrent = splitter->findFirstView();
    if (newCurrent) {
668 669 670
        if (IEditor *e = newCurrent->editor()) {
            activateEditor(newCurrent->view(), e);
        } else {
mae's avatar
mae committed
671
            setCurrentView(newCurrent);
672
        }
mae's avatar
mae committed
673
    }
mae's avatar
mae committed
674
}
mae's avatar
mae committed
675

con's avatar
con committed
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
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();
}

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

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

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

722 723 724 725 726 727 728
void EditorManager::closeOtherEditors()
{
    IEditor *current = currentEditor();
    QTC_ASSERT(current, return);
    closeOtherEditors(current);
}

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

754
bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors)
con's avatar
con committed
755 756 757
{
    if (editorsToClose.isEmpty())
        return true;
758

mae's avatar
mae committed
759
    SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
760

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

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

802
    QList<EditorView*> closedViews;
mae's avatar
mae committed
803

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

mae's avatar
mae committed
814
        removeEditor(editor);
mae's avatar
mae committed
815
        if (SplitterOrView *view = m_d->m_splitter->findView(editor)) {
816 817
            if (editor == view->view()->currentEditor())
                closedViews += view->view();
mae's avatar
mae committed
818
            view->view()->removeEditor(editor);
mae's avatar
mae committed
819
        }
con's avatar
con committed
820
    }
mae's avatar
mae committed
821

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

con's avatar
con committed
835
    emit editorsClosed(acceptedEditors);
836

con's avatar
con committed
837 838 839
    foreach (IEditor *editor, acceptedEditors) {
        delete editor;
    }
mae's avatar
mae committed
840

841 842 843
    if (currentSplitterOrView) {
        if (IEditor *editor = currentSplitterOrView->editor())
            activateEditor(currentSplitterOrView->view(), editor);
844
    }
mae's avatar
mae committed
845

846
    if (!currentEditor()) {
847
        emit currentEditorChanged(0);
848
        updateActions();
849
        updateWindowTitle();
850
    }
851

con's avatar
con committed
852 853 854
    return !closingFailed;
}

855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
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
871
    SplitterOrView *currentSplitterOrView = this->currentSplitterOrView();
872 873 874

    emit editorAboutToClose(editor);

Bill King's avatar
Bill King committed
875 876 877 878
    if(m_d->m_splitter->findView(editor)) {
        EditorView *view = m_d->m_splitter->findView(editor)->view();
        removeEditor(editor);
        view->removeEditor(editor);
879

Bill King's avatar
Bill King committed
880 881 882 883 884 885 886 887 888 889
        IEditor *newCurrent = view->currentEditor();
        if (!newCurrent)
            newCurrent = pickUnusedEditor();
        if (newCurrent) {
            activateEditor(view, newCurrent, NoActivate);
        } else {
            QModelIndex idx = m_d->m_editorModel->firstRestoredEditor();
            if (idx.isValid())
                activateEditor(idx, view, NoActivate);
        }
890 891 892 893 894 895 896 897 898 899
    }

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

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

910
Core::IEditor *EditorManager::activateEditor(const QModelIndex &index, Internal::EditorView *view, OpenEditorFlags flags)
911 912 913
{
    IEditor *editor = index.data(Qt::UserRole).value<IEditor*>();
    if (editor)  {
914
        return activateEditor(view, editor, flags);
915 916 917
    }

    QString fileName = index.data(Qt::UserRole + 1).toString();
918 919
    QString id = index.data(Qt::UserRole + 2).toString();
    return openEditor(view, fileName, id, flags);
mae's avatar
mae committed
920 921 922 923
}

Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)
{
mae's avatar
mae committed
924 925 926 927 928
    Q_ASSERT(view && editor);

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

mae's avatar
mae committed
929
    if (!view->hasEditor(editor)) {
mae's avatar
mae committed
930 931 932 933
        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
934 935 936 937 938 939 940 941
                view->addEditor(editor);
                view->setCurrentEditor(editor);
                if (!sourceView->editor()) {
                    if (IEditor *replacement = pickUnusedEditor()) {
                        sourceView->view()->addEditor(replacement);
                    }
                }
                return editor;
mae's avatar
mae committed
942 943 944
            } else if (duplicateSupported) {
                editor = duplicateEditor(editor);
                Q_ASSERT(editor);
945
                m_d->m_editorModel->makeOriginal(editor);
mae's avatar
mae committed
946 947
            }
        }
mae's avatar
mae committed