source: trunk/src/kernel/qmime_pm.cpp@ 20

Last change on this file since 20 was 20, checked in by dmik, 20 years ago

Fixed a bunch of compiler warnings

  • Property svn:keywords set to Id
File size: 19.8 KB
Line 
1/****************************************************************************
2** $Id: qmime_pm.cpp 20 2005-11-17 18:00:27Z dmik $
3**
4** Implementation of OS/2 PM MIME <-> clipboard converters
5**
6** Copyright (C) 1992-2002 Trolltech AS. All rights reserved.
7** Copyright (C) 2004 Norman ASA. Initial OS/2 Port.
8** Copyright (C) 2005 netlabs.org. Further OS/2 Development.
9**
10** This file is part of the kernel module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qmime.h"
39
40#ifndef QT_NO_MIME
41
42#include "qstrlist.h"
43#include "qimage.h"
44#include "qdatastream.h"
45#include "qdragobject.h"
46#include "qbuffer.h"
47#include "qapplication_p.h"
48#include "qtextcodec.h"
49#include "qregexp.h"
50#include "qt_os2.h"
51
52
53static QPtrList<QPMMime> mimes;
54
55/*!
56 \class QPMMime qmime.h
57 \brief The QPMMime class maps open-standard MIME to OS/2 PM Clipboard formats.
58 \ingroup io
59 \ingroup draganddrop
60 \ingroup misc
61
62 Qt's drag-and-drop and clipboard facilities use the MIME standard.
63 On X11, this maps trivially to the Xdnd protocol, but on OS/2
64 although some applications use MIME types to describe clipboard
65 formats, others use arbitrary non-standardized naming conventions,
66 or unnamed built-in formats of OS/2.
67
68 By instantiating subclasses of QPMMime that provide conversions
69 between OS/2 PM Clipboard and MIME formats, you can convert
70 proprietary clipboard formats to MIME formats.
71
72 Qt has predefined support for the following OS/2 PM Clipboard formats:
73 \list
74 \i CF_TEXT - converted to "text/plain;charset=system" or "text/plain"
75 and supported by QTextDrag.
76 \i CF_BITMAP - converted to "image/fmt", where fmt is
77 a \link QImage::outputFormats() Qt image format\endlink,
78 and supported by QImageDrag.
79 \endlist
80
81 An example use of this class would be to map the OS/2 Metafile
82 clipboard format (CF_METAFILE) to and from the MIME type "image/x-metafile".
83 This conversion might simply be adding or removing a header, or even
84 just passing on the data. See the
85 \link dnd.html Drag-and-Drop documentation\endlink for more information
86 on choosing and definition MIME types.
87
88 You can check if a MIME type is convertible using canConvert() and
89 can perform conversions with convertToMime() and convertFromMime().
90*/
91
92/*!
93 Constructs a new conversion object, adding it to the globally accessed
94 list of available convertors.
95*/
96QPMMime::QPMMime()
97{
98 mimes.append( this );
99}
100
101/*!
102 Destroys a conversion object, removing it from the global
103 list of available convertors.
104*/
105QPMMime::~QPMMime()
106{
107 mimes.remove( this );
108}
109
110
111
112struct QPMRegisteredMimeType {
113 QPMRegisteredMimeType( ulong c, const char *m ) :
114 cf( c ), mime( m ) {}
115 ulong cf;
116 QCString mime;
117};
118
119static QPtrList<QPMRegisteredMimeType> mimetypes;
120
121
122class QPMMimeAnyMime : public QPMMime {
123public:
124 const char* convertorName();
125 int countCf();
126 ulong cf( int index );
127 ulong flFor( ulong cf );
128 ulong cfFor( const char* mime );
129 const char* mimeFor( ulong cf );
130 bool canConvert( const char* mime, ulong cf );
131 QByteArray convertToMime( QByteArray data, const char *, ulong );
132 QByteArray convertFromMime( QByteArray data, const char *, ulong );
133};
134
135const char* QPMMimeAnyMime::convertorName()
136{
137 return "Any-Mime";
138}
139
140int QPMMimeAnyMime::countCf()
141{
142 return mimetypes.count();
143}
144
145ulong QPMMimeAnyMime::cf( int index )
146{
147 return mimetypes.at( index )->cf;
148}
149
150ulong QPMMimeAnyMime::flFor( ulong cf )
151{
152 // all formats in this converter assume CFI_POINTER data storage type
153 return CFI_POINTER;
154}
155
156ulong QPMMimeAnyMime::cfFor( const char* mime )
157{
158 QPMRegisteredMimeType *mt = mimetypes.current();
159 if ( mt ) // quick check with most-recent
160 if ( qstricmp( mt->mime, mime ) == 0 )
161 return mt->cf;
162 for ( mt = mimetypes.first(); mt; mt = mimetypes.next() )
163 if ( qstricmp( mt->mime, mime ) == 0 )
164 return mt->cf;
165 // try to register the mime type
166 return registerMimeType( mime );
167}
168
169const char* QPMMimeAnyMime::mimeFor( ulong cf )
170{
171 QPMRegisteredMimeType *mt = mimetypes.current();
172 if ( mt ) // quick check with most-recent
173 if ( mt->cf == cf )
174 return mt->mime;
175 for ( mt = mimetypes.first(); mt; mt = mimetypes.next() )
176 if ( mt->cf == cf )
177 return mt->mime;
178 return 0;
179}
180
181bool QPMMimeAnyMime::canConvert( const char* mime, ulong cf )
182{
183 QPMRegisteredMimeType *mt = mimetypes.current();
184 do {
185 if ( mt ) // quick check with most-recent
186 if ( mt->cf == cf )
187 break;
188 for ( mt = mimetypes.first(); mt; mt = mimetypes.next() )
189 if ( mt->cf == cf )
190 break;
191 if ( !mt ) {
192 ulong f = registerMimeType( mime );
193 return f && f == cf;
194 }
195 } while ( 0 );
196
197 return qstricmp( mt->mime, mime ) == 0;
198}
199
200QByteArray QPMMimeAnyMime::convertToMime( QByteArray data, const char *, ulong )
201{
202 return data;
203}
204
205QByteArray QPMMimeAnyMime::convertFromMime( QByteArray data, const char *, ulong )
206{
207 return data;
208}
209
210
211
212class QPMMimeText : public QPMMime {
213public:
214 QPMMimeText();
215 const char* convertorName();
216 int countCf();
217 ulong cf( int index );
218 ulong flFor( ulong cf );
219 ulong cfFor( const char* mime );
220 const char* mimeFor( ulong cf );
221 bool canConvert( const char* mime, ulong cf );
222 QByteArray convertToMime( QByteArray data, const char *, ulong cf );
223 QByteArray convertFromMime( QByteArray data, const char *, ulong cf );
224private:
225 const ulong CF_TextUnicode;
226};
227
228QPMMimeText::QPMMimeText() :
229 // register a clipboard format for unicode text
230 // ("text/unicode" is what Mozilla uses to for unicode, so Qt apps will
231 // be able to interchange unicode text with Mozilla apps)
232 CF_TextUnicode ( WinAddAtom( WinQuerySystemAtomTable(), "text/unicode" ) )
233{
234}
235
236const char* QPMMimeText::convertorName()
237{
238 return "Text";
239}
240
241int QPMMimeText::countCf()
242{
243 return 2;
244}
245
246ulong QPMMimeText::cf( int index )
247{
248 if ( index == 0 )
249 return CF_TextUnicode;
250 else if ( index == 1 )
251 return CF_TEXT;
252 return 0;
253}
254
255ulong QPMMimeText::flFor( ulong cf )
256{
257 // both CF_TEXT and CF_TextUnicode use CFI_POINTER
258 return cf == CF_TEXT || cf == CF_TextUnicode ? CFI_POINTER : 0;
259}
260
261ulong QPMMimeText::cfFor( const char* mime )
262{
263/// @todo (dmik) do we want to accept "text/plain" w/o "charset="?
264// if ( qstricmp( mime, "text/plain" ) == 0 )
265// return CF_TEXT;
266
267 QCString m( mime );
268 int i = m.find( "charset=" );
269 if ( i >= 0 ) {
270 QCString cs( m.data() + i + 8 );
271 i = cs.find( ";" );
272 if ( i >= 0 )
273 cs = cs.left( i );
274 if ( cs == "system" )
275 return CF_TEXT;
276 if ( cs == "ISO-10646-UCS-2" )
277 return CF_TextUnicode;
278 }
279 return 0;
280}
281
282const char* QPMMimeText::mimeFor( ulong cf )
283{
284 if ( cf == CF_TEXT )
285 return "text/plain;charset=system";
286 else if ( cf == CF_TextUnicode )
287 return "text/plain;charset=ISO-10646-UCS-2";
288 return 0;
289}
290
291bool QPMMimeText::canConvert( const char* mime, ulong cf )
292{
293 if ( !cf )
294 return FALSE;
295
296 return cfFor( mime ) == cf;
297}
298
299/*
300 text/plain is defined as using CRLF, but so many programs don't,
301 and programmers just look for '\n' in strings.
302 OS/2 really needs CRLF, so we ensure it here.
303*/
304
305QByteArray QPMMimeText::convertToMime( QByteArray data, const char* , ulong cf )
306{
307 if ( cf == CF_TEXT ) {
308 const char* d = data.data();
309 const int s = qstrlen( d );
310 QByteArray r( data.size() + 1 );
311 char* o = r.data();
312 int j = 0;
313 for ( int i = 0; i < s; i++ ) {
314 char c = d[i];
315 if ( c != '\r' )
316 o[j++] = c;
317 }
318 o[j] = 0;
319 return r;
320 } else if ( cf == CF_TextUnicode ) {
321 // Mozilla uses un-marked little-endian nul-terminated Unicode
322 // for "text/unicode"
323 int sz = data.size();
324 int len = 0;
325 // Find NUL
326 for ( ; len < sz - 1 && (data[len+0] || data[len+1]); len += 2 )
327 ;
328
329 QByteArray r( len + 2 );
330 r[0] = uchar( 0xff ); // BOM
331 r[1] = uchar( 0xfe );
332 memcpy( r.data() + 2, data.data(), len );
333 return r;
334 }
335
336 return QByteArray();
337}
338
339extern QTextCodec* qt_findcharset( const QCString& mimetype );
340
341QByteArray QPMMimeText::convertFromMime( QByteArray data, const char *mime, ulong cf )
342{
343 if ( cf == CF_TEXT ) {
344 // Anticipate required space for CRLFs at 1/40
345 int maxsize = data.size() + data.size() / 40 + 3;
346 QByteArray r( maxsize );
347 char* o = r.data();
348 const char* d = data.data();
349 const int s = data.size();
350 bool cr = FALSE;
351 int j = 0;
352 for ( int i = 0; i < s; i++ ) {
353 char c = d[i];
354 if ( c == '\r' )
355 cr = TRUE;
356 else {
357 if ( c == '\n' ) {
358 if ( !cr )
359 o[j++]='\r';
360 }
361 cr = FALSE;
362 }
363 o[j++] = c;
364 if ( j + 3 >= maxsize ) {
365 maxsize += maxsize / 4;
366 r.resize( maxsize );
367 o = r.data();
368 }
369 }
370 o[j] = 0;
371 return r;
372 } else if ( cf == CF_TextUnicode ) {
373 QTextCodec *codec = qt_findcharset( mime );
374 QString str = codec->toUnicode( data );
375 const QChar *u = str.unicode();
376 QString res;
377 const int s = str.length();
378 int maxsize = s + s / 40 + 3;
379 res.setLength( maxsize );
380 int ri = 0;
381 bool cr = FALSE;
382 for ( int i = 0; i < s; ++ i ) {
383 if ( *u == '\r' )
384 cr = TRUE;
385 else {
386 if ( *u == '\n' && !cr )
387 res[ri++] = QChar('\r');
388 cr = FALSE;
389 }
390 res[ri++] = *u;
391 if ( ri + 3 >= maxsize ) {
392 maxsize += maxsize / 4;
393 res.setLength( maxsize );
394 }
395 ++u;
396 }
397 res.truncate( ri );
398 const int byteLength = res.length() * 2;
399 QByteArray r( byteLength + 2 );
400 memcpy( r.data(), res.unicode(), byteLength );
401 r[byteLength] = 0;
402 r[byteLength+1] = 0;
403 return r;
404 }
405
406 return QByteArray();
407}
408
409
410class QPMMimeImage : public QPMMime {
411public:
412 const char* convertorName();
413 int countCf();
414 ulong cf( int index );
415 ulong flFor( ulong cf );
416 ulong cfFor( const char* mime );
417 const char* mimeFor( ulong cf );
418 bool canConvert( const char* mime, ulong cf );
419 QByteArray convertToMime( QByteArray data, const char *mime, ulong cf );
420 QByteArray convertFromMime( QByteArray data, const char *mime, ulong cf );
421};
422
423const char* QPMMimeImage::convertorName()
424{
425 return "Image";
426}
427
428int QPMMimeImage::countCf()
429{
430 return 1;
431}
432
433ulong QPMMimeImage::cf( int index )
434{
435 return index == 0 ? CF_BITMAP : 0;
436}
437
438ulong QPMMimeImage::flFor( ulong cf )
439{
440 // CF_BITMAP uses CFI_HANDLE
441 return cf == CF_BITMAP ? CFI_HANDLE : 0;
442}
443
444ulong QPMMimeImage::cfFor( const char *mime )
445{
446 if ( qstrnicmp( mime, "image/", 6 ) == 0 ) {
447 QStrList ofmts = QImage::outputFormats();
448 for ( const char *fmt = ofmts.first(); fmt; fmt = ofmts.next() )
449 if ( qstricmp( fmt, mime + 6 ) == 0 )
450 return CF_BITMAP;
451 }
452 return 0;
453}
454
455const char* QPMMimeImage::mimeFor( ulong cf )
456{
457 if ( cf == CF_BITMAP )
458 return "image/bmp";
459 return 0;
460}
461
462bool QPMMimeImage::canConvert( const char* mime, ulong cf )
463{
464 if ( cf == CF_BITMAP && qstrnicmp( mime, "image/", 6 ) == 0 ) {
465 QStrList ofmts = QImage::outputFormats();
466 for ( const char* fmt = ofmts.first(); fmt; fmt = ofmts.next() )
467 if ( qstricmp( fmt, mime + 6 ) == 0 )
468 return TRUE;
469 }
470 return FALSE;
471}
472
473QByteArray QPMMimeImage::convertToMime( QByteArray data, const char *mime, ulong cf )
474{
475 if ( qstrnicmp( mime, "image/", 6 ) != 0 || cf != CF_BITMAP ) // Sanity
476 return QByteArray();
477
478 HBITMAP hbm = (HBITMAP) *(ULONG *) data.data();
479
480 QPixmap pm;
481 pm.attachHandle( hbm );
482 QImage img = pm.convertToImage();
483 pm.detachHandle(); // prevent hbm from being deleted
484
485 QCString ofmt = mime + 6;
486 QByteArray ba;
487 QBuffer iod( ba );
488 iod.open( IO_WriteOnly );
489 QImageIO iio( &iod, ofmt.upper() );
490 iio.setImage( img );
491 if ( iio.write() ) {
492 iod.close();
493 return ba;
494 }
495
496 // Failed
497 return QByteArray();
498}
499
500QByteArray QPMMimeImage::convertFromMime( QByteArray data, const char *mime, ulong cf )
501{
502 if ( qstrnicmp( mime, "image/", 6 ) != 0 || cf != CF_BITMAP ) // Sanity
503 return QByteArray();
504
505 QImage img;
506 img.loadFromData( (unsigned char *) data.data(), data.size() );
507 if ( !img.isNull() ) {
508 HBITMAP hbm = QPixmap( img ).detachHandle();
509 if ( hbm ) {
510 QByteArray ba ( sizeof(HBITMAP) );
511 *(HBITMAP *) ba.data() = hbm;
512 return ba;
513 }
514 }
515
516 // Failed
517 return QByteArray();
518}
519
520
521static QPMMimeAnyMime *anymime = 0;
522
523static
524void cleanup_mimes()
525{
526 QPMMime* wm;
527 while ( (wm = mimes.first()) ) {
528 delete wm;
529 }
530 mimetypes.setAutoDelete( TRUE );
531 mimetypes.clear();
532 anymime = 0;
533}
534
535/*!
536 This is an internal function.
537*/
538void QPMMime::initialize()
539{
540 if ( mimes.isEmpty() ) {
541 new QPMMimeImage;
542 new QPMMimeText;
543 anymime = new QPMMimeAnyMime;
544
545 qAddPostRoutine( cleanup_mimes );
546 }
547}
548
549/*!
550 \internal
551 This is an internal function.
552*/
553ulong QPMMime::registerMimeType( const char *mime )
554{
555 // first, look if other non-QPMMimeAnyMime convertors support this mime,
556 // to avoid double handling of this mime type by another (probably,
557 // system-integrated and most likely more specialized) convertor and the
558 // dummy "pass-through" QPMMimeAnyMime convertor (which will handle the
559 // given mime type after once we call mimetyes.append (...)).
560 QPtrListIterator<QPMMime> it( mimes );
561 for ( QPMMime* c; (c = *it); ++ it )
562 if ( c != anymime && c->cfFor( mime ) )
563 return 0;
564
565 ulong f = WinAddAtom( WinQuerySystemAtomTable(), mime );
566 if ( !f ) {
567#ifndef QT_NO_DEBUG
568 qSystemWarning( "QPMMime: Failed to register clipboard format" );
569#endif
570 return 0;
571 }
572 QPMRegisteredMimeType *mt = mimetypes.current();
573 if ( !mt || mt->cf != f ) {
574 for ( mt = mimetypes.first(); mt && mt->cf != f; mt = mimetypes.next() )
575 ;
576 if ( !mt ) {
577 mimetypes.append( new QPMRegisteredMimeType ( f, mime ) );
578 // successful memory allocation check
579 if ( !(mt = mimetypes.current()) || mt->cf != f )
580 return 0;
581 }
582 }
583 return f;
584}
585
586/*!
587 Returns the most-recently created QPMMime that can convert
588 between the \a mime and \a cf formats. Returns 0 if no such convertor
589 exists.
590*/
591QPMMime *
592QPMMime::convertor( const char *mime, ulong cf )
593{
594 // return nothing for illegal requests
595 if ( !cf )
596 return 0;
597
598 QPMMime* wm;
599 for ( wm = mimes.first(); wm; wm = mimes.next() ) {
600 if ( wm->canConvert( mime, cf ) ) {
601 return wm;
602 }
603 }
604 return 0;
605}
606
607
608/*!
609 Returns a MIME type for \a cf, or 0 if none exists.
610*/
611const char* QPMMime::cfToMime( ulong cf )
612{
613 const char* m = 0;
614 QPMMime* wm;
615 for ( wm = mimes.first(); wm && !m; wm = mimes.next() ) {
616 m = wm->mimeFor( cf );
617 }
618 return m;
619}
620
621/*!
622 Returns a list of all currently defined QPMMime objects.
623*/
624QPtrList<QPMMime> QPMMime::all()
625{
626 return mimes;
627}
628
629/*!
630 \fn const char* QPMMime::convertorName()
631
632 Returns a name for the convertor.
633
634 All subclasses must reimplement this pure virtual function.
635*/
636
637/*!
638 \fn int QPMMime::countCf()
639
640 Returns the number of OS/2 PM Clipboard formats supported by this
641 convertor.
642
643 All subclasses must reimplement this pure virtual function.
644*/
645
646/*!
647 \fn ulong QPMMime::cf(int index)
648
649 Returns the OS/2 PM Clipboard format supported by this convertor
650 that is ordinarily at position \a index. This means that cf(0)
651 returns the first OS/2 PM Clipboard format supported, and
652 cf(countCf()-1) returns the last. If \a index is out of range the
653 return value is undefined.
654
655 All subclasses must reimplement this pure virtual function.
656*/
657
658/*!
659 \fn ulong QPMMime::flFor(ulong cf)
660
661 Returns the data storage flag for the OS/2 PM Clipboard type \a cf
662 (either \c CFI_POINTER or \c CFI_HANDLE) used by this convertor,
663 or 0 if invalid \a cf is specified.
664
665 All subclasses must reimplement this pure virtual function.
666*/
667
668/*!
669 \fn bool QPMMime::canConvert( const char* mime, ulong cf )
670
671 Returns TRUE if the convertor can convert (both ways) between
672 \a mime and \a cf; otherwise returns FALSE.
673
674 All subclasses must reimplement this pure virtual function.
675*/
676
677/*!
678 \fn const char* QPMMime::mimeFor(ulong cf)
679
680 Returns the MIME type used for OS/2 PM Clipboard format \a cf, or
681 0 if this convertor does not support \a cf.
682
683 All subclasses must reimplement this pure virtual function.
684*/
685
686/*!
687 \fn ulong QPMMime::cfFor(const char* mime)
688
689 Returns the OS/2 PM Clipboard type used for MIME type \a mime, or
690 0 if this convertor does not support \a mime.
691
692 All subclasses must reimplement this pure virtual function.
693*/
694
695/*!
696 \fn QByteArray QPMMime::convertToMime( QByteArray data, const char* mime, ulong cf )
697
698 Returns \a data converted from OS/2 PM Clipboard format \a cf
699 to MIME type \a mime.
700
701 Note that OS/2 PM Clipboard formats must all be self-terminating. The
702 input \a data may contain trailing data. If flFor(ulong) for the given \a cf
703 has the \c CFI_HANDLE flag set, then first 4 bytes of \a data represent a
704 valid OS/2 handle of the appropriate type, otherwise \a data contains data
705 itself.
706
707 All subclasses must reimplement this pure virtual function.
708*/
709
710/*!
711 \fn QByteArray QPMMime::convertFromMime( QByteArray data, const char* mime, ulong cf )
712
713 Returns \a data converted from MIME type \a mime
714 to OS/2 PM Clipboard format \a cf.
715
716 Note that OS/2 PM Clipboard formats must all be self-terminating. The
717 return value may contain trailing data. If flFor(ulong) for the given \a cf
718 has the \c CFI_HANDLE flag set, then first 4 bytes of the returned array
719 must represent a valid OS/2 handle of the appropriate type, otherwise the array
720 contains data itself.
721
722 All subclasses must reimplement this pure virtual function.
723*/
724
725#endif // QT_NO_MIME
Note: See TracBrowser for help on using the repository browser.