source: trunk/src/gui/kernel/qmime_pm.cpp@ 626

Last change on this file since 626 was 580, checked in by Dmitry A. Kuminov, 16 years ago

gui: Removed qmime.h dependency on private qpmobjectwindow_pm_p.h and on qt_os2.h (which drags in non-portable OS/2 API).

File size: 98.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** Copyright (C) 2009 netlabs.org. OS/2 parts.
8**
9** This file is part of the QtGui module of the Qt Toolkit.
10**
11** $QT_BEGIN_LICENSE:LGPL$
12** Commercial Usage
13** Licensees holding valid Qt Commercial licenses may use this file in
14** accordance with the Qt Commercial License Agreement provided with the
15** Software or, alternatively, in accordance with the terms contained in
16** a written agreement between you and Nokia.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 2.1 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 2.1 requirements
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** In addition, as a special exception, Nokia gives you certain additional
27** rights. These rights are described in the Nokia Qt 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** If you have questions regarding the use of this file, please contact
39** Nokia at [email protected].
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "qmime.h"
45
46#include "qimagereader.h"
47#include "qimagewriter.h"
48#include "qdatastream.h"
49#include "qbuffer.h"
50#include "qt_os2.h"
51#include "qapplication_p.h"
52#include "qtextcodec.h"
53#include "qregexp.h"
54#include "qalgorithms.h"
55#include "qmap.h"
56#include "qdnd_p.h"
57#include "qurl.h"
58#include "qvariant.h"
59#include "qtextdocument.h"
60#include "qdir.h"
61
62#include "qt_os2.h"
63#include "private/qpmobjectwindow_pm_p.h"
64
65//#define QDND_DEBUG // in pair with qdnd_pm.cpp
66
67#ifdef QDND_DEBUG
68# include "qdebug.h"
69# define DEBUG(a) qDebug a
70#else
71# define DEBUG(a) do {} while(0)
72#endif
73
74QT_BEGIN_NAMESPACE
75
76#if !defined(QT_NO_DRAGANDDROP)
77
78// Undoc'd DC_PREPAREITEM, see
79// http://lxr.mozilla.org/seamonkey/source/widget/src/os2/nsDragService.cpp
80#if !defined (DC_PREPAREITEM)
81#define DC_PREPAREITEM 0x40
82#endif
83
84/*! \internal
85 According to my tests, DrgFreeDragtransfer() appears to be bogus: when the
86 drag source attempts to free the DRAGTRANSFER structure passed to it in
87 DM_RENDERPREPARE/DM_RENDER by another process, the shared memory object is not
88 actually released until DrgFreeDragtransfer() is called for the second time.
89 This method tries to fix this problem.
90
91 \note The problem (and the solution) was not tested on platforms other than
92 eCS!
93*/
94void qt_DrgFreeDragtransfer(DRAGTRANSFER *xfer)
95{
96 Q_ASSERT(xfer);
97 if (xfer) {
98 BOOL ok = DrgFreeDragtransfer(xfer);
99 Q_ASSERT(ok);
100 if (ok) {
101 ULONG size = ~0, flags = 0;
102 APIRET rc = DosQueryMem(xfer, &size, &flags);
103 Q_ASSERT(rc == 0);
104 if (rc == 0 && !(flags & PAG_FREE)) {
105 PID pid;
106 TID tid;
107 ok = WinQueryWindowProcess(xfer->hwndClient, &pid, &tid);
108 Q_ASSERT(ok);
109 if (ok) {
110 PPIB ppib = 0;
111 DosGetInfoBlocks(0, &ppib);
112 if (ppib->pib_ulpid != pid) {
113 DEBUG(() << "qt_DrgFreeDragtransfer: Will free xfer"
114 << xfer << "TWICE (other process)!");
115 DrgFreeDragtransfer(xfer);
116 }
117 }
118 }
119 }
120 }
121}
122
123#define SEA_TYPE ".TYPE"
124
125/*! \internal
126 Sets a single .TYPE EA vaule on a given fle.
127*/
128static void qt_SetFileTypeEA(const char *name, const char *type)
129{
130 #pragma pack(1)
131
132 struct MY_FEA2 {
133 ULONG oNextEntryOffset; /* Offset to next entry. */
134 BYTE fEA; /* Extended attributes flag. */
135 BYTE cbName; /* Length of szName, not including NULL. */
136 USHORT cbValue; /* Value length. */
137 CHAR szName[0]; /* Extended attribute name. */
138 /* EA value follows here */
139 };
140
141 struct MY_FEA2LIST {
142 ULONG cbList; /* Total bytes of structure including full list. */
143 MY_FEA2 list[0]; /* Variable-length FEA2 structures. */
144 };
145
146 struct MY_FEA2_VAL {
147 USHORT usEAType; /* EA value type (one of EAT_* constants) */
148 USHORT usValueLen; /* Length of the value data following */
149 CHAR aValueData[0]; /* value data */
150 };
151
152 struct MY_FEA2_MVMT {
153 USHORT usEAType; /* Always EAT_MVMT */
154 USHORT usCodePage; /* 0 - default */
155 USHORT cbNumEntries; /* Number of MYFEA2_VAL structs following */
156 MY_FEA2_VAL aValues[0]; /* MYFEA2_VAL value structures */
157 };
158
159 #pragma pack()
160
161 uint typeLen = qstrlen(type);
162 uint valLen = sizeof(MY_FEA2_VAL) + typeLen;
163 uint mvmtLen = sizeof(MY_FEA2_MVMT) + valLen;
164 uint fea2Len = sizeof(MY_FEA2) + sizeof(SEA_TYPE);
165 uint fullLen = sizeof(MY_FEA2LIST) + fea2Len + mvmtLen;
166
167 uchar *eaData = new uchar[fullLen];
168
169 MY_FEA2LIST *fea2List = (MY_FEA2LIST *)eaData;
170 fea2List->cbList = fullLen;
171
172 MY_FEA2 *fea2 = fea2List->list;
173 fea2->oNextEntryOffset = 0;
174 fea2->fEA = 0;
175 fea2->cbName = sizeof(SEA_TYPE) - 1;
176 fea2->cbValue = mvmtLen;
177 strcpy(fea2->szName, SEA_TYPE);
178
179 MY_FEA2_MVMT *mvmt = (MY_FEA2_MVMT *)(fea2->szName + sizeof(SEA_TYPE));
180 mvmt->usEAType = EAT_MVMT;
181 mvmt->usCodePage = 0;
182 mvmt->cbNumEntries = 1;
183
184 MY_FEA2_VAL *val = mvmt->aValues;
185 val->usEAType = EAT_ASCII;
186 val->usValueLen = typeLen;
187 memcpy(val->aValueData, type, typeLen);
188
189 EAOP2 eaop2;
190 eaop2.fpGEA2List = 0;
191 eaop2.fpFEA2List = (FEA2LIST *)fea2List;
192 eaop2.oError = 0;
193
194 APIRET rc = DosSetPathInfo(name, FIL_QUERYEASIZE,
195 &eaop2, sizeof(eaop2), 0);
196 Q_UNUSED(rc);
197#ifndef QT_NO_DEBUG
198 if (rc)
199 qWarning("qt_SetFileTypeEA: DosSetPathInfo failed with %ld", rc);
200#endif
201
202 delete[] eaData;
203}
204
205static int hex_to_int(uchar c)
206{
207 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
208 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
209 if (c >= '0' && c <= '9') return c - '0';
210 return -1;
211}
212
213static inline int hex_to_int(char c)
214{
215 return hex_to_int((uchar) c);
216}
217
218//------------------------------------------------------------------------------
219
220struct QPMMime::DefaultDragWorker::Data : public QPMObjectWindow
221{
222 Data(DefaultDragWorker *worker, bool excl) : q(worker), exclusive(excl) {}
223
224 struct Request
225 {
226 Request(ULONG i, Provider *p, const char *m, const char *f)
227 : index(i), provider(p), drm(m), drf(f)
228 , xfer(0), rendered(false), sharedMem(0) {}
229
230 ~Request()
231 {
232 // free memory allocated for the target that requested DRM_SHAREDMEM
233 if (sharedMem)
234 DosFreeMem(sharedMem);
235 Q_ASSERT(!xfer);
236 }
237
238 ULONG index;
239 Provider *provider;
240 QByteArray drm;
241 QByteArray drf;
242 DRAGTRANSFER *xfer;
243 bool rendered;
244 PVOID sharedMem;
245 };
246
247 inline bool isInitialized() { return providers.count() != 0; }
248 void cleanupRequests();
249
250 // QPMObjectWindow interface
251 MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2);
252
253 struct DrfProvider
254 {
255 DrfProvider() : prov(0) {}
256 DrfProvider(const QByteArray &d, Provider *p) : drf(d), prov(p) {}
257 QByteArray drf;
258 Provider *prov;
259 };
260
261 typedef QList<DrfProvider> DrfProviderList;
262
263 Provider *providerFor(const char *drf)
264 {
265 foreach (const DrfProvider &dp, providers)
266 if (qstrcmp(dp.drf, drf) == 0)
267 return dp.prov;
268 return 0;
269 }
270
271 DefaultDragWorker *q;
272
273 const bool exclusive : 1;
274 DrfProviderList providers;
275
276 ULONG itemCnt;
277 QHash<ULONG, Request*> requests;
278 bool renderOk : 1;
279};
280
281void QPMMime::DefaultDragWorker::Data::cleanupRequests()
282{
283 if (requests.count()) {
284 DEBUG(("In the previous DnD session, the drop target sent "
285 "DM_RENDERPREPARE/DM_RENDER\n"
286 "for some drag item but didn't send DM_ENDCONVERSATION!"));
287 qDeleteAll(requests);
288 requests.clear();
289 }
290}
291
292QPMMime::DefaultDragWorker::DefaultDragWorker(bool exclusive)
293 : d(new Data(this, exclusive))
294{
295 d->itemCnt = 0;
296 d->renderOk = true;
297}
298
299QPMMime::DefaultDragWorker::~DefaultDragWorker()
300{
301 d->cleanupRequests();
302 delete d;
303}
304
305bool QPMMime::DefaultDragWorker::cleanup(bool isCancelled)
306{
307 // the return value is: true if the source-side Move for the given
308 // drag object should be *dis*allowed, false otherwise (including cases
309 // when this DragWorker didn't participate to the conversation at all)
310
311 // sanity check
312 Q_ASSERT(d->isInitialized());
313 if (!d->isInitialized())
314 return true;
315
316 bool moveDisallowed = false;
317
318 DEBUG(() << "DefaultDragWorker: Session ended ( cancelled" << isCancelled
319 << "requests.left" << d->requests.count() << ")");
320
321 if (d->requests.count()) {
322 // always disallow Move if not all requests got DM_ENDCONVERSATION
323 moveDisallowed = true;
324 } else {
325 // disallow Move if rendering of some item failed
326 moveDisallowed = !d->renderOk;
327 }
328
329 DEBUG(() << "DefaultDragWorker: moveDisallowed" << moveDisallowed);
330
331 // Note: remaining requests will be lazily deleted by cleanupRequests()
332 // when a new DND session is started
333
334 d->renderOk = true;
335 d->itemCnt = 0;
336
337 // Indicate we're cleaned up (i.e. the DND session is finished)
338 d->providers.clear();
339
340 return moveDisallowed;
341}
342
343bool QPMMime::DefaultDragWorker::isExclusive() const
344{
345 return d->exclusive;
346}
347
348ULONG QPMMime::DefaultDragWorker::itemCount() const
349{
350 return d->itemCnt;
351}
352
353ULONG QPMMime::DefaultDragWorker::hwnd() const
354{
355 return d->hwnd();
356}
357
358QByteArray QPMMime::DefaultDragWorker::composeFormatString()
359{
360 QByteArray formats;
361
362 // sanity checks
363 Q_ASSERT(d->isInitialized());
364 if (!d->isInitialized())
365 return formats;
366
367 bool first = true;
368 foreach(const Data::DrfProvider &p, d->providers) {
369 if (first)
370 first = false;
371 else
372 formats += ",";
373 formats += p.drf;
374 }
375
376 Q_ASSERT(!formats.isNull());
377 if (formats.isNull())
378 return formats;
379
380 // DRM_SHAREDMEM comes first to prevent native DRM_OS2FILE
381 // rendering on the target side w/o involving the source.
382 // Also, we add <DRM_SHAREDMEM,DRF_POINTERDATA> just like WPS does it
383 // (however, it doesn't help when dropping objects to it -- WPS still
384 // chooses DRM_OS2FILE).
385 formats = "(DRM_SHAREDMEM,DRM_OS2FILE)x(" + formats + "),"
386 "<DRM_SHAREDMEM,DRF_POINTERDATA>";
387
388 DEBUG(() << "DefaultDragWorker: formats" << formats
389 << ", itemCnt" << d->itemCnt);
390
391 return formats;
392}
393
394bool QPMMime::DefaultDragWorker::prepare(const char *drm, const char *drf,
395 DRAGITEM *item, ULONG itemIndex)
396{
397 // sanity checks
398 Q_ASSERT(d->isInitialized());
399 if (!d->isInitialized())
400 return false;
401
402 Q_ASSERT(item && itemIndex < d->itemCnt);
403 if (!item || itemIndex >= d->itemCnt)
404 return false;
405
406 DEBUG(() << "DefaultDragWorker: Preparing item" << itemIndex << "( id "
407 << item->ulItemID << ") for <" << drm << "," << drf << ">");
408
409 Provider *p = d->providerFor(drf);
410
411 if (!canRender(drm) || p == NULL) {
412 DEBUG(() << "DefaultDragWorker: Cannot render the given RMF");
413 return false;
414 }
415
416 Data::Request *req = d->requests.value(item->ulItemID);
417
418 if (req) {
419 // this item has been already prepared, ensure it has also been
420 // rendered already
421 Q_ASSERT(req->index == itemIndex);
422 Q_ASSERT(req->rendered);
423 if (req->index != itemIndex || !req->rendered)
424 return false;
425 // remove the old request to free resources
426 delete d->requests.take(item->ulItemID);
427 }
428
429 // store the request
430 req = new Data::Request(itemIndex, p, drm, drf);
431 d->requests.insert(item->ulItemID, req);
432
433 return true;
434}
435
436void QPMMime::DefaultDragWorker::defaultFileType(QString &type,
437 QString &ext)
438{
439 Q_ASSERT(d->providers.count());
440 if (d->providers.count()) {
441 Provider *p = d->providers.first().prov;
442 Q_ASSERT(p);
443 if (p)
444 p->fileType(d->providers.first().drf, type, ext);
445 }
446}
447
448MRESULT QPMMime::DefaultDragWorker::Data::message(ULONG msg, MPARAM mp1, MPARAM mp2)
449{
450 if (msg == DM_RENDER) {
451 DRAGTRANSFER *xfer = (DRAGTRANSFER *) mp1;
452
453 // sanity checks
454 Q_ASSERT(isInitialized());
455 Q_ASSERT(xfer);
456 if (!isInitialized() || !xfer)
457 return (MRESULT)FALSE;
458
459 Q_ASSERT(xfer->hwndClient && xfer->pditem);
460 if (!xfer->hwndClient || !xfer->pditem)
461 return (MRESULT)FALSE;
462
463 Data::Request *req = requests.value(xfer->pditem->ulItemID);
464
465 // check that this item has been prepared (should always be the case
466 // because the target would never know our hwnd() otherwise)
467 Q_ASSERT(req); // prepared
468 Q_ASSERT(!req->xfer); // no DM_RENDER requested
469 if (!req || req->xfer)
470 return (MRESULT)FALSE;
471
472 DEBUG(() << "DefaultDragWorker: Got DM_RENDER to"
473 << queryHSTR(xfer->hstrSelectedRMF) << "for item" << req->index
474 << "( id " << xfer->pditem->ulItemID << ")");
475
476 QByteArray drm, drf;
477 if (!parseRMF(xfer->hstrSelectedRMF, drm, drf))
478 Q_ASSERT(/* parseRMF() = */ FALSE);
479
480 if (req->drm != drm || req->drf != drf) {
481 xfer->fsReply = DMFL_RENDERRETRY;
482 return (MRESULT)FALSE;
483 }
484
485 // indicate that DM_RENDER was requested
486 req->xfer = xfer;
487
488 DEBUG(() << "DefaultDragWorker: Will render from ["
489 << req->provider->format(drf) << "] using provider"
490 << req->provider);
491
492 // We would like to post WM_USER to ourselves to do actual rendering
493 // after we return from DM_RENDER. But we are inside DrgDrag() at this
494 // point (our DND implementation is fully synchronous by design), so
495 // PM will not deliver this message to us until we return from
496 // DrgDrag(). Thus, we have to send it.
497
498 WinSendMsg(hwnd(), WM_USER,
499 MPFROMLONG(xfer->pditem->ulItemID), MPFROMP(req));
500
501 return (MRESULT)TRUE;
502 }
503
504 if (msg == WM_USER) {
505 // sanity checks
506 Q_ASSERT(isInitialized());
507 if (!isInitialized())
508 return (MRESULT)FALSE;
509
510 ULONG itemId = LONGFROMMP(mp1);
511
512 // sanity checks
513 Data::Request *req = requests.value(itemId);
514 Q_ASSERT(req); // prepared
515 Q_ASSERT(req->xfer != NULL); // DM_RENDER requested
516 Q_ASSERT(!req->rendered); // not yet rendered
517 Q_ASSERT((Data::Request *) PVOIDFROMMP(mp2) == req);
518 if (!req || req->xfer == NULL || req->rendered ||
519 (Data::Request *) PVOIDFROMMP(mp2) != req)
520 return (MRESULT)FALSE;
521
522 Q_ASSERT(q->source() && req->provider && req->index < itemCnt);
523 if (!q->source() || !req->provider || req->index >= itemCnt)
524 return (MRESULT)FALSE;
525
526 DEBUG(() << "DefaultDragWorker: Got DO_RENDER for item " << req->index
527 << "( id " << req->xfer->pditem->ulItemID << ")"
528 << "provider"<< req->provider << "drm" << req->drm.data()
529 << "drf" << req->drf.data());
530
531 bool ok = false;
532
533 QByteArray allData = q->source()->data(req->provider->format(req->drf));
534 QByteArray itemData;
535
536 ok = req->provider->provide(req->drf, allData, req->index, itemData);
537
538 if (ok) {
539 enum DRM { OS2File, SharedMem } drmType;
540 if (qstrcmp(req->drm, "DRM_SHAREDMEM") == 0) drmType = SharedMem;
541 else drmType = OS2File;
542
543 if (drmType == OS2File) {
544 QByteArray renderToName = queryHSTR(req->xfer->hstrRenderToName);
545 Q_ASSERT(!renderToName.isEmpty());
546 ok = !renderToName.isEmpty();
547 if (ok) {
548 DEBUG(() << "DefaultDragWorker: Will write to" << renderToName);
549 QFile file(QFile::decodeName(renderToName));
550 ok = file.open(QIODevice::WriteOnly);
551 if (ok) {
552 qint64 written = file.write(itemData, itemData.size());
553 ok = written == itemData.size();
554 file.close();
555 if (ok && req->xfer->pditem->hstrType) {
556 // since WPS ignores hstrType, write it manually
557 // to the .TYPE EA of the created file
558 qt_SetFileTypeEA(renderToName,
559 queryHSTR(req->xfer->pditem->hstrType));
560 }
561 }
562 }
563 } else {
564 PID pid;
565 TID tid;
566 bool isSameProcess = false;
567 ok = WinQueryWindowProcess(req->xfer->hwndClient, &pid, &tid);
568 if (ok) {
569 PPIB ppib = NULL;
570 DosGetInfoBlocks(NULL, &ppib);
571 isSameProcess = ppib->pib_ulpid == pid;
572
573 ULONG sz = itemData.size() + sizeof (ULONG);
574 char *ptr = NULL;
575 APIRET rc = isSameProcess ?
576 DosAllocMem((PPVOID) &ptr, sz,
577 PAG_COMMIT | PAG_READ | PAG_WRITE) :
578 DosAllocSharedMem((PPVOID) &ptr, NULL, sz,
579 OBJ_GIVEABLE | PAG_COMMIT |
580 PAG_READ | PAG_WRITE);
581 ok = rc == 0;
582 if (ok && !isSameProcess) {
583 rc = DosGiveSharedMem(ptr, pid, PAG_READ);
584 ok = rc == 0;
585 }
586 if (ok) {
587 *(ULONG *) ptr = itemData.size();
588 memcpy(ptr + sizeof (ULONG), itemData.data(),
589 itemData.size());
590 req->xfer->hstrRenderToName = (HSTR) ptr;
591 req->sharedMem = ptr;
592 DEBUG(() << "DefaultDragWorker: Created shared memory "
593 "object" << (void *)ptr);
594#ifndef QT_NO_DEBUG
595 } else {
596 qWarning("DefaultDragWorker: DosAllocSharedMem/"
597 "DosGiveSharedMem failed with %ld", rc);
598#endif
599 }
600#ifndef QT_NO_DEBUG
601 } else {
602 qWarning("DefaultDragWorker: WinQueryWindowProcess failed"
603 "with 0x%lX", WinGetLastError(NULLHANDLE));
604#endif
605 }
606 }
607 }
608
609 req->rendered = true;
610 // cumulative render result
611 renderOk &= ok;
612
613 DEBUG(() << "DefaultDragWorker: ok" << ok
614 << "overall.renderOk" << renderOk);
615
616 // note that we don't allow the target to retry
617 USHORT reply = ok ? DMFL_RENDEROK : DMFL_RENDERFAIL;
618 DrgPostTransferMsg(req->xfer->hwndClient, DM_RENDERCOMPLETE,
619 req->xfer, reply, 0, false);
620
621 // DRAGTRANSFER is no more necessary, free it early
622 qt_DrgFreeDragtransfer(req->xfer);
623#if defined(QT_DEBUG_DND)
624 {
625 ULONG size = ~0, flags = 0;
626 DosQueryMem(req->xfer, &size, &flags);
627 DEBUG(("DefaultDragWorker: Freed DRAGTRANSFER: "
628 "req->xfer %p size %lu (0x%08lX) flags 0x%08lX",
629 req->xfer, size, size, flags));
630 }
631#endif
632 req->xfer = NULL;
633
634 return (MRESULT)FALSE;
635 }
636
637 if (msg == DM_ENDCONVERSATION) {
638 // we don't check that isInitialized() is true here, because WPS
639 // (and probably some other apps) may send this message after
640 // cleanup() is called up on return from DrgDrag
641
642 ULONG itemId = LONGFROMMP(mp1);
643 ULONG flags = LONGFROMMP(mp2);
644
645 // sanity check (don't assert, see above)
646 Data::Request *req = requests.value(itemId);
647 Q_ASSERT(req);
648 if (!req)
649 return (MRESULT)FALSE;
650
651 DEBUG(() << "DefaultDragWorker: Got DM_ENDCONVERSATION for item" << req->index
652 << "(id " << itemId << ") provider" << req->provider
653 << "drm" << req->drm << "drf" << req->drf
654 << "rendered" << req->rendered << "outdated" << !isInitialized());
655
656 // proceed further only if it's not an outdated request
657 // from the previous DND session
658 if (isInitialized()) {
659 if (!req->rendered) {
660 // we treat cancelling the render request (for any reason)
661 // as a failure
662 renderOk = false;
663 } else {
664 // the overall success is true only if target says Okay
665 renderOk &= flags == DMFL_TARGETSUCCESSFUL;
666 }
667 }
668
669 // delete the request
670 delete requests.take(itemId);
671
672 return (MRESULT)FALSE;
673 }
674
675 return (MRESULT)FALSE;
676}
677
678bool QPMMime::DefaultDragWorker::addProvider(const QByteArray &drf, Provider *provider,
679 ULONG itemCnt /* = 1 */)
680{
681 // make sure remaining requests from the previous DND session are deleted
682 d->cleanupRequests();
683
684 Q_ASSERT(!drf.isEmpty() && provider && itemCnt >= 1);
685 if (!drf.isEmpty() && provider && itemCnt >= 1) {
686 if (d->providers.count() == 0) {
687 // first provider
688 d->itemCnt = itemCnt;
689 d->providers.append(Data::DrfProvider(drf, provider));
690 return true;
691 }
692 // next provider, must not be exclusive and itemCnt must match
693 if (!d->exclusive && d->itemCnt == itemCnt) {
694 // ensure there are no dups (several providers for the same drf)
695 if (!d->providerFor(drf))
696 d->providers.append(Data::DrfProvider(drf, provider));
697 return true;
698 }
699 }
700 return false;
701}
702
703// static
704bool QPMMime::DefaultDragWorker::canRender(const char *drm)
705{
706 return qstrcmp(drm, "DRM_SHAREDMEM") == 0 ||
707 qstrcmp(drm, "DRM_OS2FILE") == 0;
708}
709
710//------------------------------------------------------------------------------
711
712struct QPMMime::DefaultDropWorker::Data : public QPMObjectWindow
713{
714 struct MimeProvider
715 {
716 MimeProvider() : prov(NULL) {}
717 MimeProvider(const QString &m, Provider *p) : mime(m), prov(p) {}
718 QString mime;
719 Provider *prov;
720 };
721
722 Data(DefaultDropWorker *worker) : q(worker) {}
723
724 // QPMObjectWindow interface
725 MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2);
726
727 typedef QList<MimeProvider> MimeProviderList;
728
729 Provider *providerFor(const QString &mime)
730 {
731 foreach (const MimeProvider &p, providers) {
732 if (p.mime == mime)
733 return p.prov;
734 }
735 return NULL;
736 }
737
738 DefaultDropWorker *q;
739
740 bool exclusive : 1;
741 MimeProviderList providers;
742
743 bool sending_DM_RENDER : 1;
744 bool got_DM_RENDERCOMPLETE : 1;
745 USHORT flags_DM_RENDERCOMPLETE;
746
747 QEventLoop eventLoop;
748};
749
750QPMMime::DefaultDropWorker::DefaultDropWorker() : d(new Data(this))
751{
752 d->exclusive = false;
753 d->sending_DM_RENDER = d->got_DM_RENDERCOMPLETE = false;
754 d->flags_DM_RENDERCOMPLETE = 0;
755}
756
757QPMMime::DefaultDropWorker::~DefaultDropWorker()
758{
759 delete d;
760}
761
762void QPMMime::DefaultDropWorker::cleanup(bool isAccepted)
763{
764 if (d->eventLoop.isRunning()) {
765#ifndef QT_NO_DEBUG
766 qWarning("The previous drag source didn't post DM_RENDERCOMPLETE!\n"
767 "Contact the drag source developer.");
768#endif
769 d->eventLoop.exit(1);
770 }
771
772 d->providers.clear();
773 d->exclusive = false;
774 d->sending_DM_RENDER = d->got_DM_RENDERCOMPLETE = false;
775 d->flags_DM_RENDERCOMPLETE = 0;
776}
777
778bool QPMMime::DefaultDropWorker::isExclusive() const
779{
780 return d->exclusive;
781}
782
783bool QPMMime::DefaultDropWorker::hasFormat(const QString &mimeType) const
784{
785 return d->providerFor(mimeType) != NULL;
786}
787
788QStringList QPMMime::DefaultDropWorker::formats() const
789{
790 QStringList mimes;
791 foreach(const Data::MimeProvider &p, d->providers)
792 mimes << p.mime;
793 return mimes;
794}
795
796static QByteArray composeTempFileName()
797{
798 QByteArray tmpDir =
799 QFile::encodeName(QDir::toNativeSeparators(QDir::tempPath()));
800
801 static bool srandDone = false;
802 if (!srandDone) {
803 srand(time(NULL));
804 srandDone = true;
805 }
806
807 ULONG num = rand();
808 enum { Attempts = 100 };
809 int attempts = Attempts;
810
811 QString tmpName;
812 do {
813 tmpName.sprintf("%s\\%08lX.tmp", tmpDir.constData(), num);
814 if (!QFile::exists(tmpName))
815 break;
816 num = rand();
817 } while (--attempts > 0);
818
819 Q_ASSERT(attempts > 0);
820 if (attempts <= 0)
821 tmpName.clear();
822
823 return QFile::encodeName(tmpName);
824}
825
826QVariant QPMMime::DefaultDropWorker::retrieveData(const QString &mimeType,
827 QVariant::Type preferredType) const
828{
829 Q_UNUSED(preferredType);
830
831 DEBUG(() << "DefaultDropWorker::retrieveData: mimeType" << mimeType);
832
833 QVariant ret;
834
835 Q_ASSERT(info());
836 if (!info())
837 return ret;
838
839 ULONG itemCount = DrgQueryDragitemCount(info());
840 Q_ASSERT(itemCount);
841 if (!itemCount)
842 return ret;
843
844 Provider *provider = d->providerFor(mimeType);
845 if (!provider)
846 return ret;
847
848 QByteArray drf = provider->drf(mimeType);
849 Q_ASSERT(!drf.isEmpty());
850 if (drf.isEmpty())
851 return ret;
852
853 // Note: Allocating and freeing DRAGTRANSFER structures is a real mess. It's
854 // absolutely unclear how they can be reused for multiple items and/or render
855 // requests. My practice shows, that they cannot be reused at all, especially
856 // when the source and the target are the same process: if we have multiple
857 // items and use the same DRAGTRANSFER for all of them, the source will call
858 // DrgFreeDragtransfer() every time that will eventually destroy the memory
859 // object before the target finishes to work with it, so that the next
860 // DrgFreeDragtransfer() will generate a segfault in PMCTLS. Also note that
861 // using a number > 1 as an argument to DrgAllocDragtransfer() won't help
862 // because that will still allocate a single memory object. Thus, we will
863 // always allocate a new struct per every item. It seems to work.
864
865 QByteArray renderToName = composeTempFileName();
866 HSTR hstrRenderToName = DrgAddStrHandle(renderToName);
867
868 HSTR rmfOS2File =
869 DrgAddStrHandle(QString().sprintf("<DRM_OS2FILE,%s>",
870 drf.data()).toLocal8Bit());
871 HSTR rmfSharedMem =
872 DrgAddStrHandle(QString().sprintf("<DRM_SHAREDMEM,%s>",
873 drf.data()).toLocal8Bit());
874
875 MRESULT mrc;
876 bool renderOk = false;
877
878 DRAGTRANSFER *xfer = NULL;
879 QByteArray srcFileName;
880
881 QByteArray allData, itemData;
882
883 DEBUG(() << "DefaultDropWorker::retrieveData: itemCount" << itemCount);
884
885 for (ULONG i = 0; i < itemCount; ++i) {
886 DRAGITEM *item = DrgQueryDragitemPtr(info(), i);
887 Q_ASSERT(item);
888 if (!item) {
889 renderOk = false;
890 break;
891 }
892
893 DEBUG(() << "DefaultDropWorker::retrieveData: item" << i
894 << "hstrRMF" << queryHSTR(item->hstrRMF));
895
896 enum { None, OS2File, SharedMem } drm = None;
897 bool needToTalk = true;
898
899 // determine the mechanism to use (prefer DRM_SHAREDMEM)
900
901 if (DrgVerifyRMF(item, "DRM_SHAREDMEM", drf) &&
902 DrgVerifyRMF(item, "DRM_SHAREDMEM", "DRF_POINTERDATA"))
903 drm = SharedMem;
904 if (DrgVerifyRMF(item, "DRM_OS2FILE", drf)) {
905 srcFileName = querySourceNameFull(item);
906 // If the source provides the full file name, we prefer DRM_OS2FILE
907 // even if there is also DRM_SHAREDMEM available because we don't
908 // need to do any communication in this case at all. This will help
909 // with some native drag sources (such as DragText) that cannot send
910 // DM_RENDERCOMPLETE synchronously (before we return from DM_DROP)
911 // and would hang otherwise.
912 if (!srcFileName.isEmpty()) {
913 needToTalk = false;
914 drm = OS2File;
915 } else if (drm == None) {
916 srcFileName = renderToName;
917 drm = OS2File;
918 }
919 }
920 Q_ASSERT(drm != None);
921 if (drm == None) {
922 renderOk = false;
923 break;
924 }
925
926 if (needToTalk) {
927 // need to perform a conversation with the source,
928 // allocate a new DRAGTRANSFER structure for each item
929 xfer = DrgAllocDragtransfer(1);
930 Q_ASSERT(xfer);
931 if (!xfer) {
932 renderOk = false;
933 break;
934 }
935
936 xfer->cb = sizeof(DRAGTRANSFER);
937 xfer->hwndClient = d->hwnd();
938 xfer->ulTargetInfo = (ULONG) info();
939 xfer->usOperation = info()->usOperation;
940
941 xfer->pditem = item;
942 if (drm == OS2File) {
943 xfer->hstrSelectedRMF = rmfOS2File;
944 xfer->hstrRenderToName = hstrRenderToName;
945 } else {
946 xfer->hstrSelectedRMF = rmfSharedMem;
947 xfer->hstrRenderToName = 0;
948 }
949
950 DEBUG(() << "DefaultDropWorker: Will use"
951 << queryHSTR(xfer->hstrSelectedRMF) << "to render item" << item);
952
953 mrc = (MRESULT)TRUE;
954 if ((item->fsControl & DC_PREPARE) ||
955 (item->fsControl & DC_PREPAREITEM)) {
956 DEBUG(("DefaultDropWorker: Sending DM_RENDERPREPARE to 0x%08lX...",
957 info()->hwndSource));
958 mrc = DrgSendTransferMsg(info()->hwndSource, DM_RENDERPREPARE,
959 MPFROMP (xfer), 0);
960 DEBUG(("DefaultDropWorker: Finisned sending DM_RENDERPREPARE\n"
961 " mrc %p xfer->fsReply 0x%08hX", mrc, xfer->fsReply));
962 renderOk = (BOOL) mrc;
963 }
964
965 if ((BOOL) mrc) {
966 DEBUG(("DefaultDropWorker: Sending DM_RENDER to 0x%08lX...",
967 item->hwndItem));
968 d->sending_DM_RENDER = true;
969 mrc = DrgSendTransferMsg(item->hwndItem, DM_RENDER,
970 MPFROMP(xfer), 0);
971 d->sending_DM_RENDER = false;
972 DEBUG(("DefaultDropWorker: Finisned Sending DM_RENDER\n"
973 " mrc %p xfer->fsReply 0x%hX got_DM_RENDERCOMPLETE %d",
974 mrc, xfer->fsReply, d->got_DM_RENDERCOMPLETE));
975
976 if (!(BOOL) mrc || d->got_DM_RENDERCOMPLETE) {
977 if (d->got_DM_RENDERCOMPLETE)
978 renderOk = (d->flags_DM_RENDERCOMPLETE & DMFL_RENDEROK);
979 else
980 renderOk = false;
981 } else {
982 // synchronously wait for DM_RENDERCOMPLETE
983 DEBUG(() << "DefaultDropWorker: Waiting for DM_RENDERCOMPLETE...");
984 int result = d->eventLoop.exec();
985 DEBUG(("DefaultDropWorker: Finished waiting for "
986 "DM_RENDERCOMPLETE (result %d)\n"
987 " got_DM_RENDERCOMPLETE %d usFS 0x%hX",
988 result, d->got_DM_RENDERCOMPLETE, d->flags_DM_RENDERCOMPLETE));
989 Q_UNUSED(result);
990 // JFTR: at this point, cleanup() might have been called,
991 // as a result of either program exit or getting another
992 // DM_DRAGOVER (if the source app has crashed) before getting
993 // DM_RENDERCOMPLETE from the source. Use data members with
994 // care!
995 renderOk = d->got_DM_RENDERCOMPLETE &&
996 (d->flags_DM_RENDERCOMPLETE & DMFL_RENDEROK);
997 }
998
999 d->got_DM_RENDERCOMPLETE = false;
1000 }
1001 } else {
1002 DEBUG(() << "DefaultDropWorker: Source supports < DRM_OS2FILE,"
1003 << drf << "> and provides a file" << srcFileName
1004 << "for item" << item << "(no need to render)");
1005 renderOk = true;
1006 }
1007
1008 if (renderOk) {
1009 if (drm == OS2File) {
1010 DEBUG(() << "DefaultDragWorker: Will read from" << srcFileName);
1011 QFile file(QFile::decodeName(srcFileName));
1012 renderOk = file.open(QIODevice::ReadOnly);
1013 if (renderOk) {
1014 itemData = file.readAll();
1015 renderOk = file.error() == QFile::NoError;
1016 file.close();
1017 }
1018 if (needToTalk) {
1019 // only delete the file if we provided it for rendering
1020 bool ok = file.remove();
1021 Q_ASSERT((ok = ok));
1022 Q_UNUSED(ok);
1023 }
1024 } else {
1025 Q_ASSERT(xfer->hstrRenderToName);
1026 renderOk = xfer->hstrRenderToName != 0;
1027 if (renderOk) {
1028 const char *ptr = (const char *) xfer->hstrRenderToName;
1029 ULONG size = ~0;
1030 ULONG flags = 0;
1031 APIRET rc = DosQueryMem((PVOID) ptr, &size, &flags);
1032 renderOk = rc == 0;
1033 if (renderOk) {
1034 DEBUG(("DefaultDropWorker: Got shared data %p size %lu "
1035 "(0x%08lX) flags 0x%08lX", ptr, size, size, flags));
1036 Q_ASSERT((flags & (PAG_COMMIT | PAG_READ | PAG_BASE)) ==
1037 (PAG_COMMIT | PAG_READ | PAG_BASE));
1038 renderOk = (flags & (PAG_COMMIT | PAG_READ | PAG_BASE)) ==
1039 (PAG_COMMIT | PAG_READ | PAG_BASE);
1040#ifndef QT_NO_DEBUG
1041 } else {
1042 qWarning("DefaultDropWorker: DosQueryMem failed with %ld", rc);
1043#endif
1044 }
1045 if (renderOk) {
1046 ULONG realSize = *(ULONG *) ptr;
1047 DEBUG(() << "DefaultDropWorker: realSize" << realSize);
1048 Q_ASSERT(realSize <= size);
1049 renderOk = realSize <= size;
1050 if (renderOk) {
1051 itemData.resize(realSize);
1052 memcpy(itemData.data(), ptr + sizeof(ULONG), realSize);
1053 }
1054 }
1055 // free memory only if it is given by another process,
1056 // otherwise DefaultDragWorker will free it
1057 if (flags & PAG_SHARED)
1058 DosFreeMem((PVOID) xfer->hstrRenderToName);
1059 }
1060 }
1061 }
1062
1063 if (renderOk)
1064 renderOk = provider->provide(mimeType, i, itemData, allData);
1065
1066 if (needToTalk) {
1067 // free the DRAGTRANSFER structure
1068 DrgFreeDragtransfer(xfer);
1069#if defined(QT_DEBUG_DND)
1070 {
1071 ULONG size = ~0, flags = 0;
1072 DosQueryMem(xfer, &size, &flags);
1073 DEBUG(("DefaultDropWorker: Freed DRAGTRANSFER: "
1074 "xfer=%p, size=%lu(0x%08lX), flags=0x%08lX",
1075 xfer, size, size, flags));
1076 }
1077#endif
1078 xfer = NULL;
1079 }
1080
1081 if (!renderOk)
1082 break;
1083 }
1084
1085 DEBUG(() << "DefaultDropWorker: renderOk" << renderOk);
1086
1087 DrgDeleteStrHandle(rmfSharedMem);
1088 DrgDeleteStrHandle(rmfOS2File);
1089 DrgDeleteStrHandle(hstrRenderToName);
1090
1091 if (renderOk)
1092 ret = allData;
1093
1094 return ret;
1095}
1096
1097MRESULT QPMMime::DefaultDropWorker::Data::message(ULONG msg, MPARAM mp1, MPARAM mp2)
1098{
1099 switch (msg) {
1100 case DM_RENDERCOMPLETE: {
1101 // sanity check
1102 Q_ASSERT(q->info());
1103 if (!q->info())
1104 return (MRESULT)FALSE;
1105
1106 DEBUG(("DefaultDropWorker: Got DM_RENDERCOMPLETE"));
1107 got_DM_RENDERCOMPLETE = true;
1108 flags_DM_RENDERCOMPLETE = SHORT1FROMMP(mp2);
1109
1110 if (sending_DM_RENDER)
1111 {
1112#ifndef QT_NO_DEBUG
1113 DRAGTRANSFER *xfer = (DRAGTRANSFER *) mp1;
1114 qWarning("Drag item 0x%08lX sent DM_RENDERCOMPLETE w/o first "
1115 "replying to DM_RENDER!\n"
1116 "Contact the drag source developer.",
1117 xfer->pditem->hwndItem);
1118#endif
1119 return (MRESULT)FALSE;
1120 }
1121
1122 // stop synchronous waiting for DM_RENDERCOMPLETE
1123 if (eventLoop.isRunning())
1124 eventLoop.exit();
1125 return (MRESULT)FALSE;
1126 }
1127 default:
1128 break;
1129 }
1130
1131 return (MRESULT)FALSE;
1132}
1133
1134bool QPMMime::DefaultDropWorker::addProvider(const QString &mimeType,
1135 Provider *provider)
1136{
1137 Q_ASSERT(!mimeType.isEmpty() && provider);
1138 if (!mimeType.isEmpty() && provider && !d->exclusive) {
1139 // ensure there are no dups (several providers for the same mime)
1140 if (!d->providerFor(mimeType))
1141 d->providers.append(Data::MimeProvider(mimeType, provider));
1142 return true;
1143 }
1144 return false;
1145}
1146
1147bool QPMMime::DefaultDropWorker::addExclusiveProvider(const QString &mimeType,
1148 Provider *provider)
1149{
1150 Q_ASSERT(!mimeType.isEmpty() && provider);
1151 if (!mimeType.isEmpty() && provider && !d->exclusive) {
1152 d->exclusive = true;
1153 d->providers.clear();
1154 d->providers.append(Data::MimeProvider(mimeType, provider));
1155 return true;
1156 }
1157 return false;
1158}
1159
1160// static
1161bool QPMMime::DefaultDropWorker::canRender(DRAGITEM *item, const char *drf)
1162{
1163 return DrgVerifyRMF(item, "DRM_OS2FILE", drf) ||
1164 (DrgVerifyRMF(item, "DRM_SHAREDMEM", drf) &&
1165 DrgVerifyRMF(item, "DRM_SHAREDMEM", "DRF_POINTERDATA"));
1166}
1167
1168/*! \internal
1169
1170 Parses the rendering mechanism/format specification of the given \a item
1171 and stores only those mechanism branches in the given \a list that represent
1172 mechanisms supported by this worker. Returns false if fails to parse the
1173 RMF specification. Note that if no supported mechanisms are found, true is
1174 returned but the \a list will simply contain zero items.
1175
1176 \note The method clears the given \a list variable before proceeding.
1177
1178 \sa canRender(), PMMime::parseRMFs()
1179*/
1180// static
1181bool QPMMime::DefaultDropWorker::getSupportedRMFs(DRAGITEM *item,
1182 QList<QByteArrayList> &list)
1183{
1184 if (!parseRMFs(item->hstrRMF, list))
1185 return false;
1186
1187 for (QList<QByteArrayList>::iterator rmf = list.begin(); rmf != list.end();) {
1188 QByteArrayList::iterator mf = rmf->begin();
1189 Q_ASSERT(mf != rmf->end());
1190 const char *drm = *mf;
1191 if (qstrcmp(drm, "DRM_OS2FILE") == 0) {
1192 ++rmf;
1193 continue;
1194 }
1195 if (qstrcmp(drm, "DRM_SHAREDMEM") == 0) {
1196 // accept DRM_SHAREDMEM only if there is DRF_POINTERDATA
1197 for(; mf != rmf->end(); ++mf) {
1198 const char *drf = *mf;
1199 if (qstrcmp(drf, "DRF_POINTERDATA") == 0)
1200 break;
1201 }
1202 if (mf != rmf->end()) {
1203 ++rmf;
1204 continue;
1205 }
1206 }
1207 // remove the unsupported mechanism branch from the list
1208 rmf = list.erase(rmf);
1209 }
1210
1211 return true;
1212}
1213
1214#endif // !QT_NO_DRAGANDDROP
1215
1216//------------------------------------------------------------------------------
1217
1218class QPMMimeList
1219{
1220public:
1221 QPMMimeList();
1222 ~QPMMimeList();
1223 void addMime(QPMMime *mime);
1224 void removeMime(QPMMime *mime);
1225 QList<QPMMime*> mimes();
1226
1227private:
1228 void init();
1229 bool initialized;
1230 QList<QPMMime*> list;
1231};
1232
1233Q_GLOBAL_STATIC(QPMMimeList, theMimeList);
1234
1235
1236/*!
1237 \class QPMMime
1238 \brief The QMPMime class maps open-standard MIME to OS/2 PM Clipboard
1239 formats.
1240 \ingroup io
1241 \ingroup draganddrop
1242 \ingroup misc
1243
1244 Qt's drag-and-drop and clipboard facilities use the MIME standard.
1245 On X11, this maps trivially to the Xdnd protocol, but on OS/2
1246 although some applications use MIME types to describe clipboard
1247 formats, others use arbitrary non-standardized naming conventions,
1248 or unnamed built-in formats of the Presentation Manager.
1249
1250 By instantiating subclasses of QPMMime that provide conversions between OS/2
1251 PM Clipboard and MIME formats, you can convert proprietary clipboard formats
1252 to MIME formats.
1253
1254 Qt has predefined support for the following PM Clipboard formats (custom
1255 formats registered in the system atom table by name are given in double
1256 quotes):
1257
1258 \table
1259 \header \o PM Format \o Equivalent MIME type
1260 \row \o \c CF_TEXT \o \c text/plain (system codepage,
1261 zero-terminated string)
1262 \row \o \c "text/unicode" \o \c text/plain (16-bit Unicode,
1263 zero-terminated string, Mozilla-compatible)
1264 \row \o \c "text/html" \o \c text/html (16-bit Unicode,
1265 zero-terminated string, Mozilla-compatible)
1266 \row \o \c CF_BITMAP \o \c{image/xyz}, where \c xyz is
1267 a \l{QImageWriter::supportedImageFormats()}{Qt image format}
1268 \row \o \c "x-mime:<mime>" \o data in the format corresponding to the given
1269 MIME type \c <mime>
1270 \endtable
1271
1272 Note that all "x-mime:<mime>" formats use the CFI_POINTER storage type. That
1273 is, the clipboard contains a pointer to the memory block containing the MIME
1274 data in the corresponding format. The first 4 bytes of this memory block
1275 always contain the length of the subsequent MIME data array, in bytes.
1276
1277 An example use of this class by the user application would be to map the
1278 PM Metafile clipboard format (\c CF_METAFILE) to and from the MIME type
1279 \c{image/x-metafile}. This conversion might simply be adding or removing a
1280 header, or even just passing on the data. See \l{Drag and Drop} for more
1281 information on choosing and definition MIME types.
1282*/
1283
1284/*!
1285Constructs a new conversion object, adding it to the globally accessed
1286list of available converters.
1287*/
1288QPMMime::QPMMime()
1289{
1290 theMimeList()->addMime(this);
1291}
1292
1293/*!
1294Destroys a conversion object, removing it from the global
1295list of available converters.
1296*/
1297QPMMime::~QPMMime()
1298{
1299 theMimeList()->removeMime(this);
1300}
1301
1302/*!
1303 Registers the MIME type \a mime, and returns an ID number
1304 identifying the format on OS/2. Intended to be used by QPMMime
1305 implementations for registering custom clipboard formats they use.
1306*/
1307// static
1308ULONG QPMMime::registerMimeType(const QString &mime)
1309{
1310 ULONG cf = WinAddAtom(WinQuerySystemAtomTable(), mime.toLocal8Bit());
1311 if (!cf) {
1312#ifndef QT_NO_DEBUG
1313 qWarning("QPMMime: WinAddAtom failed with 0x%lX",
1314 WinGetLastError(NULLHANDLE));
1315#endif
1316 return 0;
1317 }
1318
1319 return cf;
1320}
1321
1322/*!
1323 Unregisters the MIME type identified by \a id which was previously
1324 registered with registerMimeType().
1325*/
1326// static
1327void QPMMime::unregisterMimeType(ULONG mimeId)
1328{
1329 WinDeleteAtom(WinQuerySystemAtomTable(), mimeId);
1330}
1331
1332/*!
1333 Returns a list of all currently defined QPMMime objects.
1334*/
1335// static
1336QList<QPMMime*> QPMMime::all()
1337{
1338 return theMimeList()->mimes();
1339}
1340
1341/*!
1342 Allocates a block of shared memory of the given size and returns the address
1343 of this block. This memory block may be then filled with data and returned
1344 by convertFromMimeData() as the value of the CFI_POINTER type.
1345*/
1346// static
1347ULONG QPMMime::allocateMemory(size_t size)
1348{
1349 if (size == 0)
1350 return 0;
1351
1352 ULONG data = 0;
1353
1354 // allocate giveable memory for the array
1355 APIRET arc = DosAllocSharedMem((PVOID *)&data, NULL, size,
1356 PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE);
1357 if (arc != NO_ERROR) {
1358#ifndef QT_NO_DEBUG
1359 qWarning("QPMMime::allocateMemory: DosAllocSharedMem failed with %lu", arc);
1360#endif
1361 return 0;
1362 }
1363
1364 return data;
1365}
1366
1367/*!
1368 Frees memory allocated by allocateMemory(). Normally, not used because the
1369 CFI_POINTER memory blocks are owned by the system after
1370 convertFromMimeData() returns.
1371*/
1372// static
1373void QPMMime::freeMemory(ULONG addr)
1374{
1375 DosFreeMem((PVOID)addr);
1376}
1377
1378/*!
1379 \fn QList<MimeCFPair> QPMMime::formatsForMimeData(const QMimeData *mimeData) const
1380
1381 Returns a list of ULONG values representing the different OS/2 PM
1382 clipboard formats that can be provided for the \a mimeData, in order of
1383 precedence (the most suitable format goes first), or an empty list if
1384 neither of the mime types provided by \a mimeData is supported by this
1385 converter. Note that each item in the returned list is actually a pair
1386 consisting of the mime type name and the corresponding format identifier.
1387
1388 All subclasses must reimplement this pure virtual function.
1389*/
1390
1391/*!
1392 \fn bool QPMMime::convertFromMimeData(const QMimeData *mimeData, ULONG format,
1393 ULONG &flags, ULONG *data) const
1394
1395 Converts the \a mimeData to the specified \a format.
1396
1397 If \a data is not NULL, a handle to the converted data should be then placed
1398 in a variable pointed to by \a data and with the necessary flags describing
1399 the handle returned in the \a flags variable.
1400
1401 The following flags describing the data storage type are recognized:
1402
1403 \table
1404 \row \o \c CFI_POINTER \o \a data is a pointer to a block of memory
1405 allocated with QPMMime::allocateMemory()
1406 \row \o \c CFI_HANDLE \o \a data is a handle to the appropriate
1407 PM resource
1408 \endtable
1409
1410 If \a data is NULL then a delayed conversion is requested by the caller.
1411 The implementation should return the appropriate flags in the \a flags
1412 variable and may perform the real data conversion later when this method is
1413 called again with \a data being non-NULL.
1414
1415 Return true if the conversion was successful.
1416
1417 All subclasses must reimplement this pure virtual function.
1418*/
1419
1420/*!
1421 \fn QList<MimeCFPair> QPMMime::mimesForFormats(const QList<ULONG> &formats) const
1422
1423 Returns a list of mime types that will be created form the specified \a list
1424 of \a formats, in order of precedence (the most suitable mime type comes
1425 first), or an empty list if neither of the \a formats is supported by this
1426 converter. Note that each item in the returned list is actually a pair
1427 consisting of the mime type name and the corresponding format identifier.
1428
1429 All subclasses must reimplement this pure virtual function.
1430*/
1431
1432/*!
1433 \fn QVariant QPMMime::convertFromFormat(ULONG format, ULONG flags, ULONG data,
1434 const QString &mimeType,
1435 QVariant::Type preferredType) const
1436
1437 Returns a QVariant containing the converted from the \a data in the
1438 specified \a format with the given \a flags to the requested \a mimeType. If
1439 possible the QVariant should be of the \a preferredType to avoid needless
1440 conversions.
1441
1442 All subclasses must reimplement this pure virtual function.
1443*/
1444
1445/*!
1446 \fn DragWorker *QPMMime::dragWorkerFor(const QString &mimeType,
1447 QMimeData *mimeData)
1448
1449 Returns a DragWorker instance suitable for converting \a mimeType to a set
1450 of drag items for the Direct Manipulation (Drag And Drop) session. If this
1451 converter does not support the given MIME type, this method should return 0.
1452
1453 See the QPMMime::DragWorker class description for more information.
1454
1455 The default implementation of this method returns 0.
1456*/
1457
1458/*!
1459 \fn DropWorker *QPMMime::dropWorkerFor(DRAGINFO *info)
1460
1461 Returns a DropWorker instance suitable for converting drag items represented
1462 by the \a info structure to MIME data when these items are dropped on a Qt
1463 widget at the end of the Direct manipulation session. If this converter does
1464 not support the given set of drag items, this method should return 0.
1465
1466 See the QPMMime::DropWorker class description for more information.
1467
1468 The default implementation of this method returns 0.
1469*/
1470
1471/*!
1472 \class QPMMime::DragWorker
1473
1474 This class is responsible for providing the drag items for the Direct
1475 Manipulation session.
1476
1477 Drag workers can be super exclusive (solely responsible for converting the
1478 given mime type to a set of DRAGITEM structures), exclusive (cannot coexist
1479 with other workers but don't manage the DRAGINFO/DRAGITEM creation), or
1480 cooperative (can coexist with other drag workers and share the same set of
1481 DRAGITEM structures in order to represent different mime data types). As
1482 opposed to super exclusive workers (identified by isExclusive() returning
1483 TRUE and by itemCount() returning zero), exclusive and cooperative workers
1484 do not create DRAGINFO/DRAGITEM structures on their own, they implement a
1485 subset of methods that is used by the drag manager to fill drag structures
1486 it creates.
1487
1488 If a super exlusive or an exclusive worker is encoundered when starting the
1489 drag session, it will be used only if there are no any other workers found
1490 for \b other mime types of the object being dragged. If a cooperative worker
1491 with the item count greater than one is encountered, it will be used only if
1492 all other found workers are also cooperative and require the same number of
1493 items. In both cases, if the above conditions are broken, the respective
1494 workers are discarded (ignored). Instead, a special fall-back cooperative
1495 worker (that requires a single DRAGITEM, supports any mime type and can
1496 coexist with other one-item cooperative workers) will be used for the given
1497 mime type.
1498
1499 \note Subclasses must NOT free the DRAGINFO structure they allocated and
1500 returned by createDragInfo().
1501
1502 \note Every exclusive drag worker must implement createDragInfo() and must
1503 not implement composeFormatSting()/canRender()/prepare()/defaultFileType().
1504 And vice versa, every cooperative drag worker must implement the latter
1505 three functions but not the former two.
1506
1507 \note The return value of cleanup() is whether the Move operation is
1508 disallowed by this worker or not (if the worker doesn't participate in the
1509 DND session, it should return FALSE, to let other workers allow Move).
1510*/
1511
1512/*!
1513 \class QPMMime::DefaultDragWorker
1514
1515 This class is a DragWorker implementation that supports standard
1516 DRM_SHAREDMEM and DRM_OS2FILE and rendering mechanisms. It uses
1517 QPMMime::DefaultDragWorker::Provider subclasses to map mime types of the
1518 object being dragged to rendering formats and apply preprocessing of data
1519 before rendering.
1520*/
1521
1522/*!
1523 \class QPMMime::DropWorker
1524
1525 This class is responsible for interpreting the drag items after the Direct
1526 Manipulation session ends up in a drop.
1527
1528 Drop workers can be exclusive (solely responsible for converting the given
1529 set of DRAGITEM structures) or cooperative (can coexist with other drop
1530 workers in order to produce different mime data types from the same set of
1531 DRAGITEM structures). If an exclusive drop worker is encountered when
1532 processing the drop event, all other workers are silently ignored.
1533
1534 \note Subclasses must NOT free the DRAGINFO structure pointed to by info().
1535
1536 \note Subclasses must NOT send DM_ENDCONVERSATION to the source.
1537*/
1538
1539/*!
1540 \class QPMMime::DefaultDropWorker
1541
1542 This class is a DropWorker implementation that supports standard
1543 DRM_SHAREDMEM and DRM_OS2FILE and rendering mechanisms. It uses
1544 QPMMime::DefaultDropWorker::Provider subclasses to map various rendering
1545 formats to particular mime types and apply postprocessing of data after