formeditorw.cpp 34.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 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
31
#include "formeditorw.h"
#include "formwindoweditor.h"
32
#include "formwindowfile.h"
con's avatar
con committed
33
34
35
#include "settingsmanager.h"
#include "settingspage.h"
#include "editorwidget.h"
36
#include "editordata.h"
37
#include "qtcreatorintegration.h"
Lasse Holmstedt's avatar
Lasse Holmstedt committed
38
#include "designercontext.h"
39
#include <widgethost.h>
con's avatar
con committed
40

41
#include <coreplugin/editortoolbar.h>
Lasse Holmstedt's avatar
Lasse Holmstedt committed
42
#include <coreplugin/designmode.h>
con's avatar
con committed
43
#include <coreplugin/coreconstants.h>
44
#include <coreplugin/icore.h>
45
#include <coreplugin/infobar.h>
kh1's avatar
kh1 committed
46
#include <coreplugin/helpmanager.h>
47
#include <coreplugin/actionmanager/actionmanager.h>
48
#include <coreplugin/actionmanager/actioncontainer.h>
49
#include <coreplugin/modemanager.h>
50
#include <coreplugin/minisplitter.h>
51
#include <coreplugin/mimedatabase.h>
52
#include <coreplugin/outputpane.h>
hjk's avatar
hjk committed
53
#include <utils/qtcassert.h>
con's avatar
con committed
54

55
56
57
#include <QDesignerFormEditorPluginInterface>
#include <QDesignerFormEditorInterface>
#include <QDesignerComponents>
58
#include <QDesignerFormWindowManagerInterface>
59
60
61
62
#include <QDesignerWidgetBoxInterface>
#include <abstractobjectinspector.h>
#include <QDesignerPropertyEditorInterface>
#include <QDesignerActionEditorInterface>
con's avatar
con committed
63

64
65
66
67
68
69
70
71
72
73
74
75
76
#include <QAction>
#include <QActionGroup>
#include <QApplication>
#include <QCursor>
#include <QDockWidget>
#include <QMessageBox>
#include <QKeySequence>
#include <QPrintDialog>
#include <QPrinter>
#include <QPainter>
#include <QStyle>
#include <QToolBar>
#include <QVBoxLayout>
con's avatar
con committed
77

78
79
80
81
82
#include <QDebug>
#include <QSettings>
#include <QSignalMapper>
#include <QPluginLoader>
#include <QTime>
con's avatar
con committed
83

84
static const char settingsGroupC[] = "Designer";
con's avatar
con committed
85

86
87
/* Actions of the designer plugin:
 * Designer provides a toolbar which is subject to a context change (to
Tobias Hunger's avatar
Tobias Hunger committed
88
 * "edit mode" context) when it is focused.
89
90
91
92
93
 * In order to prevent its actions from being disabled/hidden by that context
 * change, the actions are registered on the global context. In currentEditorChanged(),
 * the ones that are present in the global edit menu are set visible/invisible manually.
 * The designer context is currently used for Cut/Copy/Paste, etc. */

con's avatar
con committed
94
95
static inline QIcon designerIcon(const QString &iconName)
{
96
    const QIcon icon = QDesignerFormEditorInterface::createIcon(iconName);
con's avatar
con committed
97
98
99
100
101
    if (icon.isNull())
        qWarning() << "Unable to locate " << iconName;
    return icon;
}

hjk's avatar
hjk committed
102
using namespace Core;
con's avatar
con committed
103
104
using namespace Designer::Constants;

105
106
namespace Designer {
namespace Internal {
con's avatar
con committed
107

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* A stub-like, read-only text editor which displays UI files as text. Could be used as a
  * read/write editor too, but due to lack of XML editor, highlighting and other such
  * functionality, editing is disabled.
  * Provides an informational title bar containing a button triggering a
  * switch to design mode.
  * Internally manages a FormWindowEditor and uses the plain text
  * editable embedded in it.  */
class DesignerXmlEditorWidget : public TextEditor::BaseTextEditorWidget
{
public:
    DesignerXmlEditorWidget() {}

    void finalizeInitialization()
    {
        setupAsPlainEditor();
        setReadOnly(true);
    }
};

class FormWindowEditorFactory : public TextEditor::BaseTextEditorFactory
{
public:
    FormWindowEditorFactory()
    {
        setId(Designer::Constants::K_DESIGNER_XML_EDITOR_ID);
        setEditorCreator([]() { return new FormWindowEditor; });
        setEditorWidgetCreator([]() { return new Internal::DesignerXmlEditorWidget; });
        setDuplicatedSupported(false);
    }

138
139
140
141
142
    FormWindowEditor *create(QDesignerFormWindowInterface *form)
    {
        setDocumentCreator([form]() { return new FormWindowFile(form); });
        return qobject_cast<FormWindowEditor *>(createEditor());
    }
143
144
};

con's avatar
con committed
145
146
// --------- FormEditorW

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
class FormEditorData
{
public:
    Q_DECLARE_TR_FUNCTIONS(FormEditorW)

public:
    FormEditorData();
    ~FormEditorData();

    void activateEditMode(int id);
    void toolChanged(int);
    void print();
    void setPreviewMenuEnabled(bool e);
    void updateShortcut(QObject *command);

    void fullInit();

    void saveSettings(QSettings *s);

    void initDesignerSubWindows();

    void setupActions();
    void setupViewActions();
    void addDockViewAction(Core::ActionContainer *viewMenu,
                           int index,
                           const Core::Context &context,
                           const QString &title, Core::Id id);

    Core::ActionContainer *createPreviewStyleMenu(QActionGroup *actionGroup);

    void critical(const QString &errorMessage);
    void bindShortcut(Core::Command *command, QAction *action);
    QAction *createEditModeAction(QActionGroup *ag,
                                  const Core::Context &context,
                                  Core::ActionContainer *medit,
                                  const QString &actionName,
                                  Core::Id id,
                                  int toolNumber,
                                  const QString &iconName = QString(),
                                  const QString &keySequence = QString());
    Core::Command *addToolAction(QAction *a,
                                 const Core::Context &context, Core::Id id,
                                 Core::ActionContainer *c1, const QString &keySequence = QString(),
                                 Core::Id groupId = Core::Id());
    QToolBar *createEditorToolBar() const;
    Core::IEditor *createEditor();
con's avatar
con committed
193

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
public:
    QDesignerFormEditorInterface *m_formeditor;
    QDesignerIntegrationInterface *m_integration;
    QDesignerFormWindowManagerInterface *m_fwm;
    FormEditorW::InitializationStage m_initStage;

    QWidget *m_designerSubWindows[Designer::Constants::DesignerSubWindowCount];

    QAction *m_lockAction;
    QAction *m_resetLayoutAction;

    QList<Core::IOptionsPage *> m_settingsPages;
    QActionGroup *m_actionGroupEditMode;
    QAction *m_actionPrint;
    QAction *m_actionPreview;
    QActionGroup *m_actionGroupPreviewInStyle;
    QMenu *m_previewInStyleMenu;
    QAction *m_actionAboutPlugins;
    QSignalMapper m_shortcutMapper;

    DesignerContext *m_context;
    Core::Context m_contexts;

    QList<Core::Id> m_toolActionIds;
    QWidget *m_modeWidget;
    EditorWidget *m_editorWidget;
    Core::DesignMode *m_designMode;

    QWidget *m_editorToolBar;
    Core::EditorToolBar *m_toolBar;

    QMap<Core::Command *, QAction *> m_commandToDesignerAction;
226
    FormWindowEditorFactory *m_xmlEditorFactory;
227
228
229
230
231
232
};

static FormEditorData *d = 0;
static FormEditorW *m_instance = 0;

FormEditorData::FormEditorData() :
con's avatar
con committed
233
234
235
    m_formeditor(QDesignerComponents::createFormEditor(0)),
    m_integration(0),
    m_fwm(0),
236
    m_initStage(FormEditorW::RegisterPlugins),
con's avatar
con committed
237
    m_actionGroupEditMode(0),
238
239
240
    m_actionPrint(0),
    m_actionPreview(0),
    m_actionGroupPreviewInStyle(0),
241
    m_previewInStyleMenu(0),
242
    m_actionAboutPlugins(0),
Lasse Holmstedt's avatar
Lasse Holmstedt committed
243
    m_context(0),
244
245
    m_modeWidget(0),
    m_editorWidget(0),
Lasse Holmstedt's avatar
Lasse Holmstedt committed
246
247
    m_designMode(0),
    m_editorToolBar(0),
248
249
    m_toolBar(0),
    m_xmlEditorFactory(0)
con's avatar
con committed
250
{
251
    if (Designer::Constants::Internal::debug)
con's avatar
con committed
252
        qDebug() << Q_FUNC_INFO;
253
254
    QTC_ASSERT(!d, return);
    d = this;
con's avatar
con committed
255
256
257
258

    qFill(m_designerSubWindows, m_designerSubWindows + Designer::Constants::DesignerSubWindowCount,
          static_cast<QWidget *>(0));

hjk's avatar
hjk committed
259
    m_formeditor->setTopLevel(ICore::mainWindow());
con's avatar
con committed
260
261
    m_formeditor->setSettingsManager(new SettingsManager());

262
    m_fwm = m_formeditor->formWindowManager();
hjk's avatar
hjk committed
263
    QTC_ASSERT(m_fwm, return);
con's avatar
con committed
264

265
    m_contexts.add(Designer::Constants::C_FORMEDITOR);
266

con's avatar
con committed
267
268
269
270
271
272
273
    setupActions();

    foreach (QDesignerOptionsPageInterface *designerPage, m_formeditor->optionsPages()) {
        SettingsPage *settingsPage = new SettingsPage(designerPage);
        m_settingsPages.append(settingsPage);
    }

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
    QObject::connect(EditorManager::instance(), &EditorManager::currentEditorChanged, [this](IEditor *editor) {
        if (Designer::Constants::Internal::debug)
            qDebug() << Q_FUNC_INFO << editor << " of " << m_fwm->formWindowCount();

        if (editor && editor->document()->id() == Constants::K_DESIGNER_XML_EDITOR_ID) {
            FormWindowEditor *xmlEditor = qobject_cast<FormWindowEditor *>(editor);
            QTC_ASSERT(xmlEditor, return);
            FormEditorW::ensureInitStage(FormEditorW::FullyInitialized);
            SharedTools::WidgetHost *fw = m_editorWidget->formWindowEditorForXmlEditor(xmlEditor);
            QTC_ASSERT(fw, return);
            m_editorWidget->setVisibleEditor(xmlEditor);
            m_fwm->setActiveFormWindow(fw->formWindow());
        }
    });

    QObject::connect(&m_shortcutMapper, static_cast<void(QSignalMapper::*)(QObject *)>(&QSignalMapper::mapped),
        [this](QObject *ob) { updateShortcut(ob); });
291
292

    m_xmlEditorFactory = new FormWindowEditorFactory;
con's avatar
con committed
293
294
}

295
FormEditorData::~FormEditorData()
con's avatar
con committed
296
{
297
    if (m_context)
hjk's avatar
hjk committed
298
        ICore::removeContextObject(m_context);
299
    if (m_initStage == FormEditorW::FullyInitialized) {
hjk's avatar
hjk committed
300
        QSettings *s = ICore::settings();
301
302
303
        s->beginGroup(QLatin1String(settingsGroupC));
        m_editorWidget->saveSettings(s);
        s->endGroup();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
304

305
306
307
308
        m_designMode->unregisterDesignWidget(m_modeWidget);
        delete m_modeWidget;
        m_modeWidget = 0;
    }
con's avatar
con committed
309
310

    delete m_formeditor;
311
312
    qDeleteAll(m_settingsPages);
    m_settingsPages.clear();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
313
314
    delete m_integration;

315
    delete m_xmlEditorFactory ;
316
    d = 0;
317
318
}

319
// Add an actioon to toggle the view state of a dock window
320
void FormEditorData::addDockViewAction(ActionContainer *viewMenu,
hjk's avatar
hjk committed
321
                                    int index, const Context &context,
322
                                    const QString &title, Id id)
323
324
325
326
{
    if (const QDockWidget *dw = m_editorWidget->designerDockWidgets()[index]) {
        QAction *action = dw->toggleViewAction();
        action->setText(title);
hjk's avatar
hjk committed
327
328
        Command *cmd = addToolAction(action, context, id, viewMenu, QString());
        cmd->setAttribute(Command::CA_Hide);
329
330
331
    }
}

332
void FormEditorData::setupViewActions()
333
334
{
    // Populate "View" menu of form editor menu
hjk's avatar
hjk committed
335
    ActionContainer *viewMenu = ActionManager::actionContainer(Core::Constants::M_WINDOW_VIEWS);
336
    QTC_ASSERT(viewMenu, return);
337

Eike Ziller's avatar
Eike Ziller committed
338
    addDockViewAction(viewMenu, WidgetBoxSubWindow, m_contexts,
hjk's avatar
hjk committed
339
                      tr("Widget box"), "FormEditor.WidgetBox");
Lasse Holmstedt's avatar
Lasse Holmstedt committed
340

Eike Ziller's avatar
Eike Ziller committed
341
    addDockViewAction(viewMenu, ObjectInspectorSubWindow, m_contexts,
hjk's avatar
hjk committed
342
                      tr("Object Inspector"), "FormEditor.ObjectInspector");
Lasse Holmstedt's avatar
Lasse Holmstedt committed
343

Eike Ziller's avatar
Eike Ziller committed
344
    addDockViewAction(viewMenu, PropertyEditorSubWindow, m_contexts,
hjk's avatar
hjk committed
345
                      tr("Property Editor"), "FormEditor.PropertyEditor");
346

Eike Ziller's avatar
Eike Ziller committed
347
    addDockViewAction(viewMenu, SignalSlotEditorSubWindow, m_contexts,
hjk's avatar
hjk committed
348
                      tr("Signals && Slots Editor"), "FormEditor.SignalsAndSlotsEditor");
349

Eike Ziller's avatar
Eike Ziller committed
350
    addDockViewAction(viewMenu, ActionEditorSubWindow, m_contexts,
hjk's avatar
hjk committed
351
                      tr("Action Editor"), "FormEditor.ActionEditor");
352
353
    // Reset
    Command *cmd = addToolAction(m_editorWidget->menuSeparator(), m_contexts, "FormEditor.SeparatorReset", viewMenu);
hjk's avatar
hjk committed
354
    cmd->setAttribute(Command::CA_Hide);
355

hjk's avatar
hjk committed
356
    cmd = addToolAction(m_editorWidget->resetLayoutAction(), m_contexts, "FormEditor.ResetToDefaultLayout", viewMenu);
357
358
359
360

    QObject::connect(m_editorWidget, &EditorWidget::resetLayout,
                     m_editorWidget, &EditorWidget::resetToDefaultLayout);

hjk's avatar
hjk committed
361
    cmd->setAttribute(Command::CA_Hide);
con's avatar
con committed
362
363
}

364
void FormEditorData::fullInit()
con's avatar
con committed
365
{
366
    QTC_ASSERT(m_initStage == FormEditorW::RegisterPlugins, return);
con's avatar
con committed
367
    QTime *initTime = 0;
368
    if (Designer::Constants::Internal::debug) {
con's avatar
con committed
369
370
371
372
        initTime = new QTime;
        initTime->start();
    }

373
374
    QDesignerComponents::createTaskMenu(m_formeditor, m_instance);
    QDesignerComponents::initializePlugins(m_formeditor);
con's avatar
con committed
375
376
    QDesignerComponents::initializeResources();
    initDesignerSubWindows();
377
    m_integration = new QtCreatorIntegration(m_formeditor, m_instance);
con's avatar
con committed
378
    m_formeditor->setIntegration(m_integration);
379
    // Connect Qt Designer help request to HelpManager.
380
    QObject::connect(m_integration, SIGNAL(creatorHelpRequested(QUrl)),
381
        HelpManager::instance(), SLOT(handleHelpRequest(QUrl)));
con's avatar
con committed
382
383
384
385
386

    /**
     * This will initialize our TabOrder, Signals and slots and Buddy editors.
     */
    QList<QObject*> plugins = QPluginLoader::staticInstances();
387
    plugins += m_formeditor->pluginInstances();
con's avatar
con committed
388
389
390
391
392
393
394
    foreach (QObject *plugin, plugins) {
        if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast<QDesignerFormEditorPluginInterface*>(plugin)) {
            if (!formEditorPlugin->isInitialized())
                formEditorPlugin->initialize(m_formeditor);
        }
    }

395
396
397
    if (m_actionAboutPlugins)
        m_actionAboutPlugins->setEnabled(true);

398
    if (Designer::Constants::Internal::debug) {
con's avatar
con committed
399
400
401
        qDebug() << Q_FUNC_INFO << initTime->elapsed() << "ms";
        delete initTime;
    }
402

403
404
405
406
407
    QObject::connect(EditorManager::instance(), &EditorManager::editorsClosed, [this] (QList<IEditor*> editors) {
        foreach (IEditor *editor, editors)
            m_editorWidget->removeFormWindowEditor(editor);
    });

408
    // Nest toolbar and editor widget
409
    m_editorWidget = new EditorWidget;
hjk's avatar
hjk committed
410
    QSettings *settings = ICore::settings();
411
    settings->beginGroup(QLatin1String(settingsGroupC));
412
413
    m_editorWidget->restoreSettings(settings);
    settings->endGroup();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
414

Lasse Holmstedt's avatar
Lasse Holmstedt committed
415
    m_editorToolBar = createEditorToolBar();
416
    m_toolBar = new EditorToolBar;
hjk's avatar
hjk committed
417
    m_toolBar->setToolbarCreationFlags(EditorToolBar::FlagsStandalone);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
418
419
420
    m_toolBar->setNavigationVisible(false);
    m_toolBar->addCenterToolBar(m_editorToolBar);

hjk's avatar
hjk committed
421
    m_designMode = DesignMode::instance();
422
423
424
425
426
    m_modeWidget = new QWidget;
    m_modeWidget->setObjectName(QLatin1String("DesignerModeWidget"));
    QVBoxLayout *layout = new QVBoxLayout;
    layout->setMargin(0);
    layout->setSpacing(0);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
427
    layout->addWidget(m_toolBar);
428
429
    // Avoid mode switch to 'Edit' mode when the application started by
    // 'Run' in 'Design' mode emits output.
hjk's avatar
hjk committed
430
    MiniSplitter *splitter = new MiniSplitter(Qt::Vertical);
431
    splitter->addWidget(m_editorWidget);
hjk's avatar
hjk committed
432
    QWidget *outputPane = new OutputPanePlaceHolder(m_designMode, splitter);
433
434
    outputPane->setObjectName(QLatin1String("DesignerOutputPanePlaceHolder"));
    splitter->addWidget(outputPane);
435
    layout->addWidget(splitter);
436
437
    m_modeWidget->setLayout(layout);

hjk's avatar
hjk committed
438
    Context designerContexts = m_contexts;
439
    designerContexts.add(Core::Constants::C_EDITORMANAGER);
440
    m_context = new DesignerContext(designerContexts, m_modeWidget, m_instance);
hjk's avatar
hjk committed
441
    ICore::addContextObject(m_context);
442

443
    m_designMode->registerDesignWidget(m_modeWidget, QStringList(QLatin1String(FORM_MIMETYPE)), m_contexts);
444
445

    setupViewActions();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
446

447
    m_initStage = FormEditorW::FullyInitialized;
con's avatar
con committed
448
449
}

450
void FormEditorData::initDesignerSubWindows()
con's avatar
con committed
451
452
453
454
{
    qFill(m_designerSubWindows, m_designerSubWindows + Designer::Constants::DesignerSubWindowCount, static_cast<QWidget*>(0));

    QDesignerWidgetBoxInterface *wb = QDesignerComponents::createWidgetBox(m_formeditor, 0);
con's avatar
con committed
455
    wb->setWindowTitle(tr("Widget Box"));
456
    wb->setObjectName(QLatin1String("WidgetBox"));
con's avatar
con committed
457
458
459
460
    m_formeditor->setWidgetBox(wb);
    m_designerSubWindows[WidgetBoxSubWindow] = wb;

    QDesignerObjectInspectorInterface *oi = QDesignerComponents::createObjectInspector(m_formeditor, 0);
con's avatar
con committed
461
    oi->setWindowTitle(tr("Object Inspector"));
462
    oi->setObjectName(QLatin1String("ObjectInspector"));
con's avatar
con committed
463
464
465
466
    m_formeditor->setObjectInspector(oi);
    m_designerSubWindows[ObjectInspectorSubWindow] = oi;

    QDesignerPropertyEditorInterface *pe = QDesignerComponents::createPropertyEditor(m_formeditor, 0);
con's avatar
con committed
467
    pe->setWindowTitle(tr("Property Editor"));
468
    pe->setObjectName(QLatin1String("PropertyEditor"));
con's avatar
con committed
469
470
471
472
    m_formeditor->setPropertyEditor(pe);
    m_designerSubWindows[PropertyEditorSubWindow] = pe;

    QWidget *se = QDesignerComponents::createSignalSlotEditor(m_formeditor, 0);
Daniel Molkentin's avatar
Daniel Molkentin committed
473
    se->setWindowTitle(tr("Signals && Slots Editor"));
474
    se->setObjectName(QLatin1String("SignalsAndSlotsEditor"));
con's avatar
con committed
475
476
477
    m_designerSubWindows[SignalSlotEditorSubWindow] = se;

    QDesignerActionEditorInterface *ae = QDesignerComponents::createActionEditor(m_formeditor, 0);
con's avatar
con committed
478
    ae->setWindowTitle(tr("Action Editor"));
479
    ae->setObjectName(QLatin1String("ActionEditor"));
con's avatar
con committed
480
481
    m_formeditor->setActionEditor(ae);
    m_designerSubWindows[ActionEditorSubWindow] = ae;
482
    m_initStage = FormEditorW::SubwindowsInitialized;
con's avatar
con committed
483
484
}

485
QList<Core::IOptionsPage *> FormEditorW::optionsPages()
486
{
487
    return d->m_settingsPages;
488
489
}

con's avatar
con committed
490
491
void FormEditorW::ensureInitStage(InitializationStage s)
{
492
    if (Designer::Constants::Internal::debug)
con's avatar
con committed
493
        qDebug() << Q_FUNC_INFO << s;
494
495
496
497
498
    if (!d) {
        m_instance = new FormEditorW;
        d = new FormEditorData;
    }
    if (d->m_initStage >= s)
con's avatar
con committed
499
500
        return;
    QApplication::setOverrideCursor(Qt::WaitCursor);
501
    d->fullInit();
con's avatar
con committed
502
503
504
    QApplication::restoreOverrideCursor();
}

505
void FormEditorW::deleteInstance()
con's avatar
con committed
506
{
507
508
509
510
    delete d;
    d = 0;
    delete m_instance;
    m_instance = 0;
con's avatar
con committed
511
512
}

513
IEditor *FormEditorW::createEditor()
con's avatar
con committed
514
{
515
516
    ensureInitStage(FullyInitialized);
    return d->createEditor();
con's avatar
con committed
517
518
}

519
void FormEditorData::setupActions()
con's avatar
con committed
520
521
{
    //menus
hjk's avatar
hjk committed
522
    ActionContainer *medit = ActionManager::actionContainer(Core::Constants::M_EDIT);
523
    ActionContainer *mformtools = ActionManager::actionContainer(M_FORMEDITOR);
con's avatar
con committed
524
525

    //overridden actions
hjk's avatar
hjk committed
526
527
528
529
530
531
    bindShortcut(ActionManager::registerAction(m_fwm->actionUndo(), Core::Constants::UNDO, m_contexts), m_fwm->actionUndo());
    bindShortcut(ActionManager::registerAction(m_fwm->actionRedo(), Core::Constants::REDO, m_contexts), m_fwm->actionRedo());
    bindShortcut(ActionManager::registerAction(m_fwm->actionCut(), Core::Constants::CUT, m_contexts), m_fwm->actionCut());
    bindShortcut(ActionManager::registerAction(m_fwm->actionCopy(), Core::Constants::COPY, m_contexts), m_fwm->actionCopy());
    bindShortcut(ActionManager::registerAction(m_fwm->actionPaste(), Core::Constants::PASTE, m_contexts), m_fwm->actionPaste());
    bindShortcut(ActionManager::registerAction(m_fwm->actionSelectAll(), Core::Constants::SELECTALL, m_contexts), m_fwm->actionSelectAll());
con's avatar
con committed
532

533
    m_actionPrint = new QAction(m_instance);
hjk's avatar
hjk committed
534
    bindShortcut(ActionManager::registerAction(m_actionPrint, Core::Constants::PRINT, m_contexts), m_actionPrint);
535
    QObject::connect(m_actionPrint, &QAction::triggered, [this]() { print(); });
con's avatar
con committed
536

537
538
539
    //'delete' action. Do not set a shortcut as Designer handles
    // the 'Delete' key by event filter. Setting a shortcut triggers
    // buggy behaviour on Mac (Pressing Delete in QLineEdit removing the widget).
hjk's avatar
hjk committed
540
    Command *command;
541
    command = ActionManager::registerAction(m_fwm->actionDelete(), "FormEditor.Edit.Delete", m_contexts);
542
    bindShortcut(command, m_fwm->actionDelete());
hjk's avatar
hjk committed
543
    command->setAttribute(Command::CA_Hide);
con's avatar
con committed
544
545
    medit->addAction(command, Core::Constants::G_EDIT_COPYPASTE);

546
    m_actionGroupEditMode = new QActionGroup(m_instance);
con's avatar
con committed
547
    m_actionGroupEditMode->setExclusive(true);
548
549
    QObject::connect(m_actionGroupEditMode, &QActionGroup::triggered,
            [this](QAction *a) { activateEditMode(a->data().toInt()); });
con's avatar
con committed
550

551
    medit->addSeparator(m_contexts, Core::Constants::G_EDIT_OTHER);
con's avatar
con committed
552

553
    m_toolActionIds.push_back("FormEditor.WidgetEditor");
Eike Ziller's avatar
Eike Ziller committed
554
    createEditModeAction(m_actionGroupEditMode, m_contexts, medit,
555
                         tr("Edit Widgets"), m_toolActionIds.back(),
con's avatar
con committed
556
557
                         EditModeWidgetEditor, QLatin1String("widgettool.png"), tr("F3"));

558
    m_toolActionIds.push_back("FormEditor.SignalsSlotsEditor");
Eike Ziller's avatar
Eike Ziller committed
559
    createEditModeAction(m_actionGroupEditMode, m_contexts, medit,
560
                         tr("Edit Signals/Slots"), m_toolActionIds.back(),
con's avatar
con committed
561
562
                         EditModeSignalsSlotEditor, QLatin1String("signalslottool.png"), tr("F4"));

563
    m_toolActionIds.push_back("FormEditor.BuddyEditor");
Eike Ziller's avatar
Eike Ziller committed
564
    createEditModeAction(m_actionGroupEditMode, m_contexts, medit,
565
                         tr("Edit Buddies"), m_toolActionIds.back(),
con's avatar
con committed
566
567
                         EditModeBuddyEditor, QLatin1String("buddytool.png"));

568
    m_toolActionIds.push_back("FormEditor.TabOrderEditor");
Eike Ziller's avatar
Eike Ziller committed
569
    createEditModeAction(m_actionGroupEditMode, m_contexts, medit,
570
                         tr("Edit Tab Order"),  m_toolActionIds.back(),
con's avatar
con committed
571
572
573
                         EditModeTabOrderEditor, QLatin1String("tabordertool.png"));

    //tool actions
574
    m_toolActionIds.push_back("FormEditor.LayoutHorizontally");
hjk's avatar
hjk committed
575
    const QString horizLayoutShortcut = UseMacShortcuts ? tr("Meta+Shift+H") : tr("Ctrl+H");
Eike Ziller's avatar
Eike Ziller committed
576
    addToolAction(m_fwm->actionHorizontalLayout(), m_contexts,
577
578
                  m_toolActionIds.back(), mformtools, horizLayoutShortcut);

579
    m_toolActionIds.push_back("FormEditor.LayoutVertically");
hjk's avatar
hjk committed
580
    const QString vertLayoutShortcut = UseMacShortcuts ? tr("Meta+L") : tr("Ctrl+L");
Eike Ziller's avatar
Eike Ziller committed
581
    addToolAction(m_fwm->actionVerticalLayout(), m_contexts,
582
                  m_toolActionIds.back(),  mformtools, vertLayoutShortcut);
con's avatar
con committed
583

584
    m_toolActionIds.push_back("FormEditor.SplitHorizontal");
Eike Ziller's avatar
Eike Ziller committed
585
    addToolAction(m_fwm->actionSplitHorizontal(), m_contexts,
con's avatar
con committed
586
587
                  m_toolActionIds.back(), mformtools);

588
    m_toolActionIds.push_back("FormEditor.SplitVertical");
Eike Ziller's avatar
Eike Ziller committed
589
    addToolAction(m_fwm->actionSplitVertical(), m_contexts,
con's avatar
con committed
590
591
                  m_toolActionIds.back(), mformtools);

592
    m_toolActionIds.push_back("FormEditor.LayoutForm");
Eike Ziller's avatar
Eike Ziller committed
593
    addToolAction(m_fwm->actionFormLayout(), m_contexts,
con's avatar
con committed
594
595
                  m_toolActionIds.back(),  mformtools);

596
    m_toolActionIds.push_back("FormEditor.LayoutGrid");
hjk's avatar
hjk committed
597
    const QString gridShortcut = UseMacShortcuts ? tr("Meta+Shift+G") : tr("Ctrl+G");
Eike Ziller's avatar
Eike Ziller committed
598
    addToolAction(m_fwm->actionGridLayout(), m_contexts,
599
                  m_toolActionIds.back(),  mformtools, gridShortcut);
con's avatar
con committed
600

601
    m_toolActionIds.push_back("FormEditor.LayoutBreak");
Eike Ziller's avatar
Eike Ziller committed
602
    addToolAction(m_fwm->actionBreakLayout(), m_contexts,
con's avatar
con committed
603
604
                  m_toolActionIds.back(), mformtools);

605
    m_toolActionIds.push_back("FormEditor.LayoutAdjustSize");
hjk's avatar
hjk committed
606
    const QString adjustShortcut = UseMacShortcuts ? tr("Meta+J") : tr("Ctrl+J");
Eike Ziller's avatar
Eike Ziller committed
607
    addToolAction(m_fwm->actionAdjustSize(), m_contexts,
608
                  m_toolActionIds.back(),  mformtools, adjustShortcut);
con's avatar
con committed
609

610
    m_toolActionIds.push_back("FormEditor.SimplifyLayout");
Eike Ziller's avatar
Eike Ziller committed
611
    addToolAction(m_fwm->actionSimplifyLayout(), m_contexts,
con's avatar
con committed
612
613
                  m_toolActionIds.back(),  mformtools);

614
    mformtools->addSeparator(m_contexts);
con's avatar
con committed
615

616
617
    addToolAction(m_fwm->actionLower(), m_contexts, "FormEditor.Lower", mformtools);
    addToolAction(m_fwm->actionRaise(), m_contexts, "FormEditor.Raise", mformtools);
con's avatar
con committed
618
619

    // Commands that do not go into the editor toolbar
620
    mformtools->addSeparator(m_contexts);
con's avatar
con committed
621

622
    m_actionPreview = m_fwm->action(QDesignerFormWindowManagerInterface::DefaultPreviewAction);
hjk's avatar
hjk committed
623
    QTC_ASSERT(m_actionPreview, return);
624
    addToolAction(m_actionPreview, m_contexts, "FormEditor.Preview", mformtools, tr("Alt+Shift+R"));
con's avatar
con committed
625
626

    // Preview in style...
627
628
    m_actionGroupPreviewInStyle = m_fwm->actionGroup(QDesignerFormWindowManagerInterface::StyledPreviewActionGroup);

hjk's avatar
hjk committed
629
    ActionContainer *previewAC = createPreviewStyleMenu(m_actionGroupPreviewInStyle);
630
631
632
    m_previewInStyleMenu = previewAC->menu();
    mformtools->addMenu(previewAC);
    setPreviewMenuEnabled(false);
con's avatar
con committed
633
634

    // Form settings
635
    medit->addSeparator(m_contexts, Core::Constants::G_EDIT_OTHER);
con's avatar
con committed
636

637
    mformtools->addSeparator(m_contexts);
638

639
    mformtools->addSeparator(m_contexts, Core::Constants::G_DEFAULT_THREE);
640
    QAction *actionFormSettings = m_fwm->action(QDesignerFormWindowManagerInterface::FormWindowSettingsDialogAction);
641
642
    addToolAction(actionFormSettings, m_contexts, "FormEditor.FormSettings", mformtools,
                  QString(), Core::Constants::G_DEFAULT_THREE);
643

644
    mformtools->addSeparator(m_contexts, Core::Constants::G_DEFAULT_THREE);
645
    m_actionAboutPlugins = new QAction(tr("About Qt Designer Plugins..."), m_instance);
646
    m_actionAboutPlugins->setMenuRole(QAction::NoRole);
647
648
    addToolAction(m_actionAboutPlugins, m_contexts, "FormEditor.AboutPlugins", mformtools,
                  QString(), Core::Constants::G_DEFAULT_THREE);
649
650
    QObject::connect(m_actionAboutPlugins, &QAction::triggered,
        m_fwm, &QDesignerFormWindowManagerInterface::showPluginDialog);
651
652
    m_actionAboutPlugins->setEnabled(false);

con's avatar
con committed
653
    // FWM
654
655
656
657
658
    QObject::connect(m_fwm, &QDesignerFormWindowManagerInterface::activeFormWindowChanged,
        [this] (QDesignerFormWindowInterface *afw) {
            m_fwm->closeAllPreviews();
            setPreviewMenuEnabled(afw != 0);
        });
con's avatar
con committed
659
660
}

661
QToolBar *FormEditorData::createEditorToolBar() const
con's avatar
con committed
662
{
663
    QToolBar *editorToolBar = new QToolBar;
hjk's avatar
hjk committed
664
665
666
    const QList<Id>::const_iterator cend = m_toolActionIds.constEnd();
    for (QList<Id>::const_iterator it = m_toolActionIds.constBegin(); it != cend; ++it) {
        Command *cmd = ActionManager::command(*it);
hjk's avatar
hjk committed
667
        QTC_ASSERT(cmd, continue);
con's avatar
con committed
668
669
        QAction *action = cmd->action();
        if (!action->icon().isNull()) // Simplify grid has no action yet
670
            editorToolBar->addAction(action);
con's avatar
con committed
671
    }
672
673
674
675
    const int size = editorToolBar->style()->pixelMetric(QStyle::PM_SmallIconSize);
    editorToolBar->setIconSize(QSize(size, size));
    editorToolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    return editorToolBar;
con's avatar
con committed
676
677
}

678
ActionContainer *FormEditorData::createPreviewStyleMenu(QActionGroup *actionGroup)
con's avatar
con committed
679
680
{
    const QString menuId = QLatin1String(M_FORMEDITOR_PREVIEW);
hjk's avatar
hjk committed
681
    ActionContainer *menuPreviewStyle = ActionManager::createMenu(M_FORMEDITOR_PREVIEW);
con's avatar
con committed
682
683
684
685
686
687
688
689
690
691
692
    menuPreviewStyle->menu()->setTitle(tr("Preview in"));

    // The preview menu is a list of invisible actions for the embedded design
    // device profiles (integer data) followed by a separator and the styles
    // (string data). Make device profiles update their text and hide them
    // in the configuration dialog.
    const QList<QAction*> actions = actionGroup->actions();

    const QString deviceProfilePrefix = QLatin1String("DeviceProfile");
    const QChar dot = QLatin1Char('.');

hjk's avatar
hjk committed
693
    foreach (QAction* a, actions) {
con's avatar
con committed
694
695
696
697
698
699
700
701
702
        QString name = menuId;
        name += dot;
        const QVariant data = a->data();
        const bool isDeviceProfile = data.type() == QVariant::Int;
        if (isDeviceProfile) {
            name += deviceProfilePrefix;
            name += dot;
        }
        name += data.toString();
hjk's avatar
hjk committed
703
        Command *command = ActionManager::registerAction(a, Id::fromString(name), m_contexts);
704
        bindShortcut(command, a);
con's avatar
con committed
705
        if (isDeviceProfile) {
hjk's avatar
hjk committed
706
707
            command->setAttribute(Command::CA_UpdateText);
            command->setAttribute(Command::CA_NonConfigurable);
con's avatar
con committed
708
709
710
711
712
713
        }
        menuPreviewStyle->addAction(command);
    }
    return menuPreviewStyle;
}

714
void FormEditorData::setPreviewMenuEnabled(bool e)
715
716
717
718
719
{
    m_actionPreview->setEnabled(e);
    m_previewInStyleMenu->setEnabled(e);
}

720
void FormEditorData::saveSettings(QSettings *s)
con's avatar
con committed
721
{
722
    s->beginGroup(QLatin1String(settingsGroupC));
723
    m_editorWidget->saveSettings(s);
724
    s->endGroup();
con's avatar
con committed
725
726
}

727
void FormEditorData::critical(const QString &errorMessage)
con's avatar
con committed
728
{
hjk's avatar
hjk committed
729
    QMessageBox::critical(ICore::mainWindow(), tr("Designer"),  errorMessage);
con's avatar
con committed
730
731
}

732
// Apply the command shortcut to the action and connects to the command's keySequenceChanged signal
733
void FormEditorData::bindShortcut(Command *command, QAction *action)
734
735
{
    m_commandToDesignerAction.insert(command, action);
736
737
738
    QObject::connect(command, SIGNAL(keySequenceChanged()),
                     &m_shortcutMapper, SLOT(map()));
    m_shortcutMapper.setMapping(command, command);
739
740
741
742
    updateShortcut(command);
}

// Create an action to activate a designer tool
743
QAction *FormEditorData::createEditModeAction(QActionGroup *ag,
hjk's avatar
hjk committed
744
745
                                     const Context &context,
                                     ActionContainer *medit,
746
                                     const QString &actionName,
747
                                     Id id,
748
749
750
751
752
753
754
755
                                     int toolNumber,
                                     const QString &iconName,
                                     const QString &keySequence)
{
    QAction *rc = new QAction(actionName, ag);
    rc->setCheckable(true);
    if (!iconName.isEmpty())
         rc->setIcon(designerIcon(iconName));
hjk's avatar
hjk committed
756
    Command *command = ActionManager::registerAction(rc, id, context);
757
    command->setAttribute(Core::Command::CA_Hide);
758
759
760
761
762
763
764
765
766
767
    if (!keySequence.isEmpty())
        command->setDefaultKeySequence(QKeySequence(keySequence));
    bindShortcut(command, rc);
    medit->addAction(command, Core::Constants::G_EDIT_OTHER);
    rc->setData(toolNumber);
    ag->addAction(rc);
    return rc;
}

// Create a tool action
768
769
770
Command *FormEditorData::addToolAction(QAction *a, const Context &context, Id id,
                                       ActionContainer *c1, const QString &keySequence,
                                       Core::Id groupId)
771
{
hjk's avatar
hjk committed
772
    Command *command = ActionManager::registerAction(a, id, context);
773
774
    if (!keySequence.isEmpty())
        command->setDefaultKeySequence(QKeySequence(keySequence));
775
776
    if (!a->isSeparator())
        bindShortcut(command, a);
777
    c1->addAction(command, groupId);
778
    return command;
779
780
}

781
Core::IEditor *FormEditorData::createEditor()
con's avatar
con committed
782
{
783
784
785
    if (Designer::Constants::Internal::debug)
        qDebug() << "FormEditorW::createEditor";
    // Create and associate form and text editor.
con's avatar
con committed
786
    m_fwm->closeAllPreviews();
787
    QDesignerFormWindowInterface *form = m_fwm->createFormWindow(0);
788
789
    QTC_ASSERT(form, return 0);
    QObject::connect(form, &QDesignerFormWindowInterface::toolChanged, [this] (int i) { toolChanged(i); });
790

791
792
    SharedTools::WidgetHost *widgetHost = new SharedTools::WidgetHost( /* parent */ 0, form);
    FormWindowEditor *formWindowEditor = m_xmlEditorFactory->create(form);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
793

794
795
    m_editorWidget->add(widgetHost, formWindowEditor);
    m_toolBar->addEditor(formWindowEditor);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
796

797
798
799
800
801
802
803
    if (formWindowEditor) {
        Core::InfoBarEntry info(Core::Id(Constants::INFO_READ_ONLY),
                                tr("This file can only be edited in <b>Design</b> mode."));
        info.setCustomButtonInfo(tr("Switch Mode"), []() { ModeManager::activateMode(Core::Constants::MODE_DESIGN); });
        formWindowEditor->document()->infoBar()->addInfo(info);
    }
    return formWindowEditor;
con's avatar
con committed
804
805
}

806
QDesignerFormEditorInterface *FormEditorW::designerEditor()
807
{
808
809
    ensureInitStage(FullyInitialized);
    return d->m_formeditor;
810
811
}

812
QWidget * const *FormEditorW::designerSubWindows()
con's avatar
con committed
813
{
814
815
    ensureInitStage(SubwindowsInitialized);
    return d->m_designerSubWindows;
con's avatar
con committed
816
817
}

818
SharedTools::WidgetHost *FormEditorW::activeWidgetHost()
con's avatar
con committed
819
{
820
821
822
823
824
    ensureInitStage(FullyInitialized);
    if (d->m_editorWidget)
        return d->m_editorWidget->activeEditor().widgetHost;
    return 0;
}
con's avatar
con committed
825

826
827
828
829
830
831
FormWindowEditor *FormEditorW::activeEditor()
{
    ensureInitStage(FullyInitialized);
    if (d->m_editorWidget)
        return d->m_editorWidget->activeEditor().formWindowEditor;
    return 0;
con's avatar
con committed
832
833
}

834
void FormEditorData::updateShortcut(QObject *command)
con's avatar
con committed
835
{
836
837
838
839
840
841
842
    Command *c = qobject_cast<Command *>(command);
    if (!c)
        return;
    QAction *a = m_commandToDesignerAction.value(c);
    if (!a)
        return;
    a->setShortcut(c->action()->shortcut());
con's avatar
con committed
843
844
}

845
void FormEditorData::activateEditMode(int id)
con's avatar
con committed
846
847
848
849
850
851
{
    if (const int count = m_fwm->formWindowCount())
        for (int i = 0; i <  count; i++)
             m_fwm->formWindow(i)->setCurrentTool(id);
}

852
void FormEditorData::toolChanged(int t)
con's avatar
con committed
853
854
855
856
857
858
859
860
861
862
863
864
865
866
{
    typedef QList<QAction *> ActionList;
    if (const QAction *currentAction = m_actionGroupEditMode->checkedAction())
        if (currentAction->data().toInt() == t)
            return;
    const ActionList actions = m_actionGroupEditMode->actions();
    const ActionList::const_iterator cend = actions.constEnd();
    for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it)
        if ( (*it)->data().toInt() == t) {
            (*it)->setChecked(true);
            break;
        }
}

867
void FormEditorData::print()
con's avatar
con committed
868
869
870
871
872
873
{
    // Printing code courtesy of designer_actions.cpp
    QDesignerFormWindowInterface *fw = m_fwm->activeFormWindow();
    if (!fw)
        return;

hjk's avatar
hjk committed
874
    QPrinter *printer = ICore::printer();
hjk's avatar
hjk committed
875
876
877
    const bool oldFullPage =  printer->fullPage();
    const QPrinter::Orientation oldOrientation =  printer->orientation ();
    printer->setFullPage(false);
con's avatar
con committed
878
879
880
    do {
        // Grab the image to be able to a suggest suitable orientation
        QString errorMessage;
881
        const QPixmap pixmap = m_fwm->createPreviewPixmap();
con's avatar
con committed
882
        if (pixmap.isNull()) {
Jure Repinc's avatar
Jure Repinc committed
883
            critical(tr("The image could not be created: %1").arg(errorMessage));
con's avatar
con committed
884
885
886
887
            break;
        }

        const QSizeF pixmapSize = pixmap.size();
hjk's avatar
hjk committed
888
        printer->setOrientation( pixmapSize.width() > pixmapSize.height() ?  QPrinter::Landscape :  QPrinter::Portrait);
con's avatar
con committed
889
890

        // Printer parameters
hjk's avatar
hjk committed
891
        QPrintDialog dialog(printer, fw);
con's avatar
con committed
892
893
894
        if (!dialog.exec())
           break;

hjk's avatar
hjk committed
895
        QWidget *mainWindow = ICore::mainWindow();
hjk's avatar
hjk committed
896
897
        const QCursor oldCursor = mainWindow->cursor();
        mainWindow->setCursor(Qt::WaitCursor);
con's avatar
con committed
898
        // Estimate of required scaling to make form look the same on screen and printer.
hjk's avatar
hjk committed
899
        const double suggestedScaling = static_cast<double>(printer->physicalDpiX()) /  static_cast<double>(fw->physicalDpiX());
con's avatar
con committed
900

hjk's avatar
hjk committed
901
        QPainter painter(printer);
con's avatar
con committed
902
903
904
905
906
907
908
        painter.setRenderHint(QPainter::SmoothPixmapTransform);

        // Clamp to page
        const QRectF page =  painter.viewport();
        const double maxScaling = qMin(page.size().width() / pixmapSize.width(), page.size().height() / pixmapSize.height());
        const double scaling = qMin(suggestedScaling, maxScaling);

Felix Geyer's avatar
Felix Geyer committed
909
910