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
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 "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"
55#include "qclipboard_p.h"
56
57#include "qt_os2.h"
58#include "private/qpmobjectwindow_pm_p.h"
59
60//#define QCLIPBOARD_DEBUG
61
62#ifdef QCLIPBOARD_DEBUG
63# include "qdebug.h"
64# define DEBUG(a) qDebug a
65#else
66# define DEBUG(a) do {} while(0)
67#endif
68
69QT_BEGIN_NAMESPACE
70
71////////////////////////////////////////////////////////////////////////////////
72
73class QClipboardWatcher : public QInternalMimeData
74{
75public:
76 QClipboardWatcher() {}
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;
81
82private:
83
84 bool peekData(bool leaveOpen = false) const;
85
86 mutable QList<ULONG> formats;
87 mutable QList<QPMMime::Match> matches;
88};
89
90bool QClipboardWatcher::peekData(bool leaveOpen) const
91{
92 if (!WinOpenClipbrd(NULLHANDLE)) {
93#ifndef QT_NO_DEBUG
94 qWarning("QClipboardWatcher::peekData: WinOpenClipbrd "
95 "failed with 0x%lX", WinGetLastError(NULLHANDLE));
96#endif
97 return false;
98 }
99
100 QList<ULONG> newFormats;
101 ULONG cf = 0;
102 while ((cf = WinEnumClipbrdFmts(NULLHANDLE, cf)))
103 newFormats << cf;
104
105 if (!leaveOpen)
106 WinCloseClipbrd(NULLHANDLE);
107
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;
114 matches = QPMMime::allConvertersFromFormats(formats);
115
116#ifdef QCLIPBOARD_DEBUG
117 foreach(ULONG cf, formats)
118 DEBUG(("QClipboardWatcher::peekData: have format 0x%lX (%ls)",
119 cf, QPMMime::formatName(cf).utf16()));
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;
127}
128
129bool QClipboardWatcher::hasFormat_sys(const QString &mime) const
130{
131 if (!peekData())
132 return false;
133
134 foreach (QPMMime::Match match, matches)
135 if (match.mime == mime)
136 return true;
137
138 return false;
139}
140
141QStringList QClipboardWatcher::formats_sys() const
142{
143 QStringList fmts;
144
145 if (!peekData())
146 return fmts;
147
148 foreach (QPMMime::Match match, matches)
149 fmts << match.mime;
150
151 return fmts;
152}
153
154QVariant QClipboardWatcher::retrieveData_sys(const QString &mime,
155 QVariant::Type type) const
156{
157 QVariant result;
158
159 if (!peekData(true /*leaveOpen*/))
160 return result;
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);
169 }
170 break;
171 }
172 }
173
174 WinCloseClipbrd(NULLHANDLE);
175
176 return result;
177}
178
179////////////////////////////////////////////////////////////////////////////////
180
181class QClipboardData : public QPMObjectWindow
182{
183public:
184 QClipboardData();
185 ~QClipboardData();
186
187 void setSource(QMimeData *s);
188
189 void setAsClipboardViewer();
190 bool ownsClipboard() const;
191 void putAllMimeToClipboard(bool isDelayed);
192 void flushClipboard();
193
194 const QMimeData *mimeData() const;
195
196 static QClipboardData *instance()
197 {
198 if (instancePtr == 0) {
199 instancePtr = new QClipboardData;
200 }
201 Q_ASSERT(instancePtr);
202 return instancePtr;
203 }
204
205 static void deleteInstance()
206 {
207 delete instancePtr;
208 instancePtr = 0;
209 }
210
211private:
212 bool setClipboard(QPMMime *converter, ULONG format, bool isDelayed);
213
214 MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2);
215
216 QMimeData *src;
217 QList<QPMMime::Match> matches;
218 HWND prevClipboardViewer;
219
220 QClipboardWatcher watcher;
221
222 bool ignore_WM_DESTROYCLIPBOARD;
223
224 static QClipboardData *instancePtr;
225};
226
227// static
228QClipboardData *QClipboardData::instancePtr = 0;
229
230QClipboardData::QClipboardData()
231 : src(0), prevClipboardViewer(NULLHANDLE)
232 , ignore_WM_DESTROYCLIPBOARD(false)
233{
234}
235
236QClipboardData::~QClipboardData()
237{
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;
249 delete src;
250 src = s;
251
252 // build the list of all mime <-> cf matches
253 matches.clear();
254 if (src)
255 matches = QPMMime::allConvertersFromMimeData(src);
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 "
262 "(%ls) priority %d", match.converter, match.format,
263 QPMMime::formatName(match.format).utf16(), match.priority));
264 }
265#endif
266}
267
268void QClipboardData::setAsClipboardViewer()
269{
270 DEBUG(("QClipboardData::setAsClipboardViewer"));
271
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
285bool QClipboardData::ownsClipboard() const
286{
287 return src && hwnd() == WinQueryClipbrdOwner(NULLHANDLE);
288}
289
290bool QClipboardData::setClipboard(QPMMime *converter, ULONG format,
291 bool isDelayed)
292{
293 Q_ASSERT(src);
294 if (!src)
295 return false;
296
297 bool ok, ok2;
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());
305 ok2 = WinSetClipbrdData(NULLHANDLE, 0, format, flags);
306 }
307 } else {
308 // render now
309 ok = converter->convertFromMimeData(src, format, flags, &data);
310 if (ok)
311 ok2 = WinSetClipbrdData(NULLHANDLE, data, format, flags);
312 }
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));
316#ifndef QT_NO_DEBUG
317 if (!ok2) {
318 qWarning("QClipboardData::setClipboard: WinSetClipbrdData "
319 "failed with 0x%lX", WinGetLastError(NULLHANDLE));
320 }
321#endif
322
323 return ok && ok2;
324}
325
326void QClipboardData::putAllMimeToClipboard(bool isDelayed)
327{
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));
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
345 qWarning("QClipboardData::putAllMimeToClipboard: WinEmptyClipbrd "
346 "failed with 0x%lX", WinGetLastError(NULLHANDLE));
347#endif
348 WinCloseClipbrd(NULLHANDLE);
349 return;
350 }
351
352 if (src) {
353 foreach(QPMMime::Match match, matches)
354 setClipboard(match.converter, match.format, isDelayed);
355 }
356
357 WinCloseClipbrd(NULLHANDLE);
358}
359
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
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
377MRESULT QClipboardData::message(ULONG msg, MPARAM mp1, MPARAM mp2)
378{
379 DEBUG(("QClipboardData::message: msg %08lX, mp1 %p, mp2 %p",
380 msg, mp1, mp2));
381
382 switch (msg) {
383
384 case WM_DRAWCLIPBOARD: {
385 DEBUG(("QClipboardData::message: WM_DRAWCLIPBOARD"));
386
387 if (hwnd() != WinQueryClipbrdOwner(NULLHANDLE) && src) {
388 // we no longer own the clipboard, clean up the clipboard object
389 setSource(0);
390 }
391
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 }
408 break;
409
410 case WM_DESTROYCLIPBOARD: {
411 DEBUG(("QClipboardData::message: WM_DESTROYCLIPBOARD:"));
412 if (!ignore_WM_DESTROYCLIPBOARD)
413 setSource(0);
414 }
415 break;
416
417 case WM_RENDERFMT: {
418 DEBUG(("QClipboardData::message: WM_RENDERFMT: CF 0x%lX (%ls)",
419 (ULONG)mp1, QPMMime::formatName((ULONG)mp1).utf16()));
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;
430
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;
439
440 default:
441 break;
442 }
443
444 DEBUG(("QClipboardData::message: END"));
445 return FALSE;
446}
447
448////////////////////////////////////////////////////////////////////////////////
449
450QClipboard::~QClipboard()
451{
452 QClipboardData::deleteInstance();
453}
454
455void QClipboard::setMimeData(QMimeData *src, Mode mode)
456{
457 DEBUG(() << "QClipboard::setMimeData: src" << src << "mode" << mode);
458
459 if (mode != Clipboard) {
460 delete src;
461 return;
462 }
463
464 QClipboardData *d = QClipboardData::instance();
465 d->setSource(src);
466
467 if (!src)
468 return; // nothing to do
469
470 // use delayed rendering only if the application runs the event loop
471 bool runsEventLoop = d_func()->threadData->loopLevel != 0;
472
473 d->putAllMimeToClipboard(runsEventLoop);
474}
475
476void QClipboard::clear(Mode mode)
477{
478 setMimeData(0, Clipboard);
479}
480
481bool QClipboard::event(QEvent *e)
482{
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
488 QClipboardData::instance()->flushClipboard();
489 } else {
490 // this is sent by QClipboardData to notify about clipboard data change
491 emitChanged(QClipboard::Clipboard);
492 }
493
494 return true;
495}
496
497void QClipboard::connectNotify(const char *signal)
498{
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 }
508}
509
510const QMimeData *QClipboard::mimeData(Mode mode) const
511{
512 if (mode != Clipboard)
513 return 0;
514
515 return QClipboardData::instance()->mimeData();
516}
517
518bool QClipboard::supportsMode(Mode mode) const
519{
520 return (mode == Clipboard);
521}
522
523bool QClipboard::ownsMode(Mode mode) const
524{
525 if (mode == Clipboard) {
526 return QClipboardData::instance()->ownsClipboard();
527 }
528 return false;
529}
530
531void QClipboard::ownerDestroyed()
532{
533 // not used
534}
535
536QT_END_NAMESPACE
537
538#endif // QT_NO_CLIPBOARD
Note: See TracBrowser for help on using the repository browser.