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

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

gui/kernel: mime: Don't store the shared block size at its end (doesn't seem to be actually necessary).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 16.9 KB
RevLine 
[321]1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** Copyright (C) 2009 netlabs.org. OS/2 parts.
7**
8** This file is part of the QtGui module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial Usage
12** Licensees holding valid Qt Commercial licenses may use this file in
13** accordance with the Qt Commercial License Agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and Nokia.
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 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Nokia gives you certain
26** additional rights. These rights are described in the Nokia Qt LGPL
27** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28** 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 are unsure which license is appropriate for your use, please
39** contact the sales department 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
[323]62#define QMIME_DEBUG
63
[321]64QT_BEGIN_NAMESPACE
65
[323]66class QPMMimeList
67{
68public:
69 QPMMimeList();
70 ~QPMMimeList();
71 void addMime(QPMMime *mime);
72 void removeMime(QPMMime *mime);
73 QList<QPMMime*> mimes();
74
75private:
76 void init();
77 bool initialized;
78 QList<QPMMime*> list;
79};
80
81Q_GLOBAL_STATIC(QPMMimeList, theMimeList);
82
83
[321]84/*!
85 \class QPMMime
86 \brief The QMPMime class maps open-standard MIME to OS/2 PM Clipboard
87 formats.
88 \ingroup io
89 \ingroup draganddrop
90 \ingroup misc
91
92 Qt's drag-and-drop and clipboard facilities use the MIME standard.
93 On X11, this maps trivially to the Xdnd protocol, but on OS/2
94 although some applications use MIME types to describe clipboard
95 formats, others use arbitrary non-standardized naming conventions,
96 or unnamed built-in formats of the Presentation Manager.
97
98 By instantiating subclasses of QPMMime that provide conversions between OS/2
99 PM Clipboard and MIME formats, you can convert proprietary clipboard formats
100 to MIME formats.
101
102 Qt has predefined support for the following PM Clipboard formats:
103
104 \table
105 \header \o PM Format \o Equivalent MIME type
106 \row \o \c CF_TEXT \o \c text/plain
107 \row \o \c CF_BITMAP \o \c{image/xyz}, where \c xyz is
108 a \l{QImageWriter::supportedImageFormats()}{Qt image format}
109 \endtable
110
111 An example use of this class would be to map the PM Metafile
112 clipboard format (\c CF_METAFILE) to and from the MIME type
113 \c{image/x-metafile}. This conversion might simply be adding or removing a
114 header, or even just passing on the data. See \l{Drag and Drop} for more
115 information on choosing and definition MIME types.
116*/
117
[323]118/*!
119Constructs a new conversion object, adding it to the globally accessed
120list of available converters.
121*/
[321]122QPMMime::QPMMime()
123{
[323]124 theMimeList()->addMime(this);
[321]125}
126
[323]127/*!
128Destroys a conversion object, removing it from the global
129list of available converters.
130*/
[321]131QPMMime::~QPMMime()
132{
[323]133 theMimeList()->removeMime(this);
[321]134}
135
[323]136/*!
137 Registers the MIME type \a mime, and returns an ID number
[332]138 identifying the format on OS/2. Intended to be used by QPMMime
139 implementations for registering custom clipboard formats they use.
[323]140*/
[332]141// static
[323]142ULONG QPMMime::registerMimeType(const QString &mime)
143{
[332]144 ULONG cf = WinAddAtom(WinQuerySystemAtomTable(), mime.toLocal8Bit());
[323]145 if (!cf) {
146#ifndef QT_NO_DEBUG
147 qWarning("QPMMime: WinAddAtom failed with %lX",
148 WinGetLastError(NULLHANDLE));
149#endif
150 return 0;
151 }
152
153 return cf;
154}
155
156/*!
[332]157 Allocates a block of shared memory of the given size and returns the address
158 of this block. This memory block may be then filled with data and returned
159 by convertFromMimeData() as the value of the CFI_POINTER type.
160*/
161// static
162ULONG QPMMime::allocateMemory(size_t size)
163{
164 if (size == 0)
165 return 0;
166
167 ULONG data = 0;
168
169 // allocate giveable memory for the array + dword for its size
170 APIRET arc = DosAllocSharedMem((PVOID *)&data, NULL, size + 4,
171 PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE);
172 if (arc != NO_ERROR) {
173#ifndef QT_NO_DEBUG
174 qWarning("QPMMime::allocateMemory: DosAllocSharedMem failed with %lu", arc);
175#endif
176 return 0;
177 }
[335]178
179 /// @todo I think we don't need it any more
180#if 0
[332]181 // get the size rounded to the page boundary (4K)
182 ULONG sz = ~0, flags = 0;
183 arc = DosQueryMem((PVOID)(data + size - 1), &sz, &flags);
184 if (arc != NO_ERROR) {
185#ifndef QT_NO_DEBUG
186 qWarning("QPMMime::allocateMemory: DosQueryMem failed with %lu", arc);
187#endif
188 DosFreeMem((PVOID)data);
189 return 0;
190 }
191 sz += (size - 1);
192 // store the exact size to the last dword of the last page
193 *(((ULONG *)(data + sz)) - 1) = size;
[335]194#endif
[332]195
196 return data;
197}
198
199/*!
200 Frees memory allocated by allocateMemory(). Normally, not used because the
201 CFI_POINTER memory blocks are owned by the system after
202 convertFromMimeData() returns.
203*/
204// static
205void QPMMime::freeMemory(ULONG addr)
206{
207 DosFreeMem((PVOID)addr);
208}
209
210/*!
[324]211 \fn QList<ULONG> QPMMime::formatsForMimeData(const QMimeData *mimeData) const
[323]212
[324]213 Returns a list of ULONG values representing the different OS/2 PM
214 clipboard formats that can be provided for the \a mimeData, in order of
215 precedence (the most suitable format goes first), or an empty list if
216 neither of the mime types provided by \a mimeData is supported by this
217 converter.
[323]218
219 All subclasses must reimplement this pure virtual function.
220*/
221
222/*!
[324]223 \fn bool QPMMime::convertFromMimeData(const QMimeData *mimeData, ULONG format,
224 ULONG &flags, ULONG *data) const
[323]225
[324]226 Converts the \a mimeData to the specified \a format.
[323]227
[324]228 If \a data is not NULL, a handle to the converted data should then be placed
229 in a variable pointed to by \a data and with the necessary flags describing
230 the handle returned in the \a flags variable.
[323]231
[324]232 The following flags describing the data type are recognized:
[323]233
[324]234 \table
235 \row \o \c CFI_POINTER \o \a data is a pointer to a block of memory
[332]236 allocated with QPMMime::allocateMemory()
[324]237 \row \o \c CFI_HANDLE \o \a data is a handle to the appropriate
238 PM resource
239 \endtable
[323]240
[324]241 If \a data is NULL then a delayed conversion is requested by the caller.
242 The implementation should return the appropriate flags in the \a flags
243 variable and may perform the real data conversion later when this method is
244 called again with \a data being non-NULL.
[323]245
[324]246 Return true if the conversion was successful.
[323]247
248 All subclasses must reimplement this pure virtual function.
249*/
250
251/*!
[334]252 \fn QList<MimeCFPair> QPMMime::mimesForFormats(const QList<ULONG> &formats) const
[323]253
[324]254 Returns a list of mime types that will be created form the specified \a list
255 of \a formats, in order of precedence (the most suitable mime type comes
256 first), or an empty list if neither of the \a formats is supported by this
[334]257 converter. Note that each pair in the returned list consists of the mime
258 type name and the corresponding format identifier.
[323]259
260 All subclasses must reimplement this pure virtual function.
261*/
262
263/*!
[324]264 \fn QVariant QPMMime::convertFromFormat(ULONG format, ULONG flags, ULONG data,
265 const QString &mimeType,
266 QVariant::Type preferredType) const
[323]267
[324]268 Returns a QVariant containing the converted from the \a data in the
269 specified \a format with the given \a flags to the requested \a mimeType. If
270 possible the QVariant should be of the \a preferredType to avoid needless
271 conversions.
[323]272
273 All subclasses must reimplement this pure virtual function.
274*/
275
276// static
[324]277QList<QPMMime::Match> QPMMime::allConvertersFromFormats(const QList<ULONG> &formats)
[323]278{
[324]279 QList<Match> matches;
280
[323]281 QList<QPMMime*> mimes = theMimeList()->mimes();
282 for (int i = mimes.size()-1; i >= 0; --i) {
[334]283 QList<MimeCFPair> fmts = mimes[i]->mimesForFormats(formats);
[324]284 int priority = 0;
[334]285 foreach (MimeCFPair fmt, fmts) {
[324]286 ++priority;
287 QList<Match>::iterator it = matches.begin();
288 for (; it != matches.end(); ++it) {
289 Match &match = *it;
[334]290 if (match.mime == fmt.first) {
[324]291 // replace if priority is higher, ignore otherwise
292 if (priority < match.priority) {
293 match.converter = mimes[i];
[334]294 match.format = fmt.second;
[324]295 match.priority = priority;
296 }
297 break;
298 }
[323]299 }
[324]300 if (it == matches.end()) {
[334]301 matches += Match(mimes[i], fmt.first, fmt.second, priority);
[324]302 }
[323]303 }
304 }
305
[324]306 return matches;
[323]307}
308
309// static
[324]310QList<QPMMime::Match> QPMMime::allConvertersFromMimeData(const QMimeData *mimeData)
[323]311{
[324]312 QList<Match> matches;
313
[323]314 QList<QPMMime*> mimes = theMimeList()->mimes();
315 for (int i = mimes.size()-1; i >= 0; --i) {
[324]316 QList<ULONG> cfs = mimes[i]->formatsForMimeData(mimeData);
317 int priority = 0;
318 foreach (ULONG cf, cfs) {
319 ++priority;
320 QList<Match>::iterator it = matches.begin();
321 for (; it != matches.end(); ++it) {
322 Match &match = *it;
323 if (match.format == cf) {
324 // replace if priority is higher, ignore otherwise
325 if (priority < match.priority) {
326 match.converter = mimes[i];
327 match.priority = priority;
328 }
329 break;
330 }
331 }
332 if (it == matches.end()) {
333 matches += Match(mimes[i], cf, priority);
334 }
335 }
[323]336 }
337
[324]338 return matches;
[323]339}
340
341////////////////////////////////////////////////////////////////////////////////
342
[332]343class QPMMimeText : public QPMMime
344{
345public:
346 QPMMimeText();
347
348 QList<ULONG> formatsForMimeData(const QMimeData *mimeData) const;
349 bool convertFromMimeData(const QMimeData *mimeData, ULONG format,
350 ULONG &flags, ULONG *data) const;
351
[334]352 QList<MimeCFPair> mimesForFormats(const QList<ULONG> &formats) const;
[332]353 QVariant convertFromFormat(ULONG format, ULONG flags, ULONG data,
354 const QString &mimeType,
355 QVariant::Type preferredType) const;
356
357 const ULONG CF_TextUnicode;
358};
359
360QPMMimeText::QPMMimeText()
361 // "text/unicode" is what Mozilla uses to for unicode, so Qt apps will
362 // be able to interchange unicode text with Mozilla apps
363 : CF_TextUnicode (registerMimeType(QLatin1String("text/unicode")))
364{
365}
366
367QList<ULONG> QPMMimeText::formatsForMimeData(const QMimeData *mimeData) const
368{
369 QList<ULONG> cfs;
370 if (mimeData->hasText())
371 cfs << CF_TEXT << CF_TextUnicode;
372 return cfs;
373}
374
375// text/plain is defined as using CRLF, but so many programs don't,
376// and programmers just look for '\n' in strings.
377// OS/2 really needs CRLF, so we ensure it here.
378bool QPMMimeText::convertFromMimeData(const QMimeData *mimeData, ULONG format,
379 ULONG &flags, ULONG *data) const
380{
381 if (!mimeData->hasText())
382 return false;
383
384 flags = CFI_POINTER;
385
386 if (data == NULL)
387 return true; // delayed rendering, nothing to do
388
389 QByteArray r;
390
391 if (format == CF_TEXT) {
392 QByteArray str = mimeData->text().toLocal8Bit();
393 // Anticipate required space for CRLFs at 1/40
[335]394 int maxsize = str.size()+str.size()/40+1;
[332]395 r.fill('\0', maxsize);
[334]396 char *o = r.data();
397 const char *d = str.data();
[332]398 const int s = str.size();
399 bool cr = false;
400 int j = 0;
401 for (int i = 0; i < s; i++) {
402 char c = d[i];
403 if (c == '\r')
404 cr = true;
405 else {
406 if (c == '\n') {
407 if (!cr)
408 o[j++] = '\r';
409 }
410 cr = false;
411 }
412 o[j++] = c;
[335]413 if (j+1 >= maxsize) {
[332]414 maxsize += maxsize/4;
415 r.resize(maxsize);
416 o = r.data();
417 }
418 }
[335]419 if (j < r.size())
420 o[j] = '\0';
[332]421 } else if (format == CF_TextUnicode) {
422 QString str = mimeData->text();
423 const QChar *u = str.unicode();
424 QString res;
425 const int s = str.length();
426 int maxsize = s + s/40 + 3;
427 res.resize(maxsize);
428 int ri = 0;
429 bool cr = false;
430 for (int i = 0; i < s; ++i) {
431 if (*u == QLatin1Char('\r'))
432 cr = true;
433 else {
434 if (*u == QLatin1Char('\n') && !cr)
435 res[ri++] = QLatin1Char('\r');
436 cr = false;
437 }
438 res[ri++] = *u;
439 if (ri+3 >= maxsize) {
440 maxsize += maxsize/4;
441 res.resize(maxsize);
442 }
443 ++u;
444 }
445 res.truncate(ri);
446 const int byteLength = res.length()*2;
447 r.fill('\0', byteLength + 2);
448 memcpy(r.data(), res.unicode(), byteLength);
449 r[byteLength] = 0;
450 r[byteLength+1] = 0;
451 } else{
452 return false;
453 }
454
455 *data = QPMMime::allocateMemory(r.size());
456 if (!*data)
457 return false;
458
459 memcpy((void *)*data, r.data(), r.size());
460 return true;
461}
462
[334]463QList<QPMMime::MimeCFPair> QPMMimeText::mimesForFormats(const QList<ULONG> &formats) const
[332]464{
[334]465 QList<MimeCFPair> mimes;
[332]466 foreach(ULONG cf, formats) {
[334]467 // prefer unicode over local8Bit
468 if (cf == CF_TextUnicode)
469 mimes.prepend(qMakePair(QString(QLatin1String("text/plain")), cf));
470 if (cf == CF_TEXT)
471 mimes.append(qMakePair(QString(QLatin1String("text/plain")), cf));
[332]472 }
473 return mimes;
474}
475
476QVariant QPMMimeText::convertFromFormat(ULONG format, ULONG flags, ULONG data,
477 const QString &mimeType,
478 QVariant::Type preferredType) const
479{
[334]480 QVariant ret;
481
482 // @todo why is it startsWith? the rest of the mime specification (encoding,
483 // etc) isn't taken into account... Anyway, copied the logic from Windows.
484 if (!mimeType.startsWith("text/plain"))
485 return ret;
486 if (!(flags & CFI_POINTER) || !data)
487 return ret;
488
489 QString str;
490
491 if (format == CF_TEXT) {
492 const char *d = (const char *)data;
493 QByteArray r("");
494 if (*d) {
495 const int s = qstrlen(d);
496 r.fill('\0', s);
497 char *o = r.data();
498 int j = 0;
499 for (int i = 0; i < s; i++) {
500 char c = d[i];
501 if (c != '\r')
502 o[j++] = c;
503 }
504 }
505 str = QString::fromLocal8Bit(r);
506 } else if (format == CF_TextUnicode) {
507 str = QString::fromUtf16((const unsigned short *)data);
508 str.replace(QLatin1String("\r\n"), QLatin1String("\n"));
509 }
510
511 if (preferredType == QVariant::String)
512 ret = str;
513 else
514 ret = str.toUtf8();
515
516 return ret;
[332]517}
518
[323]519////////////////////////////////////////////////////////////////////////////////
520
521QPMMimeList::QPMMimeList()
522 : initialized(false)
523{
524}
525
526QPMMimeList::~QPMMimeList()
527{
528 while (list.size())
529 delete list.first();
530}
531
532
533void QPMMimeList::init()
534{
535 if (!initialized) {
536 initialized = true;
[332]537 new QPMMimeText;
[323]538 }
539}
540
541void QPMMimeList::addMime(QPMMime *mime)
542{
543 init();
544 list.append(mime);
545}
546
547void QPMMimeList::removeMime(QPMMime *mime)
548{
549 init();
550 list.removeAll(mime);
551}
552
553QList<QPMMime*> QPMMimeList::mimes()
554{
555 init();
556 return list;
557}
558
[321]559QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.