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