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

Last change on this file since 504 was 412, checked in by Dmitry A. Kuminov, 15 years ago

general: Made it build using GCC4 and fixed a couple warnings.

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