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

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