editormanager.cpp 58 KB
Newer Older
con's avatar
con committed
1
2
3
4
/***************************************************************************
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
7
8
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
9
10
11
**
** Non-Open Source Usage
**
con's avatar
con committed
12
13
14
** Licensees may use this file in accordance with the Qt Beta Version
** License Agreement, Agreement version 2.2 provided with the Software or,
** alternatively, in accordance with the terms contained in a written
15
16
17
18
** agreement between you and Nokia.
**
** GNU General Public License Usage
**
con's avatar
con committed
19
20
21
22
23
24
25
26
27
28
** Alternatively, this file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the packaging
** of this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
**
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html.
**
** In addition, as a special exception, Nokia gives you certain additional
29
** rights. These rights are described in the Nokia Qt GPL Exception
30
** version 1.3, included in the file GPL_EXCEPTION.txt in this package.
31
**
con's avatar
con committed
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"
con's avatar
con committed
38
39
#include "openwithdialog.h"
#include "filemanager.h"
40
#include "icore.h"
con's avatar
con committed
41
42
#include "iversioncontrol.h"
#include "mimedatabase.h"
43
44
45
#include "saveitemsdialog.h"
#include "tabpositionindicator.h"
#include "vcsmanager.h"
con's avatar
con committed
46
47
48
49

#include <coreplugin/coreconstants.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/uniqueidmanager.h>
50
#include <coreplugin/actionmanager/actionmanager.h>
con's avatar
con committed
51
52
53
54
#include <coreplugin/editormanager/ieditorfactory.h>
#include <coreplugin/baseview.h>
#include <coreplugin/imode.h>

55
56
#include <extensionsystem/pluginmanager.h>

hjk's avatar
hjk committed
57
58
59
#include <utils/qtcassert.h>

#include <QtCore/QDebug>
con's avatar
con committed
60
61
62
#include <QtCore/QFileInfo>
#include <QtCore/QMap>
#include <QtCore/QProcess>
hjk's avatar
hjk committed
63
64
#include <QtCore/QSet>
#include <QtCore/QSettings>
con's avatar
con committed
65
66
67
68

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

using namespace Core;
using namespace Core::Internal;

enum { debugEditorManager=0 };

82
83
84
85
86
static inline ExtensionSystem::PluginManager *pluginManager()
{
    return ExtensionSystem::PluginManager::instance();
}

con's avatar
con committed
87
88
89
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
//===================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 *)));
}

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

129
130
namespace Core {
struct EditorManagerPrivate {
con's avatar
con committed
131
132
133
134
135
136
137
138
    struct EditLocation {
        QPointer<IEditor> editor;
        QString fileName;
        QString kind;
        QVariant state;
    };
    explicit EditorManagerPrivate(ICore *core, QWidget *parent);
    ~EditorManagerPrivate();
139
    Internal::EditorView *m_view;
mae's avatar
mae committed
140
    Internal::SplitterOrView *m_splitter;
mae's avatar
mae committed
141
    QPointer<IEditor> m_currentEditor;
mae's avatar
mae committed
142
    QPointer<SplitterOrView> m_currentView;
143

con's avatar
con committed
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    ICore *m_core;


    // actions
    QAction *m_revertToSavedAction;
    QAction *m_saveAction;
    QAction *m_saveAsAction;
    QAction *m_closeCurrentEditorAction;
    QAction *m_closeAllEditorsAction;
    QAction *m_gotoNextDocHistoryAction;
    QAction *m_gotoPreviousDocHistoryAction;
    QAction *m_goBackAction;
    QAction *m_goForwardAction;
    QAction *m_openInExternalEditorAction;
158
159
160
    QAction *m_splitAction;
    QAction *m_splitSideBySideAction;
    QAction *m_unsplitAction;
161
    QAction *m_gotoOtherWindowAction;
con's avatar
con committed
162
163
164
165
166
167
168
169
170
171
172
173
174
175

    QList<IEditor *> m_editorHistory;
    QList<EditLocation *> m_navigationHistory;
    int currentNavigationHistoryPosition;
    Internal::OpenEditorsWindow *m_windowPopup;
    Core::BaseView *m_openEditorsView;
    Internal::EditorClosingCoreListener *m_coreListener;

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

    QString fileFilters;
    QString selectedFilter;

176
    EditorModel *m_editorModel;
con's avatar
con committed
177
178
    QString m_externalEditor;
};
179
}
con's avatar
con committed
180
181

EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) :
182
    m_view(0),
183
    m_splitter(0),
con's avatar
con committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
    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)),
    m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Document in History"), parent)),
    m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Document in History"), parent)),
    m_goBackAction(new QAction(EditorManager::tr("Go back"), parent)),
    m_goForwardAction(new QAction(EditorManager::tr("Go forward"), parent)),
    m_openInExternalEditorAction(new QAction(EditorManager::tr("Open in External Editor"), parent)),
    currentNavigationHistoryPosition(-1),
    m_windowPopup(0),
    m_coreListener(0)
{
199
    m_editorModel = new EditorModel(parent);
con's avatar
con committed
200
201
202
203
204
205
206
207
208
209
}

EditorManagerPrivate::~EditorManagerPrivate()
{
    qDeleteAll(m_navigationHistory);
    m_navigationHistory.clear();
}

EditorManager *EditorManager::m_instance = 0;

210
211
212
213
214
215
216
217
218
219
static Command *createSeparator(ActionManager *am, QObject *parent,
                                const QString &name,
                                const QList<int> &context)
{
    QAction *tmpaction = new QAction(parent);
    tmpaction->setSeparator(true);
    Command *cmd = am->registerAction(tmpaction, name, context);
    return cmd;
}

con's avatar
con committed
220
221
222
223
224
225
226
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
227
            this, SLOT(handleContextChange(Core::IContext *)));
con's avatar
con committed
228
229
230
231
232

    const QList<int> gc =  QList<int>() << Constants::C_GLOBAL_ID;
    const QList<int> editManagerContext =
            QList<int>() << m_d->m_core->uniqueIDManager()->uniqueIdentifier(Constants::C_EDITORMANAGER);

233
    ActionManager *am = m_d->m_core->actionManager();
234
    ActionContainer *mfile = am->actionContainer(Constants::M_FILE);
con's avatar
con committed
235
236

    //Revert to saved
con's avatar
con committed
237
    Command *cmd = am->registerAction(m_d->m_revertToSavedAction,
con's avatar
con committed
238
                                       Constants::REVERTTOSAVED, editManagerContext);
con's avatar
con committed
239
    cmd->setAttribute(Command::CA_UpdateText);
con's avatar
con committed
240
241
242
243
244
245
246
247
248
249
250
251
252
    cmd->setDefaultText(tr("Revert File to Saved"));
    mfile->addAction(cmd, Constants::G_FILE_SAVE);
    connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved()));

    //Save Action
    am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext);
    connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));

    //Save As Action
    am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext);
    connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs()));

    //Window Menu
253
    ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
con's avatar
con committed
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283

    //Window menu separators
    QAction *tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
    cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Split"), editManagerContext);
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);

    tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
    cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Close"), editManagerContext);
    mwindow->addAction(cmd, Constants::G_WINDOW_CLOSE);

    tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
    cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Navigate"), editManagerContext);
    mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE);

    tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
    cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Navigate.Groups"), editManagerContext);
    mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE_GROUPS);

    tmpaction = new QAction(this);
    tmpaction->setSeparator(true);
    cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Bottom"), editManagerContext);
    mwindow->addAction(cmd, Constants::G_WINDOW_LIST);

    //Close Action
    cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W")));
con's avatar
con committed
284
    cmd->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
    cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text());
    mfile->addAction(cmd, Constants::G_FILE_CLOSE);
    connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor()));

    //Close All Action
    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()));

    // Goto Previous In History Action
    cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editManagerContext);
#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
    cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editManagerContext);
#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
    cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editManagerContext);
#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
    cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editManagerContext);
#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()));

335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
    m_d->m_splitAction = new QAction(tr("Split"), this);
    cmd = am->registerAction(m_d->m_splitAction, Constants::SPLIT, editManagerContext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+E,1")));
    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);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+E,2")));
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
    connect(m_d->m_splitSideBySideAction, SIGNAL(triggered()), this, SLOT(splitSideBySide()));

    m_d->m_unsplitAction = new QAction(tr("Unsplit"), this);
    cmd = am->registerAction(m_d->m_unsplitAction, Constants::UNSPLIT, editManagerContext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+E,0")));
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
    connect(m_d->m_unsplitAction, SIGNAL(triggered()), this, SLOT(unsplit()));

353
354
355
356
357
    m_d->m_gotoOtherWindowAction = new QAction(tr("Goto other window"), this);
    cmd = am->registerAction(m_d->m_gotoOtherWindowAction, Constants::GOTO_OTHER_WINDOW, editManagerContext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+E,o")));
    mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT);
    connect(m_d->m_gotoOtherWindowAction, SIGNAL(triggered()), this, SLOT(gotoOtherWindow()));
358

con's avatar
con committed
359

360
361
    ActionContainer *medit = am->actionContainer(Constants::M_EDIT);
    ActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED);
362
    medit->addMenu(advancedMenu, Constants::G_EDIT_ADVANCED);
con's avatar
con committed
363
    advancedMenu->menu()->setTitle(tr("&Advanced"));
364
365
366
367
368
369
370
371
372
373
374
375
    advancedMenu->appendGroup(Constants::G_EDIT_FORMAT);
    advancedMenu->appendGroup(Constants::G_EDIT_COLLAPSING);
    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);
    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
376

con's avatar
con committed
377
378
    cmd = am->registerAction(m_d->m_openInExternalEditorAction, Constants::OPEN_IN_EXTERNAL_EDITOR, editManagerContext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Alt+V,Alt+I")));
379
    advancedMenu->addAction(cmd, Constants::G_EDIT_EDITOR);
con's avatar
con committed
380
381
382
383
    connect(m_d->m_openInExternalEditorAction, SIGNAL(triggered()), this, SLOT(openInExternalEditor()));


    // other setup
mae's avatar
mae committed
384
385
386
387
    m_d->m_splitter = new SplitterOrView(m_d->m_editorModel);
    m_d->m_splitter->setRoot(true);
    m_d->m_view = m_d->m_splitter->view();

con's avatar
con committed
388

389
390
391
392
    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->addWidget(m_d->m_splitter);
con's avatar
con committed
393
394
395
396
397
398
399
400
401

    updateActions();

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

EditorManager::~EditorManager()
{
    if (m_d->m_core) {
402
        ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
con's avatar
con committed
403
        if (m_d->m_coreListener) {
404
            pm->removeObject(m_d->m_coreListener);
con's avatar
con committed
405
406
            delete m_d->m_coreListener;
        }
407
        pm->removeObject(m_d->m_openEditorsFactory);
con's avatar
con committed
408
409
410
411
412
413
414
415
416
417
418
        delete m_d->m_openEditorsFactory;
    }
    delete m_d;
}

void EditorManager::init()
{
    QList<int> context;
    context << m_d->m_core->uniqueIDManager()->uniqueIdentifier("QtCreator.OpenDocumentsView");

    m_d->m_coreListener = new EditorClosingCoreListener(this);
419
420
    
    pluginManager()->addObject(m_d->m_coreListener);
con's avatar
con committed
421
422

    m_d->m_openEditorsFactory = new OpenEditorsViewFactory();
423
    pluginManager()->addObject(m_d->m_openEditorsFactory);
con's avatar
con committed
424
425
}

Roberto Raggi's avatar
Roberto Raggi committed
426
427
428
429
430
431
432
433
434
435
436
437
438
439
QString EditorManager::defaultExternalEditor() const
{
#ifdef Q_OS_MAC
    return m_d->m_core->resourcePath()
            +QLatin1String("/runInTerminal.command vi %f %l %c %W %H %x %y");
#elif defined(Q_OS_UNIX)
    return QLatin1String("xterm -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\"");
#elif defined (Q_OS_WIN)
    return QLatin1String("notepad %f");
#else
    return QString();
#endif
}

con's avatar
con committed
440
441
442
443
444
445
446
447
448
void EditorManager::updateEditorHistory()
{
    IEditor *editor = currentEditor();
    if (!editor)
        return;
    m_d->m_editorHistory.removeAll(editor);
    m_d->m_editorHistory.prepend(editor);
}

mae's avatar
mae committed
449
void EditorManager::removeEditor(IEditor *editor)
con's avatar
con committed
450
{
mae's avatar
mae committed
451
    bool isDuplicate = m_d->m_editorModel->isDuplicate(editor);
mae's avatar
mae committed
452
    m_d->m_editorModel->removeEditor(editor);
mae's avatar
mae committed
453
454
455
    if (!isDuplicate) {
        m_d->m_core->fileManager()->removeFile(editor->file());
    }
mae's avatar
mae committed
456
457
    m_d->m_editorHistory.removeAll(editor);
    m_d->m_core->removeContextObject(editor);
con's avatar
con committed
458
459
460

}

mae's avatar
mae committed
461
void EditorManager::handleContextChange(IContext *context)
con's avatar
con committed
462
463
464
465
{
    if (debugEditorManager)
        qDebug() << Q_FUNC_INFO;
    IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0;
466
    if (editor) {
con's avatar
con committed
467
468
469
470
        setCurrentEditor(editor);
    } else {
        updateActions();
    }
471
472
}

con's avatar
con committed
473
474
void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory)
{
mae's avatar
mae committed
475
    setCurrentView(0);
mae's avatar
mae committed
476
    if (m_d->m_currentEditor == editor)
con's avatar
con committed
477
        return;
mae's avatar
mae committed
478

mae's avatar
mae committed
479
    m_d->m_currentEditor = editor;
con's avatar
con committed
480
481
482
483
    if (editor) {
        bool addToHistory = (!ignoreNavigationHistory && editor != currentEditor());
        if (addToHistory)
            addCurrentPositionToNavigationHistory(true);
484
485
486

        EditorView *view = m_d->m_splitter->findView(editor)->view();
        view->setCurrentEditor(editor);
con's avatar
con committed
487
    }
mae's avatar
mae committed
488
489
490
    updateActions();
    updateEditorHistory();
    emit currentEditorChanged(editor);
con's avatar
con committed
491
492
}

mae's avatar
mae committed
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512

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

Core::Internal::SplitterOrView *EditorManager::currentView() const
{
    return m_d->m_currentView;
}

con's avatar
con committed
513
514
515
516
517
518
519
520
521
522
523
524
525
QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
{
    QList<IEditor *> found;
    QString fixedname = FileManager::fixFileName(filename);
    foreach (IEditor *editor, openedEditors()) {
        if (fixedname == FileManager::fixFileName(editor->file()->fileName()))
            found << editor;
    }
    return found;
}

IEditor *EditorManager::currentEditor() const
{
mae's avatar
mae committed
526
    return m_d->m_currentEditor;
con's avatar
con committed
527
528
529
530
531
532
533
534
}


// SLOT connected to action
// since this is potentially called in the event handler of the editor
// we simply postpone it with a single shot timer
void EditorManager::closeEditor()
{
mae's avatar
mae committed
535
    closeEditor(m_d->m_currentEditor);
con's avatar
con committed
536
537
}

mae's avatar
mae committed
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
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
561
void EditorManager::closeView(Core::Internal::EditorView *view)
con's avatar
con committed
562
{
mae's avatar
mae committed
563
    if (!view)
con's avatar
con committed
564
        return;
mae's avatar
mae committed
565

566
    if (view == m_d->m_view) {
567
568
        if (IEditor *e = view->currentEditor())
            closeEditors(QList<IEditor *>() << e);
mae's avatar
mae committed
569
570
571
572
573
574
        return;
    }

    emptyView(view);

    SplitterOrView *splitterOrView = m_d->m_splitter->findView(view);
575
    Q_ASSERT(splitterOrView);
mae's avatar
mae committed
576
577
578
    Q_ASSERT(splitterOrView->view() == view);
    SplitterOrView *splitter = m_d->m_splitter->findSplitter(splitterOrView);
    Q_ASSERT(splitterOrView->hasEditors() == false);
579
    splitterOrView->hide();
mae's avatar
mae committed
580
581
582
583
584
585
586
587
588
589
    delete splitterOrView;

    splitter->unsplit();

    SplitterOrView *newCurrent = splitter->findFirstView();
    if (newCurrent) {
        if (newCurrent->editor())
            activateEditor(newCurrent->view(), newCurrent->editor());
        else
            setCurrentView(newCurrent);
mae's avatar
mae committed
590
    }
591
    updateEditorHistory();
mae's avatar
mae committed
592
}
mae's avatar
mae committed
593

mae's avatar
mae committed
594
595
596
597
598
void EditorManager::closeEditor(Core::IEditor *editor)
{
    if (!editor)
        return;
    closeEditors(QList<IEditor *>() << editor);
con's avatar
con committed
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
}

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

616
QList<IFile *> EditorManager::filesForEditors(QList<IEditor *> editors) const
con's avatar
con committed
617
618
619
620
621
622
{
    QSet<IEditor *> handledEditors;
    QList<IFile *> files;
    foreach (IEditor *editor, editors) {
        if (!handledEditors.contains(editor)) {
            files << editor->file();
623
            handledEditors.insert(editor);
con's avatar
con committed
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
        }
    }
    return files;
}

bool EditorManager::closeAllEditors(bool askAboutModifiedEditors)
{
    return closeEditors(openedEditors(), askAboutModifiedEditors);
}

bool EditorManager::closeEditors(const QList<IEditor*> editorsToClose, bool askAboutModifiedEditors)
{
    if (editorsToClose.isEmpty())
        return true;
    bool closingFailed = false;
    QList<IEditor*> acceptedEditors;
    //ask all core listeners to check whether the editor can be closed
    const QList<ICoreListener *> listeners =
642
        pluginManager()->getObjects<ICoreListener>();
con's avatar
con committed
643
644
    foreach (IEditor *editor, editorsToClose) {
        bool editorAccepted = true;
mae's avatar
mae committed
645
646
        if (m_d->m_editorModel->isDuplicate(editor))
            editor = m_d->m_editorModel->originalForDuplicate(editor);
con's avatar
con committed
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
        foreach (ICoreListener *listener, listeners) {
            if (!listener->editorAboutToClose(editor)) {
                editorAccepted = false;
                closingFailed = false;
                break;
            }
        }
        if (editorAccepted)
            acceptedEditors.append(editor);
    }
    if (acceptedEditors.isEmpty())
        return false;
    //ask whether to save modified files
    if (askAboutModifiedEditors) {
        bool cancelled = false;
662
        QList<IFile*> list = ICore::instance()->fileManager()->
con's avatar
con committed
663
664
665
666
667
668
669
670
671
672
673
            saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled);
        if (cancelled)
            return false;
        if (!list.isEmpty()) {
            QSet<IEditor*> skipSet = editorsForFiles(list).toSet();
            acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList();
            closingFailed = false;
        }
    }
    if (acceptedEditors.isEmpty())
        return false;
mae's avatar
mae committed
674
675
676
677
678

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

mae's avatar
mae committed
679
680
681
    QList<EditorView*> currentViews;
    EditorView *currentView = 0;
    if (currentEditor())
con's avatar
con committed
682
        addCurrentPositionToNavigationHistory(true);
mae's avatar
mae committed
683

con's avatar
con committed
684
685
686
687
688
689
690
691
    // remove the editors
    foreach (IEditor *editor, acceptedEditors) {
        emit editorAboutToClose(editor);
        if (!editor->file()->fileName().isEmpty()) {
            QByteArray state = editor->saveState();
            if (!state.isEmpty())
                m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state));
        }
mae's avatar
mae committed
692

mae's avatar
mae committed
693
        removeEditor(editor);
mae's avatar
mae committed
694
695
696
697
698
699
        if (SplitterOrView *view = m_d->m_splitter->findView(editor)) {
            if (editor == view->view()->currentEditor()) {
                currentViews += view->view();
                if (editor == m_d->m_currentEditor)
                    currentView = view->view();
            }
mae's avatar
mae committed
700
            view->view()->removeEditor(editor);
mae's avatar
mae committed
701
        }
con's avatar
con committed
702
    }
mae's avatar
mae committed
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722

    foreach (EditorView *view, currentViews) {
        IEditor *newCurrent = view->currentEditor();
        if (!newCurrent)
            newCurrent = pickUnusedEditor();
        if (!newCurrent) {
            // pick the first one that can be duplicated
            foreach (IEditor *e, m_d->m_editorHistory) {
                if (e->duplicateSupported()) {
                    newCurrent = e;
                    break;
                }
            }
        }

        if (newCurrent)
            activateEditor(view, newCurrent, NoActivate);
    }


con's avatar
con committed
723
724
725
726
    emit editorsClosed(acceptedEditors);
    foreach (IEditor *editor, acceptedEditors) {
        delete editor;
    }
mae's avatar
mae committed
727

728
729
730
731
732
    if (currentView) {
        setCurrentView(m_d->m_splitter->findView(currentView));
        if (IEditor *e = currentView->currentEditor())
            activateEditor(currentView, e);
    }
mae's avatar
mae committed
733

con's avatar
con committed
734
735
736
    return !closingFailed;
}

mae's avatar
mae committed
737
738
739
740
741
742
743
744
745
IEditor *EditorManager::pickUnusedEditor() const
{
    foreach (IEditor *editor, m_d->m_editorHistory) {
        SplitterOrView *view = m_d->m_splitter->findView(editor);
        if (!view || view->editor() != editor)
            return editor;
    }
    return 0;
}
con's avatar
con committed
746

mae's avatar
mae committed
747
void EditorManager::activateEditor(IEditor *editor, OpenEditorFlags flags)
mae's avatar
mae committed
748
{
mae's avatar
mae committed
749
    SplitterOrView *splitterOrView = m_d->m_currentView;
750
751
    if (splitterOrView && splitterOrView->splitter())
        splitterOrView = 0; // safety if currentView gets out of sync
mae's avatar
mae committed
752
    setCurrentView(0);
mae's avatar
mae committed
753

754
755
756
757
758
759
760
761
    if (editor && (flags & ActivateInPlace)) {
        SplitterOrView *place = m_d->m_splitter->findView(editor);
        if (place && !place->isSplitter()) {
            splitterOrView = place;
        }
    }


mae's avatar
mae committed
762
763
    if (!splitterOrView)
        splitterOrView = m_d->m_splitter->findEmptyView();
mae's avatar
mae committed
764

765
    if (!splitterOrView && m_d->m_currentEditor) {
mae's avatar
mae committed
766
        splitterOrView = m_d->m_splitter->findView(m_d->m_currentEditor);
767
768
769
        if (splitterOrView && !splitterOrView->isVisible()) // safety if currentEditor gets out of sync
            splitterOrView = 0;
    }
mae's avatar
mae committed
770
771
772
773
774
775
776
777
778
779
780
781
782

    if (!splitterOrView)
        splitterOrView = m_d->m_splitter->findFirstView();
    if (!splitterOrView) {
        splitterOrView = m_d->m_splitter;
    }

    activateEditor(splitterOrView->view(), editor, flags);
}

Core::IEditor *EditorManager::placeEditor(Core::Internal::EditorView *view, Core::IEditor *editor)
{
    Q_ASSERT(view && editor)            ;
mae's avatar
mae committed
783
    if (!view->hasEditor(editor)) {
mae's avatar
mae committed
784
785
786
787
        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
788
789
790
791
792
793
794
795
                view->addEditor(editor);
                view->setCurrentEditor(editor);
                if (!sourceView->editor()) {
                    if (IEditor *replacement = pickUnusedEditor()) {
                        sourceView->view()->addEditor(replacement);
                    }
                }
                return editor;
mae's avatar
mae committed
796
797
798
799
800
            } else if (duplicateSupported) {
                editor = duplicateEditor(editor);
                Q_ASSERT(editor);
            }
        }
mae's avatar
mae committed
801
802
        view->addEditor(editor);
    }
mae's avatar
mae committed
803
804
    return editor;
}
mae's avatar
mae committed
805

mae's avatar
mae committed
806
807
808
void EditorManager::activateEditor(Core::Internal::EditorView *view, Core::IEditor *editor, OpenEditorFlags flags)
{
    Q_ASSERT(view)            ;
mae's avatar
mae committed
809

mae's avatar
mae committed
810
811
812
    if (!editor && !m_d->m_currentEditor) {
        setCurrentEditor(0, (flags & IgnoreNavigationHistory));
        return;
mae's avatar
mae committed
813
814
    }

mae's avatar
mae committed
815
    bool hasCurrent = (view->currentEditor() != 0);
816

mae's avatar
mae committed
817
818
    editor = placeEditor(view, editor);
    if (!(flags & NoActivate) || !hasCurrent)
mae's avatar
mae committed
819
        view->setCurrentEditor(editor);
mae's avatar
mae committed
820
821
822
823
824

    if (!(flags & NoActivate)) {
        setCurrentEditor(editor, (flags & IgnoreNavigationHistory));
        ensureEditorManagerVisible();
        editor->widget()->setFocus();
mae's avatar
mae committed
825
826
827
    }
}

con's avatar
con committed
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
/* Find editors for a mimetype, best matching at the front
 * of the list. Recurse over the parent classes of the mimetype to
 * find them. */
static void mimeTypeFactoryRecursion(const MimeDatabase *db,
                                     const MimeType &mimeType,
                                     const QList<IEditorFactory*> &allFactories,
                                     bool firstMatchOnly,
                                     QList<IEditorFactory*> *list)
{
    typedef QList<IEditorFactory*> EditorFactoryList;
    // Loop factories to find type
    const QString type = mimeType.type();
    const EditorFactoryList::const_iterator fcend = allFactories.constEnd();
    for (EditorFactoryList::const_iterator fit = allFactories.constBegin(); fit != fcend; ++fit) {
        // Exclude duplicates when recursing over xml or C++ -> C -> text.
        IEditorFactory *factory = *fit;
        if (!list->contains(factory) && factory->mimeTypes().contains(type)) {
            list->push_back(*fit);
            if (firstMatchOnly)
                return;
            break;
        }
    }
    // Any parent classes? -> recurse
    QStringList parentTypes = mimeType.subClassesOf();
    if (parentTypes.empty())
        return;
    const QStringList::const_iterator pcend = parentTypes .constEnd();
    for (QStringList::const_iterator pit = parentTypes .constBegin(); pit != pcend; ++pit) {
        if (const MimeType parent = db->findByType(*pit))
            mimeTypeFactoryRecursion(db, parent, allFactories, firstMatchOnly, list);
    }
}

EditorManager::EditorFactoryList
    EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const
{
    EditorFactoryList rc;
866
    const EditorFactoryList allFactories = pluginManager()->getObjects<IEditorFactory>();
con's avatar
con committed
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
    mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allFactories, bestMatchOnly, &rc);
    if (debugEditorManager)
        qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc;
    return rc;
}

IEditor *EditorManager::createEditor(const QString &editorKind,
                                     const QString &fileName)
{
    typedef QList<IEditorFactory*> FactoryList;
    if (debugEditorManager)
        qDebug() << Q_FUNC_INFO << editorKind << fileName;


    EditorFactoryList factories;
    if (editorKind.isEmpty()) {
        // Find by mime type
        const MimeType mimeType = m_d->m_core->mimeDatabase()->findByFile(QFileInfo(fileName));
        if (!mimeType) {
            qWarning("%s unable to determine mime type of %s/%s.",
                     Q_FUNC_INFO, fileName.toUtf8().constData(), editorKind.toUtf8().constData());
            return 0;
        }
        factories = editorFactories(mimeType, true);
    } else {
        // Find by editor kind
893
        const EditorFactoryList allFactories = pluginManager()->getObjects<IEditorFactory>();
con's avatar
con committed
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
        const EditorFactoryList::const_iterator acend = allFactories.constEnd();
        for (EditorFactoryList::const_iterator ait = allFactories.constBegin(); ait != acend; ++ait) {
            if (editorKind == (*ait)->kind()) {
                factories.push_back(*ait);
                break;
            }
        }
    }
    if (factories.empty()) {
        qWarning("%s: unable to find an editor factory for the file '%s', editor kind '%s'.",
                 Q_FUNC_INFO, fileName.toUtf8().constData(), editorKind.toUtf8().constData());
        return 0;
    }

    IEditor *editor = factories.front()->createEditor(this);
    if (editor)
        connect(editor, SIGNAL(changed()), this, SLOT(updateActions()));
    if (editor)
        emit editorCreated(editor, fileName);
    return editor;
}

mae's avatar
mae committed
916
void EditorManager::addEditor(IEditor *editor, bool isDuplicate)
con's avatar
con committed
917
918
919
920
{
    if (!editor)
        return;
    m_d->m_core->addContextObject(editor);
mae's avatar
mae committed
921

mae's avatar
mae committed
922
    m_d->m_editorModel->addEditor(editor, isDuplicate);
mae's avatar
mae committed
923
924
925
926
927
928
929
930
    if (!isDuplicate) {
        m_d->m_core->fileManager()->addFile(editor->file());
        m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName());
    }

    m_d->m_editorHistory.removeAll(editor);
    m_d->m_editorHistory.prepend(editor);

con's avatar
con committed
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
    emit editorOpened(editor);
}

// Run the OpenWithDialog and return the editor kind
// selected by the user.
QString EditorManager::getOpenWithEditorKind(const QString &fileName) const
{
    QStringList editorKinds;
    // Collect editors that can open the file
    if (const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName)) {
        const EditorFactoryList editors = editorFactories(mt, false);
        const int size = editors.size();
        for (int i = 0; i < size; i++) {
            editorKinds.push_back(editors.at(i)->kind());
        }
    }
    if (editorKinds.empty())
        return QString();

    // Run dialog.
    OpenWithDialog dialog(fileName, m_d->m_core->mainWindow());
    dialog.setEditors(editorKinds);
    dialog.setCurrentEditor(0);
    if (dialog.exec() != QDialog::Accepted)
        return QString();
    return dialog.editor();
}

static QString formatFileFilters(const Core::ICore *core, QString *selectedFilter)
{
    QString rc;
    // Compile list of filter strings. If we find a glob  matching all files,
    // put it last and set it as default selectedFilter.
    QStringList filters = core->mimeDatabase()->filterStrings();
    filters.sort();
    selectedFilter->clear();
    if (filters.empty())
        return rc;
    const QString filterSeparator = QLatin1String(";;");
    bool hasAllFilter = false;
    const int size = filters.size();
    for (int i = 0; i < size; i++) {
        const QString &filterString = filters.at(i);
        if (filterString.isEmpty()) { // binary editor
            hasAllFilter = true;
        } else {
            if (!rc.isEmpty())
                rc += filterSeparator;
            rc += filterString;
        }
    }
    if (hasAllFilter) {
        // prepend all files filter
        // prepending instead of appending to work around a but in Qt/Mac
        QString allFilesFilter = QLatin1String("All Files (*)");
        if (!rc.isEmpty())
            allFilesFilter += filterSeparator;
        rc.prepend(allFilesFilter);
        *selectedFilter = allFilesFilter;
    } else {
        *selectedFilter = filters.front();
    }
    return rc;
}

IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorKind,
mae's avatar
mae committed
997
                                   EditorManager::OpenEditorFlags flags)
con's avatar
con committed
998
999
1000
{
    if (debugEditorManager)
        qDebug() << Q_FUNC_INFO << fileName << editorKind;