source: trunk/src/gui/kernel/qclipboard_pm.cpp@ 347

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

gui/kernel: QClipboard: Minor order fix,

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Revision Author Id
File size: 15.0 KB
RevLine 
[95]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 "qclipboard.h"
45
46#ifndef QT_NO_CLIPBOARD
47
48#include "qapplication.h"
49#include "qapplication_p.h"
50#include "qeventloop.h"
51#include "qwidget.h"
52#include "qevent.h"
53#include "qmime.h"
54#include "qdnd_p.h"
[327]55#include "qclipboard_p.h"
[95]56
[324]57#include "qt_os2.h"
58#include "private/qpmobjectwindow_pm_p.h"
59
[331]60//#define QCLIPBOARD_DEBUG
[323]61
[324]62#ifdef QCLIPBOARD_DEBUG
[331]63# include "qdebug.h"
64# define DEBUG(a) qDebug a
65#else
66# define DEBUG(a) do {} while(0)
[324]67#endif
68
[95]69QT_BEGIN_NAMESPACE
70
[321]71////////////////////////////////////////////////////////////////////////////////
72
73class QClipboardWatcher : public QInternalMimeData
74{
75public:
[334]76 QClipboardWatcher() {}
[321]77
78 bool hasFormat_sys(const QString &mimetype) const;
79 QStringList formats_sys() const;
80 QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const;
[324]81
82private:
83
[334]84 bool peekData(bool leaveOpen = false) const;
[324]85
86 mutable QList<ULONG> formats;
87 mutable QList<QPMMime::Match> matches;
[321]88};
89
[334]90bool QClipboardWatcher::peekData(bool leaveOpen) const
[321]91{
[324]92 if (!WinOpenClipbrd(NULLHANDLE)) {
[321]93#ifndef QT_NO_DEBUG
[326]94 qWarning("QClipboardWatcher::peekData: WinOpenClipbrd "
95 "failed with 0x%lX", WinGetLastError(NULLHANDLE));
[321]96#endif
[334]97 return false;
[324]98 }
99
[334]100 QList<ULONG> newFormats;
[324]101 ULONG cf = 0;
102 while ((cf = WinEnumClipbrdFmts(NULLHANDLE, cf)))
[334]103 newFormats << cf;
[324]104
[334]105 if (!leaveOpen)
106 WinCloseClipbrd(NULLHANDLE);
[324]107
[334]108 // optimization: we don't want to call the potentially expensive
109 // allConvertersFromFormats() unlesss we really got a different set
110 if (newFormats == formats)
111 return true;
112
113 formats = newFormats;
[324]114 matches = QPMMime::allConvertersFromFormats(formats);
[334]115
116#ifdef QCLIPBOARD_DEBUG
[336]117 foreach(ULONG cf, formats)
118 DEBUG(("QClipboardWatcher::peekData: have format 0x%lX (%ls)",
119 cf, QPMMime::formatName(cf).utf16()));
[334]120 foreach(QPMMime::Match match, matches)
121 DEBUG(("QClipboardWatcher::peekData: converter %p mime \"%ls\" "
122 "format 0x%lX priority %d", match.converter, match.mime.utf16(),
123 match.format, match.priority));
124#endif
125
126 return true;
[321]127}
128
[324]129bool QClipboardWatcher::hasFormat_sys(const QString &mime) const
130{
[334]131 if (!peekData())
132 return false;
[324]133
134 foreach (QPMMime::Match match, matches)
135 if (match.mime == mime)
136 return true;
137
138 return false;
139}
140
[321]141QStringList QClipboardWatcher::formats_sys() const
142{
143 QStringList fmts;
[324]144
[334]145 if (!peekData())
146 return fmts;
[324]147
148 foreach (QPMMime::Match match, matches)
149 fmts << match.mime;
150
[321]151 return fmts;
152}
153
154QVariant QClipboardWatcher::retrieveData_sys(const QString &mime,
155 QVariant::Type type) const
156{
[323]157 QVariant result;
158
[334]159 if (!peekData(true /*leaveOpen*/))
160 return result;
[324]161
162 foreach (QPMMime::Match match, matches) {
163 if (match.mime == mime) {
164 ULONG flags;
165 if (WinQueryClipbrdFmtInfo(NULLHANDLE, match.format, &flags)) {
166 ULONG data = WinQueryClipbrdData(NULLHANDLE, match.format);
167 result = match.converter->convertFromFormat(match.format, flags,
168 data, match.mime, type);
[323]169 }
[334]170 break;
[323]171 }
[321]172 }
[324]173
[334]174 WinCloseClipbrd(NULLHANDLE);
175
[323]176 return result;
[321]177}
178
179////////////////////////////////////////////////////////////////////////////////
180
[324]181class QClipboardData : public QPMObjectWindow
[321]182{
183public:
184 QClipboardData();
185 ~QClipboardData();
186
[326]187 void setSource(QMimeData *s);
188
189 void setAsClipboardViewer();
[334]190 bool ownsClipboard() const;
[331]191 void putAllMimeToClipboard(bool isDelayed);
192 void flushClipboard();
[326]193
[334]194 const QMimeData *mimeData() const;
195
[326]196 static QClipboardData *instance()
[324]197 {
[326]198 if (instancePtr == 0) {
199 instancePtr = new QClipboardData;
200 }
201 Q_ASSERT(instancePtr);
202 return instancePtr;
[324]203 }
[321]204
[326]205 static void deleteInstance()
[324]206 {
[326]207 delete instancePtr;
208 instancePtr = 0;
[324]209 }
210
[326]211private:
[324]212 bool setClipboard(QPMMime *converter, ULONG format, bool isDelayed);
213
214 MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2);
215
216 QMimeData *src;
[326]217 QList<QPMMime::Match> matches;
218 HWND prevClipboardViewer;
219
[334]220 QClipboardWatcher watcher;
221
[326]222 bool ignore_WM_DESTROYCLIPBOARD;
223
224 static QClipboardData *instancePtr;
[321]225};
226
[326]227// static
228QClipboardData *QClipboardData::instancePtr = 0;
229
230QClipboardData::QClipboardData()
231 : src(0), prevClipboardViewer(NULLHANDLE)
232 , ignore_WM_DESTROYCLIPBOARD(false)
[321]233{
234}
235
236QClipboardData::~QClipboardData()
237{
[326]238 setSource(0);
239
240 // make sure we are not the clipboard viewer any more
241 if (hwnd() == WinQueryClipbrdViewer(NULLHANDLE))
242 WinSetClipbrdViewer(NULLHANDLE, prevClipboardViewer);
243}
244
245void QClipboardData::setSource(QMimeData *s)
246{
247 if (s == src)
248 return;
[324]249 delete src;
[326]250 src = s;
251
252 // build the list of all mime <-> cf matches
253 matches.clear();
254 if (src)
255 matches = QPMMime::allConvertersFromMimeData(src);
[331]256
257#ifdef QCLIPBOARD_DEBUG
258 if (src) {
259 DEBUG(() << "QClipboardData::setSource: mimes" << src->formats());
260 foreach(QPMMime::Match match, matches)
261 DEBUG(("QClipboardData::setSource: match: converter %p format 0x%lX "
[336]262 "(%ls) priority %d", match.converter, match.format,
263 QPMMime::formatName(match.format).utf16(), match.priority));
[331]264 }
265#endif
[321]266}
267
[326]268void QClipboardData::setAsClipboardViewer()
269{
[331]270 DEBUG(("QClipboardData::setAsClipboardViewer"));
271
[326]272 HWND clipboardViewer = WinQueryClipbrdViewer(NULLHANDLE);
273 if (hwnd() != clipboardViewer) {
274 prevClipboardViewer = clipboardViewer;
275 BOOL ok = WinSetClipbrdViewer(NULLHANDLE, hwnd());
276 Q_UNUSED(ok);
277#ifndef QT_NO_DEBUG
278 if (!ok)
279 qWarning("QClipboardData::setAsClipboardViewer: WinSetClipbrdViewer "
280 " failed with 0x%lX", WinGetLastError(NULLHANDLE));
281#endif
282 }
283}
284
[334]285bool QClipboardData::ownsClipboard() const
[326]286{
287 return src && hwnd() == WinQueryClipbrdOwner(NULLHANDLE);
288}
289
[324]290bool QClipboardData::setClipboard(QPMMime *converter, ULONG format,
291 bool isDelayed)
292{
293 Q_ASSERT(src);
294 if (!src)
295 return false;
296
[331]297 bool ok, ok2;
[324]298 ULONG flags = 0, data = 0;
299
300 if (isDelayed) {
301 // setup delayed rendering of clipboard data
302 ok = converter->convertFromMimeData(src, format, flags, 0);
303 if (ok) {
304 WinSetClipbrdOwner(NULLHANDLE, hwnd());
[331]305 ok2 = WinSetClipbrdData(NULLHANDLE, 0, format, flags);
[324]306 }
307 } else {
308 // render now
309 ok = converter->convertFromMimeData(src, format, flags, &data);
310 if (ok)
[331]311 ok2 = WinSetClipbrdData(NULLHANDLE, data, format, flags);
[324]312 }
[336]313 DEBUG(("QClipboardData::setClipboard: convert to format 0x%lX (%ls) "
314 "flags 0x%lX data 0x%lX delayed %d ok %d", format,
315 QPMMime::formatName(format).utf16(), flags, data, isDelayed, ok));
[331]316#ifndef QT_NO_DEBUG
317 if (!ok2) {
318 qWarning("QClipboardData::setClipboard: WinSetClipbrdData "
319 "failed with 0x%lX", WinGetLastError(NULLHANDLE));
320 }
[324]321#endif
322
[331]323 return ok && ok2;
[324]324}
325
[331]326void QClipboardData::putAllMimeToClipboard(bool isDelayed)
[324]327{
[331]328 DEBUG(() << "QClipboardData::putAllMimeToClipboard: isDelayed" << isDelayed);
329
330 if (!WinOpenClipbrd(NULLHANDLE)) {
331#ifndef QT_NO_DEBUG
332 qWarning("QClipboardData::putAllMimeToClipboard: WinOpenClipbrd "
333 "failed with 0x%lX", WinGetLastError(NULLHANDLE));
[326]334#endif
335 return;
336 }
337
338 // delete the clipboard contents before we render everything to make sure
339 // nothing is left there from another owner
340 ignore_WM_DESTROYCLIPBOARD = true;
341 BOOL ok = WinEmptyClipbrd(NULLHANDLE);
342 ignore_WM_DESTROYCLIPBOARD = false;
343 if (!ok) {
344#ifndef QT_NO_DEBUG
[331]345 qWarning("QClipboardData::putAllMimeToClipboard: WinEmptyClipbrd "
[326]346 "failed with 0x%lX", WinGetLastError(NULLHANDLE));
347#endif
[331]348 WinCloseClipbrd(NULLHANDLE);
[326]349 return;
[321]350 }
[326]351
352 if (src) {
353 foreach(QPMMime::Match match, matches)
354 setClipboard(match.converter, match.format, isDelayed);
355 }
356
[331]357 WinCloseClipbrd(NULLHANDLE);
[321]358}
359
[331]360void QClipboardData::flushClipboard()
361{
362 if (ownsClipboard()) {
363 putAllMimeToClipboard(false);
364 // make sure we won't be doing this again if asked in WM_RENDERALLFMTS
365 setSource(0);
366 }
367}
368
[334]369const QMimeData *QClipboardData::mimeData() const
370{
371 // short cut for local copy / paste
372 if (ownsClipboard() && src)
373 return src;
374 return &watcher;
375}
376
[326]377MRESULT QClipboardData::message(ULONG msg, MPARAM mp1, MPARAM mp2)
[321]378{
[331]379 DEBUG(("QClipboardData::message: msg %08lX, mp1 %p, mp2 %p",
380 msg, mp1, mp2));
[326]381
382 switch (msg) {
383
384 case WM_DRAWCLIPBOARD: {
[331]385 DEBUG(("QClipboardData::message: WM_DRAWCLIPBOARD"));
386
[345]387 if (hwnd() != WinQueryClipbrdOwner(NULLHANDLE) && src) {
388 // we no longer own the clipboard, clean up the clipboard object
389 setSource(0);
390 }
391
[326]392 // ask QClipboard to emit changed() signals
393 QClipboardEvent e(reinterpret_cast<QEventPrivate *>(1));
394 qt_sendSpontaneousEvent(QApplication::clipboard(), &e);
395
396 // PM doesn't inform the previous clipboard viewer if another
397 // app changes it (nor does it support viewer chains in some other
398 // way). The best we can do is to propagate the message to the
399 // previous clipboard viewer ourselves (though there is no guarantee
400 // that all non-Qt apps will do the same).
401 if (prevClipboardViewer) {
402 // propagate the message to the previous clipboard viewer
403 BOOL ok = WinPostMsg(prevClipboardViewer, msg, mp1, mp2);
404 if (!ok)
405 prevClipboardViewer = NULLHANDLE;
406 }
407 }
[331]408 break;
[326]409
[331]410 case WM_DESTROYCLIPBOARD: {
411 DEBUG(("QClipboardData::message: WM_DESTROYCLIPBOARD:"));
[326]412 if (!ignore_WM_DESTROYCLIPBOARD)
413 setSource(0);
[331]414 }
415 break;
[326]416
[331]417 case WM_RENDERFMT: {
[336]418 DEBUG(("QClipboardData::message: WM_RENDERFMT: CF 0x%lX (%ls)",
419 (ULONG)mp1, QPMMime::formatName((ULONG)mp1).utf16()));
[331]420 if (src) {
421 foreach(QPMMime::Match match, matches) {
422 if (match.format == (ULONG)mp1) {
423 setClipboard(match.converter, match.format, false);
424 break;
425 }
426 }
427 }
428 }
429 break;
[326]430
[331]431 case WM_RENDERALLFMTS: {
432 DEBUG(("QClipboardData::message: WM_RENDERALLFMTS:"));
433 if (src) {
434 foreach(QPMMime::Match match, matches)
435 setClipboard(match.converter, match.format, false);
436 }
437 }
438 break;
[326]439
440 default:
441 break;
442 }
443
[331]444 DEBUG(("QClipboardData::message: END"));
445 return FALSE;
[321]446}
447
448////////////////////////////////////////////////////////////////////////////////
449
[95]450QClipboard::~QClipboard()
451{
[326]452 QClipboardData::deleteInstance();
[95]453}
454
455void QClipboard::setMimeData(QMimeData *src, Mode mode)
456{
[331]457 DEBUG(() << "QClipboard::setMimeData: src" << src << "mode" << mode);
[326]458
[324]459 if (mode != Clipboard) {
460 delete src;
[323]461 return;
[324]462 }
[323]463
[326]464 QClipboardData *d = QClipboardData::instance();
[324]465 d->setSource(src);
[323]466
[324]467 if (!src)
468 return; // nothing to do
469
[327]470 // use delayed rendering only if the application runs the event loop
471 bool runsEventLoop = d_func()->threadData->loopLevel != 0;
[324]472
[331]473 d->putAllMimeToClipboard(runsEventLoop);
[95]474}
475
476void QClipboard::clear(Mode mode)
477{
[326]478 setMimeData(0, Clipboard);
[95]479}
480
481bool QClipboard::event(QEvent *e)
482{
[326]483 if (e->type() != QEvent::Clipboard)
484 return QObject::event(e);
485
486 if (!((QClipboardEvent*)e)->data()) {
487 // this is sent by QApplication to render all formats at app shut down
[331]488 QClipboardData::instance()->flushClipboard();
[326]489 } else {
490 // this is sent by QClipboardData to notify about clipboard data change
491 emitChanged(QClipboard::Clipboard);
492 }
493
494 return true;
[95]495}
496
497void QClipboard::connectNotify(const char *signal)
498{
[326]499 if (qstrcmp(signal, SIGNAL(dataChanged())) == 0) {
500 // ensure we are up and running (by instantiating QClipboardData and
501 // setting it as the clipboard viewer to receive notifications on
502 // clipboard contents chages) but block signals so the dataChange signal
503 // is not emitted while being connected to.
504 bool blocked = blockSignals(true);
505 QClipboardData::instance()->setAsClipboardViewer();
506 blockSignals(blocked);
507 }
[95]508}
509
510const QMimeData *QClipboard::mimeData(Mode mode) const
511{
[334]512 if (mode != Clipboard)
513 return 0;
514
515 return QClipboardData::instance()->mimeData();
[95]516}
517
518bool QClipboard::supportsMode(Mode mode) const
519{
520 return (mode == Clipboard);
521}
522
523bool QClipboard::ownsMode(Mode mode) const
524{
[326]525 if (mode == Clipboard) {
526 return QClipboardData::instance()->ownsClipboard();
527 }
[95]528 return false;
529}
530
531void QClipboard::ownerDestroyed()
532{
[326]533 // not used
[95]534}
535
536QT_END_NAMESPACE
537
538#endif // QT_NO_CLIPBOARD
Note: See TracBrowser for help on using the repository browser.