|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 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 documentation of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 /*! |
|
43 \example draganddrop/fridgemagnets |
|
44 \title Fridge Magnets Example |
|
45 |
|
46 The Fridge Magnets example shows how to supply more than one type |
|
47 of MIME-encoded data with a drag and drop operation. |
|
48 |
|
49 \image fridgemagnets-example.png |
|
50 |
|
51 With this application the user can play around with a collection |
|
52 of fridge magnets, using drag and drop to form new sentences from |
|
53 the words on the magnets. The example consists of two classes: |
|
54 |
|
55 \list |
|
56 \o \c DragLabel is a custom widget representing one |
|
57 single fridge magnet. |
|
58 \o \c DragWidget provides the main application window. |
|
59 \endlist |
|
60 |
|
61 We will first take a look at the \c DragLabel class, then we will |
|
62 examine the \c DragWidget class. |
|
63 |
|
64 \section1 DragLabel Class Definition |
|
65 |
|
66 Each fridge magnet is represented by an instance of the \c |
|
67 DragLabel class: |
|
68 |
|
69 \snippet examples/draganddrop/fridgemagnets/draglabel.h 0 |
|
70 |
|
71 Each instance of this QLabel subclass will be used to display an |
|
72 pixmap generated from a text string. Since we cannot store both |
|
73 text and a pixmap in a standard label, we declare a private variable |
|
74 to hold the original text, and we define an additional member |
|
75 function to allow it to be accessed. |
|
76 |
|
77 \section1 DragLabel Class Implementation |
|
78 |
|
79 In the \c DragLabel constructor, we first create a QImage object |
|
80 on which we will draw the fridge magnet's text and frame: |
|
81 |
|
82 \snippet examples/draganddrop/fridgemagnets/draglabel.cpp 0 |
|
83 |
|
84 Its size depends on the current font size, and its format is |
|
85 QImage::Format_ARGB32_Premultiplied; i.e., the image is stored |
|
86 using a premultiplied 32-bit ARGB format (0xAARRGGBB). |
|
87 |
|
88 We then construct a font object that uses the application's |
|
89 default font, and set its style strategy. The style strategy tells |
|
90 the font matching algorithm what type of fonts should be used to |
|
91 find an appropriate default family. The QFont::ForceOutline forces |
|
92 the use of outline fonts. |
|
93 |
|
94 To draw the text and frame onto the image, we use the QPainter |
|
95 class. QPainter provides highly optimized methods to do most of |
|
96 the drawing GUI programs require. It can draw everything from |
|
97 simple lines to complex shapes like pies and chords. It can also |
|
98 draw aligned text and pixmaps. |
|
99 |
|
100 \snippet examples/draganddrop/fridgemagnets/draglabel.cpp 1 |
|
101 |
|
102 A painter can be activated by passing a paint device to the |
|
103 constructor, or by using the \l{QPainter::}{begin()} method as we |
|
104 do in this example. The \l{QPainter::}{end()} method deactivates |
|
105 it. Note that the latter function is called automatically upon |
|
106 destruction when the painter is actived by its constructor. The |
|
107 QPainter::Antialiasing render hint ensures that the paint engine |
|
108 will antialias the edges of primitives if possible. |
|
109 |
|
110 When the painting is done, we convert our image to a pixmap using |
|
111 QPixmap's \l {QPixmap::}{fromImage()} method. This method also |
|
112 takes an optional flags argument, and converts the given image to |
|
113 a pixmap using the specified flags to control the conversion (the |
|
114 flags argument is a bitwise-OR of the Qt::ImageConversionFlags; |
|
115 passing 0 for flags sets all the default options). |
|
116 |
|
117 \snippet examples/draganddrop/fridgemagnets/draglabel.cpp 2 |
|
118 |
|
119 Finally, we set the label's \l{QLabel::pixmap}{pixmap property} |
|
120 and store the label's text for later use. |
|
121 |
|
122 \e{Note that setting the pixmap clears any previous content, including |
|
123 any text previously set using QLabel::setText(), and disables |
|
124 the label widget's buddy shortcut, if any.} |
|
125 |
|
126 \section1 DragWidget Class Definition |
|
127 |
|
128 The \c DragWidget class inherits QWidget, providing support for |
|
129 drag and drop operations: |
|
130 |
|
131 \snippet examples/draganddrop/fridgemagnets/dragwidget.h 0 |
|
132 |
|
133 To make the widget responsive to drag and drop operations, we simply |
|
134 reimplement the \l{QWidget::}{dragEnterEvent()}, |
|
135 \l{QWidget::}{dragMoveEvent()} and \l{QWidget::}{dropEvent()} event |
|
136 handlers inherited from QWidget. |
|
137 |
|
138 We also reimplement \l{QWidget::}{mousePressEvent()} to make the |
|
139 widget responsive to mouse clicks. This is where we will write code |
|
140 to start drag and drop operations. |
|
141 |
|
142 \section1 DragWidget Class Implementation |
|
143 |
|
144 In the constructor, we first open the file containing the words on |
|
145 our fridge magnets: |
|
146 |
|
147 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 0 |
|
148 |
|
149 QFile is an I/O device for reading and writing text and binary |
|
150 files and resources, and may be used by itself or in combination |
|
151 with QTextStream or QDataStream. We have chosen to read the |
|
152 contents of the file using the QTextStream class that provides a |
|
153 convenient interface for reading and writing text. |
|
154 |
|
155 We then create the fridge magnets. As long as there is data (the |
|
156 QTextStream::atEnd() method returns true if there is no more data |
|
157 to be read from the stream), we read one line at a time using |
|
158 QTextStream's \l {QTextStream::}{readLine()} method. |
|
159 |
|
160 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 1 |
|
161 |
|
162 For each line, we create a \c DragLabel object using the read line |
|
163 as text, we calculate its position and ensure that it is visible by |
|
164 calling the QWidget::show() method. We set the Qt::WA_DeleteOnClose |
|
165 attribute on each label to ensure that any unused labels will be |
|
166 deleted; we will need to create new labels and delete old ones when |
|
167 they are dragged around, and this ensures that the example does not |
|
168 leak memory. |
|
169 |
|
170 We also set the \c FridgeMagnets widget's palette, minimum size |
|
171 and window title. |
|
172 |
|
173 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 2 |
|
174 |
|
175 Finally, to enable our user to move the fridge magnets around, we |
|
176 must also set the \c FridgeMagnets widget's |
|
177 \l{QWidget::acceptDrops}{acceptDrops} property. |
|
178 |
|
179 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 3 |
|
180 |
|
181 Setting this property to true announces to the system that this |
|
182 widget \e may be able to accept drop events (events that are sent |
|
183 when drag and drop actions are completed). Later, we will |
|
184 implement the functions that ensure that the widget accepts the |
|
185 drop events it is interested in. |
|
186 |
|
187 \section2 Dragging |
|
188 |
|
189 Let's take a look at the \l{QWidget::}{mousePressEvent()} event |
|
190 handler, where drag and drop operations begin: |
|
191 |
|
192 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 13 |
|
193 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 14 |
|
194 |
|
195 Mouse events occur when a mouse button is pressed or released |
|
196 inside a widget, or when the mouse cursor is moved. By |
|
197 reimplementing the \l{QWidget::}{mousePressEvent()} method we |
|
198 ensure that we will receive mouse press events for the widget |
|
199 containing the fridge magnets. |
|
200 |
|
201 Whenever we receive such an event, we first check to see if the |
|
202 position of the click coincides with one of the labels. If not, |
|
203 we simply return. |
|
204 |
|
205 If the user clicked a label, we determine the position of the |
|
206 \e{hot spot} (the position of the click relative to the top-left |
|
207 corner of the label). We create a byte array to store the label's |
|
208 text and the hot spot, and we use a QDataStream object to stream |
|
209 the data into the byte array. |
|
210 |
|
211 With all the information in place, we create a new QMimeData object. |
|
212 As mentioned above, QMimeData objects associate the data that they |
|
213 hold with the corresponding MIME types to ensure that information |
|
214 can be safely transferred between applications. The |
|
215 \l{QMimeData::}{setData()} method sets the data associated with a |
|
216 given MIME type. In our case, we associate our item data with the |
|
217 custom \c application/x-fridgemagnet type. |
|
218 |
|
219 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 15 |
|
220 |
|
221 Note that we also associate the magnet's text with the |
|
222 \c text/plain MIME type using QMimeData's \l{QMimeData::}{setText()} |
|
223 method. Below, we will see how our widget detects both these MIME |
|
224 types with its event handlers. |
|
225 |
|
226 Finally, we create a QDrag object. It is the QDrag class that |
|
227 handles most of the details of a drag and drop operation, |
|
228 providing support for MIME-based drag and drop data transfer. The |
|
229 data to be transferred by the drag and drop operation is contained |
|
230 in a QMimeData object. When we call QDrag's |
|
231 \l{QDrag::}{setMimeData()} method the ownership of our item data is |
|
232 transferred to the QDrag object. |
|
233 |
|
234 We call the \l{QDrag::}{setPixmap()} function to set the pixmap used |
|
235 to represent the data during the drag and drop operation. |
|
236 Typically, this pixmap shows an icon that represents the MIME type |
|
237 of the data being transferred, but any pixmap can be used. In this |
|
238 example, we simply use the pixmap used by the label itself to make |
|
239 it look like the fridge magnet itself is being moved. |
|
240 |
|
241 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 16 |
|
242 |
|
243 We also specify the cursor's hot spot, its position relative to the |
|
244 top-level corner of the drag pixmap, to be the point we calculated |
|
245 above. This makes the process of dragging the label feel more natural |
|
246 because the cursor always points to the same place on the label |
|
247 during the drag operation. |
|
248 |
|
249 We start the drag operation using QDrag's \l{QDrag::}{exec()} function, |
|
250 requesting that the magnet is copied when the drag is completed. |
|
251 |
|
252 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 17 |
|
253 |
|
254 The function returns the drop action actually performed by the user |
|
255 (this can be either a copy or a move action in this case); if this |
|
256 action is equal to Qt::MoveAction we will close the activated |
|
257 fridge magnet widget because we will create a new one to replace it |
|
258 (see the \l{drop}{dropEvent()} implementation). Otherwise, if |
|
259 the drop is outside our main widget, we simply show the widget in |
|
260 its original position. |
|
261 |
|
262 \section2 Dropping |
|
263 |
|
264 When a a drag and drop action enters our widget, we will receive a |
|
265 drag enter \e event. QDragEnterEvent inherits most of its |
|
266 functionality from QDragMoveEvent, which in turn inherits most of |
|
267 its functionality from QDropEvent. Note that we must accept this |
|
268 event in order to receive the drag move events that are sent while |
|
269 the drag and drop action is in progress. The drag enter event is |
|
270 always immediately followed by a drag move event. |
|
271 |
|
272 In our \c dragEnterEvent() implementation, we first determine |
|
273 whether we support the event's MIME type or not: |
|
274 |
|
275 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 4 |
|
276 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 5 |
|
277 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 6 |
|
278 |
|
279 If the type is \c application/x-fridgemagnet and the event |
|
280 origins from any of this application's fridge magnet widgets, we |
|
281 first set the event's drop action using the |
|
282 QDropEvent::setDropAction() method. An event's drop action is the |
|
283 action to be performed on the data by the target. Qt::MoveAction |
|
284 indicates that the data is moved from the source to the target. |
|
285 |
|
286 Then we call the event's \l {QDragMoveEvent::}{accept()} method to |
|
287 indicate that we have handled the event. In general, unaccepted |
|
288 events might be propagated to the parent widget. If the event |
|
289 origins from any other widget, we simply accept the proposed |
|
290 action. |
|
291 |
|
292 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 7 |
|
293 |
|
294 We also accept the proposed action if the event's MIME type is \c |
|
295 text/plain, i.e., if QMimeData::hasText() returns true. If the |
|
296 event has any other type, on the other hand, we call the event's |
|
297 \l {QDragMoveEvent::}{ignore()} method allowing the event to be |
|
298 propagated further. |
|
299 |
|
300 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 8 |
|
301 |
|
302 Drag move events occur when the cursor enters a widget, when it |
|
303 moves within the widget, and when a modifier key is pressed on the |
|
304 keyboard while the widget has focus. Our widget will receive drag |
|
305 move events repeatedly while a drag is within its boundaries. We |
|
306 reimplement the \l {QWidget::}{dragMoveEvent()} method, and |
|
307 examine the event in the exact same way as we did with drag enter |
|
308 events. |
|
309 |
|
310 Note that the \l{QWidget::}{dropEvent()} event handler behaves |
|
311 slightly differently: We first get hold of the event's MIME |
|
312 data. |
|
313 |
|
314 \target drop |
|
315 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 9 |
|
316 |
|
317 The QMimeData class provides a container for data that |
|
318 records information about its MIME type. QMimeData objects |
|
319 associate the data that they hold with the corresponding MIME |
|
320 types to ensure that information can be safely transferred between |
|
321 applications, and copied around within the same application. |
|
322 |
|
323 We retrieve the data associated with the \c application/x-fridgemagnet |
|
324 MIME type using a data stream in order to create a new \c DragLabel |
|
325 object. |
|
326 |
|
327 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 10 |
|
328 |
|
329 The QDataStream class provides serialization of binary data to a |
|
330 QIODevice (a data stream is a binary stream of encoded information |
|
331 which is completely independent of the host computer's operating |
|
332 system, CPU or byte order). |
|
333 |
|
334 Finally, we create a label and move it to the event's position: |
|
335 |
|
336 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 11 |
|
337 |
|
338 If the source of the event is also the widget receiving the |
|
339 drop event, we set the event's drop action to Qt::MoveAction and |
|
340 call the event's \l{QDragMoveEvent::}{accept()} |
|
341 method. Otherwise, we simply accept the proposed action. This |
|
342 means that labels are moved rather than copied in the same |
|
343 window. However, if we drag a label to a second instance of the |
|
344 Fridge Magnets example, the default action is to copy it, leaving |
|
345 the original in the first instance. |
|
346 |
|
347 If the event's MIME type is \c text/plain (i.e., if |
|
348 QMimeData::hasText() returns true) we retrieve its text and split |
|
349 it into words. For each word we create a new \c DragLabel action, |
|
350 and show it at the event's position plus an offset depending on |
|
351 the number of words in the text. In the end we accept the proposed |
|
352 action. This lets the user drop selected text from a text editor or |
|
353 Web browser onto the widget to add more fridge magnets. |
|
354 |
|
355 \snippet examples/draganddrop/fridgemagnets/dragwidget.cpp 12 |
|
356 |
|
357 If the event has any other type, we call the event's |
|
358 \l{QDragMoveEvent::}{ignore()} method allowing the event to be |
|
359 propagated further. |
|
360 |
|
361 \section1 Summary |
|
362 |
|
363 We set our main widget's \l{QWidget::}{acceptDrops} property |
|
364 and reimplemented QWidget's \l{QWidget::}{dragEnterEvent()}, |
|
365 \l{QWidget::}{dragMoveEvent()} and \l{QWidget::}{dropEvent()} event |
|
366 handlers to support content dropped on our widget. |
|
367 |
|
368 In addition, we reimplemented the \l{QWidget::}{mousePressEvent()} |
|
369 function to let the user pick up fridge magnets in the first place. |
|
370 |
|
371 Because data is communicated using drag and drop operations and |
|
372 encoded using MIME types, you can run more than one instance of this |
|
373 example, and transfer magnets between them. |
|
374 */ |