1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "formwindowbase_p.h"
43 #include "connectionedit_p.h"
44 #include "qdesigner_command_p.h"
45 #include "qdesigner_propertysheet_p.h"
46 #include "qdesigner_propertyeditor_p.h"
47 #include "qdesigner_menu_p.h"
48 #include "qdesigner_menubar_p.h"
49 #include "shared_settings_p.h"
50 #include "grid_p.h"
51 #include "deviceprofile_p.h"
52 #include "qdesigner_utils_p.h"
53 
54 #include "qsimpleresource_p.h"
55 
56 #include <QtDesigner/QDesignerFormEditorInterface>
57 #include <QtDesigner/QDesignerContainerExtension>
58 #include <QtDesigner/QExtensionManager>
59 #include <QtDesigner/QDesignerTaskMenuExtension>
60 
61 #include <QtCore/qdebug.h>
62 #include <QtCore/QList>
63 #include <QtCore/QTimer>
64 #include <QtGui/QMenu>
65 #include <QtGui/QListWidget>
66 #include <QtGui/QTreeWidget>
67 #include <QtGui/QTableWidget>
68 #include <QtGui/QComboBox>
69 #include <QtGui/QTabWidget>
70 #include <QtGui/QToolBox>
71 #include <QtGui/QToolBar>
72 #include <QtGui/QStatusBar>
73 #include <QtGui/QMenu>
74 #include <QtGui/QAction>
75 #include <QtGui/QLabel>
76 
77 QT_BEGIN_NAMESPACE
78 
79 namespace qdesigner_internal {
80 
81 class FormWindowBasePrivate {
82 public:
83     explicit FormWindowBasePrivate(QDesignerFormEditorInterface *core);
84 
85     static Grid m_defaultGrid;
86 
87     QDesignerFormWindowInterface::Feature m_feature;
88     Grid m_grid;
89     bool m_hasFormGrid;
90     DesignerPixmapCache *m_pixmapCache;
91     DesignerIconCache *m_iconCache;
92     QtResourceSet *m_resourceSet;
93     QMap<QDesignerPropertySheet *, QMap<int, bool> > m_reloadableResources; // bool is dummy, QMap used as QSet
94     QMap<QDesignerPropertySheet *, QObject *> m_reloadablePropertySheets;
95     const DeviceProfile m_deviceProfile;
96     FormWindowBase::LineTerminatorMode m_lineTerminatorMode;
97     FormWindowBase::SaveResourcesBehaviour m_saveResourcesBehaviour;
98 };
99 
FormWindowBasePrivate(QDesignerFormEditorInterface * core)100 FormWindowBasePrivate::FormWindowBasePrivate(QDesignerFormEditorInterface *core) :
101     m_feature(QDesignerFormWindowInterface::DefaultFeature),
102     m_grid(m_defaultGrid),
103     m_hasFormGrid(false),
104     m_pixmapCache(0),
105     m_iconCache(0),
106     m_resourceSet(0),
107     m_deviceProfile(QDesignerSharedSettings(core).currentDeviceProfile()),
108     m_lineTerminatorMode(FormWindowBase::NativeLineTerminator),
109     m_saveResourcesBehaviour(FormWindowBase::SaveAll)
110 {
111 }
112 
113 Grid FormWindowBasePrivate::m_defaultGrid;
114 
FormWindowBase(QDesignerFormEditorInterface * core,QWidget * parent,Qt::WindowFlags flags)115 FormWindowBase::FormWindowBase(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) :
116     QDesignerFormWindowInterface(parent, flags),
117     m_d(new FormWindowBasePrivate(core))
118 {
119     syncGridFeature();
120     m_d->m_pixmapCache = new DesignerPixmapCache(this);
121     m_d->m_iconCache = new DesignerIconCache(m_d->m_pixmapCache, this);
122 }
123 
~FormWindowBase()124 FormWindowBase::~FormWindowBase()
125 {
126     delete m_d;
127 }
128 
pixmapCache() const129 DesignerPixmapCache *FormWindowBase::pixmapCache() const
130 {
131     return m_d->m_pixmapCache;
132 }
133 
iconCache() const134 DesignerIconCache *FormWindowBase::iconCache() const
135 {
136     return m_d->m_iconCache;
137 }
138 
resourceSet() const139 QtResourceSet *FormWindowBase::resourceSet() const
140 {
141     return m_d->m_resourceSet;
142 }
143 
setResourceSet(QtResourceSet * resourceSet)144 void FormWindowBase::setResourceSet(QtResourceSet *resourceSet)
145 {
146     m_d->m_resourceSet = resourceSet;
147 }
148 
addReloadableProperty(QDesignerPropertySheet * sheet,int index)149 void FormWindowBase::addReloadableProperty(QDesignerPropertySheet *sheet, int index)
150 {
151     m_d->m_reloadableResources[sheet][index] = true;
152 }
153 
removeReloadableProperty(QDesignerPropertySheet * sheet,int index)154 void FormWindowBase::removeReloadableProperty(QDesignerPropertySheet *sheet, int index)
155 {
156     m_d->m_reloadableResources[sheet].remove(index);
157     if (m_d->m_reloadableResources[sheet].count() == 0)
158         m_d->m_reloadableResources.remove(sheet);
159 }
160 
addReloadablePropertySheet(QDesignerPropertySheet * sheet,QObject * object)161 void FormWindowBase::addReloadablePropertySheet(QDesignerPropertySheet *sheet, QObject *object)
162 {
163     if (qobject_cast<QTreeWidget *>(object) ||
164             qobject_cast<QTableWidget *>(object) ||
165             qobject_cast<QListWidget *>(object) ||
166             qobject_cast<QComboBox *>(object))
167         m_d->m_reloadablePropertySheets[sheet] = object;
168 }
169 
removeReloadablePropertySheet(QDesignerPropertySheet * sheet)170 void FormWindowBase::removeReloadablePropertySheet(QDesignerPropertySheet *sheet)
171 {
172     m_d->m_reloadablePropertySheets.remove(sheet);
173 }
174 
reloadProperties()175 void FormWindowBase::reloadProperties()
176 {
177     pixmapCache()->clear();
178     iconCache()->clear();
179     QMapIterator<QDesignerPropertySheet *, QMap<int, bool> > itSheet(m_d->m_reloadableResources);
180     while (itSheet.hasNext()) {
181         QDesignerPropertySheet *sheet = itSheet.next().key();
182         QMapIterator<int, bool> itIndex(itSheet.value());
183         while (itIndex.hasNext()) {
184             const int index = itIndex.next().key();
185             const QVariant newValue = sheet->property(index);
186             if (qobject_cast<QLabel *>(sheet->object()) && sheet->propertyName(index) == QLatin1String("text")) {
187                 const PropertySheetStringValue newString = qvariant_cast<PropertySheetStringValue>(newValue);
188                 // optimize a bit, reset only if the text value might contain a reference to qt resources
189                 // (however reloading of icons other than taken from resources might not work here)
190                 if (newString.value().contains(QLatin1String(":/"))) {
191                     const QVariant resetValue = QVariant::fromValue(PropertySheetStringValue());
192                     sheet->setProperty(index, resetValue);
193                 }
194             }
195             sheet->setProperty(index, newValue);
196         }
197         if (QTabWidget *tabWidget = qobject_cast<QTabWidget *>(sheet->object())) {
198             const int count = tabWidget->count();
199             const int current = tabWidget->currentIndex();
200             const QString currentTabIcon = QLatin1String("currentTabIcon");
201             for (int i = 0; i < count; i++) {
202                 tabWidget->setCurrentIndex(i);
203                 const int index = sheet->indexOf(currentTabIcon);
204                 sheet->setProperty(index, sheet->property(index));
205             }
206             tabWidget->setCurrentIndex(current);
207         } else if (QToolBox *toolBox = qobject_cast<QToolBox *>(sheet->object())) {
208             const int count = toolBox->count();
209             const int current = toolBox->currentIndex();
210             const QString currentItemIcon = QLatin1String("currentItemIcon");
211             for (int i = 0; i < count; i++) {
212                 toolBox->setCurrentIndex(i);
213                 const int index = sheet->indexOf(currentItemIcon);
214                 sheet->setProperty(index, sheet->property(index));
215             }
216             toolBox->setCurrentIndex(current);
217         }
218     }
219     QMapIterator<QDesignerPropertySheet *, QObject *> itSh(m_d->m_reloadablePropertySheets);
220     while (itSh.hasNext()) {
221         QObject *object = itSh.next().value();
222         reloadIconResources(iconCache(), object);
223     }
224 }
225 
resourceSetActivated(QtResourceSet * resource,bool resourceSetChanged)226 void FormWindowBase::resourceSetActivated(QtResourceSet *resource, bool resourceSetChanged)
227 {
228     if (resource == resourceSet() && resourceSetChanged) {
229         reloadProperties();
230         emit pixmapCache()->reloaded();
231         emit iconCache()->reloaded();
232         if (QDesignerPropertyEditor *propertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor()))
233             propertyEditor->reloadResourceProperties();
234     }
235 }
236 
formData()237 QVariantMap FormWindowBase::formData()
238 {
239     QVariantMap rc;
240     if (m_d->m_hasFormGrid)
241         m_d->m_grid.addToVariantMap(rc, true);
242     return rc;
243 }
244 
setFormData(const QVariantMap & vm)245 void FormWindowBase::setFormData(const QVariantMap &vm)
246 {
247     Grid formGrid;
248     m_d->m_hasFormGrid = formGrid.fromVariantMap(vm);
249     if (m_d->m_hasFormGrid)
250          m_d->m_grid = formGrid;
251 }
252 
grid() const253 QPoint FormWindowBase::grid() const
254 {
255     return QPoint(m_d->m_grid.deltaX(), m_d->m_grid.deltaY());
256 }
257 
setGrid(const QPoint & grid)258 void FormWindowBase::setGrid(const QPoint &grid)
259 {
260     m_d->m_grid.setDeltaX(grid.x());
261     m_d->m_grid.setDeltaY(grid.y());
262 }
263 
hasFeature(Feature f) const264 bool FormWindowBase::hasFeature(Feature f) const
265 {
266     return f & m_d->m_feature;
267 }
268 
recursiveUpdate(QWidget * w)269 static void recursiveUpdate(QWidget *w)
270 {
271     w->update();
272 
273     const QObjectList &l = w->children();
274     const QObjectList::const_iterator cend = l.constEnd();
275     for (QObjectList::const_iterator it = l.constBegin(); it != cend; ++it) {
276         if (QWidget *w = qobject_cast<QWidget*>(*it))
277             recursiveUpdate(w);
278     }
279 }
280 
setFeatures(Feature f)281 void FormWindowBase::setFeatures(Feature f)
282 {
283     m_d->m_feature = f;
284     const bool enableGrid = f & GridFeature;
285     m_d->m_grid.setVisible(enableGrid);
286     m_d->m_grid.setSnapX(enableGrid);
287     m_d->m_grid.setSnapY(enableGrid);
288     emit featureChanged(f);
289     recursiveUpdate(this);
290 }
291 
features() const292 FormWindowBase::Feature FormWindowBase::features() const
293 {
294     return m_d->m_feature;
295 }
296 
gridVisible() const297 bool FormWindowBase::gridVisible() const
298 {
299     return m_d->m_grid.visible() && currentTool() == 0;
300 }
301 
saveResourcesBehaviour() const302 FormWindowBase::SaveResourcesBehaviour FormWindowBase::saveResourcesBehaviour() const
303 {
304     return m_d->m_saveResourcesBehaviour;
305 }
306 
setSaveResourcesBehaviour(SaveResourcesBehaviour behaviour)307 void FormWindowBase::setSaveResourcesBehaviour(SaveResourcesBehaviour behaviour)
308 {
309     m_d->m_saveResourcesBehaviour = behaviour;
310 }
311 
syncGridFeature()312 void FormWindowBase::syncGridFeature()
313 {
314     if (m_d->m_grid.snapX() || m_d->m_grid.snapY())
315         m_d->m_feature |= GridFeature;
316     else
317         m_d->m_feature &= ~GridFeature;
318 }
319 
setDesignerGrid(const Grid & grid)320 void FormWindowBase::setDesignerGrid(const  Grid& grid)
321 {
322     m_d->m_grid = grid;
323     syncGridFeature();
324     recursiveUpdate(this);
325 }
326 
designerGrid() const327 const Grid &FormWindowBase::designerGrid() const
328 {
329     return m_d->m_grid;
330 }
331 
hasFormGrid() const332 bool FormWindowBase::hasFormGrid() const
333 {
334     return m_d->m_hasFormGrid;
335 }
336 
setHasFormGrid(bool b)337 void FormWindowBase::setHasFormGrid(bool b)
338 {
339     m_d->m_hasFormGrid = b;
340 }
341 
setDefaultDesignerGrid(const Grid & grid)342 void FormWindowBase::setDefaultDesignerGrid(const Grid& grid)
343 {
344     FormWindowBasePrivate::m_defaultGrid = grid;
345 }
346 
defaultDesignerGrid()347 const Grid &FormWindowBase::defaultDesignerGrid()
348 {
349     return FormWindowBasePrivate::m_defaultGrid;
350 }
351 
initializePopupMenu(QWidget *)352 QMenu *FormWindowBase::initializePopupMenu(QWidget * /*managedWidget*/)
353 {
354     return 0;
355 }
356 
357 // Widget under mouse for finding the Widget to highlight
358 // when doing DnD. Restricts to pages by geometry if a container with
359 // a container extension (or one of its helper widgets) is hit; otherwise
360 // returns the widget as such (be it managed/unmanaged)
361 
widgetUnderMouse(const QPoint & formPos,WidgetUnderMouseMode)362 QWidget *FormWindowBase::widgetUnderMouse(const QPoint &formPos, WidgetUnderMouseMode /* wum */)
363 {
364     // widget_under_mouse might be some temporary thing like the dropLine. We need
365     // the actual widget that's part of the edited GUI.
366     QWidget *rc = widgetAt(formPos);
367     if (!rc || qobject_cast<ConnectionEdit*>(rc))
368         return 0;
369 
370     if (rc == mainContainer()) {
371         // Refuse main container areas if the main container has a container extension,
372         // for example when hitting QToolBox/QTabWidget empty areas.
373         if (qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), rc))
374             return 0;
375         return rc;
376     }
377 
378     // If we hit on container extension type container, make sure
379     // we use the top-most current page
380     if (QWidget *container = findContainer(rc, false))
381         if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), container)) {
382             // For container that do not have a "stacked" nature (QToolBox, QMdiArea),
383             // make sure the position is within the current page
384             const int ci = c->currentIndex();
385             if (ci < 0)
386                 return 0;
387             QWidget *page = c->widget(ci);
388             QRect pageGeometry = page->geometry();
389             pageGeometry.moveTo(page->mapTo(this, pageGeometry.topLeft()));
390             if (!pageGeometry.contains(formPos))
391                 return 0;
392             return page;
393         }
394 
395     return rc;
396 }
397 
deleteWidgetList(const QWidgetList & widget_list)398 void FormWindowBase::deleteWidgetList(const QWidgetList &widget_list)
399 {
400     // We need a macro here even for single widgets because the some components (for example,
401     // the signal slot editor are connected to widgetRemoved() and add their
402     // own commands (for example, to delete w's connections)
403     const QString description = widget_list.size() == 1 ?
404         tr("Delete '%1'").arg(widget_list.front()->objectName()) : tr("Delete");
405 
406     commandHistory()->beginMacro(description);
407     foreach (QWidget *w, widget_list) {
408         emit widgetRemoved(w);
409         DeleteWidgetCommand *cmd = new DeleteWidgetCommand(this);
410         cmd->init(w);
411         commandHistory()->push(cmd);
412     }
413     commandHistory()->endMacro();
414 }
415 
createExtensionTaskMenu(QDesignerFormWindowInterface * fw,QObject * o,bool trailingSeparator)416 QMenu *FormWindowBase::createExtensionTaskMenu(QDesignerFormWindowInterface *fw, QObject *o, bool trailingSeparator)
417 {
418     typedef QList<QAction *> ActionList;
419     ActionList actions;
420     // 1) Standard public extension
421     QExtensionManager *em = fw->core()->extensionManager();
422     if (const QDesignerTaskMenuExtension *extTaskMenu = qt_extension<QDesignerTaskMenuExtension*>(em, o))
423         actions += extTaskMenu->taskActions();
424     if (const QDesignerTaskMenuExtension *intTaskMenu = qobject_cast<QDesignerTaskMenuExtension *>(em->extension(o, QLatin1String("QDesignerInternalTaskMenuExtension")))) {
425         if (!actions.empty()) {
426             QAction *a = new QAction(fw);
427             a->setSeparator(true);
428             actions.push_back(a);
429         }
430         actions += intTaskMenu->taskActions();
431     }
432     if (actions.empty())
433         return 0;
434     if (trailingSeparator && !actions.back()->isSeparator()) {
435         QAction *a  = new QAction(fw);
436         a->setSeparator(true);
437         actions.push_back(a);
438     }
439     QMenu *rc = new QMenu;
440     const ActionList::const_iterator cend = actions.constEnd();
441     for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it)
442         rc->addAction(*it);
443     return rc;
444 }
445 
emitObjectRemoved(QObject * o)446 void FormWindowBase::emitObjectRemoved(QObject *o)
447 {
448     emit objectRemoved(o);
449 }
450 
deviceProfile() const451 DeviceProfile FormWindowBase::deviceProfile() const
452 {
453     return m_d->m_deviceProfile;
454 }
455 
styleName() const456 QString FormWindowBase::styleName() const
457 {
458     return m_d->m_deviceProfile.isEmpty() ? QString() : m_d->m_deviceProfile.style();
459 }
460 
emitWidgetRemoved(QWidget * w)461 void FormWindowBase::emitWidgetRemoved(QWidget *w)
462 {
463     emit widgetRemoved(w);
464 }
465 
deviceProfileName() const466 QString FormWindowBase::deviceProfileName() const
467 {
468     return m_d->m_deviceProfile.isEmpty() ? QString() : m_d->m_deviceProfile.name();
469 }
470 
setLineTerminatorMode(FormWindowBase::LineTerminatorMode mode)471 void FormWindowBase::setLineTerminatorMode(FormWindowBase::LineTerminatorMode mode)
472 {
473     m_d->m_lineTerminatorMode = mode;
474 }
475 
lineTerminatorMode() const476 FormWindowBase::LineTerminatorMode FormWindowBase::lineTerminatorMode() const
477 {
478     return m_d->m_lineTerminatorMode;
479 }
480 
triggerDefaultAction(QWidget * widget)481 void FormWindowBase::triggerDefaultAction(QWidget *widget)
482 {
483     if (QAction *action = qdesigner_internal::preferredEditAction(core(), widget))
484         QTimer::singleShot(0, action, SIGNAL(triggered()));
485 }
486 
setupDefaultAction(QDesignerFormWindowInterface * fw)487 void FormWindowBase::setupDefaultAction(QDesignerFormWindowInterface *fw)
488 {
489     QObject::connect(fw, SIGNAL(activated(QWidget*)), fw, SLOT(triggerDefaultAction(QWidget*)));
490 }
491 
fileContents() const492 QString FormWindowBase::fileContents() const
493 {
494     const bool oldValue = QSimpleResource::setWarningsEnabled(false);
495     const QString rc = contents();
496     QSimpleResource::setWarningsEnabled(oldValue);
497     return rc;
498 }
499 
500 } // namespace qdesigner_internal
501 
502 QT_END_NAMESPACE
503