qmldesignerplugin.cpp 15.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
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
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
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
****************************************************************************/
29

30
#include "exception.h"
31
32
33
#include "qmldesignerplugin.h"
#include "qmldesignerconstants.h"
#include "pluginmanager.h"
Lasse Holmstedt's avatar
Lasse Holmstedt committed
34
#include "designmodewidget.h"
35
#include "settingspage.h"
Lasse Holmstedt's avatar
Lasse Holmstedt committed
36
#include "designmodecontext.h"
37

Lasse Holmstedt's avatar
Lasse Holmstedt committed
38
39
#include <qmljseditor/qmljseditorconstants.h>

40
#include <coreplugin/actionmanager/actioncontainer.h>
41
#include <coreplugin/actionmanager/actionmanager.h>
Lasse Holmstedt's avatar
Lasse Holmstedt committed
42
#include <coreplugin/actionmanager/command.h>
43
#include <coreplugin/id.h>
44
45
#include <coreplugin/coreconstants.h>
#include <coreplugin/designmode.h>
46
47
#include <coreplugin/dialogs/iwizard.h>
#include <coreplugin/editormanager/editormanager.h>
48
#include <coreplugin/editormanager/ieditorfactory.h>
Lasse Holmstedt's avatar
Lasse Holmstedt committed
49
#include <coreplugin/editormanager/openeditorsmodel.h>
50
51
52
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
Lasse Holmstedt's avatar
Lasse Holmstedt committed
53
#include <coreplugin/modemanager.h>
54

Marco Bubke's avatar
Marco Bubke committed
55
56
#include <projectexplorer/projectexplorerconstants.h>

Lasse Holmstedt's avatar
Lasse Holmstedt committed
57
58
#include <extensionsystem/pluginmanager.h>

59
#include <utils/hostosinfo.h>
60
61
#include <utils/qtcassert.h>

62
#include <QAction>
Lasse Holmstedt's avatar
Lasse Holmstedt committed
63

64
65
66
67
68
#include <QFileInfo>
#include <QCoreApplication>
#include <qplugin.h>
#include <QDebug>
#include <QProcessEnvironment>
69
70
71

namespace QmlDesigner {

Marco Bubke's avatar
Marco Bubke committed
72
73
74
75
76
77
78
79
80
81
82
QmlDesignerPlugin *QmlDesignerPlugin::m_instance = 0;

static bool isQmlFile(Core::IEditor *editor)
{
    return editor && editor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID;
}

static bool isInDesignerMode()
{
    return Core::ModeManager::currentMode() == Core::DesignMode::instance();
}
83

84
85
86
87
88
89
bool shouldAssertInException()
{
    QProcessEnvironment processEnvironment = QProcessEnvironment::systemEnvironment();
    return !processEnvironment.value("QMLDESIGNER_ASSERT_ON_EXCEPTION").isEmpty();
}

Marco Bubke's avatar
Marco Bubke committed
90
91
QmlDesignerPlugin::QmlDesignerPlugin() :
    m_isActive(false)
92
{
93

94
95
96
97
98
99
100
101
102
    // Exceptions should never ever assert: they are handled in a number of
    // places where it is actually VALID AND EXPECTED BEHAVIOUR to get an
    // exception.
    // If you still want to see exactly where the exception originally
    // occurred, then you have various ways to do this:
    //  1. set a breakpoint on the constructor of the exception
    //  2. in gdb: "catch throw" or "catch throw Exception"
    //  3. set a breakpoint on __raise_exception()
    // And with gdb, you can even do this from your ~/.gdbinit file.
103
104
105
    // DnD is not working with gdb so this is still needed to get a good stacktrace

    Exception::setShouldAssert(shouldAssertInException());
106
107
}

Marco Bubke's avatar
Marco Bubke committed
108
QmlDesignerPlugin::~QmlDesignerPlugin()
109
{
hjk's avatar
hjk committed
110
    Core::ICore::removeContextObject(m_context);
Marco Bubke's avatar
Marco Bubke committed
111
112
    m_context = 0;
    m_instance = 0;
113
114
115
116
117
118
119
}

////////////////////////////////////////////////////
//
// INHERITED FROM ExtensionSystem::Plugin
//
////////////////////////////////////////////////////
Marco Bubke's avatar
Marco Bubke committed
120
bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage/* = 0*/) // =0;
121
{
122
    const Core::Context switchContext(QmlDesigner::Constants::C_QMLDESIGNER,
123
        QmlJSEditor::Constants::C_QMLJSEDITOR_ID);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
124
125

    QAction *switchAction = new QAction(tr("Switch Text/Design"), this);
Eike Ziller's avatar
Eike Ziller committed
126
127
    Core::Command *command = Core::ActionManager::registerAction(
                switchAction, QmlDesigner::Constants::SWITCH_TEXT_DESIGN, switchContext);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
128
129
    command->setDefaultKeySequence(QKeySequence(Qt::Key_F4));

Marco Bubke's avatar
Marco Bubke committed
130
    m_instance = this;
131

132
133
134
135
    const QString pluginPath = Utils::HostOsInfo::isMacHost()
            ? QString(QCoreApplication::applicationDirPath() + "/../PlugIns/QmlDesigner")
            : QString(QCoreApplication::applicationDirPath() + "/../"
                      + QLatin1String(IDE_LIBRARY_BASENAME) + "/qtcreator/qmldesigner");
136
    m_pluginManager.setPluginPaths(QStringList() << pluginPath);
137

Lasse Holmstedt's avatar
Lasse Holmstedt committed
138
    createDesignModeWidget();
139
    connect(switchAction, SIGNAL(triggered()), this, SLOT(switchTextDesign()));
Lasse Holmstedt's avatar
Lasse Holmstedt committed
140

Marco Bubke's avatar
Marco Bubke committed
141
    addAutoReleasedObject(new Internal::SettingsPage);
142

143

hjk's avatar
hjk committed
144
    m_settings.fromSettings(Core::ICore::settings());
145

hjk's avatar
hjk committed
146
    errorMessage->clear();
147
148
149
150

    return true;
}

Marco Bubke's avatar
Marco Bubke committed
151
void QmlDesignerPlugin::createDesignModeWidget()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
152
{
Marco Bubke's avatar
Marco Bubke committed
153
    m_mainWidget = new Internal::DesignModeWidget;
Lasse Holmstedt's avatar
Lasse Holmstedt committed
154

Marco Bubke's avatar
Marco Bubke committed
155
    m_context = new Internal::DesignModeContext(m_mainWidget);
hjk's avatar
hjk committed
156
    Core::ICore::addContextObject(m_context);
157
158
    Core::Context qmlDesignerMainContext(Constants::C_QMLDESIGNER);
    Core::Context qmlDesignerFormEditorContext(Constants::C_QMLFORMEDITOR);
159
    Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
160

Marco Bubke's avatar
Marco Bubke committed
161
162
163
164
165
    m_context->context().add(qmlDesignerMainContext);
    m_context->context().add(qmlDesignerFormEditorContext);
    m_context->context().add(qmlDesignerNavigatorContext);
    m_context->context().add(ProjectExplorer::Constants::LANG_QMLJS);

166
    m_shortCutManager.registerActions(qmlDesignerMainContext, qmlDesignerFormEditorContext, qmlDesignerNavigatorContext);
Marco Bubke's avatar
Marco Bubke committed
167
168
169
170
171

    connect(Core::ICore::editorManager(),
            SIGNAL(currentEditorChanged(Core::IEditor*)),
            this,
            SLOT(onCurrentEditorChanged(Core::IEditor*)));
Lasse Holmstedt's avatar
Lasse Holmstedt committed
172

Marco Bubke's avatar
Marco Bubke committed
173
174
175
176
    connect(Core::ICore::editorManager(),
            SIGNAL(editorsClosed(QList<Core::IEditor*>)),
            this,
            SLOT(onTextEditorsClosed(QList<Core::IEditor*>)));
Lasse Holmstedt's avatar
Lasse Holmstedt committed
177

Marco Bubke's avatar
Marco Bubke committed
178
179
//    connect(Core::ICore::editorManager(), SIGNAL(currentEditorChanged(Core::IEditor*)),
//            &m_documentManager, SLOT(currentTextEditorChanged(Core::IEditor*)));
Lasse Holmstedt's avatar
Lasse Holmstedt committed
180

Marco Bubke's avatar
Marco Bubke committed
181
182
183
184
185
186
//    connect(Core::ICore::instance(), SIGNAL(contextChanged(Core::IContext*,Core::Context)),
//            this, SLOT(contextChanged(Core::IContext*,Core::Context)));

    connect(Core::ModeManager::instance(),
            SIGNAL(currentModeChanged(Core::IMode*,Core::IMode*)),
            SLOT(onCurrentModeChanged(Core::IMode*,Core::IMode*)));
187

Lasse Holmstedt's avatar
Lasse Holmstedt committed
188
189
}

Marco Bubke's avatar
Marco Bubke committed
190
void QmlDesignerPlugin::showDesigner()
191
{
Marco Bubke's avatar
Marco Bubke committed
192
193
194
195
196
197
198
199
200
201
202
203
    Q_ASSERT(!m_documentManager.hasCurrentDesignDocument());

    m_shortCutManager.disconnectUndoActions(currentDesignDocument());

    m_documentManager.setCurrentDesignDocument(Core::EditorManager::currentEditor());

    m_shortCutManager.connectUndoActions(currentDesignDocument());

    m_mainWidget->initialize();

    if (m_documentManager.hasCurrentDesignDocument()) {
        activateAutoSynchronization();
204
        m_shortCutManager.updateActions(currentDesignDocument()->textEditor());
Marco Bubke's avatar
Marco Bubke committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
        m_viewManager.pushFileOnCrambleBar(m_documentManager.currentDesignDocument()->fileName());
    }

    m_shortCutManager.updateUndoActions(currentDesignDocument());
}

void QmlDesignerPlugin::hideDesigner()
{
    if (currentDesignDocument()->currentModel()
            && !currentDesignDocument()->hasQmlSyntaxErrors())
        jumpTextCursorToSelectedModelNode();


    if (m_documentManager.hasCurrentDesignDocument()) {
        deactivateAutoSynchronization();
        m_mainWidget->saveSettings();
221
    }
Marco Bubke's avatar
Marco Bubke committed
222
223
224
225
226
227

    m_shortCutManager.disconnectUndoActions(currentDesignDocument());

    m_documentManager.setCurrentDesignDocument(0);

    m_shortCutManager.updateUndoActions(0);
228
229
}

Marco Bubke's avatar
Marco Bubke committed
230
void QmlDesignerPlugin::changeEditor()
231
{
Marco Bubke's avatar
Marco Bubke committed
232
233
234
235
236
237
238
239
240
241
242
243
    if (m_documentManager.hasCurrentDesignDocument()) {
        deactivateAutoSynchronization();
        m_mainWidget->saveSettings();
    }

    m_shortCutManager.disconnectUndoActions(currentDesignDocument());

    m_documentManager.setCurrentDesignDocument(Core::EditorManager::currentEditor());

    m_mainWidget->initialize();

    m_shortCutManager.connectUndoActions(currentDesignDocument());
244

Marco Bubke's avatar
Marco Bubke committed
245
246
    if (m_documentManager.hasCurrentDesignDocument()) {
        activateAutoSynchronization();
Thomas Hartmann's avatar
Thomas Hartmann committed
247
        m_viewManager.pushFileOnCrambleBar(m_documentManager.currentDesignDocument()->fileName());
Marco Bubke's avatar
Marco Bubke committed
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
    }

    m_shortCutManager.updateUndoActions(currentDesignDocument());
}

void QmlDesignerPlugin::jumpTextCursorToSelectedModelNode()
{
    // visual editor -> text editor
    ModelNode selectedNode;
    if (!currentDesignDocument()->rewriterView()->selectedModelNodes().isEmpty())
        selectedNode = currentDesignDocument()->rewriterView()->selectedModelNodes().first();

    if (selectedNode.isValid()) {
        const int nodeOffset = currentDesignDocument()->rewriterView()->nodeOffset(selectedNode);
        if (nodeOffset > 0) {
            const ModelNode currentSelectedNode
                    = currentDesignDocument()->rewriterView()->nodeAtTextCursorPosition(currentDesignDocument()->plainTextEdit()->textCursor().position());
            if (currentSelectedNode != selectedNode) {
                int line, column;
                currentDesignDocument()->textEditor()->convertPosition(nodeOffset, &line, &column);
                currentDesignDocument()->textEditor()->gotoLine(line, column);
            }
270
271
        }
    }
Marco Bubke's avatar
Marco Bubke committed
272
}
273

Marco Bubke's avatar
Marco Bubke committed
274
275
276
277
278
279
void QmlDesignerPlugin::selectModelNodeUnderTextCursor()
{
    const int cursorPos = currentDesignDocument()->plainTextEdit()->textCursor().position();
    ModelNode node = currentDesignDocument()->rewriterView()->nodeAtTextCursorPosition(cursorPos);
    if (currentDesignDocument()->rewriterView() && node.isValid()) {
        currentDesignDocument()->rewriterView()->setSelectedModelNodes(QList<ModelNode>() << node);
280
    }
281
282
}

Marco Bubke's avatar
Marco Bubke committed
283
void QmlDesignerPlugin::activateAutoSynchronization()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
284
{
Marco Bubke's avatar
Marco Bubke committed
285
286
287
288
289
    // text editor -> visual editor
    if (!currentDesignDocument()->isDocumentLoaded()) {
        currentDesignDocument()->loadDocument(currentDesignDocument()->plainTextEdit());
    }

290
291
    currentDesignDocument()->activateDocumentModel();

Marco Bubke's avatar
Marco Bubke committed
292
293
    resetModelSelection();

294
295
    viewManager().attachComponentView();
    viewManager().attachViewsExceptRewriterAndComponetView();
Marco Bubke's avatar
Marco Bubke committed
296
297
298
299
300
301
302
303
304
305

    QList<RewriterView::Error> errors = currentDesignDocument()->qmlSyntaxErrors();
    if (errors.isEmpty()) {
        selectModelNodeUnderTextCursor();
        m_mainWidget->enableWidgets();
    } else {
        m_mainWidget->disableWidgets();
        m_mainWidget->showErrorMessage(errors);
    }

Thomas Hartmann's avatar
Thomas Hartmann committed
306
    currentDesignDocument()->updateSubcomponentManager();
Marco Bubke's avatar
Marco Bubke committed
307
308
309
310
311

    connect(currentDesignDocument()->rewriterView(),
            SIGNAL(errorsChanged(QList<RewriterView::Error>)),
            m_mainWidget,
            SLOT(updateErrorStatus(QList<RewriterView::Error>)));
Lasse Holmstedt's avatar
Lasse Holmstedt committed
312
313
}

Marco Bubke's avatar
Marco Bubke committed
314
void QmlDesignerPlugin::deactivateAutoSynchronization()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
315
{
Marco Bubke's avatar
Marco Bubke committed
316
317
    viewManager().detachViewsExceptRewriterAndComponetView();
    viewManager().detachComponentView();
318
319
    viewManager().detachRewriterView();
    documentManager().currentDesignDocument()->resetToDocumentModel();
Marco Bubke's avatar
Marco Bubke committed
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343

    disconnect(currentDesignDocument()->rewriterView(),
               SIGNAL(errorsChanged(QList<RewriterView::Error>)),
               m_mainWidget,
               SLOT(updateErrorStatus(QList<RewriterView::Error>)));

}

void QmlDesignerPlugin::resetModelSelection()
{
    if (currentDesignDocument()->rewriterView() && currentDesignDocument()->currentModel())
        currentDesignDocument()->rewriterView()->setSelectedModelNodes(QList<ModelNode>());
}



void QmlDesignerPlugin::onCurrentEditorChanged(Core::IEditor *editor)
{
    if (editor
            && editor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID
            && isInDesignerMode())
    {
        m_shortCutManager.updateActions(editor);
        changeEditor();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
344
    }
Marco Bubke's avatar
Marco Bubke committed
345
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
346

Marco Bubke's avatar
Marco Bubke committed
347
348
349
static bool isDesignerMode(Core::IMode *mode)
{
    return mode == Core::DesignMode::instance();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
350
351
}

Marco Bubke's avatar
Marco Bubke committed
352
void QmlDesignerPlugin::onCurrentModeChanged(Core::IMode *newMode, Core::IMode *oldMode)
353
{
Thomas Hartmann's avatar
Thomas Hartmann committed
354
355
356
    if (!Core::EditorManager::currentEditor())
        return;

357
358
359
360
    if (Core::EditorManager::currentEditor()
            && Core::EditorManager::currentEditor()->id() != QmlJSEditor::Constants::C_QMLJSEDITOR_ID)
        return;

Marco Bubke's avatar
Marco Bubke committed
361
362
363
364
365
366
367
368
369
370
371
372
373
    if ((currentDesignDocument()
         && Core::EditorManager::currentEditor() == currentDesignDocument()->editor())
            && isDesignerMode(newMode))
        return;

    if (!isDesignerMode(newMode) && isDesignerMode(oldMode))
        hideDesigner();
    else  if (Core::EditorManager::currentEditor()
              && isDesignerMode(newMode)
              && isQmlFile(Core::EditorManager::currentEditor()))
        showDesigner();
    else if (currentDesignDocument())
        hideDesigner();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
374

Marco Bubke's avatar
Marco Bubke committed
375
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
376

Marco Bubke's avatar
Marco Bubke committed
377
378
379
DesignDocument *QmlDesignerPlugin::currentDesignDocument() const
{
    return m_documentManager.currentDesignDocument();
380
381
}

Marco Bubke's avatar
Marco Bubke committed
382
Internal::DesignModeWidget *QmlDesignerPlugin::mainWidget() const
383
{
Marco Bubke's avatar
Marco Bubke committed
384
    return m_mainWidget;
385
386
}

Marco Bubke's avatar
Marco Bubke committed
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
void QmlDesignerPlugin::onTextEditorsClosed(QList<Core::IEditor*> editors)
{
    if (m_documentManager.hasCurrentDesignDocument()
            && editors.contains(m_documentManager.currentDesignDocument()->textEditor()))
        hideDesigner();

    m_documentManager.removeEditors(editors);
}

void QmlDesignerPlugin::extensionsInitialized()
{
    QStringList mimeTypes;
    mimeTypes.append("application/x-qml");

    Core::DesignMode::instance()->registerDesignWidget(m_mainWidget, mimeTypes, m_context->context());
    connect(Core::DesignMode::instance(),
            SIGNAL(actionsUpdated(Core::IEditor*)),
            &m_shortCutManager,
            SLOT(updateActions(Core::IEditor*)));
}

QmlDesignerPlugin *QmlDesignerPlugin::instance()
{
    return m_instance;
}

DocumentManager &QmlDesignerPlugin::documentManager()
{
    return m_documentManager;
}

const DocumentManager &QmlDesignerPlugin::documentManager() const
{
    return m_documentManager;
}

ViewManager &QmlDesignerPlugin::viewManager()
{
    return m_viewManager;
}

const ViewManager &QmlDesignerPlugin::viewManager() const
{
    return m_viewManager;
}

void QmlDesignerPlugin::switchTextDesign()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
434
{
435
    if (Core::ModeManager::currentMode()->id() == Core::Constants::MODE_EDIT) {
hjk's avatar
hjk committed
436
        Core::IEditor *editor = Core::EditorManager::currentEditor();
Marco Bubke's avatar
Marco Bubke committed
437
        if (editor->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID)
438
439
440
            Core::ModeManager::activateMode(Core::Constants::MODE_DESIGN);
    } else if (Core::ModeManager::currentMode()->id() == Core::Constants::MODE_DESIGN) {
        Core::ModeManager::activateMode(Core::Constants::MODE_EDIT);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
441
442
443
    }
}

Marco Bubke's avatar
Marco Bubke committed
444
DesignerSettings QmlDesignerPlugin::settings()
445
{
446
    m_settings.fromSettings(Core::ICore::settings());
447
448
449
    return m_settings;
}

Marco Bubke's avatar
Marco Bubke committed
450
void QmlDesignerPlugin::setSettings(const DesignerSettings &s)
451
452
453
{
    if (s != m_settings) {
        m_settings = s;
454
        m_settings.toSettings(Core::ICore::settings());
455
456
457
    }
}

458
459
}

Marco Bubke's avatar
Marco Bubke committed
460
Q_EXPORT_PLUGIN(QmlDesigner::QmlDesignerPlugin)