Merge remote-tracking branch 'qt/4.8'
[qt:qt.git] / tools / designer / src / designer / qdesigner_workbench.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Designer of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdesigner_workbench.h"
43 #include "qdesigner.h"
44 #include "qdesigner_actions.h"
45 #include "qdesigner_appearanceoptions.h"
46 #include "qdesigner_settings.h"
47 #include "qdesigner_toolwindow.h"
48 #include "qdesigner_formwindow.h"
49 #include "appfontdialog.h"
50
51 #include <QtDesigner/QDesignerFormEditorInterface>
52 #include <QtDesigner/QDesignerFormWindowInterface>
53 #include <QtDesigner/QDesignerFormWindowManagerInterface>
54 #include <QtDesigner/QDesignerFormEditorPluginInterface>
55 #include <QtDesigner/QDesignerWidgetBoxInterface>
56 #include <QtDesigner/QDesignerMetaDataBaseInterface>
57
58 #include <QtDesigner/QDesignerComponents>
59 #include <QtDesigner/private/qdesigner_integration_p.h>
60 #include <QtDesigner/private/pluginmanager_p.h>
61 #include <QtDesigner/private/formwindowbase_p.h>
62 #include <QtDesigner/private/actioneditor_p.h>
63
64 #include <QtCore/QDir>
65 #include <QtCore/QFile>
66 #include <QtCore/QUrl>
67 #include <QtCore/QTimer>
68 #include <QtCore/QPluginLoader>
69 #include <QtCore/qdebug.h>
70
71 #include <QtGui/QActionGroup>
72 #include <QtGui/QCloseEvent>
73 #include <QtGui/QDesktopWidget>
74 #include <QtGui/QDockWidget>
75 #include <QtGui/QMenu>
76 #include <QtGui/QMenuBar>
77 #include <QtGui/QMessageBox>
78 #include <QtGui/QPushButton>
79 #include <QtGui/QToolBar>
80 #include <QtGui/QMdiArea>
81 #include <QtGui/QMdiSubWindow>
82 #include <QtGui/QLayout>
83
84 QT_BEGIN_NAMESPACE
85
86 static const char *appFontPrefixC = "AppFonts";
87
88 typedef QList<QAction *> ActionList;
89
90 static QMdiSubWindow *mdiSubWindowOf(const QWidget *w)
91 {
92     QMdiSubWindow *rc = qobject_cast<QMdiSubWindow *>(w->parentWidget());
93     Q_ASSERT(rc);
94     return rc;
95 }
96
97 static QDockWidget *dockWidgetOf(const QWidget *w)
98 {
99     for (QWidget *parentWidget = w->parentWidget(); parentWidget ; parentWidget = parentWidget->parentWidget()) {
100         if (QDockWidget *dw = qobject_cast<QDockWidget *>(parentWidget)) {
101             return dw;
102         }
103     }
104     Q_ASSERT("Dock widget not found");
105     return 0;
106 }
107
108 // ------------ QDesignerWorkbench::Position
109 QDesignerWorkbench::Position::Position(const QMdiSubWindow *mdiSubWindow, const QPoint &mdiAreaOffset) :
110     m_minimized(mdiSubWindow->isShaded()),
111     m_position(mdiSubWindow->pos() + mdiAreaOffset)
112 {
113 }
114
115 QDesignerWorkbench::Position::Position(const QDockWidget *dockWidget) :
116     m_minimized(dockWidget->isMinimized()),
117     m_position(dockWidget->pos())
118 {
119 }
120
121 QDesignerWorkbench::Position::Position(const QWidget *topLevelWindow, const QPoint &desktopTopLeft)
122 {
123     const QWidget *window =topLevelWindow->window ();
124     Q_ASSERT(window);
125     m_minimized = window->isMinimized();
126     m_position = window->pos() - desktopTopLeft;
127 }
128
129 void QDesignerWorkbench::Position::applyTo(QMdiSubWindow *mdiSubWindow,
130                                            const QPoint &mdiAreaOffset) const
131 {
132     // QMdiSubWindow attempts to resize its children to sizeHint() when switching user interface modes.
133     // Restore old size
134     const QPoint mdiAreaPos =  QPoint(qMax(0, m_position.x() - mdiAreaOffset.x()),
135                                       qMax(0, m_position.y() - mdiAreaOffset.y()));
136     mdiSubWindow->move(mdiAreaPos);
137     const QSize decorationSize = mdiSubWindow->size() - mdiSubWindow->contentsRect().size();
138     mdiSubWindow->resize(mdiSubWindow->widget()->size() + decorationSize);
139     mdiSubWindow->show();
140     if (m_minimized) {
141         mdiSubWindow->showShaded();
142     }
143 }
144
145 void QDesignerWorkbench::Position::applyTo(QWidget *topLevelWindow, const QPoint &desktopTopLeft) const
146 {
147     QWidget *window = topLevelWindow->window ();
148     const QPoint newPos = m_position + desktopTopLeft;
149     window->move(newPos);
150     if ( m_minimized) {
151         topLevelWindow->showMinimized();
152     } else {
153         topLevelWindow->show();
154     }
155 }
156
157 void QDesignerWorkbench::Position::applyTo(QDockWidget *dockWidget) const
158 {
159     dockWidget->widget()->setVisible(true);
160     dockWidget->setVisible(!m_minimized);
161 }
162
163 static inline void addActionsToMenu(QMenu *m, const ActionList &al)
164 {
165     const ActionList::const_iterator cend = al.constEnd();
166     for (ActionList::const_iterator it = al.constBegin(); it != cend; ++it)
167         m->addAction(*it);
168 }
169
170 static inline QMenu *addMenu(QMenuBar *mb, const QString &title, const ActionList &al)
171 {
172     QMenu *rc = mb->addMenu(title);
173     addActionsToMenu(rc, al);
174     return rc;
175 }
176
177 // -------- QDesignerWorkbench
178
179 QDesignerWorkbench::QDesignerWorkbench()  :
180     m_core(QDesignerComponents::createFormEditor(this)),
181     m_windowActions(new QActionGroup(this)),
182     m_globalMenuBar(new QMenuBar),
183     m_mode(NeutralMode),
184     m_dockedMainWindow(0),
185     m_state(StateInitializing)
186 {
187     QDesignerSettings settings(m_core);
188
189     (void) QDesignerComponents::createTaskMenu(core(), this);
190
191     initializeCorePlugins();
192     QDesignerComponents::initializePlugins(core());
193     m_actionManager = new QDesignerActions(this); // accesses plugin components
194
195     m_windowActions->setExclusive(true);
196     connect(m_windowActions, SIGNAL(triggered(QAction*)), this, SLOT(formWindowActionTriggered(QAction*)));
197
198     // Build main menu bar
199     addMenu(m_globalMenuBar, tr("&File"), m_actionManager->fileActions()->actions());
200
201     QMenu *editMenu = addMenu(m_globalMenuBar, tr("&Edit"), m_actionManager->editActions()->actions());
202     editMenu->addSeparator();
203     addActionsToMenu(editMenu, m_actionManager->toolActions()->actions());
204
205     QMenu *formMenu = addMenu(m_globalMenuBar, tr("F&orm"),  m_actionManager->formActions()->actions());
206     QMenu *previewSubMenu = new QMenu(tr("Preview in"), formMenu);
207     formMenu->insertMenu(m_actionManager->previewFormAction(), previewSubMenu);
208     addActionsToMenu(previewSubMenu, m_actionManager->styleActions()->actions());
209
210     QMenu *viewMenu = m_globalMenuBar->addMenu(tr("&View"));
211
212     addMenu(m_globalMenuBar, tr("&Settings"), m_actionManager->settingsActions()->actions());
213
214     m_windowMenu = addMenu(m_globalMenuBar, tr("&Window"), m_actionManager->windowActions()->actions());
215
216     addMenu(m_globalMenuBar, tr("&Help"), m_actionManager->helpActions()->actions());
217
218     //  Add the tools in view menu order
219     QActionGroup *viewActions = new QActionGroup(this);
220     viewActions->setExclusive(false);
221
222     for (int i = 0; i < QDesignerToolWindow::StandardToolWindowCount; i++) {
223         QDesignerToolWindow *toolWindow = QDesignerToolWindow::createStandardToolWindow(static_cast< QDesignerToolWindow::StandardToolWindow>(i), this);
224         m_toolWindows.push_back(toolWindow);
225         if (QAction *action = toolWindow->action()) {
226             viewMenu->addAction(action);
227             viewActions->addAction(action);
228         }
229         // The widget box becomes the main window in top level mode
230         if (i == QDesignerToolWindow::WidgetBox)
231             connect(toolWindow, SIGNAL(closeEventReceived(QCloseEvent*)), this, SLOT(handleCloseEvent(QCloseEvent*)));
232     }
233     // Integration
234     m_integration = new qdesigner_internal::QDesignerIntegration(m_core, this);
235     connect(m_integration, SIGNAL(helpRequested(QString,QString)), m_actionManager, SLOT(helpRequested(QString,QString)));
236
237     // remaining view options (config toolbars)
238     viewMenu->addSeparator();
239     m_toolbarMenu = viewMenu->addMenu(tr("Toolbars"));
240
241     emit initialized();
242
243     connect(m_core->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)),
244                 this, SLOT(updateWindowMenu(QDesignerFormWindowInterface*)));
245
246
247     { // Add application specific options pages
248         QDesignerAppearanceOptionsPage *appearanceOptions = new QDesignerAppearanceOptionsPage(m_core);
249         connect(appearanceOptions, SIGNAL(settingsChangedDelayed()), this, SLOT(restoreUISettings()));
250         QList<QDesignerOptionsPageInterface*> optionsPages = m_core->optionsPages();
251         optionsPages.push_front(appearanceOptions);
252         m_core->setOptionsPages(optionsPages);
253     }
254
255     restoreUISettings();
256     AppFontWidget::restore(m_core->settingsManager(), QLatin1String(appFontPrefixC));
257     m_state = StateUp;
258 }
259
260 QDesignerWorkbench::~QDesignerWorkbench()
261 {
262     switch (m_mode) {
263     case NeutralMode:
264     case DockedMode:
265         qDeleteAll(m_toolWindows);
266         break;
267     case TopLevelMode: // Everything parented here
268         delete widgetBoxToolWindow();
269         break;
270     }
271 }
272
273 void QDesignerWorkbench::saveGeometriesForModeChange()
274 {
275     m_Positions.clear();
276     switch (m_mode) {
277     case NeutralMode:
278         break;
279     case TopLevelMode: {
280         const QPoint desktopOffset = QApplication::desktop()->availableGeometry().topLeft();
281         foreach (QDesignerToolWindow *tw, m_toolWindows)
282             m_Positions.insert(tw, Position(tw, desktopOffset));
283         foreach (QDesignerFormWindow *fw, m_formWindows) {
284             m_Positions.insert(fw,  Position(fw, desktopOffset));
285         }
286     }
287         break;
288     case DockedMode: {
289         const QPoint mdiAreaOffset = m_dockedMainWindow->mdiArea()->pos();
290         foreach (QDesignerToolWindow *tw, m_toolWindows) {
291             m_Positions.insert(tw, Position(dockWidgetOf(tw)));
292         }
293         foreach (QDesignerFormWindow *fw, m_formWindows) {
294             m_Positions.insert(fw, Position(mdiSubWindowOf(fw), mdiAreaOffset));
295         }
296     }
297         break;
298     }
299 }
300
301 UIMode QDesignerWorkbench::mode() const
302 {
303     return m_mode;
304 }
305
306 void QDesignerWorkbench::addFormWindow(QDesignerFormWindow *formWindow)
307 {
308     // ### Q_ASSERT(formWindow->windowTitle().isEmpty() == false);
309
310     m_formWindows.append(formWindow);
311
312
313     m_actionManager->setWindowListSeparatorVisible(true);
314
315     if (QAction *action = formWindow->action()) {
316         m_windowActions->addAction(action);
317         m_windowMenu->addAction(action);
318         action->setChecked(true);
319     }
320
321     m_actionManager->minimizeAction()->setEnabled(true);
322     m_actionManager->minimizeAction()->setChecked(false);
323     connect(formWindow, SIGNAL(minimizationStateChanged(QDesignerFormWindowInterface*,bool)),
324             this, SLOT(minimizationStateChanged(QDesignerFormWindowInterface*,bool)));
325
326     m_actionManager->editWidgets()->trigger();
327 }
328
329 Qt::WindowFlags QDesignerWorkbench::magicalWindowFlags(const QWidget *widgetForFlags) const
330 {
331     switch (m_mode) {
332         case TopLevelMode: {
333 #ifdef Q_WS_MAC
334             if (qobject_cast<const QDesignerToolWindow *>(widgetForFlags))
335                 return Qt::Tool;
336 #else
337             Q_UNUSED(widgetForFlags);
338 #endif
339             return Qt::Window;
340         }
341         case DockedMode:
342             return Qt::Window | Qt::WindowShadeButtonHint | Qt::WindowSystemMenuHint | Qt::WindowTitleHint;
343         case NeutralMode:
344             return Qt::Window;
345         default:
346             Q_ASSERT(0);
347             return 0;
348     }
349 }
350
351 QWidget *QDesignerWorkbench::magicalParent(const QWidget *w) const
352 {
353     switch (m_mode) {
354         case TopLevelMode: {
355             // Use widget box as parent for all windows except self. This will
356             // result in having just one entry in the MS Windows task bar.
357             QWidget *widgetBoxWrapper = widgetBoxToolWindow();
358             return w == widgetBoxWrapper ? 0 : widgetBoxWrapper;
359         }
360         case DockedMode:
361             return m_dockedMainWindow->mdiArea();
362         case NeutralMode:
363             return 0;
364         default:
365             Q_ASSERT(0);
366             return 0;
367     }
368 }
369
370 void QDesignerWorkbench::switchToNeutralMode()
371 {
372     QDesignerSettings settings(m_core);
373     saveGeometries(settings);
374     saveGeometriesForModeChange();
375
376     if (m_mode == TopLevelMode) {
377         delete m_topLevelData.toolbarManager;
378         m_topLevelData.toolbarManager = 0;
379         qDeleteAll(m_topLevelData.toolbars);
380         m_topLevelData.toolbars.clear();
381     }
382
383     m_mode = NeutralMode;
384
385     foreach (QDesignerToolWindow *tw, m_toolWindows) {
386         tw->setCloseEventPolicy(MainWindowBase::AcceptCloseEvents);
387         tw->setParent(0);
388     }
389
390     foreach (QDesignerFormWindow *fw, m_formWindows) {
391         fw->setParent(0);
392         fw->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
393     }
394
395 #ifndef Q_WS_MAC
396     m_globalMenuBar->setParent(0);
397 #endif
398
399     m_core->setTopLevel(0);
400     qDesigner->setMainWindow(0);
401
402     delete m_dockedMainWindow;
403     m_dockedMainWindow = 0;
404 }
405
406 void QDesignerWorkbench::switchToDockedMode()
407 {
408     if (m_mode == DockedMode)
409         return;
410
411     switchToNeutralMode();
412
413 #ifdef Q_WS_X11
414     QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, false);
415 #endif
416 #ifndef Q_WS_MAC
417     QDesignerToolWindow *widgetBoxWrapper = widgetBoxToolWindow();
418     widgetBoxWrapper->action()->setVisible(true);
419     widgetBoxWrapper->setWindowTitle(tr("Widget Box"));
420 #endif
421
422     m_mode = DockedMode;
423     const QDesignerSettings settings(m_core);
424     m_dockedMainWindow = new DockedMainWindow(this, m_toolbarMenu, m_toolWindows);
425     m_dockedMainWindow->setUnifiedTitleAndToolBarOnMac(true);
426     m_dockedMainWindow->setCloseEventPolicy(MainWindowBase::EmitCloseEventSignal);
427     connect(m_dockedMainWindow, SIGNAL(closeEventReceived(QCloseEvent*)), this, SLOT(handleCloseEvent(QCloseEvent*)));
428     connect(m_dockedMainWindow, SIGNAL(fileDropped(QString)), this, SLOT(slotFileDropped(QString)));
429     connect(m_dockedMainWindow, SIGNAL(formWindowActivated(QDesignerFormWindow*)), this, SLOT(slotFormWindowActivated(QDesignerFormWindow*)));
430     m_dockedMainWindow->restoreSettings(settings, m_dockedMainWindow->addToolWindows(m_toolWindows), desktopGeometry());
431
432     m_core->setTopLevel(m_dockedMainWindow);
433
434 #ifndef Q_WS_MAC
435     m_dockedMainWindow->setMenuBar(m_globalMenuBar);
436     m_globalMenuBar->show();
437 #endif
438     qDesigner->setMainWindow(m_dockedMainWindow);
439
440     foreach (QDesignerFormWindow *fw, m_formWindows) {
441         QMdiSubWindow *subwin = m_dockedMainWindow->createMdiSubWindow(fw, magicalWindowFlags(fw),
442                                                                        m_actionManager->closeFormAction()->shortcut());
443         subwin->hide();
444         if (QWidget *mainContainer = fw->editor()->mainContainer())
445             resizeForm(fw, mainContainer);
446     }
447
448     m_actionManager->setBringAllToFrontVisible(false);
449     m_dockedMainWindow->show();
450     // Trigger adjustMDIFormPositions() delayed as viewport size is not yet known.
451
452     if (m_state != StateInitializing)
453         QMetaObject::invokeMethod(this, "adjustMDIFormPositions", Qt::QueuedConnection);
454 }
455
456 void QDesignerWorkbench::adjustMDIFormPositions()
457 {
458     const QPoint mdiAreaOffset = m_dockedMainWindow->mdiArea()->pos();
459
460     foreach (QDesignerFormWindow *fw, m_formWindows) {
461         const PositionMap::const_iterator pit = m_Positions.constFind(fw);
462         if (pit != m_Positions.constEnd())
463             pit->applyTo(mdiSubWindowOf(fw), mdiAreaOffset);
464     }
465 }
466
467 void QDesignerWorkbench::switchToTopLevelMode()
468 {
469     if (m_mode == TopLevelMode)
470         return;
471
472     // make sure that the widgetbox is visible if it is different from neutral.
473     QDesignerToolWindow *widgetBoxWrapper = widgetBoxToolWindow();
474     Q_ASSERT(widgetBoxWrapper);
475
476     switchToNeutralMode();
477     const QPoint desktopOffset = desktopGeometry().topLeft();
478     m_mode = TopLevelMode;
479
480     // The widget box is special, it gets the menubar and gets to be the main widget.
481
482     m_core->setTopLevel(widgetBoxWrapper);
483 #ifdef Q_WS_X11
484     // For now the appmenu protocol does not make it possible to associate a
485     // menubar with all application windows. This means in top level mode you
486     // can only reach the menubar when the widgetbox window is active. Since
487     // this is quite inconvenient, better not use the native menubar in this
488     // configuration and keep the menubar in the widgetbox window.
489     QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true);
490 #endif
491 #ifndef Q_WS_MAC
492     widgetBoxWrapper->setMenuBar(m_globalMenuBar);
493     widgetBoxWrapper->action()->setVisible(false);
494     widgetBoxWrapper->setCloseEventPolicy(MainWindowBase::EmitCloseEventSignal);
495     qDesigner->setMainWindow(widgetBoxWrapper);
496     widgetBoxWrapper->setWindowTitle(MainWindowBase::mainWindowTitle());
497 #endif
498
499     const QDesignerSettings settings(m_core);
500     m_topLevelData.toolbars = MainWindowBase::createToolBars(m_actionManager, false);
501     m_topLevelData.toolbarManager = new ToolBarManager(widgetBoxWrapper, widgetBoxWrapper,
502                                                        m_toolbarMenu, m_actionManager,
503                                                        m_topLevelData.toolbars, m_toolWindows);
504     const int toolBarCount = m_topLevelData.toolbars.size();
505     for (int i = 0; i < toolBarCount; i++) {
506         widgetBoxWrapper->addToolBar(m_topLevelData.toolbars.at(i));
507         if (i == 3)
508             widgetBoxWrapper->insertToolBarBreak(m_topLevelData.toolbars.at(i));
509     }
510     m_topLevelData.toolbarManager->restoreState(settings.toolBarsState(m_mode), MainWindowBase::settingsVersion());
511     widgetBoxWrapper->restoreState(settings.mainWindowState(m_mode), MainWindowBase::settingsVersion());
512
513     bool found_visible_window = false;
514     foreach (QDesignerToolWindow *tw, m_toolWindows) {
515         tw->setParent(magicalParent(tw), magicalWindowFlags(tw));
516         settings.restoreGeometry(tw, tw->geometryHint());
517         tw->action()->setChecked(tw->isVisible());
518         found_visible_window |= tw->isVisible();
519     }
520
521     if (!m_toolWindows.isEmpty() && !found_visible_window)
522         m_toolWindows.first()->show();
523
524     m_actionManager->setBringAllToFrontVisible(true);
525
526     foreach (QDesignerFormWindow *fw, m_formWindows) {
527         fw->setParent(magicalParent(fw), magicalWindowFlags(fw));
528         fw->setAttribute(Qt::WA_DeleteOnClose, true);
529         const PositionMap::const_iterator pit = m_Positions.constFind(fw);
530         if (pit != m_Positions.constEnd()) pit->applyTo(fw, desktopOffset);
531         // Force an activate in order to refresh minimumSize, otherwise it will not be respected
532         if (QLayout *layout = fw->layout())
533             layout->invalidate();
534         if (QWidget *mainContainer = fw->editor()->mainContainer())
535             resizeForm(fw, mainContainer);
536     }
537 }
538
539 QDesignerFormWindowManagerInterface *QDesignerWorkbench::formWindowManager() const
540 {
541     return m_core->formWindowManager();
542 }
543
544 QDesignerFormEditorInterface *QDesignerWorkbench::core() const
545 {
546     return m_core;
547 }
548
549 int QDesignerWorkbench::toolWindowCount() const
550 {
551     return m_toolWindows.count();
552 }
553
554 QDesignerToolWindow *QDesignerWorkbench::toolWindow(int index) const
555 {
556     return m_toolWindows.at(index);
557 }
558
559 int QDesignerWorkbench::formWindowCount() const
560 {
561     return m_formWindows.count();
562 }
563
564 QDesignerFormWindow *QDesignerWorkbench::formWindow(int index) const
565 {
566     return m_formWindows.at(index);
567 }
568
569 QRect QDesignerWorkbench::desktopGeometry() const
570 {
571     // Return geometry of the desktop designer is running in.
572     QWidget *widget = 0;
573     switch (m_mode) {
574     case DockedMode:
575         widget = m_dockedMainWindow;
576         break;
577     case TopLevelMode:
578         widget = widgetBoxToolWindow();
579         break;
580     case NeutralMode:
581         break;
582     }
583     const QDesktopWidget *desktop = qApp->desktop();
584     const int screenNumber = widget ? desktop->screenNumber(widget) : 0;
585     return desktop->availableGeometry(screenNumber);
586 }
587
588 QRect QDesignerWorkbench::availableGeometry() const
589 {
590     if (m_mode == DockedMode)
591         return m_dockedMainWindow->mdiArea()->geometry();
592
593     const QDesktopWidget *desktop = qDesigner->desktop();
594     return desktop->availableGeometry(desktop->screenNumber(widgetBoxToolWindow()));
595 }
596
597 int QDesignerWorkbench::marginHint() const
598 {    return 20;
599 }
600
601 void QDesignerWorkbench::slotFormWindowActivated(QDesignerFormWindow* fw)
602 {
603     core()->formWindowManager()->setActiveFormWindow(fw->editor());
604 }
605
606 void QDesignerWorkbench::removeFormWindow(QDesignerFormWindow *formWindow)
607 {
608     QDesignerFormWindowInterface *editor = formWindow->editor();
609     const bool loadOk = editor->mainContainer();
610     updateBackup(editor);
611     const int index = m_formWindows.indexOf(formWindow);
612     if (index != -1) {
613         m_formWindows.removeAt(index);
614     }
615
616     if (QAction *action = formWindow->action()) {
617         m_windowActions->removeAction(action);
618         m_windowMenu->removeAction(action);
619     }
620
621     if (m_formWindows.empty()) {
622         m_actionManager->setWindowListSeparatorVisible(false);
623         // Show up new form dialog unless closing
624         if (loadOk && m_state == StateUp
625             && QDesignerSettings(m_core).showNewFormOnStartup()) {
626             QTimer::singleShot(200, m_actionManager, SLOT(createForm()));
627         }
628     }
629 }
630
631 void QDesignerWorkbench::initializeCorePlugins()
632 {
633     QList<QObject*> plugins = QPluginLoader::staticInstances();
634     plugins += core()->pluginManager()->instances();
635
636     foreach (QObject *plugin, plugins) {
637         if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast<QDesignerFormEditorPluginInterface*>(plugin)) {
638             if (!formEditorPlugin->isInitialized())
639                 formEditorPlugin->initialize(core());
640         }
641     }
642 }
643
644 void QDesignerWorkbench::saveSettings() const
645 {
646     QDesignerSettings settings(m_core);
647     settings.clearBackup();
648     saveGeometries(settings);
649     AppFontWidget::save(m_core->settingsManager(), QLatin1String(appFontPrefixC));
650 }
651
652 void QDesignerWorkbench::saveGeometries(QDesignerSettings &settings) const
653 {
654     switch (m_mode) {
655     case DockedMode:
656         m_dockedMainWindow->saveSettings(settings);
657         break;
658     case TopLevelMode:
659         settings.setToolBarsState(m_mode, m_topLevelData.toolbarManager->saveState(MainWindowBase::settingsVersion()));
660         settings.setMainWindowState(m_mode, widgetBoxToolWindow()->saveState(MainWindowBase::settingsVersion()));
661         foreach (const QDesignerToolWindow *tw, m_toolWindows)
662             settings.saveGeometryFor(tw);
663         break;
664     case NeutralMode:
665         break;
666     }
667 }
668
669 void QDesignerWorkbench::slotFileDropped(const QString &f)
670 {
671     readInForm(f);
672 }
673
674 bool QDesignerWorkbench::readInForm(const QString &fileName) const
675 {
676     return m_actionManager->readInForm(fileName);
677 }
678
679 bool QDesignerWorkbench::writeOutForm(QDesignerFormWindowInterface *formWindow, const QString &fileName) const
680 {
681     return m_actionManager->writeOutForm(formWindow, fileName);
682 }
683
684 bool QDesignerWorkbench::saveForm(QDesignerFormWindowInterface *frm)
685 {
686     return m_actionManager->saveForm(frm);
687 }
688
689 QDesignerFormWindow *QDesignerWorkbench::findFormWindow(QWidget *widget) const
690 {
691     foreach (QDesignerFormWindow *formWindow, m_formWindows) {
692         if (formWindow->editor() == widget)
693             return formWindow;
694     }
695
696     return 0;
697 }
698
699 bool QDesignerWorkbench::handleClose()
700 {
701     m_state = StateClosing;
702     QList<QDesignerFormWindow *> dirtyForms;
703     foreach (QDesignerFormWindow *w, m_formWindows) {
704         if (w->editor()->isDirty())
705             dirtyForms << w;
706     }
707
708     if (dirtyForms.size()) {
709         if (dirtyForms.size() == 1) {
710             if (!dirtyForms.at(0)->close()) {
711                 m_state = StateUp;
712                 return false;
713             }
714         } else {
715             int count = dirtyForms.size();
716             QMessageBox box(QMessageBox::Warning, tr("Save Forms?"),
717                     tr("There are %n forms with unsaved changes."
718                         " Do you want to review these changes before quitting?", "", count),
719                     QMessageBox::Cancel | QMessageBox::Discard | QMessageBox::Save);
720             box.setInformativeText(tr("If you do not review your documents, all your changes will be lost."));
721             box.button(QMessageBox::Discard)->setText(tr("Discard Changes"));
722             QPushButton *save = static_cast<QPushButton *>(box.button(QMessageBox::Save));
723             save->setText(tr("Review Changes"));
724             box.setDefaultButton(save);
725             switch (box.exec()) {
726             case QMessageBox::Cancel:
727                 m_state = StateUp;
728                 return false;
729             case QMessageBox::Save:
730                foreach (QDesignerFormWindow *fw, dirtyForms) {
731                    fw->show();
732                    fw->raise();
733                    if (!fw->close()) {
734                        m_state = StateUp;
735                        return false;
736                    }
737                }
738                break;
739             case QMessageBox::Discard:
740               foreach (QDesignerFormWindow *fw, dirtyForms) {
741                   fw->editor()->setDirty(false);
742                   fw->setWindowModified(false);
743               }
744               break;
745             }
746         }
747     }
748
749     foreach (QDesignerFormWindow *fw, m_formWindows)
750         fw->close();
751
752     saveSettings();
753     return true;
754 }
755
756 QDesignerActions *QDesignerWorkbench::actionManager() const
757 {
758     return m_actionManager;
759 }
760
761 void QDesignerWorkbench::updateWindowMenu(QDesignerFormWindowInterface *fwi)
762 {
763     bool minimizeChecked = false;
764     bool minimizeEnabled = false;
765     QDesignerFormWindow *activeFormWindow = 0;
766     do {
767         if (!fwi)
768         break;
769         activeFormWindow = qobject_cast<QDesignerFormWindow *>(fwi->parentWidget());
770         if (!activeFormWindow)
771             break;
772
773         minimizeEnabled = true;
774         minimizeChecked = isFormWindowMinimized(activeFormWindow);
775     } while (false) ;
776
777     m_actionManager->minimizeAction()->setEnabled(minimizeEnabled);
778     m_actionManager->minimizeAction()->setChecked(minimizeChecked);
779
780     if (!m_formWindows.empty()) {
781         const QList<QDesignerFormWindow*>::const_iterator cend = m_formWindows.constEnd();
782         for (QList<QDesignerFormWindow*>::const_iterator it = m_formWindows.constBegin(); it != cend; ++it)
783             (*it)->action()->setChecked(*it == activeFormWindow);
784     }
785 }
786
787 void QDesignerWorkbench::formWindowActionTriggered(QAction *a)
788 {
789     QDesignerFormWindow *fw = qobject_cast<QDesignerFormWindow *>(a->parentWidget());
790     Q_ASSERT(fw);
791
792     if (isFormWindowMinimized(fw))
793         setFormWindowMinimized(fw, false);
794
795     if (m_mode == DockedMode) {
796         if (QMdiSubWindow *subWindow = qobject_cast<QMdiSubWindow *>(fw->parent())) {
797             m_dockedMainWindow->mdiArea()->setActiveSubWindow(subWindow);
798         }
799     } else {
800         fw->activateWindow();
801         fw->raise();
802     }
803 }
804
805 void QDesignerWorkbench::closeAllToolWindows()
806 {
807     foreach (QDesignerToolWindow *tw, m_toolWindows)
808         tw->hide();
809 }
810
811 bool QDesignerWorkbench::readInBackup()
812 {
813     const QMap<QString, QString> backupFileMap = QDesignerSettings(m_core).backup();
814     if (backupFileMap.isEmpty())
815         return false;
816
817     const  QMessageBox::StandardButton answer =
818         QMessageBox::question(0, tr("Backup Information"),
819                                  tr("The last session of Designer was not terminated correctly. "
820                                        "Backup files were left behind. Do you want to load them?"),
821                                     QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
822     if (answer == QMessageBox::No)
823         return false;
824
825     const QString modifiedPlaceHolder = QLatin1String("[*]");
826     QMapIterator<QString, QString> it(backupFileMap);
827     while(it.hasNext()) {
828         it.next();
829
830         QString fileName = it.key();
831         fileName.remove(modifiedPlaceHolder);
832
833         if(m_actionManager->readInForm(it.value()))
834             formWindowManager()->activeFormWindow()->setFileName(fileName);
835
836     }
837     return true;
838 }
839
840 void QDesignerWorkbench::updateBackup(QDesignerFormWindowInterface* fwi)
841 {
842     QString fwn = QDir::convertSeparators(fwi->fileName());
843     if (fwn.isEmpty())
844         fwn = fwi->parentWidget()->windowTitle();
845
846     QDesignerSettings settings(m_core);
847     QMap<QString, QString> map = settings.backup();
848     map.remove(fwn);
849     settings.setBackup(map);
850 }
851
852 namespace {
853     void raiseWindow(QWidget *w) {
854         if (w->isMinimized())
855             w->setWindowState(w->windowState() & ~Qt::WindowMinimized);
856         w->raise();
857     }
858 }
859
860 void QDesignerWorkbench::bringAllToFront()
861 {
862     if (m_mode !=  TopLevelMode)
863         return;
864     foreach(QDesignerToolWindow *tw, m_toolWindows)
865         raiseWindow(tw);
866     foreach(QDesignerFormWindow *dfw, m_formWindows)
867         raiseWindow(dfw);
868 }
869
870 // Resize a form window taking MDI decorations into account
871 // Apply maximum size as there is no layout connection between
872 // the form's main container and the integration's outer
873 // container due to the tool widget stack.
874
875 void QDesignerWorkbench::resizeForm(QDesignerFormWindow *fw, const QWidget *mainContainer) const
876 {
877     const QSize containerSize = mainContainer->size();
878     const QSize containerMinimumSize = mainContainer->minimumSize();
879     const QSize containerMaximumSize = mainContainer->maximumSize();
880     if (m_mode != DockedMode) {
881         fw->resize(containerSize);
882         fw->setMaximumSize(containerMaximumSize);
883         return;
884     }
885     // get decorations and resize MDI
886     QMdiSubWindow *mdiSubWindow = qobject_cast<QMdiSubWindow *>(fw->parent());
887     Q_ASSERT(mdiSubWindow);
888     const QSize decorationSize = mdiSubWindow->geometry().size() - mdiSubWindow->contentsRect().size();
889     mdiSubWindow->resize(containerSize + decorationSize);
890     // In Qt::RightToLeft mode, the window can grow to be partially hidden by the right border.
891     const int mdiAreaWidth = m_dockedMainWindow->mdiArea()->width();
892     if (qApp->layoutDirection() == Qt::RightToLeft && mdiSubWindow->geometry().right() >= mdiAreaWidth)
893         mdiSubWindow->move(mdiAreaWidth - mdiSubWindow->width(), mdiSubWindow->pos().y());
894
895     if (containerMaximumSize == QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) {
896         mdiSubWindow->setMaximumSize(containerMaximumSize);
897     } else {
898         mdiSubWindow->setMaximumSize(containerMaximumSize + decorationSize);
899     }
900 }
901
902
903 // Load a form or return 0 and do cleanup. file name and editor file
904 // name in case of loading a template file.
905
906 QDesignerFormWindow * QDesignerWorkbench::loadForm(const QString &fileName,
907                                                    bool detectLineTermiantorMode,
908                                                    bool *uic3Converted,
909                                                    QString *errorMessage)
910 {
911     QFile file(fileName);
912
913     qdesigner_internal::FormWindowBase::LineTerminatorMode mode = qdesigner_internal::FormWindowBase::NativeLineTerminator;
914
915     if (detectLineTermiantorMode) {
916         if (file.open(QFile::ReadOnly)) {
917             const QString text = QString::fromUtf8(file.readLine());
918             file.close();
919
920             const int lf = text.indexOf(QLatin1Char('\n'));
921             if (lf > 0 && text.at(lf-1) == QLatin1Char('\r')) {
922                 mode = qdesigner_internal::FormWindowBase::CRLFLineTerminator;
923             } else if (lf >= 0) {
924                 mode = qdesigner_internal::FormWindowBase::LFLineTerminator;
925             }
926         }
927     }
928
929     if (!file.open(QFile::ReadOnly|QFile::Text)) {
930         *errorMessage = tr("The file <b>%1</b> could not be opened.").arg(file.fileName());
931         return 0;
932     }
933
934
935     // Create a form
936     QDesignerFormWindowManagerInterface *formWindowManager = m_core->formWindowManager();
937
938     QDesignerFormWindow *formWindow = new QDesignerFormWindow(/*formWindow=*/ 0, this);
939     addFormWindow(formWindow);
940     QDesignerFormWindowInterface *editor = formWindow->editor();
941     Q_ASSERT(editor);
942
943     // Temporarily set the file name. It is needed when converting a UIC 3 file.
944     // In this case, the file name will we be cleared on return to force a save box.
945     editor->setFileName(fileName);
946     editor->setContents(&file);
947
948     if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase *>(editor))
949         fwb->setLineTerminatorMode(mode);
950
951     switch (m_mode) {
952     case DockedMode: {
953         // below code must be after above call to setContents(), because setContents() may popup warning dialogs which would cause
954         // mdi sub window activation (because of dialogs internal call to  processEvent or such)
955         // That activation could have worse consequences, e.g. NULL resource set for active form) before the form is loaded
956         QMdiSubWindow *subWin = m_dockedMainWindow->createMdiSubWindow(formWindow, magicalWindowFlags(formWindow), m_actionManager->closeFormAction()->shortcut());
957         m_dockedMainWindow->mdiArea()->setActiveSubWindow(subWin);
958     }
959         break;
960     case TopLevelMode: {
961         const QRect formWindowGeometryHint = formWindow->geometryHint();
962         formWindow->setAttribute(Qt::WA_DeleteOnClose, true);
963         formWindow->setParent(magicalParent(formWindow), magicalWindowFlags(formWindow));
964         formWindow->resize(formWindowGeometryHint.size());
965         formWindow->move(availableGeometry().center() - formWindowGeometryHint.center());
966     }
967         break;
968     case NeutralMode:
969         break;
970     }
971
972     if (!editor->mainContainer()) {
973         removeFormWindow(formWindow);
974         formWindowManager->removeFormWindow(editor);
975         m_core->metaDataBase()->remove(editor);
976         *errorMessage = tr("The file <b>%1</b> is not a valid Designer UI file.").arg(file.fileName());
977         return 0;
978     }
979     *uic3Converted = editor->fileName().isEmpty();
980     // Did user specify another (missing) resource path -> set dirty.
981     const bool dirty = editor->property("_q_resourcepathchanged").toBool();
982     editor->setDirty(dirty);
983     resizeForm(formWindow, editor->mainContainer());
984     formWindowManager->setActiveFormWindow(editor);
985     return formWindow;
986 }
987
988
989 QDesignerFormWindow * QDesignerWorkbench::openForm(const QString &fileName, QString *errorMessage)
990 {
991     bool uic3Converted;
992     QDesignerFormWindow *rc =loadForm(fileName, true, &uic3Converted, errorMessage);
993     if (!rc)
994         return 0;
995
996     if (!uic3Converted)
997         rc->editor()->setFileName(fileName);
998     rc->firstShow();
999     return rc;
1000 }
1001
1002 QDesignerFormWindow * QDesignerWorkbench::openTemplate(const QString &templateFileName,
1003                                                        const QString &editorFileName,
1004                                                        QString *errorMessage)
1005 {
1006     bool uic3Converted;
1007     QDesignerFormWindow *rc =loadForm(templateFileName, false, &uic3Converted, errorMessage);
1008     if (!rc)
1009         return 0;
1010
1011     if (!uic3Converted)
1012         rc->editor()->setFileName(editorFileName);
1013
1014     rc->firstShow();
1015     return rc;
1016 }
1017
1018 void QDesignerWorkbench::minimizationStateChanged(QDesignerFormWindowInterface *formWindow, bool minimized)
1019 {
1020     // refresh the minimize action state
1021     if (core()->formWindowManager()->activeFormWindow() == formWindow) {
1022         m_actionManager->minimizeAction()->setChecked(minimized);
1023     }
1024 }
1025
1026 void QDesignerWorkbench::toggleFormMinimizationState()
1027 {
1028     QDesignerFormWindowInterface *fwi = core()->formWindowManager()->activeFormWindow();
1029     if (!fwi || m_mode == NeutralMode)
1030         return;
1031     QDesignerFormWindow *fw = qobject_cast<QDesignerFormWindow *>(fwi->parentWidget());
1032     Q_ASSERT(fw);
1033     setFormWindowMinimized(fw, !isFormWindowMinimized(fw));
1034 }
1035
1036 bool QDesignerWorkbench::isFormWindowMinimized(const QDesignerFormWindow *fw)
1037 {
1038     switch (m_mode) {
1039     case DockedMode:
1040         return mdiSubWindowOf(fw)->isShaded();
1041     case TopLevelMode:
1042         return fw->window()->isMinimized();
1043     default:
1044         break;
1045     }
1046     return fw->isMinimized();
1047 }
1048
1049 void QDesignerWorkbench::setFormWindowMinimized(QDesignerFormWindow *fw, bool minimized)
1050 {
1051     switch (m_mode) {
1052     case DockedMode: {
1053         QMdiSubWindow *mdiSubWindow = mdiSubWindowOf(fw);
1054         if (minimized) {
1055             mdiSubWindow->showShaded();
1056         } else {
1057             mdiSubWindow->setWindowState(mdiSubWindow->windowState() & ~Qt::WindowMinimized);
1058         }
1059     }
1060         break;
1061     case TopLevelMode:        {
1062         QWidget *window = fw->window();
1063         if (window->isMinimized()) {
1064             window->setWindowState(window->windowState() & ~Qt::WindowMinimized);
1065         } else {
1066             window->showMinimized();
1067         }
1068     }
1069         break;
1070     default:
1071         break;
1072     }
1073 }
1074
1075 void QDesignerWorkbench::restoreUISettings()
1076 {
1077     UIMode mode = QDesignerSettings(m_core).uiMode();
1078     switch (mode) {
1079         case TopLevelMode:
1080             switchToTopLevelMode();
1081             break;
1082         case DockedMode:
1083             switchToDockedMode();
1084             break;
1085
1086         default: Q_ASSERT(0);
1087     }
1088
1089     ToolWindowFontSettings fontSettings = QDesignerSettings(m_core).toolWindowFont();
1090     const QFont &font = fontSettings.m_useFont ? fontSettings.m_font : qApp->font();
1091
1092     if (font == m_toolWindows.front()->font())
1093         return;
1094
1095     foreach(QDesignerToolWindow *tw, m_toolWindows)
1096         tw->setFont(font);
1097 }
1098
1099 void QDesignerWorkbench::handleCloseEvent(QCloseEvent *ev)
1100 {
1101     ev->setAccepted(handleClose());
1102     if (ev->isAccepted())
1103         QMetaObject::invokeMethod(qDesigner, "quit", Qt::QueuedConnection);  // We're going down!
1104 }
1105
1106 QDesignerToolWindow *QDesignerWorkbench::widgetBoxToolWindow() const
1107 {
1108     return m_toolWindows.at(QDesignerToolWindow::WidgetBox);
1109 }
1110
1111 QT_END_NAMESPACE