source: trunk/src/kernel/qclipboard_pm.cpp@ 108

Last change on this file since 108 was 96, checked in by dmik, 19 years ago

QTextDrag: decode() gives mime types in Unicode encodings a higher priprity (to guarantee loseless decoding); "text/whatever" w/o any charset is reported as the first mime format (for better integration with native drop targets).

  • Property svn:keywords set to Id
File size: 17.4 KB
Line 
1/****************************************************************************
2** $Id: qclipboard_pm.cpp 96 2006-07-06 21:11:46Z dmik $
3**
4** Implementation of QClipboard class for OS/2
5**
6** Copyright (C) 1992-2000 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 "qclipboard.h"
39
40#ifndef QT_NO_CLIPBOARD
41
42#include "qapplication.h"
43#include "qapplication_p.h"
44#include "qeventloop.h"
45#include "qpixmap.h"
46#include "qdatetime.h"
47#include "qimage.h"
48#include "qmime.h"
49#include "qt_os2.h"
50
51
52/*****************************************************************************
53 Internal QClipboard functions for OS/2.
54 *****************************************************************************/
55
56static HWND prevClipboardViewer = 0;
57static QWidget *qt_cb_owner = 0;
58
59static QWidget *clipboardOwner()
60{
61 if ( !qt_cb_owner )
62 qt_cb_owner = new QWidget( 0, "internal clipboard owner" );
63 return qt_cb_owner;
64}
65
66
67/*****************************************************************************
68 QClipboard member functions for OS/2.
69 *****************************************************************************/
70
71bool QClipboard::supportsSelection() const
72{
73 return FALSE;
74}
75
76
77bool QClipboard::ownsSelection() const
78{
79 return FALSE;
80}
81
82
83bool QClipboard::ownsClipboard() const
84{
85 return qt_cb_owner && WinQueryClipbrdOwner( 0 ) == qt_cb_owner->winId();
86}
87
88
89void QClipboard::setSelectionMode(bool)
90{
91}
92
93
94bool QClipboard::selectionModeEnabled() const
95{
96 return FALSE;
97}
98
99
100void QClipboard::ownerDestroyed()
101{
102 QWidget *owner = (QWidget *)sender();
103 if ( owner == qt_cb_owner ) {
104 if ( owner->winId() == WinQueryClipbrdViewer( 0 ) )
105 WinSetClipbrdViewer( 0, prevClipboardViewer );
106 prevClipboardViewer = 0;
107 }
108}
109
110
111void QClipboard::connectNotify( const char *signal )
112{
113 if ( qstrcmp( signal, SIGNAL( dataChanged() ) ) == 0 ) {
114 QWidget *owner = clipboardOwner();
115 HWND clipboardViewer = WinQueryClipbrdViewer( 0 );
116 if ( owner->winId() != clipboardViewer ) {
117 prevClipboardViewer = clipboardViewer;
118 BOOL ok = WinSetClipbrdViewer( 0, owner->winId() );
119 Q_UNUSED( ok );
120#ifndef QT_NO_DEBUG
121 if ( !ok )
122 qSystemWarning( "QClipboard: Failed to set clipboard viewer" );
123#endif
124 connect( owner, SIGNAL( destroyed() ), SLOT( ownerDestroyed() ) );
125 }
126 }
127}
128
129class QClipboardWatcher : public QMimeSource {
130public:
131 QClipboardWatcher()
132 {
133 }
134
135 bool provides( const char* mime ) const
136 {
137 bool r = FALSE;
138 if ( WinOpenClipbrd( 0 ) ) {
139 ulong cf = 0;
140 while (!r && (cf = WinEnumClipbrdFmts( 0, cf ))) {
141 if ( QPMMime::convertor( mime, cf ) )
142 r = TRUE;
143 }
144#ifndef QT_NO_DEBUG
145 if ( !WinCloseClipbrd( 0 ) )
146 qSystemWarning( "QClipboard: Failed to close clipboard" );
147#else
148 WinCloseClipbrd( 0 );
149#endif
150 }
151#ifndef QT_NO_DEBUG
152 else
153 qSystemWarning( "QClipboardWatcher: Failed to open clipboard" );
154#endif
155 return r;
156 }
157
158 const char* format( int n ) const
159 {
160 const char* mime = 0;
161
162 if ( n >= 0 ) {
163 if ( WinOpenClipbrd( 0 ) ) {
164 ulong cf = 0;
165 while ( (cf = WinEnumClipbrdFmts( 0, cf )) ) {
166 mime = QPMMime::cfToMime( cf );
167 if ( mime ) {
168 if ( !n )
169 break;
170 n--;
171 mime = 0;
172 }
173 }
174 WinCloseClipbrd( 0 );
175 }
176 }
177 if ( !n )
178 return mime;
179 return 0;
180 }
181
182 QByteArray encodedData( const char* mime ) const
183 {
184 QByteArray r;
185 if ( WinOpenClipbrd( 0 ) ) {
186 QPtrList<QPMMime> all = QPMMime::all();
187 for ( QPMMime* c = all.first(); c; c = all.next() ) {
188 ulong cf = c->cfFor( mime );
189 if ( cf ) {
190 ULONG fl = 0;
191 BOOL ok = WinQueryClipbrdFmtInfo( 0, cf, &fl );
192 if ( ok && (fl & QPMMime::CFI_Storage) ==
193 (c->flFor( cf ) & QPMMime::CFI_Storage) ) {
194 ULONG data = WinQueryClipbrdData ( 0, cf );
195 if ( (ok = (data != 0)) ) {
196 char *dataPtr = 0;
197 uint dataSize = 0;
198 if ( fl & CFI_POINTER ) {
199 ULONG sz = ~0, flags = 0;
200 // get data size rounded to the page boundary (4K)
201 APIRET rc = DosQueryMem( (PVOID) data, &sz, &flags );
202 if ( rc ) {
203#ifndef QT_NO_DEBUG
204 qSystemWarning( "QClipboard: Failed to query memory info", rc );
205#endif
206 continue;
207 }
208 // look at the last dword of the last page
209 dataSize = *(((ULONG *)(data + sz)) - 1);
210 // If this dword seems to contain the exact data size,
211 // use it. Otherwise use the rounded size hoping
212 // that a data reader will correctly determine
213 // the exact size according to the mime type and
214 // data header
215 if ( !dataSize || dataSize > (sz - sizeof( ULONG )) )
216 dataSize = sz;
217 dataPtr = (char *) data;
218 }
219 else if ( fl & CFI_HANDLE ) {
220 // represent the handle as first 4 bytes of
221 // the data array
222 dataSize = sizeof( ULONG );
223 dataPtr = (char *) &data;
224 }
225 else {
226#ifdef QT_CHECK_RANGE
227 qWarning( "QClipboard: wrong format flags: %08lX", fl );
228#endif
229 continue;
230 }
231 r.setRawData( dataPtr, dataSize );
232 QByteArray tr = c->convertToMime( r, mime, cf );
233 tr.detach();
234 r.resetRawData( dataPtr, dataSize );
235 r = tr;
236 break;
237 }
238 }
239#ifndef QT_NO_DEBUG
240 if ( !ok )
241 qSystemWarning( "QClipboard: Failed to read clipboard data" );
242#endif
243 }
244 }
245 WinCloseClipbrd( 0 );
246 }
247#ifndef QT_NO_DEBUG
248 else
249 qSystemWarning( "QClipboard: Failed to open Clipboard" );
250#endif
251 return r;
252 }
253};
254
255
256
257class QClipboardData
258{
259public:
260 QClipboardData();
261 ~QClipboardData();
262
263 void setSource(QMimeSource* s)
264 {
265 delete src;
266 src = s;
267 if ( src )
268 src->clearCache();
269 }
270 QMimeSource* source()
271 {
272 if ( src )
273 src->clearCache();
274 return src;
275 }
276 QMimeSource* provider()
277 {
278 if (!prov)
279 prov = new QClipboardWatcher();
280 prov->clearCache();
281 return prov;
282 }
283
284private:
285 QMimeSource* src;
286 QMimeSource* prov;
287};
288
289QClipboardData::QClipboardData()
290{
291 src = 0;
292 prov = 0;
293}
294
295QClipboardData::~QClipboardData()
296{
297 delete src;
298 delete prov;
299}
300
301static QClipboardData *internalCbData = 0;
302
303static void cleanupClipboardData()
304{
305 delete internalCbData;
306 internalCbData = 0;
307}
308
309static QClipboardData *clipboardData()
310{
311 if ( internalCbData == 0 ) {
312 internalCbData = new QClipboardData;
313 Q_CHECK_PTR( internalCbData );
314 qAddPostRoutine( cleanupClipboardData );
315 }
316 return internalCbData;
317}
318
319
320//#define QT_DEBUG_CB
321
322static void setClipboardData( ulong cf, const char *mime, QPMMime *c, QMimeSource *s )
323{
324 QByteArray md = s->encodedData( mime );
325#if defined(QT_DEBUG_CB)
326 qDebug( "source is %d bytes of %s", md.size(), mime );
327#endif
328
329 md = c->convertFromMime( md, mime, cf );
330 ulong fl = c->flFor( cf );
331 int len = md.size();
332#if defined(QT_DEBUG_CB)
333 qDebug( "rendered %d bytes of CF 0x%08lX (0x%08lX) by %s",
334 len, cf, fl, c->convertorName());
335#endif
336
337 ULONG data = 0;
338 if ( fl & CFI_POINTER ) {
339 // allocate memory for the array + dword for its size
340 APIRET rc = DosAllocSharedMem( (PVOID *) &data, NULL, len + 4,
341 PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE );
342 if ( rc ) {
343#ifndef QT_NO_DEBUG
344 qSystemWarning( "QClipboard: Failed to allocate shared memory", rc );
345#endif
346 return;
347 }
348 // get the size rounded to the page boundary (4K)
349 ULONG sz = ~0, flags = 0;
350 rc = DosQueryMem( (PVOID) (data + len - 1), &sz, &flags );
351 if ( !rc ) {
352 sz += (len - 1);
353 // store the exact size to the last dword of the last page
354 *(((ULONG *)(data + sz)) - 1) = len;
355 }
356#ifndef QT_NO_DEBUG
357 else
358 qSystemWarning( "QClipboard: Failed to query memory info", rc );
359#endif
360 memcpy( (void *) data, md.data(), len );
361 }
362 else if ( fl & CFI_HANDLE ) {
363 if ( len == sizeof(ULONG) ) {
364 // treat first 4 bytes of the array as a handle
365 data = *(ULONG *) md.data();
366 } else {
367#ifdef QT_CHECK_RANGE
368 qWarning( "QClipboard: wrong CFI_HANDLE data len: %d", len );
369#endif
370 return;
371 }
372 }
373 else {
374#ifdef QT_CHECK_RANGE
375 qWarning( "QClipboard: wrong format flags: %08lX", fl );
376#endif
377 return;
378 }
379 BOOL ok = WinSetClipbrdData( 0, data, cf, fl );
380#ifndef QT_NO_DEBUG
381 if ( !ok )
382 qSystemWarning( "QClipboard: Failed to write data" );
383#else
384 Q_UNUSED( ok );
385#endif
386}
387
388static void renderFormat( ulong cf )
389{
390#if defined(QT_DEBUG_CB)
391 qDebug( "renderFormat(0x%08lX)", cf );
392#endif
393 // sanity checks
394 if ( !internalCbData )
395 return;
396 QMimeSource *s = internalCbData->source();
397 if ( !s )
398 return;
399
400 const char* mime;
401 for ( int i = 0; (mime = s->format( i )); i++ ) {
402 QPMMime* c = QPMMime::convertor( mime, cf );
403 if ( c ) {
404 setClipboardData( cf, mime, c, s );
405 return;
406 }
407 }
408}
409
410static bool ignore_WM_DESTROYCLIPBOARD = FALSE;
411
412static void renderAllFormats()
413{
414#if defined(QT_DEBUG_CB)
415 qDebug( "renderAllFormats" );
416#endif
417 // sanity checks
418 if ( !internalCbData )
419 return;
420 QMimeSource *s = internalCbData->source();
421 if ( !s )
422 return;
423 if ( !qt_cb_owner )
424 return;
425
426 if ( !WinOpenClipbrd( 0 ) ) {
427#if defined(QT_CHECK_STATE)
428 qSystemWarning( "renderAllFormats: couldn't open clipboard" );
429#endif
430 return;
431 }
432
433 ignore_WM_DESTROYCLIPBOARD = TRUE;
434 WinEmptyClipbrd( 0 );
435 ignore_WM_DESTROYCLIPBOARD = FALSE;
436
437 const char* mime;
438 QPtrList<QPMMime> all = QPMMime::all();
439 for ( int i = 0; (mime = s->format( i )); i++ ) {
440 for ( QPMMime* c = all.first(); c; c = all.next() ) {
441 if ( c->cfFor( mime ) ) {
442 for ( int j = 0; j < c->countCf(); j++ ) {
443 ulong cf = c->cf(j);
444 if ( c->canConvert( mime, cf ) ) {
445 ulong fl = 0;
446 if ( WinQueryClipbrdFmtInfo( 0, cf, &fl ) ) {
447 // the format is already rendered by some other
448 // convertor, skip it
449 continue;
450 }
451 setClipboardData( cf, mime, c, s );
452 }
453 }
454 }
455 }
456 }
457
458 WinCloseClipbrd( 0 );
459}
460
461QClipboard::~QClipboard()
462{
463 renderAllFormats();
464 delete qt_cb_owner;
465 qt_cb_owner = 0;
466}
467
468bool QClipboard::event( QEvent *e )
469{
470 if ( e->type() != QEvent::Clipboard )
471 return QObject::event( e );
472
473 QMSG *msg = (QMSG *)((QCustomEvent*)e)->data();
474 if ( !msg ) {
475 renderAllFormats();
476 return TRUE;
477 }
478
479#if defined(QT_DEBUG_CB)
480 qDebug( "QClipboard::event: msg=%08lX mp1=%p mp2=%p",
481 msg->msg, msg->mp1, msg->mp2 );
482#endif
483
484 switch ( msg->msg ) {
485
486 case WM_DRAWCLIPBOARD:
487 emit dataChanged();
488 // PM doesn't inform the previous clipboard viewer when another
489 // app changes it (nor does it support viewer chains in some other
490 // way). The best we can do is to propagate the message to the
491 // previous clipboard viewer ourselves (though there is no guarantee
492 // that all non-Qt apps will do the same).
493 if ( prevClipboardViewer ) {
494 // propagate the message to the previous clipboard viewer
495 BOOL ok = WinPostMsg ( prevClipboardViewer,
496 msg->msg, msg->mp1, msg->mp2 );
497 if ( !ok )
498 prevClipboardViewer = 0;
499 }
500 break;
501
502 case WM_DESTROYCLIPBOARD:
503 if ( !ignore_WM_DESTROYCLIPBOARD )
504 cleanupClipboardData();
505 break;
506
507 case WM_RENDERFMT:
508 renderFormat( (ULONG) msg->mp1 );
509 break;
510 case WM_RENDERALLFMTS:
511 renderAllFormats();
512 break;
513 }
514
515 return TRUE;
516}
517
518
519void QClipboard::clear( Mode mode )
520{
521 if ( mode != Clipboard ) return;
522
523 if ( WinOpenClipbrd( 0 ) ) {
524 WinEmptyClipbrd( 0 );
525 WinCloseClipbrd( 0 );
526 }
527}
528
529
530QMimeSource* QClipboard::data( Mode mode ) const
531{
532 if ( mode != Clipboard ) return 0;
533
534 QClipboardData *d = clipboardData();
535 return d->provider();
536}
537
538
539void QClipboard::setData( QMimeSource* src, Mode mode )
540{
541 if ( mode != Clipboard ) return;
542
543 if ( !WinOpenClipbrd( 0 ) ) {
544#ifndef QT_NO_DEBUG
545 qSystemWarning( "QClipboard: Failed to open clipboard" );
546#endif
547 return;
548 }
549
550 QClipboardData *d = clipboardData();
551 d->setSource( src );
552
553 ignore_WM_DESTROYCLIPBOARD = TRUE;
554 BOOL ok = WinEmptyClipbrd( 0 );
555 ignore_WM_DESTROYCLIPBOARD = FALSE;
556#ifndef QT_NO_DEBUG
557 if ( !ok )
558 qSystemWarning( "QClipboard: Failed to empty clipboard" );
559#else
560 Q_UNUSED( ok );
561#endif
562
563 // Register all the formats of src that we can render.
564 const char* mime;
565 QPtrList<QPMMime> all = QPMMime::all();
566 for ( int i = 0; (mime = src->format( i )); i++ ) {
567#if defined(QT_DEBUG_CB)
568 qDebug( "setData: src mime[%i] = %s", i, mime );
569#endif
570 for ( QPMMime* c = all.first(); c; c = all.next() ) {
571#if defined(QT_DEBUG_CB)
572 qDebug( "setData: trying convertor %s", c->convertorName() );
573#endif
574 if ( c->cfFor( mime ) ) {
575 for ( int j = 0; j < c->countCf(); j++ ) {
576 ulong cf = c->cf( j );
577 if ( c->canConvert( mime, cf ) ) {
578 ulong fl = 0;
579 if ( WinQueryClipbrdFmtInfo( 0, cf, &fl ) ) {
580 // the format is already rendered by some other
581 // convertor, skip it
582 continue;
583 }
584
585 fl = c->flFor( cf );
586#if defined(QT_DEBUG_CB)
587 qDebug( "setData: converting to CF 0x%08lX (0x%08lX)",
588 cf, fl );
589#endif
590 if ( qApp && qApp->eventLoop()->loopLevel() ) {
591 // setup delayed rendering of clipboard data
592 WinSetClipbrdOwner( 0, clipboardOwner()->winId() );
593 WinSetClipbrdData( 0, 0, cf, fl );
594 }
595 else {
596 // write now if we can't process data requests
597 setClipboardData( cf, mime, c, src );
598 }
599 }
600 }
601 // Don't search for other formats for the same mime type --
602 // we've already represented it in the clipboard and adding a
603 // new format would just introduce mime type duplicates on the
604 // clipboard reader's side (where the first encountered format
605 // would be used for actual data transfer anyway).
606 // See also QDragManager::drag().
607 break;
608 }
609 }
610 }
611
612 WinCloseClipbrd( 0 );
613}
614
615#endif // QT_NO_CLIPBOARD
Note: See TracBrowser for help on using the repository browser.