editormanager.cpp 91.8 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "editormanager.h"
31
#include "editorview.h"
32
#include "findplaceholder.h"
con's avatar
con committed
33
#include "openeditorswindow.h"
mae's avatar
mae committed
34
#include "openeditorsview.h"
35
#include "openeditorsmodel.h"
con's avatar
con committed
36
#include "openwithdialog.h"
37
38
39
#include "outputpane.h"
#include "outputpanemanager.h"
#include "rightpane.h"
40
#include "documentmanager.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
#include "vcsmanager.h"
con's avatar
con committed
46

47
#include <coreplugin/actionmanager/actioncontainer.h>
hjk's avatar
hjk committed
48
49
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
con's avatar
con committed
50
#include <coreplugin/editormanager/ieditorfactory.h>
51
#include <coreplugin/editormanager/iexternaleditor.h>
hjk's avatar
hjk committed
52
53
#include <coreplugin/editortoolbar.h>
#include <coreplugin/fileutils.h>
54
#include <coreplugin/icorelistener.h>
hjk's avatar
hjk committed
55
56
#include <coreplugin/infobar.h>
#include <coreplugin/modemanager.h>
57
#include <coreplugin/settingsdatabase.h>
58
#include <coreplugin/variablemanager.h>
59
#include <coreplugin/dialogs/readonlyfilesdialog.h>
con's avatar
con committed
60

61
62
#include <extensionsystem/pluginmanager.h>

63
#include <utils/hostosinfo.h>
hjk's avatar
hjk committed
64
65
#include <utils/qtcassert.h>

66
67
68
69
70
71
72
73
#include <QDateTime>
#include <QDebug>
#include <QFileInfo>
#include <QMap>
#include <QSet>
#include <QSettings>
#include <QTextCodec>
#include <QTimer>
con's avatar
con committed
74

75
76
77
78
79
80
81
82
#include <QAction>
#include <QShortcut>
#include <QApplication>
#include <QFileDialog>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QSplitter>
con's avatar
con committed
83
84
85

enum { debugEditorManager=0 };

86
static const char kCurrentDocumentPrefix[] = "CurrentDocument";
Friedemann Kleint's avatar
Friedemann Kleint committed
87
88
static const char kCurrentDocumentXPos[] = "CurrentDocument:XPos";
static const char kCurrentDocumentYPos[] = "CurrentDocument:YPos";
89
static const char kMakeWritableWarning[] = "Core.EditorManager.MakeWritable";
con's avatar
con committed
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
130
//===================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
131
132
133
134
135
136
137
138
139
//===================EditorManager=====================

EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0;

EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent)
    : QWidget(parent), m_mode(mode)
{
    setLayout(new QVBoxLayout);
    layout()->setMargin(0);
Robert Loehning's avatar
Robert Loehning committed
140
141
    connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)),
            this, SLOT(currentModeChanged(Core::IMode*)));
142

143
    currentModeChanged(ModeManager::currentMode());
con's avatar
con committed
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
}

EditorManagerPlaceHolder::~EditorManagerPlaceHolder()
{
    if (m_current == this) {
        EditorManager::instance()->setParent(0);
        EditorManager::instance()->hide();
    }
}

void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode)
{
    if (m_mode == mode) {
        m_current = this;
        layout()->addWidget(EditorManager::instance());
        EditorManager::instance()->show();
160
161
    } else if (m_current == this) {
        m_current = 0;
con's avatar
con committed
162
163
164
165
166
167
168
169
170
171
    }
}

EditorManagerPlaceHolder* EditorManagerPlaceHolder::current()
{
    return m_current;
}

// ---------------- EditorManager

172
namespace Core {
173
174


hjk's avatar
hjk committed
175
176
177
struct EditorManagerPrivate
{
    explicit EditorManagerPrivate(QWidget *parent);
con's avatar
con committed
178
    ~EditorManagerPrivate();
179
    QList<EditLocation> m_globalHistory;
180
    QList<Internal::SplitterOrView *> m_root;
181
    QList<IContext *> m_rootContext;
mae's avatar
mae committed
182
    QPointer<IEditor> m_currentEditor;
183
    QPointer<IEditor> m_scheduledCurrentEditor;
184
    QPointer<EditorView> m_currentView;
185
    QTimer *m_autoSaveTimer;
186

con's avatar
con committed
187
188
189
190
191
192
    // actions
    QAction *m_revertToSavedAction;
    QAction *m_saveAction;
    QAction *m_saveAsAction;
    QAction *m_closeCurrentEditorAction;
    QAction *m_closeAllEditorsAction;
con's avatar
con committed
193
    QAction *m_closeOtherEditorsAction;
con's avatar
con committed
194
195
196
197
    QAction *m_gotoNextDocHistoryAction;
    QAction *m_gotoPreviousDocHistoryAction;
    QAction *m_goBackAction;
    QAction *m_goForwardAction;
198
199
    QAction *m_splitAction;
    QAction *m_splitSideBySideAction;
200
    QAction *m_splitNewWindowAction;
201
202
    QAction *m_removeCurrentSplitAction;
    QAction *m_removeAllSplitsAction;
203
    QAction *m_gotoNextSplitAction;
con's avatar
con committed
204

205
206
207
208
    QAction *m_saveCurrentEditorContextAction;
    QAction *m_saveAsCurrentEditorContextAction;
    QAction *m_revertToSavedCurrentEditorContextAction;

209
210
211
    QAction *m_closeCurrentEditorContextAction;
    QAction *m_closeAllEditorsContextAction;
    QAction *m_closeOtherEditorsContextAction;
Robert Loehning's avatar
Robert Loehning committed
212
213
    QAction *m_openGraphicalShellAction;
    QAction *m_openTerminalAction;
214
215
    QModelIndex m_contextMenuEditorIndex;

con's avatar
con committed
216
217
218
219
220
221
    Internal::OpenEditorsWindow *m_windowPopup;
    Internal::EditorClosingCoreListener *m_coreListener;

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

222
    OpenEditorsModel *m_editorModel;
223

224
    IDocument::ReloadSetting m_reloadSetting;
225
226

    QString m_titleAddition;
227
228
229

    bool m_autoSaveEnabled;
    int m_autoSaveInterval;
con's avatar
con committed
230
};
231
}
con's avatar
con committed
232

hjk's avatar
hjk committed
233
EditorManagerPrivate::EditorManagerPrivate(QWidget *parent) :
234
    m_autoSaveTimer(0),
con's avatar
con committed
235
236
237
238
239
    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
240
    m_closeOtherEditorsAction(new QAction(EditorManager::tr("Close Others"), parent)),
mae's avatar
mae committed
241
242
    m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Open Document in History"), parent)),
    m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Open Document in History"), parent)),
243
244
    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)),
245
246
247
    m_saveCurrentEditorContextAction(new QAction(EditorManager::tr("&Save"), parent)),
    m_saveAsCurrentEditorContextAction(new QAction(EditorManager::tr("Save &As..."), parent)),
    m_revertToSavedCurrentEditorContextAction(new QAction(EditorManager::tr("Revert to Saved"), parent)),
248
249
250
    m_closeCurrentEditorContextAction(new QAction(EditorManager::tr("Close"), parent)),
    m_closeAllEditorsContextAction(new QAction(EditorManager::tr("Close All"), parent)),
    m_closeOtherEditorsContextAction(new QAction(EditorManager::tr("Close Others"), parent)),
Robert Loehning's avatar
Robert Loehning committed
251
252
    m_openGraphicalShellAction(new QAction(FileUtils::msgGraphicalShellAction(), parent)),
    m_openTerminalAction(new QAction(FileUtils::msgTerminalAction(), parent)),
con's avatar
con committed
253
    m_windowPopup(0),
254
    m_coreListener(0),
255
    m_reloadSetting(IDocument::AlwaysAsk),
256
257
    m_autoSaveEnabled(true),
    m_autoSaveInterval(5)
con's avatar
con committed
258
{
259
    m_editorModel = new OpenEditorsModel(parent);
con's avatar
con committed
260
261
262
263
}

EditorManagerPrivate::~EditorManagerPrivate()
{
264
//    clearNavigationHistory();
con's avatar
con committed
265
266
}

hjk's avatar
hjk committed
267
268
269
static EditorManager *m_instance = 0;

EditorManager *EditorManager::instance() { return m_instance; }
con's avatar
con committed
270

hjk's avatar
hjk committed
271
EditorManager::EditorManager(QWidget *parent) :
con's avatar
con committed
272
    QWidget(parent),
hjk's avatar
hjk committed
273
    d(new EditorManagerPrivate(parent))
con's avatar
con committed
274
275
276
{
    m_instance = this;

277
278
    connect(ICore::instance(), SIGNAL(contextAboutToChange(QList<Core::IContext*>)),
            this, SLOT(handleContextChange(QList<Core::IContext*>)));
con's avatar
con committed
279

280
    const Context editManagerContext(Constants::C_EDITORMANAGER);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
281
    // combined context for edit & design modes
282
    const Context editDesignContext(Constants::C_EDITORMANAGER, Constants::C_DESIGN_MODE);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
283

Eike Ziller's avatar
Eike Ziller committed
284
    ActionContainer *mfile = ActionManager::actionContainer(Constants::M_FILE);
con's avatar
con committed
285

286
    // Revert to saved
hjk's avatar
hjk committed
287
    d->m_revertToSavedAction->setIcon(QIcon::fromTheme(QLatin1String("document-revert")));
Eike Ziller's avatar
Eike Ziller committed
288
    Command *cmd = ActionManager::registerAction(d->m_revertToSavedAction,
con's avatar
con committed
289
                                       Constants::REVERTTOSAVED, editManagerContext);
con's avatar
con committed
290
    cmd->setAttribute(Command::CA_UpdateText);
291
    cmd->setDescription(tr("Revert File to Saved"));
con's avatar
con committed
292
    mfile->addAction(cmd, Constants::G_FILE_SAVE);
hjk's avatar
hjk committed
293
    connect(d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved()));
con's avatar
con committed
294

295
    // Save Action
Eike Ziller's avatar
Eike Ziller committed
296
    ActionManager::registerAction(d->m_saveAction, Constants::SAVE, editManagerContext);
297
    connect(d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveDocument()));
con's avatar
con committed
298

299
    // Save As Action
Eike Ziller's avatar
Eike Ziller committed
300
    ActionManager::registerAction(d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
301
    connect(d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveDocumentAs()));
con's avatar
con committed
302

303
    // Window Menu
Eike Ziller's avatar
Eike Ziller committed
304
    ActionContainer *mwindow = ActionManager::actionContainer(Constants::M_WINDOW);
con's avatar
con committed
305

306
    // Window menu separators
307
308
    mwindow->addSeparator(editManagerContext, Constants::G_WINDOW_SPLIT);
    mwindow->addSeparator(editManagerContext, Constants::G_WINDOW_NAVIGATE);
con's avatar
con committed
309

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

318
319
320
321
322
323
324
325
    if (Utils::HostOsInfo::isWindowsHost()) {
        // workaround for QTCREATORBUG-72
        QShortcut *sc = new QShortcut(parent);
        cmd = ActionManager::registerShortcut(sc, Constants::CLOSE_ALTERNATIVE, editManagerContext);
        cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F4")));
        cmd->setDescription(EditorManager::tr("Close"));
        connect(sc, SIGNAL(activated()), this, SLOT(closeEditor()));
    }
326

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

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

339
340
341
342
343
    //Save XXX Context Actions
    connect(d->m_saveCurrentEditorContextAction, SIGNAL(triggered()), this, SLOT(saveDocumentFromContextMenu()));
    connect(d->m_saveAsCurrentEditorContextAction, SIGNAL(triggered()), this, SLOT(saveDocumentAsFromContextMenu()));
    connect(d->m_revertToSavedCurrentEditorContextAction, SIGNAL(triggered()), this, SLOT(revertToSavedFromContextMenu()));

344
    // Close XXX Context Actions
hjk's avatar
hjk committed
345
346
347
    connect(d->m_closeAllEditorsContextAction, SIGNAL(triggered()), this, SLOT(closeAllEditors()));
    connect(d->m_closeCurrentEditorContextAction, SIGNAL(triggered()), this, SLOT(closeEditorFromContextMenu()));
    connect(d->m_closeOtherEditorsContextAction, SIGNAL(triggered()), this, SLOT(closeOtherEditorsFromContextMenu()));
348

hjk's avatar
hjk committed
349
350
    connect(d->m_openGraphicalShellAction, SIGNAL(triggered()), this, SLOT(showInGraphicalShell()));
    connect(d->m_openTerminalAction, SIGNAL(triggered()), this, SLOT(openTerminal()));
Robert Loehning's avatar
Robert Loehning committed
351

con's avatar
con committed
352
    // Goto Previous In History Action
Eike Ziller's avatar
Eike Ziller committed
353
    cmd = ActionManager::registerAction(d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editDesignContext);
354
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Alt+Tab") : tr("Ctrl+Tab")));
con's avatar
con committed
355
    mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
hjk's avatar
hjk committed
356
    connect(d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory()));
con's avatar
con committed
357
358

    // Goto Next In History Action
Eike Ziller's avatar
Eike Ziller committed
359
    cmd = ActionManager::registerAction(d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editDesignContext);
360
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Alt+Shift+Tab") : tr("Ctrl+Shift+Tab")));
con's avatar
con committed
361
    mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
hjk's avatar
hjk committed
362
    connect(d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory()));
con's avatar
con committed
363
364

    // Go back in navigation history
Eike Ziller's avatar
Eike Ziller committed
365
    cmd = ActionManager::registerAction(d->m_goBackAction, Constants::GO_BACK, editDesignContext);
366
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Ctrl+Alt+Left") : tr("Alt+Left")));
con's avatar
con committed
367
    mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
hjk's avatar
hjk committed
368
    connect(d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory()));
con's avatar
con committed
369
370

    // Go forward in navigation history
Eike Ziller's avatar
Eike Ziller committed
371
    cmd = ActionManager::registerAction(d->m_goForwardAction, Constants::GO_FORWARD, editDesignContext);
372
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Ctrl+Alt+Right") : tr("Alt+Right")));
con's avatar
con committed
373
    mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);
hjk's avatar
hjk committed
374
    connect(d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory()));
con's avatar
con committed
375

hjk's avatar
hjk committed
376
    d->m_splitAction = new QAction(tr("Split"), this);
Eike Ziller's avatar
Eike Ziller committed
377
    cmd = ActionManager::registerAction(d->m_splitAction, Constants::SPLIT, editManagerContext);
378
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+E,2") : tr("Ctrl+E,2")));
379
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
hjk's avatar
hjk committed
380
    connect(d->m_splitAction, SIGNAL(triggered()), this, SLOT(split()));
381

hjk's avatar
hjk committed
382
    d->m_splitSideBySideAction = new QAction(tr("Split Side by Side"), this);
Eike Ziller's avatar
Eike Ziller committed
383
    cmd = ActionManager::registerAction(d->m_splitSideBySideAction, Constants::SPLIT_SIDE_BY_SIDE, editManagerContext);
384
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+E,3") : tr("Ctrl+E,3")));
385
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
hjk's avatar
hjk committed
386
    connect(d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide()));
387

388
    d->m_splitNewWindowAction = new QAction(tr("Open in New Window"), this);
389
390
391
392
393
    cmd = ActionManager::registerAction(d->m_splitNewWindowAction, Constants::SPLIT_NEW_WINDOW, editManagerContext);
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+E,4") : tr("Ctrl+E,4")));
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
    connect(d->m_splitNewWindowAction, SIGNAL(triggered()), this, SLOT(splitNewWindow()));

hjk's avatar
hjk committed
394
    d->m_removeCurrentSplitAction = new QAction(tr("Remove Current Split"), this);
Eike Ziller's avatar
Eike Ziller committed
395
    cmd = ActionManager::registerAction(d->m_removeCurrentSplitAction, Constants::REMOVE_CURRENT_SPLIT, editManagerContext);
396
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+E,0") : tr("Ctrl+E,0")));
397
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
hjk's avatar
hjk committed
398
    connect(d->m_removeCurrentSplitAction, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
399

hjk's avatar
hjk committed
400
    d->m_removeAllSplitsAction = new QAction(tr("Remove All Splits"), this);
Eike Ziller's avatar
Eike Ziller committed
401
    cmd = ActionManager::registerAction(d->m_removeAllSplitsAction, Constants::REMOVE_ALL_SPLITS, editManagerContext);
402
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+E,1") : tr("Ctrl+E,1")));
403
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
hjk's avatar
hjk committed
404
    connect(d->m_removeAllSplitsAction, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
405

406
    d->m_gotoNextSplitAction = new QAction(tr("Go to Next Split or Window"), this);
407
    cmd = ActionManager::registerAction(d->m_gotoNextSplitAction, Constants::GOTO_NEXT_SPLIT, editManagerContext);
408
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+E,o") : tr("Ctrl+E,o")));
409
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
410
    connect(d->m_gotoNextSplitAction, SIGNAL(triggered()), this, SLOT(gotoNextSplit()));
411

Eike Ziller's avatar
Eike Ziller committed
412
413
    ActionContainer *medit = ActionManager::actionContainer(Constants::M_EDIT);
    ActionContainer *advancedMenu = ActionManager::createMenu(Constants::M_EDIT_ADVANCED);
414
    medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED);
con's avatar
con committed
415
    advancedMenu->menu()->setTitle(tr("Ad&vanced"));
416
417
    advancedMenu->appendGroup(Constants::G_EDIT_FORMAT);
    advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING);
mae's avatar
mae committed
418
    advancedMenu->appendGroup(Constants::G_EDIT_BLOCKS);
419
420
421
422
    advancedMenu->appendGroup(Constants::G_EDIT_FONT);
    advancedMenu->appendGroup(Constants::G_EDIT_EDITOR);

    // Advanced menu separators
423
424
425
426
    advancedMenu->addSeparator(editManagerContext, Constants::G_EDIT_COLLAPSING);
    advancedMenu->addSeparator(editManagerContext, Constants::G_EDIT_BLOCKS);
    advancedMenu->addSeparator(editManagerContext, Constants::G_EDIT_FONT);
    advancedMenu->addSeparator(editManagerContext, Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
427

con's avatar
con committed
428
    // other setup
429
430
    SplitterOrView *firstRoot = new SplitterOrView();
    d->m_root.append(firstRoot);
431
    d->m_rootContext.append(0);
432
    d->m_currentView = firstRoot->view();
con's avatar
con committed
433

434
435
436
    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->setMargin(0);
    layout->setSpacing(0);
437
    layout->addWidget(firstRoot);
con's avatar
con committed
438
439
440

    updateActions();

hjk's avatar
hjk committed
441
    d->m_windowPopup = new OpenEditorsWindow(this);
442

hjk's avatar
hjk committed
443
444
    d->m_autoSaveTimer = new QTimer(this);
    connect(d->m_autoSaveTimer, SIGNAL(timeout()), SLOT(autoSave()));
445
    updateAutoSave();
con's avatar
con committed
446
447
448
449
}

EditorManager::~EditorManager()
{
450
    m_instance = 0;
hjk's avatar
hjk committed
451
    if (ICore::instance()) {
hjk's avatar
hjk committed
452
        if (d->m_coreListener) {
453
            ExtensionSystem::PluginManager::removeObject(d->m_coreListener);
hjk's avatar
hjk committed
454
            delete d->m_coreListener;
con's avatar
con committed
455
        }
456
        ExtensionSystem::PluginManager::removeObject(d->m_openEditorsFactory);
hjk's avatar
hjk committed
457
        delete d->m_openEditorsFactory;
con's avatar
con committed
458
    }
459
460
461
462
463

    // close all extra windows
    for (int i = 1; i < d->m_root.size(); ++i) {
        SplitterOrView *root = d->m_root.at(i);
        disconnect(root, SIGNAL(destroyed(QObject*)), this, SLOT(rootDestroyed(QObject*)));
464
465
        IContext *rootContext = d->m_rootContext.at(i);
        ICore::removeContextObject(rootContext);
466
        delete root;
467
        delete rootContext;
468
469
    }
    d->m_root.clear();
470
    d->m_rootContext.clear();
471

hjk's avatar
hjk committed
472
    delete d;
con's avatar
con committed
473
474
475
476
}

void EditorManager::init()
{
hjk's avatar
hjk committed
477
    d->m_coreListener = new EditorClosingCoreListener(this);
478
    ExtensionSystem::PluginManager::addObject(d->m_coreListener);
con's avatar
con committed
479

hjk's avatar
hjk committed
480
    d->m_openEditorsFactory = new OpenEditorsViewFactory();
481
    ExtensionSystem::PluginManager::addObject(d->m_openEditorsFactory);
con's avatar
con committed
482

Eike Ziller's avatar
Eike Ziller committed
483
484
    VariableManager::registerFileVariables(kCurrentDocumentPrefix, tr("Current document"));
    VariableManager::registerVariable(kCurrentDocumentXPos,
con's avatar
con committed
485
        tr("X-coordinate of the current editor's upper left corner, relative to screen."));
Eike Ziller's avatar
Eike Ziller committed
486
    VariableManager::registerVariable(kCurrentDocumentYPos,
con's avatar
con committed
487
        tr("Y-coordinate of the current editor's upper left corner, relative to screen."));
Eike Ziller's avatar
Eike Ziller committed
488
    connect(VariableManager::instance(), SIGNAL(variableUpdateRequested(QByteArray)),
hjk's avatar
hjk committed
489
            this, SLOT(updateVariable(QByteArray)));
con's avatar
con committed
490
491
}

492
493
void EditorManager::updateAutoSave()
{
hjk's avatar
hjk committed
494
495
    if (d->m_autoSaveEnabled)
        d->m_autoSaveTimer->start(d->m_autoSaveInterval * (60 * 1000));
496
    else
hjk's avatar
hjk committed
497
        d->m_autoSaveTimer->stop();
498
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
499

Lasse Holmstedt's avatar
Lasse Holmstedt committed
500
EditorToolBar *EditorManager::createToolBar(QWidget *parent)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
501
{
Lasse Holmstedt's avatar
Lasse Holmstedt committed
502
    return new EditorToolBar(parent);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
503
504
}

mae's avatar
mae committed
505
void EditorManager::removeEditor(IEditor *editor)
con's avatar
con committed
506
{
hjk's avatar
hjk committed
507
508
    bool isDuplicate = d->m_editorModel->isDuplicate(editor);
    d->m_editorModel->removeEditor(editor);
hjk's avatar
hjk committed
509
    if (!isDuplicate)
510
        DocumentManager::removeDocument(editor->document());
hjk's avatar
hjk committed
511
    ICore::removeContextObject(editor);
con's avatar
con committed
512
513
}

514
void EditorManager::handleContextChange(const QList<Core::IContext *> &context)
con's avatar
con committed
515
516
517
{
    if (debugEditorManager)
        qDebug() << Q_FUNC_INFO;
518
    d->m_scheduledCurrentEditor = 0;
519
520
521
522
    IEditor *editor = 0;
    foreach (IContext *c, context)
        if ((editor = qobject_cast<IEditor*>(c)))
            break;
523
524
525
526
527
528
529
530
531
    if (editor && editor != d->m_currentEditor) {
        // Delay actually setting the current editor to after the current event queue has been handled
        // Without doing this, e.g. clicking into projects tree or locator would always open editors
        // in the main window. That is because clicking anywhere in the main window (even over e.g.
        // the locator line edit) first activates the window and sets focus to its focus widget.
        // Only afterwards the focus is shifted to the widget that received the click.
        d->m_scheduledCurrentEditor = editor;
        QTimer::singleShot(0, this, SLOT(setCurrentEditorFromContextChange()));
    } else {
532
        updateActions();
533
    }
534
535
}

con's avatar
con committed
536
537
void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
{
538
539
540
    if (editor)
        setCurrentView(0);

hjk's avatar
hjk committed
541
    if (d->m_currentEditor == editor)
con's avatar
con committed
542
        return;
hjk's avatar
hjk committed
543
    if (d->m_currentEditor && !ignoreNavigationHistory)
544
        addCurrentPositionToNavigationHistory();
mae's avatar
mae committed
545

hjk's avatar
hjk committed
546
    d->m_currentEditor = editor;
con's avatar
con committed
547
    if (editor) {
548
549
        if (EditorView *view = viewForEditor(editor))
            view->setCurrentEditor(editor);
550
551
        // update global history
        EditorView::updateEditorHistory(editor, d->m_globalHistory);
con's avatar
con committed
552
    }
mae's avatar
mae committed
553
    updateActions();
554
    updateWindowTitle();
mae's avatar
mae committed
555
    emit currentEditorChanged(editor);
con's avatar
con committed
556
557
}

mae's avatar
mae committed
558

559
void EditorManager::setCurrentView(Internal::EditorView *view)
mae's avatar
mae committed
560
{
hjk's avatar
hjk committed
561
    if (view == d->m_currentView)
mae's avatar
mae committed
562
563
        return;

564
    EditorView *old = d->m_currentView;
hjk's avatar
hjk committed
565
    d->m_currentView = view;
mae's avatar
mae committed
566
567
568
569
570

    if (old)
        old->update();
    if (view)
        view->update();
571

572
    if (view && !view->currentEditor()) {
573
        view->setFocus();
574
        ICore::raiseWindow(view);
575
    }
mae's avatar
mae committed
576
577
}

578
Internal::EditorView *EditorManager::currentEditorView() const
mae's avatar
mae committed
579
{
580
    EditorView *view = d->m_currentView;
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
    if (!view) {
        if (d->m_currentEditor) {
            view = viewForEditor(d->m_currentEditor);
            QTC_ASSERT(view, view = d->m_root.first()->findFirstView());
        }
        QTC_CHECK(view);
        if (!view) { // should not happen, we should always have either currentview or currentdocument
            foreach (SplitterOrView *root, d->m_root) {
                if (root->window()->isActiveWindow()) {
                    view = root->findFirstView();
                    break;
                }
            }
            QTC_ASSERT(view, view = d->m_root.first()->findFirstView());
        }
    }
597
    return view;
mae's avatar
mae committed
598
599
}

600
601
602
603
604
605
606
607
608
609
610
EditorView *EditorManager::viewForEditor(IEditor *editor)
{
    QWidget *w = editor->widget();
    while (w) {
        w = w->parentWidget();
        if (EditorView *view = qobject_cast<EditorView *>(w))
            return view;
    }
    return 0;
}

611
SplitterOrView *EditorManager::findRoot(const EditorView *view, int *rootIndex)
612
613
{
    SplitterOrView *current = view->parentSplitterOrView();
614
615
616
617
618
619
620
    while (current) {
        int index = m_instance->d->m_root.indexOf(current);
        if (index >= 0) {
            if (rootIndex)
                *rootIndex = index;
            return current;
        }
621
622
        current = current->findParentSplitter();
    }
623
624
    QTC_CHECK(false); // we should never have views without a root
    return 0;
625
626
}

con's avatar
con committed
627
628
629
QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
{
    QList<IEditor *> found;
630
    QString fixedname = DocumentManager::fixFileName(filename, DocumentManager::KeepLinks);
con's avatar
con committed
631
    foreach (IEditor *editor, openedEditors()) {
632
        if (fixedname == DocumentManager::fixFileName(editor->document()->fileName(), DocumentManager::KeepLinks))
con's avatar
con committed
633
634
635
636
637
            found << editor;
    }
    return found;
}

638
QList<IEditor *> EditorManager::editorsForDocument(IDocument *document) const
639
640
641
{
    QList<IEditor *> found;
    foreach (IEditor *editor, openedEditors()) {
642
        if (editor->document() == document)
643
644
645
646
647
            found << editor;
    }
    return found;
}

hjk's avatar
hjk committed
648
IEditor *EditorManager::currentEditor()
con's avatar
con committed
649
{
hjk's avatar
hjk committed
650
    return m_instance->d->m_currentEditor;
con's avatar
con committed
651
652
}

mae's avatar
mae committed
653
654
655
656
657
658
659
void EditorManager::emptyView(Core::Internal::EditorView *view)
{
    if (!view)
        return;

    QList<IEditor *> editors = view->editors();
    foreach (IEditor *editor, editors) {
hjk's avatar
hjk committed
660
        if (!d->m_editorModel->isDuplicate(editor)) {
661
662
663
664
665
666
667
668
669
670
671
672
673
674
            QList<IEditor *> duplicates = d->m_editorModel->duplicatesFor(editor);
            if (!duplicates.isEmpty()) {
                d->m_editorModel->makeOriginal(duplicates.first());
            } else {
                // it's the only editor for that file and it's not a duplicate,
                // so we need to keep it around (--> in the editor model)
                if (currentEditor() == editor) {
                    // we don't want a current editor that is not open in a view
                    setCurrentEditor(0);
                }
                editors.removeAll(editor);
                view->removeEditor(editor);
                continue; // don't close the editor
            }
mae's avatar
mae committed
675
676
677
678
679
        }
        emit editorAboutToClose(editor);
        removeEditor(editor);
        view->removeEditor(editor);
    }
680
681
682
683
684
    if (!editors.isEmpty()) {
        emit editorsClosed(editors);
        foreach (IEditor *editor, editors) {
            delete editor;
        }
mae's avatar
mae committed
685
686
687
    }
}

688
689
690
691
692
693
694
695
696
697
698
699
700
void EditorManager::splitNewWindow(Internal::EditorView *view)
{
    SplitterOrView *splitter;
    IEditor *editor = view->currentEditor();
    IEditor *newEditor = 0;
    if (editor && editor->duplicateSupported())
        newEditor = m_instance->duplicateEditor(editor);
    else
        newEditor = editor; // move to the new view
    splitter = new SplitterOrView;
    splitter->setAttribute(Qt::WA_DeleteOnClose);
    splitter->setAttribute(Qt::WA_QuitOnClose, false); // don't prevent Qt Creator from closing
    splitter->resize(QSize(800, 600));
701
702
703
704
    IContext *context = new IContext;
    context->setContext(Context(Constants::C_EDITORMANAGER));
    context->setWidget(splitter);
    ICore::addContextObject(context);
Eike Ziller's avatar
Eike Ziller committed
705
706
707
    m_instance->d->m_root.append(splitter);
    m_instance->d->m_rootContext.append(context);
    connect(splitter, SIGNAL(destroyed(QObject*)), m_instance, SLOT(rootDestroyed(QObject*)));
708
709
710
711
712
713
714
715
716
    splitter->show();
    ICore::raiseWindow(splitter);
    if (newEditor)
        m_instance->activateEditor(splitter->view(), newEditor, IgnoreNavigationHistory);
    else
        splitter->view()->setFocus();
    m_instance->updateActions();
}

mae's avatar
mae committed
717
void EditorManager::closeView(Core::Internal::EditorView *view)
con's avatar
con committed
718
{
mae's avatar
mae committed
719
    if (!view)
con's avatar
con committed
720
        return;
mae's avatar
mae committed
721

mae's avatar
mae committed
722
723
    emptyView(view);

724
    SplitterOrView *splitterOrView = view->parentSplitterOrView();
725
    Q_ASSERT(splitterOrView);
mae's avatar
mae committed
726
    Q_ASSERT(splitterOrView->view() == view);
727
    SplitterOrView *splitter = splitterOrView->findParentSplitter();
mae's avatar
mae committed
728
    Q_ASSERT(splitterOrView->hasEditors() == false);
729
    splitterOrView->hide();
mae's avatar
mae committed
730
731
732
733
    delete splitterOrView;

    splitter->unsplit();

734
    EditorView *newCurrent = splitter->findFirstView();
mae's avatar
mae committed
735
    if (newCurrent) {
736
737
        if (IEditor *e = newCurrent->currentEditor())
            activateEditor(newCurrent, e);
738
        else
mae's avatar
mae committed
739
            setCurrentView(newCurrent);
mae's avatar
mae committed
740
    }
mae's avatar
mae committed
741
}
mae's avatar
mae committed
742

con's avatar
con committed
743
QList<IEditor*>
744
    EditorManager::editorsForDocuments(QList<IDocument*> documents) const
con's avatar
con committed
745
746
747
{
    const QList<IEditor *> editors = openedEditors();
    QSet<IEditor *> found;
748
    foreach (IDocument *document, documents) {
con's avatar
con committed
749
        foreach (IEditor *editor, editors) {
750
            if (editor->document() == document && !found.contains(editor))
con's avatar
con committed
751
752
753
754
755
756
                    found << editor;
        }
    }
    return found.toList();
}

757
QList<IDocument *> EditorManager::documentsForEditors(QList<IEditor *> editors) const
con's avatar
con committed
758
759
{
    QSet<IEditor *> handledEditors;
760
    QList<IDocument *> documents;
con's avatar
con committed
761
762
    foreach (IEditor *editor, editors) {
        if (!handledEditors.contains(editor)) {
763
            documents << editor->document();
764
            handledEditors.insert(editor);
con's avatar
con committed
765
766
        }
    }
767
    return documents;
con's avatar
con committed
768
769
770
771
}

bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
{
hjk's avatar
hjk committed
772
    d->m_editorModel->removeAllRestoredEditors();
773
    if (closeEditors(openedEditors(), askAboutModifiedEditors)) {
hjk's avatar
hjk committed
774
//        d->clearNavigationHistory();
775
776
777
        return true;
    }
    return false;
con's avatar
con committed
778
779
}

780
void EditorManager::closeOtherEditors(IEditor *editor)
con's avatar
con committed
781
{
hjk's avatar
hjk committed
782
    d->m_editorModel->removeAllRestoredEditors();
con's avatar
con committed
783
    QList<IEditor*> editors = openedEditors();
784
    editors.removeAll(editor);
con's avatar
con committed
785
786
787
    closeEditors(editors, true);
}

788
789
790
791
792
793
794
void EditorManager::closeOtherEditors()
{
    IEditor *current = currentEditor();
    QTC_ASSERT(current, return);
    closeOtherEditors(current);
}

795
796
797
// SLOT connected to action
void EditorManager::closeEditor()
{
hjk's avatar
hjk committed
798
    if (!d->m_currentEditor)
799
        return;
800
    addCurrentPositionToNavigationHistory();
hjk's avatar
hjk committed
801
    closeEditor(d->m_currentEditor);
802
803
}

804
805
806
807
808
809
static void assignAction(QAction *self, QAction *other)
{
    self->setText(other->text());
    self->setIcon(other->icon());
    self->setShortcut(other->shortcut());
    self->setEnabled(other->isEnabled());
810
    self->setIconVisibleInMenu(other->isIconVisibleInMenu());
811
812
813
}

void EditorManager::addSaveAndCloseEditorActions(QMenu *contextMenu, const QModelIndex &editorIndex)
814
815
{
    QTC_ASSERT(contextMenu, return);
hjk's avatar
hjk committed
816
    d->m_contextMenuEditorIndex = editorIndex;
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835

    assignAction(d->m_saveCurrentEditorContextAction, ActionManager::command(Constants::SAVE)->action());
    assignAction(d->m_saveAsCurrentEditorContextAction, ActionManager::command(Constants::SAVEAS)->action());
    assignAction(d->m_revertToSavedCurrentEditorContextAction, ActionManager::command(Constants::REVERTTOSAVED)->action());

    IEditor *editor = d->m_contextMenuEditorIndex.data(Qt::UserRole).value<Core::IEditor*>();

    setupSaveActions(editor,
                     d->m_saveCurrentEditorContextAction,
                     d->m_saveAsCurrentEditorContextAction,
                     d->m_revertToSavedCurrentEditorContextAction);

    contextMenu->addAction(d->m_saveCurrentEditorContextAction);
    contextMenu->addAction(d->m_saveAsCurrentEditorContextAction);
    contextMenu->addAction(ActionManager::command(Constants::SAVEALL)->action());
    contextMenu->addAction(d->m_revertToSavedCurrentEditorContextAction);

    contextMenu->addSeparator();

hjk's avatar
hjk committed
836
    d->m_closeCurrentEditorContextAction->setText(editorIndex.isValid()
837
838
                                                    ? tr("Close \"%1\"").arg(editorIndex.data().toString())
                                                    : tr("Close Editor"));
hjk's avatar
hjk committed
839
    d->m_closeOtherEditorsContextAction->setText(editorIndex.isValid()
840
841
                                                   ? tr("Close All Except \"%1\"").arg(editorIndex.data().toString())
                                                   : tr("Close Other Editors"));
hjk's avatar
hjk committed
842
843
844
845
846
847
    d->m_closeCurrentEditorContextAction->setEnabled(editorIndex.isValid());
    d->m_closeOtherEditorsContextAction->setEnabled(editorIndex.isValid());
    d->m_closeAllEditorsContextAction->setEnabled(!openedEditors().isEmpty());
    contextMenu->addAction(d->m_closeCurrentEditorContextAction);
    contextMenu->addAction(d->m_closeAllEditorsContextAction);
    contextMenu->addAction(d->m_closeOtherEditorsContextAction);
848
849
}

Robert Loehning's avatar
Robert Loehning committed
850
851
852
void EditorManager::addNativeDirActions(QMenu *contextMenu, const QModelIndex &editorIndex)
{
    QTC_ASSERT(contextMenu, return);
hjk's avatar
hjk committed
853
854
855
856
    d->m_openGraphicalShellAction->setEnabled(editorIndex.isValid());
    d->m_openTerminalAction->setEnabled(editorIndex.isValid());
    contextMenu->addAction(d->m_openGraphicalShellAction);
    contextMenu->addAction(d->m_openTerminalAction);