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

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

gui/kernel: mime: Implemented mime->clipboard interface for "text/plain" (only putting to the system clipboard so far).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 15.4 KB
Line 
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
62#define QMIME_DEBUG
63
64QT_BEGIN_NAMESPACE
65
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
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
118/*!
119Constructs a new conversion object, adding it to the globally accessed
120list of available converters.
121*/
122QPMMime::QPMMime()
123{
124 theMimeList()->addMime(this);
125}
126
127/*!
128Destroys a conversion object, removing it from the global
129list of available converters.
130*/
131QPMMime::~QPMMime()
132{
133 theMimeList()->removeMime(this);
134}
135
136/*!
137 Registers the MIME type \a mime, and returns an ID number
138 identifying the format on OS/2. Intended to be used by QPMMime
139 implementations for registering custom clipboard formats they use.
140*/
141// static
142ULONG QPMMime::registerMimeType(const QString &mime)
143{
144 ULONG cf = WinAddAtom(WinQuerySystemAtomTable(), mime.toLocal8Bit());
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/*!
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 }
178 // get the size rounded to the page boundary (4K)
179 ULONG sz = ~0, flags = 0;
180 arc = DosQueryMem((PVOID)(data + size - 1), &sz, &flags);
181 if (arc != NO_ERROR) {
182#ifndef QT_NO_DEBUG
183 qWarning("QPMMime::allocateMemory: DosQueryMem failed with %lu", arc);
184#endif
185 DosFreeMem((PVOID)data);
186 return 0;
187 }
188 sz += (size - 1);
189 // store the exact size to the last dword of the last page
190 *(((ULONG *)(data + sz)) - 1) = size;
191
192 return data;
193}
194
195/*!
196 Frees memory allocated by allocateMemory(). Normally, not used because the
197 CFI_POINTER memory blocks are owned by the system after
198 convertFromMimeData() returns.
199*/
200// static
201void QPMMime::freeMemory(ULONG addr)
202{
203 DosFreeMem((PVOID)addr);
204}
205
206/*!
207 \fn QList<ULONG> QPMMime::formatsForMimeData(const QMimeData *mimeData) const
208
209 Returns a list of ULONG values representing the different OS/2 PM
210 clipboard formats that can be provided for the \a mimeData, in order of
211 precedence (the most suitable format goes first), or an empty list if
212 neither of the mime types provided by \a mimeData is supported by this
213 converter.
214
215 All subclasses must reimplement this pure virtual function.
216*/
217
218/*!
219 \fn bool QPMMime::convertFromMimeData(const QMimeData *mimeData, ULONG format,
220 ULONG &flags, ULONG *data) const
221
222 Converts the \a mimeData to the specified \a format.
223
224 If \a data is not NULL, a handle to the converted data should then be placed
225 in a variable pointed to by \a data and with the necessary flags describing
226 the handle returned in the \a flags variable.
227
228 The following flags describing the data type are recognized:
229
230 \table
231 \row \o \c CFI_POINTER \o \a data is a pointer to a block of memory
232 allocated with QPMMime::allocateMemory()
233 \row \o \c CFI_HANDLE \o \a data is a handle to the appropriate
234 PM resource
235 \endtable
236
237 If \a data is NULL then a delayed conversion is requested by the caller.
238 The implementation should return the appropriate flags in the \a flags
239 variable and may perform the real data conversion later when this method is
240 called again with \a data being non-NULL.
241
242 Return true if the conversion was successful.
243
244 All subclasses must reimplement this pure virtual function.
245*/
246
247/*!
248 \fn QStringList QPMMime::mimesForFormats(const QList<ULONG> &formats) const
249
250 Returns a list of mime types that will be created form the specified \a list
251 of \a formats, in order of precedence (the most suitable mime type comes
252 first), or an empty list if neither of the \a formats is supported by this
253 converter.
254
255 All subclasses must reimplement this pure virtual function.
256*/
257
258/*!
259 \fn QVariant QPMMime::convertFromFormat(ULONG format, ULONG flags, ULONG data,
260 const QString &mimeType,
261 QVariant::Type preferredType) const
262
263 Returns a QVariant containing the converted from the \a data in the
264 specified \a format with the given \a flags to the requested \a mimeType. If
265 possible the QVariant should be of the \a preferredType to avoid needless
266 conversions.
267
268 All subclasses must reimplement this pure virtual function.
269*/
270
271// static
272QList<QPMMime::Match> QPMMime::allConvertersFromFormats(const QList<ULONG> &formats)
273{
274 QList<Match> matches;
275
276 QList<QPMMime*> mimes = theMimeList()->mimes();
277 for (int i = mimes.size()-1; i >= 0; --i) {
278 QStringList fmts = mimes[i]->mimesForFormats(formats);
279 int priority = 0;
280 foreach (QString fmt, fmts) {
281 ++priority;
282 QList<Match>::iterator it = matches.begin();
283 for (; it != matches.end(); ++it) {
284 Match &match = *it;
285 if (match.mime == fmt) {
286 // replace if priority is higher, ignore otherwise
287 if (priority < match.priority) {
288 match.converter = mimes[i];
289 match.priority = priority;
290 }
291 break;
292 }
293 }
294 if (it == matches.end()) {
295 matches += Match(mimes[i], fmt, priority);
296 }
297 }
298 }
299
300 return matches;
301}
302
303// static
304QList<QPMMime::Match> QPMMime::allConvertersFromMimeData(const QMimeData *mimeData)
305{
306 QList<Match> matches;
307
308 QList<QPMMime*> mimes = theMimeList()->mimes();
309 for (int i = mimes.size()-1; i >= 0; --i) {
310 QList<ULONG> cfs = mimes[i]->formatsForMimeData(mimeData);
311 int priority = 0;
312 foreach (ULONG cf, cfs) {
313 ++priority;
314 QList<Match>::iterator it = matches.begin();
315 for (; it != matches.end(); ++it) {
316 Match &match = *it;
317 if (match.format == cf) {
318 // replace if priority is higher, ignore otherwise
319 if (priority < match.priority) {
320 match.converter = mimes[i];
321 match.priority = priority;
322 }
323 break;
324 }
325 }
326 if (it == matches.end()) {
327 matches += Match(mimes[i], cf, priority);
328 }
329 }
330 }
331
332 return matches;
333}
334
335////////////////////////////////////////////////////////////////////////////////
336
337class QPMMimeText : public QPMMime
338{
339public:
340 QPMMimeText();
341
342 QList<ULONG> formatsForMimeData(const QMimeData *mimeData) const;
343 bool convertFromMimeData(const QMimeData *mimeData, ULONG format,
344 ULONG &flags, ULONG *data) const;
345
346 QStringList mimesForFormats(const QList<ULONG> &formats) const;
347 QVariant convertFromFormat(ULONG format, ULONG flags, ULONG data,
348 const QString &mimeType,
349 QVariant::Type preferredType) const;
350
351 const ULONG CF_TextUnicode;
352};
353
354QPMMimeText::QPMMimeText()
355 // "text/unicode" is what Mozilla uses to for unicode, so Qt apps will
356 // be able to interchange unicode text with Mozilla apps
357 : CF_TextUnicode (registerMimeType(QLatin1String("text/unicode")))
358{
359}
360
361QList<ULONG> QPMMimeText::formatsForMimeData(const QMimeData *mimeData) const
362{
363 QList<ULONG> cfs;
364 if (mimeData->hasText())
365 cfs << CF_TEXT << CF_TextUnicode;
366 return cfs;
367}
368
369// text/plain is defined as using CRLF, but so many programs don't,
370// and programmers just look for '\n' in strings.
371// OS/2 really needs CRLF, so we ensure it here.
372bool QPMMimeText::convertFromMimeData(const QMimeData *mimeData, ULONG format,
373 ULONG &flags, ULONG *data) const
374{
375 if (!mimeData->hasText())
376 return false;
377
378 flags = CFI_POINTER;
379
380 if (data == NULL)
381 return true; // delayed rendering, nothing to do
382
383 QByteArray r;
384
385 if (format == CF_TEXT) {
386 QByteArray str = mimeData->text().toLocal8Bit();
387 // Anticipate required space for CRLFs at 1/40
388 int maxsize = str.size()+str.size()/40+3;
389 r.fill('\0', maxsize);
390 char* o = r.data();
391 const char* d = str.data();
392 const int s = str.size();
393 bool cr = false;
394 int j = 0;
395 for (int i = 0; i < s; i++) {
396 char c = d[i];
397 if (c == '\r')
398 cr = true;
399 else {
400 if (c == '\n') {
401 if (!cr)
402 o[j++] = '\r';
403 }
404 cr = false;
405 }
406 o[j++] = c;
407 if (j+3 >= maxsize) {
408 maxsize += maxsize/4;
409 r.resize(maxsize);
410 o = r.data();
411 }
412 }
413 o[j] = 0;
414 } else if (format == CF_TextUnicode) {
415 QString str = mimeData->text();
416 const QChar *u = str.unicode();
417 QString res;
418 const int s = str.length();
419 int maxsize = s + s/40 + 3;
420 res.resize(maxsize);
421 int ri = 0;
422 bool cr = false;
423 for (int i = 0; i < s; ++i) {
424 if (*u == QLatin1Char('\r'))
425 cr = true;
426 else {
427 if (*u == QLatin1Char('\n') && !cr)
428 res[ri++] = QLatin1Char('\r');
429 cr = false;
430 }
431 res[ri++] = *u;
432 if (ri+3 >= maxsize) {
433 maxsize += maxsize/4;
434 res.resize(maxsize);
435 }
436 ++u;
437 }
438 res.truncate(ri);
439 const int byteLength = res.length()*2;
440 r.fill('\0', byteLength + 2);
441 memcpy(r.data(), res.unicode(), byteLength);
442 r[byteLength] = 0;
443 r[byteLength+1] = 0;
444 } else{
445 return false;
446 }
447
448 *data = QPMMime::allocateMemory(r.size());
449 if (!*data)
450 return false;
451
452 memcpy((void *)*data, r.data(), r.size());
453 return true;
454}
455
456QStringList QPMMimeText::mimesForFormats(const QList<ULONG> &formats) const
457{
458 QStringList mimes;
459 foreach(ULONG cf, formats) {
460 if (cf == CF_TEXT || cf == CF_TextUnicode){
461 mimes << QLatin1String("text/plain");
462 break;
463 }
464 }
465 return mimes;
466}
467
468QVariant QPMMimeText::convertFromFormat(ULONG format, ULONG flags, ULONG data,
469 const QString &mimeType,
470 QVariant::Type preferredType) const
471{
472 return QVariant();
473}
474
475////////////////////////////////////////////////////////////////////////////////
476
477QPMMimeList::QPMMimeList()
478 : initialized(false)
479{
480}
481
482QPMMimeList::~QPMMimeList()
483{
484 while (list.size())
485 delete list.first();
486}
487
488
489void QPMMimeList::init()
490{
491 if (!initialized) {
492 initialized = true;
493 new QPMMimeText;
494 }
495}
496
497void QPMMimeList::addMime(QPMMime *mime)
498{
499 init();
500 list.append(mime);
501}
502
503void QPMMimeList::removeMime(QPMMime *mime)
504{
505 init();
506 list.removeAll(mime);
507}
508
509QList<QPMMime*> QPMMimeList::mimes()
510{
511 init();
512 return list;
513}
514
515QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.