1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Designer of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "qdesigner_dnditem_p.h"
30#include "formwindowbase_p.h"
31#include <QtDesigner/private/ui4_p.h>
32
33#include <QtGui/qpainter.h>
34#include <QtGui/qbitmap.h>
35#include <QtGui/qpixmap.h>
36#include <QtGui/qimage.h>
37#include <QtWidgets/qlabel.h>
38#include <QtGui/qdrag.h>
39#include <QtGui/qcursor.h>
40#include <QtGui/qevent.h>
41#include <QtGui/qrgb.h>
42
43#include <QtCore/qmap.h>
44
45QT_BEGIN_NAMESPACE
46
47namespace qdesigner_internal {
48
49QDesignerDnDItem::QDesignerDnDItem(DropType type, QWidget *source) :
50 m_source(source),
51 m_type(type),
52 m_dom_ui(nullptr),
53 m_widget(nullptr),
54 m_decoration(nullptr)
55{
56}
57
58void QDesignerDnDItem::init(DomUI *ui, QWidget *widget, QWidget *decoration,
59 const QPoint &global_mouse_pos)
60{
61 Q_ASSERT(widget != nullptr || ui != nullptr);
62 Q_ASSERT(decoration != nullptr);
63
64 m_dom_ui = ui;
65 m_widget = widget;
66 m_decoration = decoration;
67
68 m_hot_spot = global_mouse_pos - m_decoration->geometry().topLeft();
69}
70
71QDesignerDnDItem::~QDesignerDnDItem()
72{
73 if (m_decoration != nullptr)
74 m_decoration->deleteLater();
75 delete m_dom_ui;
76}
77
78DomUI *QDesignerDnDItem::domUi() const
79{
80 return m_dom_ui;
81}
82
83QWidget *QDesignerDnDItem::decoration() const
84{
85 return m_decoration;
86}
87
88QPoint QDesignerDnDItem::hotSpot() const
89{
90 return m_hot_spot;
91}
92
93QWidget *QDesignerDnDItem::widget() const
94{
95 return m_widget;
96}
97
98QDesignerDnDItem::DropType QDesignerDnDItem::type() const
99{
100 return m_type;
101}
102
103QWidget *QDesignerDnDItem::source() const
104{
105 return m_source;
106}
107
108void QDesignerDnDItem::setDomUi(DomUI *dom_ui)
109{
110 delete m_dom_ui;
111 m_dom_ui = dom_ui;
112}
113
114// ---------- QDesignerMimeData
115
116// Make pixmap transparent on Windows only. Mac is transparent by default, Unix usually does not work.
117#ifdef Q_OS_WIN
118# define TRANSPARENT_DRAG_PIXMAP
119#endif
120
121QDesignerMimeData::QDesignerMimeData(const QDesignerDnDItems &items, QDrag *drag) :
122 m_items(items)
123{
124 enum { Alpha = 200 };
125 QPoint decorationTopLeft;
126 switch (m_items.size()) {
127 case 0:
128 break;
129 case 1: {
130 QWidget *deco = m_items.first()->decoration();
131 decorationTopLeft = deco->pos();
132 const QPixmap widgetPixmap = deco->grab(rectangle: QRect(0, 0, -1, -1));
133#ifdef TRANSPARENT_DRAG_PIXMAP
134 QImage image(widgetPixmap.size(), QImage::Format_ARGB32);
135 image.setDevicePixelRatio(widgetPixmap.devicePixelRatio());
136 image.fill(QColor(Qt::transparent).rgba());
137 QPainter painter(&image);
138 painter.drawPixmap(QPoint(0, 0), widgetPixmap);
139 painter.end();
140 setImageTransparency(image, Alpha);
141 drag->setPixmap(QPixmap::fromImage(image));
142#else
143 drag->setPixmap(widgetPixmap);
144#endif
145 }
146 break;
147 default: {
148 // determine size of drag decoration by uniting all geometries
149 const QDesignerDnDItems::const_iterator cend = m_items.constEnd();
150 QDesignerDnDItems::const_iterator it =m_items.constBegin();
151 QRect unitedGeometry = (*it)->decoration()->geometry();
152 const qreal devicePixelRatio = (*it)->decoration()->devicePixelRatioF();
153 for (++it; it != cend; ++it )
154 unitedGeometry = unitedGeometry .united(r: (*it)->decoration()->geometry());
155
156 // paint with offset. At the same time, create a mask bitmap, containing widget rectangles.
157 const QSize imageSize = (QSizeF(unitedGeometry.size()) * devicePixelRatio).toSize();
158 QImage image(imageSize, QImage::Format_ARGB32);
159 image.setDevicePixelRatio(devicePixelRatio);
160 image.fill(pixel: QColor(Qt::transparent).rgba());
161 QBitmap mask(imageSize);
162 mask.setDevicePixelRatio(devicePixelRatio);
163 mask.clear();
164 // paint with offset, determine action
165 QPainter painter(&image);
166 QPainter maskPainter(&mask);
167 decorationTopLeft = unitedGeometry.topLeft();
168 for (it = m_items.constBegin() ; it != cend; ++it ) {
169 QWidget *w = (*it)->decoration();
170 const QPixmap wp = w->grab(rectangle: QRect(0, 0, -1, -1));
171 const QPoint pos = w->pos() - decorationTopLeft;
172 painter.drawPixmap(p: pos, pm: wp);
173 maskPainter.fillRect(r: QRect(pos, w->size()), c: Qt::color1);
174 }
175 painter.end();
176 maskPainter.end();
177#ifdef TRANSPARENT_DRAG_PIXMAP
178 setImageTransparency(image, Alpha);
179#endif
180 QPixmap pixmap = QPixmap::fromImage(image);
181 pixmap.setMask(mask);
182 drag->setPixmap(pixmap);
183 }
184 break;
185 }
186 // determine hot spot and reconstruct the exact starting position as form window
187 // introduces some offset when detecting DnD
188 m_globalStartPos = m_items.first()->decoration()->pos() + m_items.first()->hotSpot();
189 m_hotSpot = m_globalStartPos - decorationTopLeft;
190 drag->setHotSpot(m_hotSpot);
191
192 drag->setMimeData(this);
193}
194
195QDesignerMimeData::~QDesignerMimeData()
196{
197 const QDesignerDnDItems::const_iterator cend = m_items.constEnd();
198 for (QDesignerDnDItems::const_iterator it = m_items.constBegin(); it != cend; ++it )
199 delete *it;
200}
201
202Qt::DropAction QDesignerMimeData::proposedDropAction() const
203{
204 return m_items.first()->type() == QDesignerDnDItemInterface::CopyDrop ? Qt::CopyAction : Qt::MoveAction;
205}
206
207Qt::DropAction QDesignerMimeData::execDrag(const QDesignerDnDItems &items, QWidget * dragSource)
208{
209 if (items.isEmpty())
210 return Qt::IgnoreAction;
211
212 QDrag *drag = new QDrag(dragSource);
213 QDesignerMimeData *mimeData = new QDesignerMimeData(items, drag);
214
215 // Store pointers to widgets that are to be re-shown if a move operation is canceled
216 QWidgetList reshowWidgets;
217 const QDesignerDnDItems::const_iterator cend = items.constEnd();
218 for (QDesignerDnDItems::const_iterator it = items.constBegin(); it != cend; ++it )
219 if (QWidget *w = (*it)->widget())
220 if ((*it)->type() == QDesignerDnDItemInterface::MoveDrop)
221 reshowWidgets.push_back(t: w);
222
223 const Qt::DropAction executedAction = drag->exec(supportedActions: Qt::CopyAction|Qt::MoveAction, defaultAction: mimeData->proposedDropAction());
224
225 if (executedAction == Qt::IgnoreAction) {
226 for (QWidget *w : qAsConst(t&: reshowWidgets))
227 w->show();
228 }
229
230 return executedAction;
231}
232
233
234void QDesignerMimeData::moveDecoration(const QPoint &globalPos) const
235{
236 const QPoint relativeDistance = globalPos - m_globalStartPos;
237 const QDesignerDnDItems::const_iterator cend = m_items.constEnd();
238 for (QDesignerDnDItems::const_iterator it =m_items.constBegin(); it != cend; ++it ) {
239 QWidget *w = (*it)->decoration();
240 w->move(w->pos() + relativeDistance);
241 }
242}
243
244void QDesignerMimeData::removeMovedWidgetsFromSourceForm(const QDesignerDnDItems &items)
245{
246 typedef QMultiMap<FormWindowBase *, QWidget *> FormWidgetMap;
247 FormWidgetMap formWidgetMap;
248 // Find moved widgets per form
249 const QDesignerDnDItems::const_iterator cend = items.constEnd();
250 for (QDesignerDnDItems::const_iterator it = items.constBegin(); it != cend; ++it )
251 if ((*it)->type() == QDesignerDnDItemInterface::MoveDrop)
252 if (QWidget *w = (*it)->widget())
253 if (FormWindowBase *fb = qobject_cast<FormWindowBase *>(object: (*it)->source()))
254 formWidgetMap.insert(akey: fb, avalue: w);
255
256 const auto &formWindows = formWidgetMap.uniqueKeys();
257 for (FormWindowBase *fb : formWindows)
258 fb->deleteWidgetList(widget_list: formWidgetMap.values(akey: fb));
259}
260
261void QDesignerMimeData::acceptEventWithAction(Qt::DropAction desiredAction, QDropEvent *e)
262{
263 if (e->proposedAction() == desiredAction) {
264 e->acceptProposedAction();
265 } else {
266 e->setDropAction(desiredAction);
267 e->accept();
268 }
269}
270
271void QDesignerMimeData::acceptEvent(QDropEvent *e) const
272{
273 acceptEventWithAction(desiredAction: proposedDropAction(), e);
274}
275
276void QDesignerMimeData::setImageTransparency(QImage &image, int alpha)
277{
278 const int height = image.height();
279 for (int l = 0; l < height; l++) {
280 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(l));
281 QRgb *lineEnd = line + image.width();
282 for ( ; line < lineEnd; line++) {
283 const QRgb rgba = *line;
284 *line = qRgba(r: qRed(rgb: rgba), g: qGreen(rgb: rgba), b: qBlue(rgb: rgba), a: alpha);
285 }
286 }
287}
288
289} // namespace qdesigner_internal
290
291QT_END_NAMESPACE
292

source code of qttools/src/designer/src/lib/shared/qdesigner_dnditem.cpp