diff options
Diffstat (limited to 'src/gui/guikernel')
102 files changed, 42683 insertions, 0 deletions
diff --git a/src/gui/guikernel/qclipboard.cpp b/src/gui/guikernel/qclipboard.cpp new file mode 100644 index 0000000000..5ff47bd136 --- /dev/null +++ b/src/gui/guikernel/qclipboard.cpp @@ -0,0 +1,667 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qclipboard.h" + +#ifndef QT_NO_CLIPBOARD + +#include "qapplication.h" +#include "private/qapplication_p.h" +#include "qpixmap.h" +#include "qclipboard_p.h" +#include "qvariant.h" +#include "qbuffer.h" +#include "qimage.h" +#include "qtextcodec.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QClipboard + \brief The QClipboard class provides access to the window system clipboard. + + The clipboard offers a simple mechanism to copy and paste data + between applications. + + QClipboard supports the same data types that QDrag does, and uses + similar mechanisms. For advanced clipboard usage read \l{Drag and + Drop}. + + There is a single QClipboard object in an application, accessible + as QApplication::clipboard(). + + Example: + \snippet doc/src/snippets/code/src_gui_kernel_qclipboard.cpp 0 + + QClipboard features some convenience functions to access common + data types: setText() allows the exchange of Unicode text and + setPixmap() and setImage() allows the exchange of QPixmaps and + QImages between applications. The setMimeData() function is the + ultimate in flexibility: it allows you to add any QMimeData into + the clipboard. There are corresponding getters for each of these, + e.g. text(), image() and pixmap(). You can clear the clipboard by + calling clear(). + + A typical example of the use of these functions follows: + + \snippet doc/src/snippets/droparea.cpp 0 + + \section1 Notes for X11 Users + + \list + + \i The X11 Window System has the concept of a separate selection + and clipboard. When text is selected, it is immediately available + as the global mouse selection. The global mouse selection may + later be copied to the clipboard. By convention, the middle mouse + button is used to paste the global mouse selection. + + \i X11 also has the concept of ownership; if you change the + selection within a window, X11 will only notify the owner and the + previous owner of the change, i.e. it will not notify all + applications that the selection or clipboard data changed. + + \i Lastly, the X11 clipboard is event driven, i.e. the clipboard + will not function properly if the event loop is not running. + Similarly, it is recommended that the contents of the clipboard + are stored or retrieved in direct response to user-input events, + e.g. mouse button or key presses and releases. You should not + store or retrieve the clipboard contents in response to timer or + non-user-input events. + + \i Since there is no standard way to copy and paste files between + applications on X11, various MIME types and conventions are currently + in use. For instance, Nautilus expects files to be supplied with a + \c{x-special/gnome-copied-files} MIME type with data beginning with + the cut/copy action, a newline character, and the URL of the file. + + \endlist + + \section1 Notes for Mac OS X Users + + Mac OS X supports a separate find buffer that holds the current + search string in Find operations. This find clipboard can be accessed + by specifying the FindBuffer mode. + + \section1 Notes for Windows and Mac OS X Users + + \list + + \i Windows and Mac OS X do not support the global mouse + selection; they only supports the global clipboard, i.e. they + only add text to the clipboard when an explicit copy or cut is + made. + + \i Windows and Mac OS X does not have the concept of ownership; + the clipboard is a fully global resource so all applications are + notified of changes. + + \endlist + + \sa QApplication +*/ + +#ifndef Q_WS_X11 +// for X11 there is a separate implementation of a constructor. +/*! + \internal + + Constructs a clipboard object. + + Do not call this function. + + Call QApplication::clipboard() instead to get a pointer to the + application's global clipboard object. + + There is only one clipboard in the window system, and creating + more than one object to represent it is almost certainly an error. +*/ + +QClipboard::QClipboard(QObject *parent) + : QObject(*new QClipboardPrivate, parent) +{ + // nothing +} +#endif + +#ifndef Q_WS_WIN32 +/*! + \internal + + Destroys the clipboard. + + You should never delete the clipboard. QApplication will do this + when the application terminates. +*/ +QClipboard::~QClipboard() +{ +} +#endif + +/*! + \fn void QClipboard::changed(QClipboard::Mode mode) + \since 4.2 + + This signal is emitted when the data for the given clipboard \a + mode is changed. + + \sa dataChanged(), selectionChanged(), findBufferChanged() +*/ + +/*! + \fn void QClipboard::dataChanged() + + This signal is emitted when the clipboard data is changed. + + On Mac OS X and with Qt version 4.3 or higher, clipboard + changes made by other applications will only be detected + when the application is activated. + + \sa findBufferChanged(), selectionChanged(), changed() +*/ + +/*! + \fn void QClipboard::selectionChanged() + + This signal is emitted when the selection is changed. This only + applies to windowing systems that support selections, e.g. X11. + Windows and Mac OS X don't support selections. + + \sa dataChanged(), findBufferChanged(), changed() +*/ + +/*! + \fn void QClipboard::findBufferChanged() + \since 4.2 + + This signal is emitted when the find buffer is changed. This only + applies to Mac OS X. + + With Qt version 4.3 or higher, clipboard changes made by other + applications will only be detected when the application is activated. + + \sa dataChanged(), selectionChanged(), changed() +*/ + + +/*! \enum QClipboard::Mode + \keyword clipboard mode + + This enum type is used to control which part of the system clipboard is + used by QClipboard::mimeData(), QClipboard::setMimeData() and related functions. + + \value Clipboard indicates that data should be stored and retrieved from + the global clipboard. + + \value Selection indicates that data should be stored and retrieved from + the global mouse selection. Support for \c Selection is provided only on + systems with a global mouse selection (e.g. X11). + + \value FindBuffer indicates that data should be stored and retrieved from + the Find buffer. This mode is used for holding search strings on Mac OS X. + + \omitvalue LastMode + + \sa QClipboard::supportsSelection() +*/ + + +/***************************************************************************** + QApplication member functions related to QClipboard. + *****************************************************************************/ + +// text handling is done directly in qclipboard_qws, for now + +/*! + \fn bool QClipboard::event(QEvent *e) + \reimp +*/ + +/*! + \overload + + Returns the clipboard text in subtype \a subtype, or an empty string + if the clipboard does not contain any text. If \a subtype is null, + any subtype is acceptable, and \a subtype is set to the chosen + subtype. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, the + text is retrieved from the global clipboard. If \a mode is + QClipboard::Selection, the text is retrieved from the global + mouse selection. + + Common values for \a subtype are "plain" and "html". + + Note that calling this function repeatedly, for instance from a + key event handler, may be slow. In such cases, you should use the + \c dataChanged() signal instead. + + \sa setText(), mimeData() +*/ +QString QClipboard::text(QString &subtype, Mode mode) const +{ + const QMimeData *const data = mimeData(mode); + if (!data) + return QString(); + + const QStringList formats = data->formats(); + if (subtype.isEmpty()) { + if (formats.contains(QLatin1String("text/plain"))) + subtype = QLatin1String("plain"); + else { + for (int i = 0; i < formats.size(); ++i) + if (formats.at(i).startsWith(QLatin1String("text/"))) { + subtype = formats.at(i).mid(5); + break; + } + if (subtype.isEmpty()) + return QString(); + } + } else if (!formats.contains(QLatin1String("text/") + subtype)) { + return QString(); + } + + const QByteArray rawData = data->data(QLatin1String("text/") + subtype); + +#ifndef QT_NO_TEXTCODEC + QTextCodec* codec = QTextCodec::codecForMib(106); // utf-8 is default + if (subtype == QLatin1String("html")) + codec = QTextCodec::codecForHtml(rawData, codec); + else + codec = QTextCodec::codecForUtfText(rawData, codec); + return codec->toUnicode(rawData); +#else //QT_NO_TEXTCODEC + return rawData; +#endif //QT_NO_TEXTCODEC +} + +/*! + Returns the clipboard text as plain text, or an empty string if the + clipboard does not contain any text. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, the + text is retrieved from the global clipboard. If \a mode is + QClipboard::Selection, the text is retrieved from the global + mouse selection. If \a mode is QClipboard::FindBuffer, the + text is retrieved from the search string buffer. + + \sa setText(), mimeData() +*/ +QString QClipboard::text(Mode mode) const +{ + const QMimeData *data = mimeData(mode); + return data ? data->text() : QString(); +} + +/*! + Copies \a text into the clipboard as plain text. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, the + text is stored in the global clipboard. If \a mode is + QClipboard::Selection, the text is stored in the global + mouse selection. If \a mode is QClipboard::FindBuffer, the + text is stored in the search string buffer. + + \sa text(), setMimeData() +*/ +void QClipboard::setText(const QString &text, Mode mode) +{ + QMimeData *data = new QMimeData; + data->setText(text); + setMimeData(data, mode); +} + +/*! + Returns the clipboard image, or returns a null image if the + clipboard does not contain an image or if it contains an image in + an unsupported image format. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, the + image is retrieved from the global clipboard. If \a mode is + QClipboard::Selection, the image is retrieved from the global + mouse selection. + + \sa setImage() pixmap() mimeData(), QImage::isNull() +*/ +QImage QClipboard::image(Mode mode) const +{ + const QMimeData *data = mimeData(mode); + if (!data) + return QImage(); + return qvariant_cast<QImage>(data->imageData()); +} + +/*! + Copies the \a image into the clipboard. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, the + image is stored in the global clipboard. If \a mode is + QClipboard::Selection, the data is stored in the global + mouse selection. + + This is shorthand for: + + \snippet doc/src/snippets/code/src_gui_kernel_qclipboard.cpp 1 + + \sa image(), setPixmap() setMimeData() +*/ +void QClipboard::setImage(const QImage &image, Mode mode) +{ + QMimeData *data = new QMimeData; + data->setImageData(image); + setMimeData(data, mode); +} + +/*! + Returns the clipboard pixmap, or null if the clipboard does not + contain a pixmap. Note that this can lose information. For + example, if the image is 24-bit and the display is 8-bit, the + result is converted to 8 bits, and if the image has an alpha + channel, the result just has a mask. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, the + pixmap is retrieved from the global clipboard. If \a mode is + QClipboard::Selection, the pixmap is retrieved from the global + mouse selection. + + \sa setPixmap() image() mimeData() QPixmap::convertFromImage() +*/ +QPixmap QClipboard::pixmap(Mode mode) const +{ + const QMimeData *data = mimeData(mode); + return data ? qvariant_cast<QPixmap>(data->imageData()) : QPixmap(); +} + +/*! + Copies \a pixmap into the clipboard. Note that this is slower + than setImage() because it needs to convert the QPixmap to a + QImage first. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, the + pixmap is stored in the global clipboard. If \a mode is + QClipboard::Selection, the pixmap is stored in the global + mouse selection. + + \sa pixmap() setImage() setMimeData() +*/ +void QClipboard::setPixmap(const QPixmap &pixmap, Mode mode) +{ + QMimeData *data = new QMimeData; + data->setImageData(pixmap); + setMimeData(data, mode); +} + + +/*! + \fn QMimeData *QClipboard::mimeData(Mode mode) const + + Returns a reference to a QMimeData representation of the current + clipboard data. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, the + data is retrieved from the global clipboard. If \a mode is + QClipboard::Selection, the data is retrieved from the global + mouse selection. If \a mode is QClipboard::FindBuffer, the + data is retrieved from the search string buffer. + + The text(), image(), and pixmap() functions are simpler + wrappers for retrieving text, image, and pixmap data. + + \sa setMimeData() +*/ + +/*! + \fn void QClipboard::setMimeData(QMimeData *src, Mode mode) + + Sets the clipboard data to \a src. Ownership of the data is + transferred to the clipboard. If you want to remove the data + either call clear() or call setMimeData() again with new data. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, the + data is stored in the global clipboard. If \a mode is + QClipboard::Selection, the data is stored in the global + mouse selection. If \a mode is QClipboard::FindBuffer, the + data is stored in the search string buffer. + + The setText(), setImage() and setPixmap() functions are simpler + wrappers for setting text, image and pixmap data respectively. + + \sa mimeData() +*/ + +/*! + \fn void QClipboard::clear(Mode mode) + Clear the clipboard contents. + + The \a mode argument is used to control which part of the system + clipboard is used. If \a mode is QClipboard::Clipboard, this + function clears the global clipboard contents. If \a mode is + QClipboard::Selection, this function clears the global mouse + selection contents. If \a mode is QClipboard::FindBuffer, this + function clears the search string buffer. + + \sa QClipboard::Mode, supportsSelection() +*/ + +#ifdef QT3_SUPPORT +/*! + \fn QMimeSource *QClipboard::data(Mode mode) const + \compat + + Use mimeData() instead. +*/ +QMimeSource *QClipboard::data(Mode mode) const +{ + Q_D(const QClipboard); + + if (supportsMode(mode) == false) + return 0; + + if (d->compat_data[mode]) + return d->compat_data[mode]; + + d->wrapper[mode]->data = mimeData(mode); + return d->wrapper[mode]; +} + + +/*! + \fn void QClipboard::setData(QMimeSource *src, Mode mode) + \compat + + Use setMimeData() instead. +*/ +void QClipboard::setData(QMimeSource *source, Mode mode) +{ + Q_D(QClipboard); + + if (supportsMode(mode) == false) + return; + + d->compat_data[mode] = source; + setMimeData(new QMimeSourceWrapper(d, mode), mode); +} +#endif // QT3_SUPPORT + +/*! + Returns true if the clipboard supports mouse selection; otherwise + returns false. +*/ +bool QClipboard::supportsSelection() const +{ + return supportsMode(Selection); +} + +/*! + Returns true if the clipboard supports a separate search buffer; otherwise + returns false. +*/ +bool QClipboard::supportsFindBuffer() const +{ + return supportsMode(FindBuffer); +} + +/*! + Returns true if this clipboard object owns the clipboard data; + otherwise returns false. +*/ +bool QClipboard::ownsClipboard() const +{ + return ownsMode(Clipboard); +} + +/*! + Returns true if this clipboard object owns the mouse selection + data; otherwise returns false. +*/ +bool QClipboard::ownsSelection() const +{ + return ownsMode(Selection); +} + +/*! + \since 4.2 + + Returns true if this clipboard object owns the find buffer data; + otherwise returns false. +*/ +bool QClipboard::ownsFindBuffer() const +{ + return ownsMode(FindBuffer); +} + +/*! + \internal + \fn bool QClipboard::supportsMode(Mode mode) const; + Returns true if the clipboard supports the clipboard mode speacified by \a mode; + otherwise returns false. +*/ + +/*! + \internal + \fn bool QClipboard::ownsMode(Mode mode) const; + Returns true if the clipboard supports the clipboard data speacified by \a mode; + otherwise returns false. +*/ + +/*! + \internal + Emits the appropriate changed signal for \a mode. +*/ +void QClipboard::emitChanged(Mode mode) +{ + switch (mode) { + case Clipboard: + emit dataChanged(); + break; + case Selection: + emit selectionChanged(); + break; + case FindBuffer: + emit findBufferChanged(); + break; + default: + break; + } + emit changed(mode); +} + +const char* QMimeDataWrapper::format(int n) const +{ + if (formats.isEmpty()) { + QStringList fmts = data->formats(); + for (int i = 0; i < fmts.size(); ++i) + formats.append(fmts.at(i).toLatin1()); + } + if (n < 0 || n >= formats.size()) + return 0; + return formats.at(n).data(); +} + +QByteArray QMimeDataWrapper::encodedData(const char *format) const +{ + if (QLatin1String(format) != QLatin1String("application/x-qt-image")){ + return data->data(QLatin1String(format)); + } else{ + QVariant variant = data->imageData(); + QImage img = qvariant_cast<QImage>(variant); + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + img.save(&buffer, "PNG"); + return ba; + } +} + +QVariant QMimeSourceWrapper::retrieveData(const QString &mimetype, QVariant::Type) const +{ + return source->encodedData(mimetype.toLatin1()); +} + +bool QMimeSourceWrapper::hasFormat(const QString &mimetype) const +{ + return source->provides(mimetype.toLatin1()); +} + +QStringList QMimeSourceWrapper::formats() const +{ + QStringList fmts; + int i = 0; + const char *fmt; + while ((fmt = source->format(i))) { + fmts.append(QLatin1String(fmt)); + ++i; + } + return fmts; +} + +#endif // QT_NO_CLIPBOARD + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qclipboard.h b/src/gui/guikernel/qclipboard.h new file mode 100644 index 0000000000..0a9e28ac8c --- /dev/null +++ b/src/gui/guikernel/qclipboard.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCLIPBOARD_H +#define QCLIPBOARD_H + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_CLIPBOARD + +class QMimeSource; +class QMimeData; +class QImage; +class QPixmap; + +class QClipboardPrivate; + +class Q_GUI_EXPORT QClipboard : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QClipboard) +private: + QClipboard(QObject *parent); + ~QClipboard(); + +public: + enum Mode { Clipboard, Selection, FindBuffer, LastMode = FindBuffer }; + + void clear(Mode mode = Clipboard); + + bool supportsSelection() const; + bool supportsFindBuffer() const; + + bool ownsSelection() const; + bool ownsClipboard() const; + bool ownsFindBuffer() const; + + QString text(Mode mode = Clipboard) const; + QString text(QString& subtype, Mode mode = Clipboard) const; + void setText(const QString &, Mode mode = Clipboard); + +#ifdef QT3_SUPPORT + QT3_SUPPORT QMimeSource *data(Mode mode = Clipboard) const; + QT3_SUPPORT void setData(QMimeSource*, Mode mode = Clipboard); +#endif + const QMimeData *mimeData(Mode mode = Clipboard ) const; + void setMimeData(QMimeData *data, Mode mode = Clipboard); + + QImage image(Mode mode = Clipboard) const; + QPixmap pixmap(Mode mode = Clipboard) const; + void setImage(const QImage &, Mode mode = Clipboard); + void setPixmap(const QPixmap &, Mode mode = Clipboard); + +Q_SIGNALS: + void changed(QClipboard::Mode mode); + void selectionChanged(); + void findBufferChanged(); + void dataChanged(); +private Q_SLOTS: + void ownerDestroyed(); + +protected: + void connectNotify(const char *); + bool event(QEvent *); + + friend class QApplication; + friend class QApplicationPrivate; + friend class QGuiApplication; + friend class QBaseApplication; + friend class QDragManager; + friend class QMimeSource; + +private: + Q_DISABLE_COPY(QClipboard) + + bool supportsMode(Mode mode) const; + bool ownsMode(Mode mode) const; + void emitChanged(Mode mode); +}; + +#endif // QT_NO_CLIPBOARD + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QCLIPBOARD_H diff --git a/src/gui/guikernel/qclipboard_mac.cpp b/src/gui/guikernel/qclipboard_mac.cpp new file mode 100644 index 0000000000..4a8bc56e41 --- /dev/null +++ b/src/gui/guikernel/qclipboard_mac.cpp @@ -0,0 +1,634 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qclipboard.h" +#include "qapplication.h" +#include "qbitmap.h" +#include "qdatetime.h" +#include "qdebug.h" +#include "qapplication_p.h" +#include <private/qt_mac_p.h> +#include "qevent.h" +#include "qurl.h" +#include <stdlib.h> +#include <string.h> +#include "qt_cocoa_helpers_mac_p.h" + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +/***************************************************************************** + QClipboard debug facilities + *****************************************************************************/ +//#define DEBUG_PASTEBOARD + +#ifndef QT_NO_CLIPBOARD + +/***************************************************************************** + QClipboard member functions for mac. + *****************************************************************************/ + +static QMacPasteboard *qt_mac_pasteboards[2] = {0, 0}; + +static inline QMacPasteboard *qt_mac_pasteboard(QClipboard::Mode mode) +{ + Q_ASSERT(mode == QClipboard::Clipboard || mode == QClipboard::FindBuffer); + if (mode == QClipboard::Clipboard) + return qt_mac_pasteboards[0]; + else + return qt_mac_pasteboards[1]; +} + +static void qt_mac_cleanupPasteboard() { + delete qt_mac_pasteboards[0]; + delete qt_mac_pasteboards[1]; + qt_mac_pasteboards[0] = 0; + qt_mac_pasteboards[1] = 0; +} + +static bool qt_mac_updateScrap(QClipboard::Mode mode) +{ + if(!qt_mac_pasteboards[0]) { + qt_mac_pasteboards[0] = new QMacPasteboard(kPasteboardClipboard, QMacPasteboardMime::MIME_CLIP); + qt_mac_pasteboards[1] = new QMacPasteboard(kPasteboardFind, QMacPasteboardMime::MIME_CLIP); + qAddPostRoutine(qt_mac_cleanupPasteboard); + return true; + } + return qt_mac_pasteboard(mode)->sync(); +} + +void QClipboard::clear(Mode mode) +{ + if (!supportsMode(mode)) + return; + qt_mac_updateScrap(mode); + qt_mac_pasteboard(mode)->clear(); + setMimeData(0, mode); +} + +void QClipboard::ownerDestroyed() +{ +} + + +void QClipboard::connectNotify(const char *signal) +{ + Q_UNUSED(signal); +} + +bool QClipboard::event(QEvent *e) +{ + if(e->type() != QEvent::Clipboard) + return QObject::event(e); + + if (qt_mac_updateScrap(QClipboard::Clipboard)) { + emitChanged(QClipboard::Clipboard); + } + + if (qt_mac_updateScrap(QClipboard::FindBuffer)) { + emitChanged(QClipboard::FindBuffer); + } + + return QObject::event(e); +} + +const QMimeData *QClipboard::mimeData(Mode mode) const +{ + if (!supportsMode(mode)) + return 0; + qt_mac_updateScrap(mode); + return qt_mac_pasteboard(mode)->mimeData(); +} + +void QClipboard::setMimeData(QMimeData *src, Mode mode) +{ + if (!supportsMode(mode)) + return; + qt_mac_updateScrap(mode); + qt_mac_pasteboard(mode)->setMimeData(src); + emitChanged(mode); +} + +bool QClipboard::supportsMode(Mode mode) const +{ + return (mode == Clipboard || mode == FindBuffer); +} + +bool QClipboard::ownsMode(Mode mode) const +{ + Q_UNUSED(mode); + return false; +} + +#endif // QT_NO_CLIPBOARD + +/***************************************************************************** + QMacPasteboard code +*****************************************************************************/ + +QMacPasteboard::QMacPasteboard(PasteboardRef p, uchar mt) +{ + mac_mime_source = false; + mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL); + paste = p; + CFRetain(paste); +} + +QMacPasteboard::QMacPasteboard(uchar mt) +{ + mac_mime_source = false; + mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL); + paste = 0; + OSStatus err = PasteboardCreate(0, &paste); + if(err == noErr) { + PasteboardSetPromiseKeeper(paste, promiseKeeper, this); + } else { + qDebug("PasteBoard: Error creating pasteboard: [%d]", (int)err); + } +} + +QMacPasteboard::QMacPasteboard(CFStringRef name, uchar mt) +{ + mac_mime_source = false; + mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL); + paste = 0; + OSStatus err = PasteboardCreate(name, &paste); + if(err == noErr) { + PasteboardSetPromiseKeeper(paste, promiseKeeper, this); + } else { + qDebug("PasteBoard: Error creating pasteboard: %s [%d]", QCFString::toQString(name).toLatin1().constData(), (int)err); + } +} + +QMacPasteboard::~QMacPasteboard() +{ + // commit all promises for paste after exit close + for (int i = 0; i < promises.count(); ++i) { + const Promise &promise = promises.at(i); + QCFString flavor = QCFString(promise.convertor->flavorFor(promise.mime)); + promiseKeeper(paste, (PasteboardItemID)promise.itemId, flavor, this); + } + + if(paste) + CFRelease(paste); +} + +PasteboardRef +QMacPasteboard::pasteBoard() const +{ + return paste; +} + +OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id, CFStringRef flavor, void *_qpaste) +{ + QMacPasteboard *qpaste = (QMacPasteboard*)_qpaste; + const long promise_id = (long)id; + + // Find the kept promise + const QString flavorAsQString = QCFString::toQString(flavor); + QMacPasteboard::Promise promise; + for (int i = 0; i < qpaste->promises.size(); i++){ + QMacPasteboard::Promise tmp = qpaste->promises[i]; + if (tmp.itemId == promise_id && tmp.convertor->canConvert(tmp.mime, flavorAsQString)){ + promise = tmp; + break; + } + } + + if (!promise.itemId && flavorAsQString == QLatin1String("com.trolltech.qt.MimeTypeName")) { + // we have promised this data, but wont be able to convert, so return null data. + // This helps in making the application/x-qt-mime-type-name hidden from normal use. + QByteArray ba; + QCFType<CFDataRef> data = CFDataCreate(0, (UInt8*)ba.constData(), ba.size()); + PasteboardPutItemFlavor(paste, id, flavor, data, kPasteboardFlavorNoFlags); + return noErr; + } + + if (!promise.itemId) { + // There was no promise that could deliver data for the + // given id and flavor. This should not happend. + qDebug("Pasteboard: %d: Request for %ld, %s, but no promise found!", __LINE__, promise_id, qPrintable(flavorAsQString)); + return cantGetFlavorErr; + } + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: Calling in promise for %s[%ld] [%s] (%s) [%d]", qPrintable(promise.mime), promise_id, + qPrintable(flavorAsQString), qPrintable(promise.convertor->convertorName()), promise.offset); +#endif + + QList<QByteArray> md = promise.convertor->convertFromMime(promise.mime, promise.data, flavorAsQString); + if (md.size() <= promise.offset) + return cantGetFlavorErr; + const QByteArray &ba = md[promise.offset]; + QCFType<CFDataRef> data = CFDataCreate(0, (UInt8*)ba.constData(), ba.size()); + PasteboardPutItemFlavor(paste, id, flavor, data, kPasteboardFlavorNoFlags); + return noErr; +} + +bool +QMacPasteboard::hasOSType(int c_flavor) const +{ + if (!paste) + return false; + + sync(); + + ItemCount cnt = 0; + if(PasteboardGetItemCount(paste, &cnt) || !cnt) + return false; + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: hasOSType [%c%c%c%c]", (c_flavor>>24)&0xFF, (c_flavor>>16)&0xFF, + (c_flavor>>8)&0xFF, (c_flavor>>0)&0xFF); +#endif + for(uint index = 1; index <= cnt; ++index) { + + PasteboardItemID id; + if(PasteboardGetItemIdentifier(paste, index, &id) != noErr) + return false; + + QCFType<CFArrayRef> types; + if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr) + return false; + + const int type_count = CFArrayGetCount(types); + for(int i = 0; i < type_count; ++i) { + CFStringRef flavor = (CFStringRef)CFArrayGetValueAtIndex(types, i); + const int os_flavor = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(flavor, kUTTagClassOSType)); + if(os_flavor == c_flavor) { +#ifdef DEBUG_PASTEBOARD + qDebug(" - Found!"); +#endif + return true; + } + } + } +#ifdef DEBUG_PASTEBOARD + qDebug(" - NotFound!"); +#endif + return false; +} + +bool +QMacPasteboard::hasFlavor(QString c_flavor) const +{ + if (!paste) + return false; + + sync(); + + ItemCount cnt = 0; + if(PasteboardGetItemCount(paste, &cnt) || !cnt) + return false; + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: hasFlavor [%s]", qPrintable(c_flavor)); +#endif + for(uint index = 1; index <= cnt; ++index) { + + PasteboardItemID id; + if(PasteboardGetItemIdentifier(paste, index, &id) != noErr) + return false; + + PasteboardFlavorFlags flags; + if(PasteboardGetItemFlavorFlags(paste, id, QCFString(c_flavor), &flags) == noErr) { +#ifdef DEBUG_PASTEBOARD + qDebug(" - Found!"); +#endif + return true; + } + } +#ifdef DEBUG_PASTEBOARD + qDebug(" - NotFound!"); +#endif + return false; +} + +class QMacPasteboardMimeSource : public QMimeData { + const QMacPasteboard *paste; +public: + QMacPasteboardMimeSource(const QMacPasteboard *p) : QMimeData(), paste(p) { } + ~QMacPasteboardMimeSource() { } + virtual QStringList formats() const { return paste->formats(); } + virtual QVariant retrieveData(const QString &format, QVariant::Type type) const { return paste->retrieveData(format, type); } +}; + +QMimeData +*QMacPasteboard::mimeData() const +{ + if(!mime) { + mac_mime_source = true; + mime = new QMacPasteboardMimeSource(this); + + } + return mime; +} + +class QMacMimeData : public QMimeData +{ +public: + QVariant variantData(const QString &mime) { return retrieveData(mime, QVariant::Invalid); } +private: + QMacMimeData(); +}; + +void +QMacPasteboard::setMimeData(QMimeData *mime_src) +{ + if (!paste) + return; + + if (mime == mime_src || (!mime_src && mime && mac_mime_source)) + return; + mac_mime_source = false; + delete mime; + mime = mime_src; + + QList<QMacPasteboardMime*> availableConverters = QMacPasteboardMime::all(mime_type); + if (mime != 0) { + clear_helper(); + QStringList formats = mime_src->formats(); + +#ifdef QT_MAC_USE_COCOA + // QMimeData sub classes reimplementing the formats() might not expose the + // temporary "application/x-qt-mime-type-name" mimetype. So check the existence + // of this mime type while doing drag and drop. + QString dummyMimeType(QLatin1String("application/x-qt-mime-type-name")); + if (!formats.contains(dummyMimeType)) { + QByteArray dummyType = mime_src->data(dummyMimeType); + if (!dummyType.isEmpty()) { + formats.append(dummyMimeType); + } + } +#endif + for(int f = 0; f < formats.size(); ++f) { + QString mimeType = formats.at(f); + for (QList<QMacPasteboardMime *>::Iterator it = availableConverters.begin(); it != availableConverters.end(); ++it) { + QMacPasteboardMime *c = (*it); + QString flavor(c->flavorFor(mimeType)); + if(!flavor.isEmpty()) { + QVariant mimeData = static_cast<QMacMimeData*>(mime_src)->variantData(mimeType); +#if 0 + //### Grrr, why didn't I put in a virtual int QMacPasteboardMime::count()? --Sam + const int numItems = c->convertFromMime(mimeType, mimeData, flavor).size(); +#else + int numItems = 1; //this is a hack but it is much faster than allowing conversion above + if(c->convertorName() == QLatin1String("FileURL")) + numItems = mime_src->urls().count(); +#endif + for(int item = 0; item < numItems; ++item) { + const int itemID = item+1; //id starts at 1 + promises.append(QMacPasteboard::Promise(itemID, c, mimeType, mimeData, item)); + PasteboardPutItemFlavor(paste, (PasteboardItemID)itemID, QCFString(flavor), 0, kPasteboardFlavorNoFlags); +#ifdef DEBUG_PASTEBOARD + qDebug(" - adding %d %s [%s] <%s> [%d]", + itemID, qPrintable(mimeType), qPrintable(flavor), qPrintable(c->convertorName()), item); +#endif + } + } + } + } + } +} + +QStringList +QMacPasteboard::formats() const +{ + if (!paste) + return QStringList(); + + sync(); + + QStringList ret; + ItemCount cnt = 0; + if(PasteboardGetItemCount(paste, &cnt) || !cnt) + return ret; + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: Formats [%d]", (int)cnt); +#endif + for(uint index = 1; index <= cnt; ++index) { + + PasteboardItemID id; + if(PasteboardGetItemIdentifier(paste, index, &id) != noErr) + continue; + + QCFType<CFArrayRef> types; + if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr) + continue; + + const int type_count = CFArrayGetCount(types); + for(int i = 0; i < type_count; ++i) { + const QString flavor = QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(types, i)); +#ifdef DEBUG_PASTEBOARD + qDebug(" -%s", qPrintable(QString(flavor))); +#endif + QString mimeType = QMacPasteboardMime::flavorToMime(mime_type, flavor); + if(!mimeType.isEmpty() && !ret.contains(mimeType)) { +#ifdef DEBUG_PASTEBOARD + qDebug(" -<%d> %s [%s]", ret.size(), qPrintable(mimeType), qPrintable(QString(flavor))); +#endif + ret << mimeType; + } + } + } + return ret; +} + +bool +QMacPasteboard::hasFormat(const QString &format) const +{ + if (!paste) + return false; + + sync(); + + ItemCount cnt = 0; + if(PasteboardGetItemCount(paste, &cnt) || !cnt) + return false; + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: hasFormat [%s]", qPrintable(format)); +#endif + for(uint index = 1; index <= cnt; ++index) { + + PasteboardItemID id; + if(PasteboardGetItemIdentifier(paste, index, &id) != noErr) + continue; + + QCFType<CFArrayRef> types; + if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr) + continue; + + const int type_count = CFArrayGetCount(types); + for(int i = 0; i < type_count; ++i) { + const QString flavor = QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(types, i)); +#ifdef DEBUG_PASTEBOARD + qDebug(" -%s [0x%x]", qPrintable(QString(flavor)), mime_type); +#endif + QString mimeType = QMacPasteboardMime::flavorToMime(mime_type, flavor); +#ifdef DEBUG_PASTEBOARD + if(!mimeType.isEmpty()) + qDebug(" - %s", qPrintable(mimeType)); +#endif + if(mimeType == format) + return true; + } + } + return false; +} + +QVariant +QMacPasteboard::retrieveData(const QString &format, QVariant::Type) const +{ + if (!paste) + return QVariant(); + + sync(); + + ItemCount cnt = 0; + if(PasteboardGetItemCount(paste, &cnt) || !cnt) + return QByteArray(); + +#ifdef DEBUG_PASTEBOARD + qDebug("Pasteboard: retrieveData [%s]", qPrintable(format)); +#endif + const QList<QMacPasteboardMime *> mimes = QMacPasteboardMime::all(mime_type); + for(int mime = 0; mime < mimes.size(); ++mime) { + QMacPasteboardMime *c = mimes.at(mime); + QString c_flavor = c->flavorFor(format); + if(!c_flavor.isEmpty()) { + // Handle text/plain a little differently. Try handling Unicode first. + bool checkForUtf16 = (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text") + || c_flavor == QLatin1String("public.utf8-plain-text")); + if (checkForUtf16 || c_flavor == QLatin1String("public.utf16-plain-text")) { + // Try to get the NSStringPboardType from NSPasteboard, newlines are mapped + // correctly (as '\n') in this data. The 'public.utf16-plain-text' type + // usually maps newlines to '\r' instead. + QString str = qt_mac_get_pasteboardString(paste); + if (!str.isEmpty()) + return str; + } + if (checkForUtf16 && hasFlavor(QLatin1String("public.utf16-plain-text"))) + c_flavor = QLatin1String("public.utf16-plain-text"); + + QVariant ret; + QList<QByteArray> retList; + for(uint index = 1; index <= cnt; ++index) { + PasteboardItemID id; + if(PasteboardGetItemIdentifier(paste, index, &id) != noErr) + continue; + + QCFType<CFArrayRef> types; + if(PasteboardCopyItemFlavors(paste, id, &types ) != noErr) + continue; + + const int type_count = CFArrayGetCount(types); + for(int i = 0; i < type_count; ++i) { + CFStringRef flavor = static_cast<CFStringRef>(CFArrayGetValueAtIndex(types, i)); + if(c_flavor == QCFString::toQString(flavor)) { + QCFType<CFDataRef> macBuffer; + if(PasteboardCopyItemFlavorData(paste, id, flavor, &macBuffer) == noErr) { + QByteArray buffer((const char *)CFDataGetBytePtr(macBuffer), CFDataGetLength(macBuffer)); + if(!buffer.isEmpty()) { +#ifdef DEBUG_PASTEBOARD + qDebug(" - %s [%s] (%s)", qPrintable(format), qPrintable(QCFString::toQString(flavor)), qPrintable(c->convertorName())); +#endif + buffer.detach(); //detach since we release the macBuffer + retList.append(buffer); + break; //skip to next element + } + } + } else { +#ifdef DEBUG_PASTEBOARD + qDebug(" - NoMatch %s [%s] (%s)", qPrintable(c_flavor), qPrintable(QCFString::toQString(flavor)), qPrintable(c->convertorName())); +#endif + } + } + } + + if (!retList.isEmpty()) { + ret = c->convertToMime(format, retList, c_flavor); + return ret; + } + } + } + return QVariant(); +} + +void QMacPasteboard::clear_helper() +{ + if (paste) + PasteboardClear(paste); + promises.clear(); +} + +void +QMacPasteboard::clear() +{ +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: clear!"); +#endif + clear_helper(); +} + +bool +QMacPasteboard::sync() const +{ + if (!paste) + return false; + const bool fromGlobal = PasteboardSynchronize(paste) & kPasteboardModified; + + if (fromGlobal) + const_cast<QMacPasteboard *>(this)->setMimeData(0); + +#ifdef DEBUG_PASTEBOARD + if(fromGlobal) + qDebug("Pasteboard: Synchronize!"); +#endif + return fromGlobal; +} + + + + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qclipboard_p.h b/src/gui/guikernel/qclipboard_p.h new file mode 100644 index 0000000000..c82694cfb8 --- /dev/null +++ b/src/gui/guikernel/qclipboard_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCLIPBOARD_P_H +#define QCLIPBOARD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qobject_p.h" +#include "QtGui/qmime.h" +#include "QtGui/qclipboard.h" + +QT_BEGIN_NAMESPACE + +class QClipboardPrivate; + +class QMimeDataWrapper : public QMimeSource +{ +public: + QMimeDataWrapper() {} + + const char* format(int n) const; + QByteArray encodedData(const char*) const; + + mutable QList<QByteArray> formats; + const QMimeData *data; +}; + +class QMimeSourceWrapper : public QMimeData +{ +public: + QMimeSourceWrapper(QClipboardPrivate *priv, QClipboard::Mode m); + ~QMimeSourceWrapper(); + + bool hasFormat(const QString &mimetype) const; + QStringList formats() const; + +protected: + QVariant retrieveData(const QString &mimetype, QVariant::Type) const; +private: + QClipboardPrivate *d; + QClipboard::Mode mode; + QMimeSource *source; +}; + + +class QClipboardPrivate : public QObjectPrivate +{ +public: + QClipboardPrivate() : QObjectPrivate() { + for (int i = 0; i <= QClipboard::LastMode; ++i) { + compat_data[i] = 0; + wrapper[i] = new QMimeDataWrapper(); + } + } + ~QClipboardPrivate() { + for (int i = 0; i <= QClipboard::LastMode; ++i) { + delete wrapper[i]; + delete compat_data[i]; + } + } + + mutable QMimeDataWrapper *wrapper[QClipboard::LastMode + 1]; + mutable QMimeSource *compat_data[QClipboard::LastMode + 1]; +}; + +inline QMimeSourceWrapper::QMimeSourceWrapper(QClipboardPrivate *priv, QClipboard::Mode m) + : QMimeData() +{ + d = priv; + mode = m; + source = d->compat_data[mode]; +} + +inline QMimeSourceWrapper::~QMimeSourceWrapper() +{ + if (d->compat_data[mode] == source) + d->compat_data[mode] = 0; + delete source; +} + +QT_END_NAMESPACE + +#endif // QCLIPBOARD_P_H diff --git a/src/gui/guikernel/qclipboard_qpa.cpp b/src/gui/guikernel/qclipboard_qpa.cpp new file mode 100644 index 0000000000..89c0afa22d --- /dev/null +++ b/src/gui/guikernel/qclipboard_qpa.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qclipboard.h" + +#ifndef QT_NO_CLIPBOARD + +#include "qmimedata.h" +#include "private/qapplication_p.h" +#include "qplatformclipboard_qpa.h" + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +void QClipboard::clear(Mode mode) +{ + setMimeData(0,mode); +} + + +bool QClipboard::event(QEvent *e) +{ + return QObject::event(e); +} + +const QMimeData* QClipboard::mimeData(Mode mode) const +{ + QPlatformClipboard *clipboard = QGuiApplicationPrivate::platformIntegration()->clipboard(); + if (!clipboard->supportsMode(mode)) return 0; + return clipboard->mimeData(mode); +} + +void QClipboard::setMimeData(QMimeData* src, Mode mode) +{ + QPlatformClipboard *clipboard = QGuiApplicationPrivate::platformIntegration()->clipboard(); + if (!clipboard->supportsMode(mode)) return; + + clipboard->setMimeData(src,mode); + + emitChanged(mode); +} + +bool QClipboard::supportsMode(Mode mode) const +{ + QPlatformClipboard *clipboard = QGuiApplicationPrivate::platformIntegration()->clipboard(); + return clipboard->supportsMode(mode); +} + +bool QClipboard::ownsMode(Mode mode) const +{ + if (mode == Clipboard) + qWarning("QClipboard::ownsClipboard: UNIMPLEMENTED!"); + return false; +} + +void QClipboard::connectNotify( const char * ) +{ +} + +void QClipboard::ownerDestroyed() +{ +} + +#endif // QT_NO_CLIPBOARD + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qclipboard_s60.cpp b/src/gui/guikernel/qclipboard_s60.cpp new file mode 100644 index 0000000000..0dafae0996 --- /dev/null +++ b/src/gui/guikernel/qclipboard_s60.cpp @@ -0,0 +1,331 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qclipboard.h" + +#ifndef QT_NO_CLIPBOARD + +#include "qapplication.h" +#include "qbitmap.h" +#include "qdatetime.h" +#include "qbuffer.h" +#include "qwidget.h" +#include "qevent.h" +#include "private/qcore_symbian_p.h" +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include "txtclipboard.h" +#endif +#include "txtetext.h" +#include <QtDebug> + +// Symbian's clipboard +#include <baclipb.h> +QT_BEGIN_NAMESPACE + +const TUid KQtCbDataStream = {0x2001B2DD}; +const TInt KPlainTextBegin = 0; + +class QClipboardData +{ +public: + QClipboardData(); + ~QClipboardData(); + + void setSource(QMimeData* s) + { + if (s == src) + return; + delete src; + src = s; + } + QMimeData* source() + { return src; } + bool connected() + { return connection; } + void clear(); + +private: + QMimeData* src; + bool connection; +}; + +QClipboardData::QClipboardData():src(0),connection(true) +{ + clear(); +} + +QClipboardData::~QClipboardData() +{ + connection = false; + delete src; +} + +void QClipboardData::clear() +{ + QMimeData* newSrc = new QMimeData; + delete src; + src = newSrc; +} + +static QClipboardData *internalCbData = 0; + +static void cleanupClipboardData() +{ + delete internalCbData; + internalCbData = 0; +} + +static QClipboardData *clipboardData() +{ + if (internalCbData == 0) { + internalCbData = new QClipboardData; + if (internalCbData) + { + if (!internalCbData->connected()) + { + delete internalCbData; + internalCbData = 0; + } + else + { + qAddPostRoutine(cleanupClipboardData); + } + } + } + return internalCbData; +} + +void writeToStreamLX(const QMimeData* aData, RWriteStream& aStream) +{ + // This function both leaves and throws exceptions. There must be no destructor + // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. + QStringList headers = aData->formats(); + aStream << TCardinality(headers.count()); + for (QStringList::const_iterator iter= headers.constBegin();iter != headers.constEnd();iter++) + { + HBufC* stringData = TPtrC(reinterpret_cast<const TUint16*>((*iter).utf16())).AllocLC(); + QByteArray ba = aData->data((*iter)); + // mime type + aStream << TCardinality(stringData->Size()); + aStream << *(stringData); + // mime data + aStream << TCardinality(ba.size()); + aStream.WriteL(reinterpret_cast<const uchar*>(ba.constData()),ba.size()); + CleanupStack::PopAndDestroy(stringData); + } +} + +void writeToSymbianStoreLX(const QMimeData* aData, CClipboard* clipboard) +{ + // This function both leaves and throws exceptions. There must be no destructor + // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. + if (aData->hasText()) { + CPlainText* text = CPlainText::NewL(); + CleanupStack::PushL(text); + + TPtrC textPtr(qt_QString2TPtrC(aData->text())); + text->InsertL(KPlainTextBegin, textPtr); + text->CopyToStoreL(clipboard->Store(), clipboard->StreamDictionary(), + KPlainTextBegin, textPtr.Length()); + CleanupStack::PopAndDestroy(text); + } +} + +void readSymbianStoreLX(QMimeData* aData, CClipboard* clipboard) +{ + // This function both leaves and throws exceptions. There must be no destructor + // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. + CPlainText* text = CPlainText::NewL(); + CleanupStack::PushL(text); + TInt dataLength = text->PasteFromStoreL(clipboard->Store(), clipboard->StreamDictionary(), + KPlainTextBegin); + if (dataLength == 0) { + User::Leave(KErrNotFound); + } + HBufC* hBuf = HBufC::NewL(dataLength); + TPtr buf = hBuf->Des(); + text->Extract(buf, KPlainTextBegin, dataLength); + + QString string = qt_TDesC2QString(buf); + CleanupStack::PopAndDestroy(text); + + aData->setText(string); +} + +void readFromStreamLX(QMimeData* aData,RReadStream& aStream) +{ + // This function both leaves and throws exceptions. There must be no destructor + // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. + TCardinality mimeTypeCount; + aStream >> mimeTypeCount; + for (int i = 0; i< mimeTypeCount;i++) + { + // mime type + TCardinality mimeTypeSize; + aStream >> mimeTypeSize; + HBufC* mimeTypeBuf = HBufC::NewLC(aStream,mimeTypeSize); + QString mimeType = QString(reinterpret_cast<const QChar *>(mimeTypeBuf->Des().Ptr()), + mimeTypeBuf->Length()); + CleanupStack::PopAndDestroy(mimeTypeBuf); + // mime data + TCardinality dataSize; + aStream >> dataSize; + QByteArray ba; + ba.reserve(dataSize); + aStream.ReadL(reinterpret_cast<uchar*>(ba.data_ptr()->data),dataSize); + ba.data_ptr()->size = dataSize; + aData->setData(mimeType,ba); + } +} + + +/***************************************************************************** + QClipboard member functions + *****************************************************************************/ + +void QClipboard::clear(Mode mode) +{ + setText(QString(), mode); +} +const QMimeData* QClipboard::mimeData(Mode mode) const +{ + if (mode != Clipboard) return 0; + QClipboardData *d = clipboardData(); + bool dataExists(false); + if (d) + { + TRAPD(err,{ + RFs fs = qt_s60GetRFs(); + CClipboard* cb = CClipboard::NewForReadingLC(fs); + Q_ASSERT(cb); + //stream for qt + RStoreReadStream stream; + TStreamId stid = (cb->StreamDictionary()).At(KQtCbDataStream); + if (stid != 0) { + stream.OpenLC(cb->Store(),stid); + QT_TRYCATCH_LEAVING(readFromStreamLX(d->source(),stream)); + CleanupStack::PopAndDestroy(&stream); + dataExists = true; + } + else { + //symbian clipboard + RStoreReadStream symbianStream; + TStreamId symbianStId = (cb->StreamDictionary()).At(KClipboardUidTypePlainText); + if (symbianStId != 0) { + symbianStream.OpenLC(cb->Store(), symbianStId); + QT_TRYCATCH_LEAVING(readSymbianStoreLX(d->source(), cb)); + CleanupStack::PopAndDestroy(&symbianStream); + dataExists = true; + } + } + CleanupStack::PopAndDestroy(cb); + }); + if (err != KErrNone){ + qDebug()<< "clipboard is empty/err: " << err; + } + + if (dataExists) { + return d->source(); + } + } + return 0; +} + + +void QClipboard::setMimeData(QMimeData* src, Mode mode) +{ + if (mode != Clipboard) return; + QClipboardData *d = clipboardData(); + if (d) + { + TRAPD(err,{ + RFs fs = qt_s60GetRFs(); + CClipboard* cb = CClipboard::NewForWritingLC(fs); + //stream for qt + RStoreWriteStream stream; + TStreamId stid = stream.CreateLC(cb->Store()); + QT_TRYCATCH_LEAVING(writeToStreamLX(src,stream)); + d->setSource(src); + stream.CommitL(); + (cb->StreamDictionary()).AssignL(KQtCbDataStream,stid); + cb->CommitL(); + + //stream for symbian + RStoreWriteStream symbianStream; + TStreamId symbianStId = symbianStream.CreateLC(cb->Store()); + QT_TRYCATCH_LEAVING(writeToSymbianStoreLX(src, cb)); + (cb->StreamDictionary()).AssignL(KClipboardUidTypePlainText, symbianStId); + cb->CommitL(); + CleanupStack::PopAndDestroy(3,cb); + }); + if (err != KErrNone){ + qDebug()<< "clipboard write err :" << err; + } + } + emitChanged(QClipboard::Clipboard); +} + +bool QClipboard::supportsMode(Mode mode) const +{ + return (mode == Clipboard); +} + +bool QClipboard::ownsMode(Mode mode) const +{ + if (mode == Clipboard) + qWarning("QClipboard::ownsClipboard: UNIMPLEMENTED!"); + return false; +} + +bool QClipboard::event(QEvent * /* e */) +{ + return true; +} + +void QClipboard::connectNotify( const char * ) +{ +} + +void QClipboard::ownerDestroyed() +{ +} +QT_END_NAMESPACE +#endif // QT_NO_CLIPBOARD diff --git a/src/gui/guikernel/qclipboard_win.cpp b/src/gui/guikernel/qclipboard_win.cpp new file mode 100644 index 0000000000..ea41165b9c --- /dev/null +++ b/src/gui/guikernel/qclipboard_win.cpp @@ -0,0 +1,398 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qclipboard.h" + +#ifndef QT_NO_CLIPBOARD + +#include "qapplication.h" +#include "qapplication_p.h" +#include "qeventloop.h" +#include "qwidget.h" +#include "qevent.h" +#include "qmime.h" +#include "qt_windows.h" +#include "qdnd_p.h" +#include <private/qwidget_p.h> +#include <private/qsystemlibrary_p.h> + +QT_BEGIN_NAMESPACE + +#if defined(Q_OS_WINCE) +QT_BEGIN_INCLUDE_NAMESPACE +#include "qguifunctions_wince.h" +QT_END_INCLUDE_NAMESPACE + +HRESULT QtCeGetClipboard(IDataObject** obj); +HRESULT QtCeSetClipboard(IDataObject* obj); +void QtCeFlushClipboard(); + +#define OleGetClipboard QtCeGetClipboard +#define OleSetClipboard QtCeSetClipboard +#define OleFlushClipboard QtCeFlushClipboard + +#endif + +typedef BOOL (WINAPI *PtrIsHungAppWindow)(HWND); + +static PtrIsHungAppWindow ptrIsHungAppWindow = 0; + +class QClipboardWatcher : public QInternalMimeData { +public: + QClipboardWatcher() + : QInternalMimeData() + { + } + + bool hasFormat_sys(const QString &mimetype) const; + QStringList formats_sys() const; + QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const; +}; + + +bool QClipboardWatcher::hasFormat_sys(const QString &mime) const +{ + IDataObject * pDataObj = 0; + + if (OleGetClipboard(&pDataObj) != S_OK && !pDataObj) // Sanity + return false; + + bool has = QWindowsMime::converterToMime(mime, pDataObj) != 0; + + pDataObj->Release(); + + return has; +} + +QStringList QClipboardWatcher::formats_sys() const +{ + QStringList fmts; + IDataObject * pDataObj = 0; + + if (OleGetClipboard(&pDataObj) != S_OK && !pDataObj) // Sanity + return QStringList(); + + fmts = QWindowsMime::allMimesForFormats(pDataObj); + + pDataObj->Release(); + + return fmts; +} + +QVariant QClipboardWatcher::retrieveData_sys(const QString &mimeType, QVariant::Type type) const +{ + QVariant result; + IDataObject * pDataObj = 0; + + if (OleGetClipboard(&pDataObj) != S_OK && !pDataObj) // Sanity + return result; + + QWindowsMime *converter = QWindowsMime::converterToMime(mimeType, pDataObj); + + if (converter) + result = converter->convertToMime(mimeType, pDataObj, type); + + pDataObj->Release(); + + return result; +} + +class QClipboardData +{ +public: + QClipboardData() + : iData(0) + , nextClipboardViewer(0) + { + clipBoardViewer = new QWidget(); + clipBoardViewer->createWinId(); + clipBoardViewer->setObjectName(QLatin1String("internal clipboard owner")); + // We don't need this internal widget to appear in QApplication::topLevelWidgets() + if (QWidgetPrivate::allWidgets) + QWidgetPrivate::allWidgets->remove(clipBoardViewer); + } + + ~QClipboardData() + { + Q_ASSERT(clipBoardViewer->testAttribute(Qt::WA_WState_Created)); + ChangeClipboardChain(clipBoardViewer->internalWinId(), nextClipboardViewer); + delete clipBoardViewer; + releaseIData(); + } + + void releaseIData() + { + if (iData) { + delete iData->mimeData(); + iData->releaseQt(); + iData->Release(); + iData = 0; + } + } + + QOleDataObject * iData; + QWidget *clipBoardViewer; + HWND nextClipboardViewer; + QClipboardWatcher watcher; +}; + +static QClipboardData *ptrClipboardData = 0; + +static QClipboardData *clipboardData() +{ + if (ptrClipboardData == 0) { + ptrClipboardData = new QClipboardData; + // this needs to be done here to avoid recursion + Q_ASSERT(ptrClipboardData->clipBoardViewer->testAttribute(Qt::WA_WState_Created)); + ptrClipboardData->nextClipboardViewer = SetClipboardViewer(ptrClipboardData->clipBoardViewer->internalWinId()); + } + return ptrClipboardData; +} + +static void cleanupClipboardData() +{ + delete ptrClipboardData; + ptrClipboardData = 0; +} + +#if defined(Q_OS_WINCE) +HRESULT QtCeGetClipboard(IDataObject** obj) +{ + HWND owner = ptrClipboardData->clipBoardViewer->internalWinId(); + if (!OpenClipboard(owner)) + return !S_OK; + + if (!IsClipboardFormatAvailable(CF_TEXT) && !IsClipboardFormatAvailable(CF_UNICODETEXT)) + return !S_OK; + + HANDLE clipData = GetClipboardData(CF_TEXT); + QString clipText; + if (clipData == 0) { + clipData = GetClipboardData(CF_UNICODETEXT); + if (clipData != 0) + clipText = QString::fromWCharArray((wchar_t *)clipData); + } else { + clipText = QString::fromLatin1((const char*)clipData); + } + + QMimeData *mimeData = new QMimeData(); + mimeData->setText(clipText); + QOleDataObject* data = new QOleDataObject(mimeData); + *obj = data; + CloseClipboard(); + return S_OK; +} + +HRESULT QtCeSetClipboard(IDataObject* obj) +{ + HWND owner = ptrClipboardData->clipBoardViewer->internalWinId(); + if (!OpenClipboard(owner)) + return !S_OK; + + bool result = false; + if (obj == 0) { + result = true; + EmptyClipboard(); + CloseClipboard(); + } else { + QOleDataObject* qobj = static_cast<QOleDataObject*>(obj); + + const QMimeData* data = qobj->mimeData(); + if (data->hasText()) { + EmptyClipboard(); + result = SetClipboardData(CF_UNICODETEXT, wcsdup(reinterpret_cast<const wchar_t *> (data->text().utf16()))) != NULL; + CloseClipboard(); + result = true; + } + } + return result ? S_OK : !S_OK; +} + +void QtCeFlushClipboard() { } +#endif + + + +QClipboard::~QClipboard() +{ + cleanupClipboardData(); +} + +void QClipboard::setMimeData(QMimeData *src, Mode mode) +{ + if (mode != Clipboard) + return; + QClipboardData *d = clipboardData(); + + if (!(d->iData && d->iData->mimeData() == src)) { + d->releaseIData(); + d->iData = new QOleDataObject(src); + } + + if (OleSetClipboard(d->iData) != S_OK) { + d->releaseIData(); + qErrnoWarning("QClipboard::setMimeData: Failed to set data on clipboard"); + return; + } +#if defined(Q_OS_WINCE) + // As WinCE does not support notifications we send the signal here + // We will get no event when the clipboard changes outside... + emit dataChanged(); + emit changed(Clipboard); +#endif +} + +void QClipboard::clear(Mode mode) +{ + if (mode != Clipboard) return; + + QClipboardData *d = clipboardData(); + + d->releaseIData(); + + if (OleSetClipboard(0) != S_OK) { + qErrnoWarning("QClipboard::clear: Failed to clear data on clipboard"); + return; + } +#if defined(Q_OS_WINCE) + // As WinCE does not support notifications we send the signal here + // We will get no event when the clipboard changes outside... + emit dataChanged(); + emit changed(Clipboard); +#endif +} + +bool QClipboard::event(QEvent *e) +{ + if (e->type() != QEvent::Clipboard) + return QObject::event(e); + + QClipboardData *d = clipboardData(); + + MSG *m = (MSG *)((QClipboardEvent*)e)->data(); + if (!m) { + // this is sent to render all formats at app shut down + if (ownsClipboard()) { + OleFlushClipboard(); + d->releaseIData(); + } + return true; + } + + bool propagate = false; + + if (m->message == WM_CHANGECBCHAIN) { + if ((HWND)m->wParam == d->nextClipboardViewer) + d->nextClipboardViewer = (HWND)m->lParam; + else + propagate = true; + } else if (m->message == WM_DRAWCLIPBOARD) { + emitChanged(QClipboard::Clipboard); + if (!ownsClipboard() && d->iData) + // clean up the clipboard object if we no longer own the clipboard + d->releaseIData(); + propagate = true; + } + if (propagate && d->nextClipboardViewer) { + if (ptrIsHungAppWindow == 0) { + QSystemLibrary library(QLatin1String("User32")); + ptrIsHungAppWindow = (PtrIsHungAppWindow)library.resolve("IsHungAppWindow"); + } + if (ptrIsHungAppWindow && ptrIsHungAppWindow(d->nextClipboardViewer)) { + qWarning("%s: Cowardly refusing to send clipboard message to hung application...", Q_FUNC_INFO); + } else { + SendMessage(d->nextClipboardViewer, m->message, m->wParam, m->lParam); + } + } + + return true; +} + +void QClipboard::connectNotify(const char *signal) +{ + if (qstrcmp(signal,SIGNAL(dataChanged())) == 0) { + // ensure we are up and running but block signals so the dataChange signal + // is not emitted while being connected to. + bool blocked = blockSignals(true); + QClipboardData *d = clipboardData(); + blockSignals(blocked); + Q_UNUSED(d); + } +} + +const QMimeData *QClipboard::mimeData(Mode mode) const +{ + if (mode != Clipboard) + return 0; + + QClipboardData *data = clipboardData(); + // sort cut for local copy / paste + if (ownsClipboard() && data->iData->mimeData()) + return data->iData->mimeData(); + return &data->watcher; +} + +bool QClipboard::supportsMode(Mode mode) const +{ + return (mode == Clipboard); +} + +bool QClipboard::ownsMode(Mode mode) const +{ + if (mode == Clipboard) { + QClipboardData *d = clipboardData(); +#if !defined(Q_OS_WINCE) + return d->iData && OleIsCurrentClipboard(d->iData) == S_OK; +#else + return d->iData && GetClipboardOwner() == d->clipBoardViewer->internalWinId(); +#endif + } else { + return false; + } +} + +void QClipboard::ownerDestroyed() +{ +} + +QT_END_NAMESPACE + +#endif // QT_NO_CLIPBOARD diff --git a/src/gui/guikernel/qclipboard_x11.cpp b/src/gui/guikernel/qclipboard_x11.cpp new file mode 100644 index 0000000000..d566c86e04 --- /dev/null +++ b/src/gui/guikernel/qclipboard_x11.cpp @@ -0,0 +1,1539 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #define QCLIPBOARD_DEBUG +// #define QCLIPBOARD_DEBUG_VERBOSE + +#ifdef QCLIPBOARD_DEBUG +# define DEBUG qDebug +#else +# define DEBUG if (false) qDebug +#endif + +#ifdef QCLIPBOARD_DEBUG_VERBOSE +# define VDEBUG qDebug +#else +# define VDEBUG if (false) qDebug +#endif + +#include "qplatformdefs.h" + +#include "qclipboard.h" +#include "qclipboard_p.h" + +#ifndef QT_NO_CLIPBOARD + +#include "qabstracteventdispatcher.h" +#include "qapplication.h" +#include "qdesktopwidget.h" +#include "qbitmap.h" +#include "qiodevice.h" +#include "qbuffer.h" +#include "qtextcodec.h" +#include "qlist.h" +#include "qmap.h" +#include "qapplication_p.h" +#include "qevent.h" +#include "qt_x11_p.h" +#include "qx11info_x11.h" +#include "qimagewriter.h" +#include "qelapsedtimer.h" +#include "qvariant.h" +#include "qdnd_p.h" +#include <private/qwidget_p.h> + +#ifndef QT_NO_XFIXES +#include <X11/extensions/Xfixes.h> +#endif // QT_NO_XFIXES + +QT_BEGIN_NAMESPACE + +/***************************************************************************** + Internal QClipboard functions for X11. + *****************************************************************************/ + +static int clipboard_timeout = 5000; // 5s timeout on clipboard operations + +static QWidget * owner = 0; +static QWidget *requestor = 0; +static bool timer_event_clear = false; +static int timer_id = 0; + +static int pending_timer_id = 0; +static bool pending_clipboard_changed = false; +static bool pending_selection_changed = false; + + +// event capture mechanism for qt_xclb_wait_for_event +static bool waiting_for_data = false; +static bool has_captured_event = false; +static Window capture_event_win = XNone; +static int capture_event_type = -1; +static XEvent captured_event; + +class QClipboardWatcher; // forward decl +static QClipboardWatcher *selection_watcher = 0; +static QClipboardWatcher *clipboard_watcher = 0; + +static void cleanup() +{ + delete owner; + delete requestor; + owner = 0; + requestor = 0; +} + +static +void setupOwner() +{ + if (owner) + return; + owner = new QWidget(0); + owner->setObjectName(QLatin1String("internal clipboard owner")); + owner->createWinId(); + requestor = new QWidget(0); + requestor->createWinId(); + requestor->setObjectName(QLatin1String("internal clipboard requestor")); + // We don't need this internal widgets to appear in QApplication::topLevelWidgets() + if (QWidgetPrivate::allWidgets) { + QWidgetPrivate::allWidgets->remove(owner); + QWidgetPrivate::allWidgets->remove(requestor); + } + qAddPostRoutine(cleanup); +} + + +class QClipboardWatcher : public QInternalMimeData { +public: + QClipboardWatcher(QClipboard::Mode mode); + ~QClipboardWatcher(); + bool empty() const; + virtual bool hasFormat_sys(const QString &mimetype) const; + virtual QStringList formats_sys() const; + + QVariant retrieveData_sys(const QString &mimetype, QVariant::Type type) const; + QByteArray getDataInFormat(Atom fmtatom) const; + + Atom atom; + mutable QStringList formatList; + mutable QByteArray format_atoms; +}; + +class QClipboardData +{ +private: + QMimeData *&mimeDataRef() const + { + if(mode == QClipboard::Selection) + return selectionData; + return clipboardData; + } + +public: + QClipboardData(QClipboard::Mode mode); + ~QClipboardData(); + + void setSource(QMimeData* s) + { + if ((mode == QClipboard::Selection && selectionData == s) + || clipboardData == s) { + return; + } + + if (selectionData != clipboardData) { + delete mimeDataRef(); + } + + mimeDataRef() = s; + } + + QMimeData *source() const + { + return mimeDataRef(); + } + + void clear() + { + timestamp = CurrentTime; + if (selectionData == clipboardData) { + mimeDataRef() = 0; + } else { + QMimeData *&src = mimeDataRef(); + delete src; + src = 0; + } + } + + static QMimeData *selectionData; + static QMimeData *clipboardData; + Time timestamp; + QClipboard::Mode mode; +}; + +QMimeData *QClipboardData::selectionData = 0; +QMimeData *QClipboardData::clipboardData = 0; + +QClipboardData::QClipboardData(QClipboard::Mode clipboardMode) +{ + timestamp = CurrentTime; + mode = clipboardMode; +} + +QClipboardData::~QClipboardData() +{ clear(); } + + +static QClipboardData *internalCbData = 0; +static QClipboardData *internalSelData = 0; + +static void cleanupClipboardData() +{ + delete internalCbData; + internalCbData = 0; +} + +static QClipboardData *clipboardData() +{ + if (internalCbData == 0) { + internalCbData = new QClipboardData(QClipboard::Clipboard); + qAddPostRoutine(cleanupClipboardData); + } + return internalCbData; +} + +static void cleanupSelectionData() +{ + delete internalSelData; + internalSelData = 0; +} + +static QClipboardData *selectionData() +{ + if (internalSelData == 0) { + internalSelData = new QClipboardData(QClipboard::Selection); + qAddPostRoutine(cleanupSelectionData); + } + return internalSelData; +} + +class QClipboardINCRTransaction +{ +public: + QClipboardINCRTransaction(Window w, Atom p, Atom t, int f, QByteArray d, unsigned int i); + ~QClipboardINCRTransaction(void); + + int x11Event(XEvent *event); + + Window window; + Atom property, target; + int format; + QByteArray data; + unsigned int increment; + unsigned int offset; +}; + +typedef QMap<Window,QClipboardINCRTransaction*> TransactionMap; +static TransactionMap *transactions = 0; +static QApplication::EventFilter prev_event_filter = 0; +static int incr_timer_id = 0; + +static bool qt_x11_incr_event_filter(void *message, long *result) +{ + XEvent *event = reinterpret_cast<XEvent *>(message); + TransactionMap::Iterator it = transactions->find(event->xany.window); + if (it != transactions->end()) { + if ((*it)->x11Event(event) != 0) + return true; + } + if (prev_event_filter) + return prev_event_filter(event, result); + return false; +} + +/* + called when no INCR activity has happened for 'clipboard_timeout' + milliseconds... we assume that all unfinished transactions have + timed out and remove everything from the transaction map +*/ +static void qt_xclb_incr_timeout(void) +{ + qWarning("QClipboard: Timed out while sending data"); + + while (transactions) + delete *transactions->begin(); +} + +QClipboardINCRTransaction::QClipboardINCRTransaction(Window w, Atom p, Atom t, int f, + QByteArray d, unsigned int i) + : window(w), property(p), target(t), format(f), data(d), increment(i), offset(0u) +{ + DEBUG("QClipboard: sending %d bytes (INCR transaction %p)", d.size(), this); + + XSelectInput(X11->display, window, PropertyChangeMask); + + if (! transactions) { + VDEBUG("QClipboard: created INCR transaction map"); + transactions = new TransactionMap; + prev_event_filter = qApp->setEventFilter(qt_x11_incr_event_filter); + incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout); + } + transactions->insert(window, this); +} + +QClipboardINCRTransaction::~QClipboardINCRTransaction(void) +{ + VDEBUG("QClipboard: destroyed INCR transacton %p", this); + + XSelectInput(X11->display, window, NoEventMask); + + transactions->remove(window); + if (transactions->isEmpty()) { + VDEBUG("QClipboard: no more INCR transactions"); + delete transactions; + transactions = 0; + + (void)qApp->setEventFilter(prev_event_filter); + + if (incr_timer_id != 0) { + QApplication::clipboard()->killTimer(incr_timer_id); + incr_timer_id = 0; + } + } +} + +int QClipboardINCRTransaction::x11Event(XEvent *event) +{ + if (event->type != PropertyNotify + || (event->xproperty.state != PropertyDelete + || event->xproperty.atom != property)) + return 0; + + // restart the INCR timer + if (incr_timer_id) QApplication::clipboard()->killTimer(incr_timer_id); + incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout); + + unsigned int bytes_left = data.size() - offset; + if (bytes_left > 0) { + unsigned int xfer = qMin(increment, bytes_left); + VDEBUG("QClipboard: sending %d bytes, %d remaining (INCR transaction %p)", + xfer, bytes_left - xfer, this); + + XChangeProperty(X11->display, window, property, target, format, + PropModeReplace, (uchar *) data.data() + offset, xfer); + offset += xfer; + } else { + // INCR transaction finished... + XChangeProperty(X11->display, window, property, target, format, + PropModeReplace, (uchar *) data.data(), 0); + delete this; + } + + return 1; +} + + +/***************************************************************************** + QClipboard member functions for X11. + *****************************************************************************/ + +struct qt_init_timestamp_data +{ + Time timestamp; +}; + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +static Bool qt_init_timestamp_scanner(Display*, XEvent *event, XPointer arg) +{ + qt_init_timestamp_data *data = + reinterpret_cast<qt_init_timestamp_data*>(arg); + switch(event->type) + { + case ButtonPress: + case ButtonRelease: + data->timestamp = event->xbutton.time; + break; + case MotionNotify: + data->timestamp = event->xmotion.time; + break; + case XKeyPress: + case XKeyRelease: + data->timestamp = event->xkey.time; + break; + case PropertyNotify: + data->timestamp = event->xproperty.time; + break; + case EnterNotify: + case LeaveNotify: + data->timestamp = event->xcrossing.time; + break; + case SelectionClear: + data->timestamp = event->xselectionclear.time; + break; + default: + break; + } +#ifndef QT_NO_XFIXES + if (X11->use_xfixes && event->type == (X11->xfixes_eventbase + XFixesSelectionNotify)) { + XFixesSelectionNotifyEvent *req = + reinterpret_cast<XFixesSelectionNotifyEvent *>(event); + data->timestamp = req->selection_timestamp; + } +#endif + return false; +} + +#if defined(Q_C_CALLBACKS) +} +#endif + +QClipboard::QClipboard(QObject *parent) + : QObject(*new QClipboardPrivate, parent) +{ + // create desktop widget since we need it to get PropertyNotify or + // XFixesSelectionNotify events when someone changes the + // clipboard. + (void)QApplication::desktop(); + +#ifndef QT_NO_XFIXES + if (X11->use_xfixes && X11->ptrXFixesSelectSelectionInput) { + const unsigned long eventMask = + XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask; + for (int i = 0; i < X11->screenCount; ++i) { + X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(i), + XA_PRIMARY, eventMask); + X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(i), + ATOM(CLIPBOARD), eventMask); + } + } +#endif // QT_NO_XFIXES + + if (X11->time == CurrentTime) { + // send a dummy event to myself to get the timestamp from X11. + qt_init_timestamp_data data; + data.timestamp = CurrentTime; + XEvent ev; + XCheckIfEvent(X11->display, &ev, &qt_init_timestamp_scanner, (XPointer)&data); + if (data.timestamp == CurrentTime) { + setupOwner(); + // We need this value just for completeness, we don't use it. + long dummy = 0; + Window ownerId = owner->internalWinId(); + XChangeProperty(X11->display, ownerId, + ATOM(CLIP_TEMPORARY), XA_INTEGER, 32, + PropModeReplace, (uchar*)&dummy, 1); + XWindowEvent(X11->display, ownerId, PropertyChangeMask, &ev); + data.timestamp = ev.xproperty.time; + XDeleteProperty(X11->display, ownerId, ATOM(CLIP_TEMPORARY)); + } + X11->time = data.timestamp; + } +} + +void QClipboard::clear(Mode mode) +{ + setMimeData(0, mode); +} + + +bool QClipboard::supportsMode(Mode mode) const +{ + return (mode == Clipboard || mode == Selection); +} + +bool QClipboard::ownsMode(Mode mode) const +{ + if (mode == Clipboard) + return clipboardData()->timestamp != CurrentTime; + else if(mode == Selection) + return selectionData()->timestamp != CurrentTime; + else + return false; +} + + +// event filter function... captures interesting events while +// qt_xclb_wait_for_event is running the event loop +static bool qt_x11_clipboard_event_filter(void *message, long *) +{ + XEvent *event = reinterpret_cast<XEvent *>(message); + if (event->xany.type == capture_event_type && + event->xany.window == capture_event_win) { + VDEBUG("QClipboard: event_filter(): caught event type %d", event->type); + has_captured_event = true; + captured_event = *event; + return true; + } + return false; +} + +static Bool checkForClipboardEvents(Display *, XEvent *e, XPointer) +{ + return ((e->type == SelectionRequest && (e->xselectionrequest.selection == XA_PRIMARY + || e->xselectionrequest.selection == ATOM(CLIPBOARD))) + || (e->type == SelectionClear && (e->xselectionclear.selection == XA_PRIMARY + || e->xselectionclear.selection == ATOM(CLIPBOARD)))); +} + +bool QX11Data::clipboardWaitForEvent(Window win, int type, XEvent *event, int timeout) +{ + QElapsedTimer started; + started.start(); + QElapsedTimer now = started; + + if (QAbstractEventDispatcher::instance()->inherits("QtMotif") + || QApplication::clipboard()->property("useEventLoopWhenWaiting").toBool()) { + if (waiting_for_data) { + Q_ASSERT(!"QClipboard: internal error, qt_xclb_wait_for_event recursed"); + return false; + } + waiting_for_data = true; + + + has_captured_event = false; + capture_event_win = win; + capture_event_type = type; + + QApplication::EventFilter old_event_filter = + qApp->setEventFilter(qt_x11_clipboard_event_filter); + + do { + if (XCheckTypedWindowEvent(display, win, type, event)) { + waiting_for_data = false; + qApp->setEventFilter(old_event_filter); + return true; + } + + XSync(X11->display, false); + usleep(50000); + + now.start(); + + QEventLoop::ProcessEventsFlags flags(QEventLoop::ExcludeUserInputEvents + | QEventLoop::ExcludeSocketNotifiers + | QEventLoop::WaitForMoreEvents + | QEventLoop::X11ExcludeTimers); + QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(); + eventDispatcher->processEvents(flags); + + if (has_captured_event) { + waiting_for_data = false; + *event = captured_event; + qApp->setEventFilter(old_event_filter); + return true; + } + } while (started.msecsTo(now) < timeout); + + waiting_for_data = false; + qApp->setEventFilter(old_event_filter); + } else { + do { + if (XCheckTypedWindowEvent(X11->display,win,type,event)) + return true; + + // process other clipboard events, since someone is probably requesting data from us + XEvent e; + if (XCheckIfEvent(X11->display, &e, checkForClipboardEvents, 0)) + qApp->x11ProcessEvent(&e); + + now.start(); + + XFlush(X11->display); + + // sleep 50 ms, so we don't use up CPU cycles all the time. + struct timeval usleep_tv; + usleep_tv.tv_sec = 0; + usleep_tv.tv_usec = 50000; + select(0, 0, 0, 0, &usleep_tv); + } while (started.msecsTo(now) < timeout); + } + return false; +} + + +static inline int maxSelectionIncr(Display *dpy) +{ return XMaxRequestSize(dpy) > 65536 ? 65536*4 : XMaxRequestSize(dpy)*4 - 100; } + +bool QX11Data::clipboardReadProperty(Window win, Atom property, bool deleteProperty, + QByteArray *buffer, int *size, Atom *type, int *format) +{ + int maxsize = maxSelectionIncr(display); + ulong bytes_left; // bytes_after + ulong length; // nitems + uchar *data; + Atom dummy_type; + int dummy_format; + int r; + + if (!type) // allow null args + type = &dummy_type; + if (!format) + format = &dummy_format; + + // Don't read anything, just get the size of the property data + r = XGetWindowProperty(display, win, property, 0, 0, False, + AnyPropertyType, type, format, + &length, &bytes_left, &data); + if (r != Success || (type && *type == XNone)) { + buffer->resize(0); + return false; + } + XFree((char*)data); + + int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left; + + VDEBUG("QClipboard: read_property(): initial property length: %d", proplen); + + switch (*format) { + case 8: + default: + format_inc = sizeof(char) / 1; + break; + + case 16: + format_inc = sizeof(short) / 2; + proplen *= sizeof(short) / 2; + break; + + case 32: + format_inc = sizeof(long) / 4; + proplen *= sizeof(long) / 4; + break; + } + + int newSize = proplen; + buffer->resize(newSize); + + bool ok = (buffer->size() == newSize); + VDEBUG("QClipboard: read_property(): buffer resized to %d", buffer->size()); + + if (ok && newSize) { + // could allocate buffer + + while (bytes_left) { + // more to read... + + r = XGetWindowProperty(display, win, property, offset, maxsize/4, + False, AnyPropertyType, type, format, + &length, &bytes_left, &data); + if (r != Success || (type && *type == XNone)) + break; + + offset += length / (32 / *format); + length *= format_inc * (*format) / 8; + + // Here we check if we get a buffer overflow and tries to + // recover -- this shouldn't normally happen, but it doesn't + // hurt to be defensive + if ((int)(buffer_offset + length) > buffer->size()) { + length = buffer->size() - buffer_offset; + + // escape loop + bytes_left = 0; + } + + memcpy(buffer->data() + buffer_offset, data, length); + buffer_offset += length; + + XFree((char*)data); + } + + if (*format == 8 && *type == ATOM(COMPOUND_TEXT)) { + // convert COMPOUND_TEXT to a multibyte string + XTextProperty textprop; + textprop.encoding = *type; + textprop.format = *format; + textprop.nitems = buffer_offset; + textprop.value = (unsigned char *) buffer->data(); + + char **list_ret = 0; + int count; + if (XmbTextPropertyToTextList(display, &textprop, &list_ret, + &count) == Success && count && list_ret) { + offset = buffer_offset = strlen(list_ret[0]); + buffer->resize(offset); + memcpy(buffer->data(), list_ret[0], offset); + } + if (list_ret) XFreeStringList(list_ret); + } + } + + // correct size, not 0-term. + if (size) + *size = buffer_offset; + + VDEBUG("QClipboard: read_property(): buffer size %d, buffer offset %d, offset %d", + buffer->size(), buffer_offset, offset); + + if (deleteProperty) + XDeleteProperty(display, win, property); + + XFlush(display); + + return ok; +} + +QByteArray QX11Data::clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm) +{ + XEvent event; + + QByteArray buf; + QByteArray tmp_buf; + bool alloc_error = false; + int length; + int offset = 0; + + if (nbytes > 0) { + // Reserve buffer + zero-terminator (for text data) + // We want to complete the INCR transfer even if we cannot + // allocate more memory + buf.resize(nbytes+1); + alloc_error = buf.size() != nbytes+1; + } + + for (;;) { + XFlush(display); + if (!clipboardWaitForEvent(win,PropertyNotify,&event,clipboard_timeout)) + break; + if (event.xproperty.atom != property || + event.xproperty.state != PropertyNewValue) + continue; + if (X11->clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0)) { + if (length == 0) { // no more data, we're done + if (nullterm) { + buf.resize(offset+1); + buf[offset] = '\0'; + } else { + buf.resize(offset); + } + return buf; + } else if (!alloc_error) { + if (offset+length > (int)buf.size()) { + buf.resize(offset+length+65535); + if (buf.size() != offset+length+65535) { + alloc_error = true; + length = buf.size() - offset; + } + } + memcpy(buf.data()+offset, tmp_buf.constData(), length); + tmp_buf.resize(0); + offset += length; + } + } else { + break; + } + } + + // timed out ... create a new requestor window, otherwise the requestor + // could consider next request to be still part of this timed out request + delete requestor; + requestor = new QWidget(0); + requestor->setObjectName(QLatin1String("internal clipboard requestor")); + // We don't need this internal widget to appear in QApplication::topLevelWidgets() + if (QWidgetPrivate::allWidgets) + QWidgetPrivate::allWidgets->remove(requestor); + + return QByteArray(); +} + +static Atom send_targets_selection(QClipboardData *d, Window window, Atom property) +{ + QVector<Atom> types; + QStringList formats = QInternalMimeData::formatsHelper(d->source()); + for (int i = 0; i < formats.size(); ++i) { + QList<Atom> atoms = X11->xdndMimeAtomsForFormat(formats.at(i)); + for (int j = 0; j < atoms.size(); ++j) { + if (!types.contains(atoms.at(j))) + types.append(atoms.at(j)); + } + } + types.append(ATOM(TARGETS)); + types.append(ATOM(MULTIPLE)); + types.append(ATOM(TIMESTAMP)); + types.append(ATOM(SAVE_TARGETS)); + + XChangeProperty(X11->display, window, property, XA_ATOM, 32, + PropModeReplace, (uchar *) types.data(), types.size()); + return property; +} + +static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom property) +{ + Atom atomFormat = target; + int dataFormat = 0; + QByteArray data; + + QByteArray fmt = X11->xdndAtomToString(target); + if (fmt.isEmpty()) { // Not a MIME type we have + DEBUG("QClipboard: send_selection(): converting to type '%s' is not supported", fmt.data()); + return XNone; + } + DEBUG("QClipboard: send_selection(): converting to type '%s'", fmt.data()); + + if (X11->xdndMimeDataForAtom(target, d->source(), &data, &atomFormat, &dataFormat)) { + + VDEBUG("QClipboard: send_selection():\n" + " property type %lx\n" + " property name '%s'\n" + " format %d\n" + " %d bytes\n", + target, X11->xdndMimeAtomToString(atomFormat).toLatin1().data(), dataFormat, data.size()); + + // don't allow INCR transfers when using MULTIPLE or to + // Motif clients (since Motif doesn't support INCR) + static Atom motif_clip_temporary = ATOM(CLIP_TEMPORARY); + bool allow_incr = property != motif_clip_temporary; + + // X_ChangeProperty protocol request is 24 bytes + const int increment = (XMaxRequestSize(X11->display) * 4) - 24; + if (data.size() > increment && allow_incr) { + long bytes = data.size(); + XChangeProperty(X11->display, window, property, + ATOM(INCR), 32, PropModeReplace, (uchar *) &bytes, 1); + + (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment); + return property; + } + + // make sure we can perform the XChangeProperty in a single request + if (data.size() > increment) + return XNone; // ### perhaps use several XChangeProperty calls w/ PropModeAppend? + int dataSize = data.size() / (dataFormat / 8); + // use a single request to transfer data + XChangeProperty(X11->display, window, property, atomFormat, + dataFormat, PropModeReplace, (uchar *) data.data(), + dataSize); + } + return property; +} + +/*! \internal + Internal cleanup for Windows. +*/ +void QClipboard::ownerDestroyed() +{ } + + +/*! \internal + Internal optimization for Windows. +*/ +void QClipboard::connectNotify(const char *) +{ } + + +bool QClipboard::event(QEvent *e) +{ + if (e->type() == QEvent::Timer) { + QTimerEvent *te = (QTimerEvent *) e; + + if (waiting_for_data) // should never happen + return false; + + if (te->timerId() == timer_id) { + killTimer(timer_id); + timer_id = 0; + + timer_event_clear = true; + if (selection_watcher) // clear selection + selectionData()->clear(); + if (clipboard_watcher) // clear clipboard + clipboardData()->clear(); + timer_event_clear = false; + + return true; + } else if (te->timerId() == pending_timer_id) { + // I hate klipper + killTimer(pending_timer_id); + pending_timer_id = 0; + + if (pending_clipboard_changed) { + pending_clipboard_changed = false; + clipboardData()->clear(); + emitChanged(QClipboard::Clipboard); + } + if (pending_selection_changed) { + pending_selection_changed = false; + selectionData()->clear(); + emitChanged(QClipboard::Selection); + } + + return true; + } else if (te->timerId() == incr_timer_id) { + killTimer(incr_timer_id); + incr_timer_id = 0; + + qt_xclb_incr_timeout(); + + return true; + } else { + return QObject::event(e); + } + } else if (e->type() != QEvent::Clipboard) { + return QObject::event(e); + } + + XEvent *xevent = (XEvent *)(((QClipboardEvent *)e)->data()); + Display *dpy = X11->display; + + if (!xevent) { + // That means application exits and we need to give clipboard + // content to the clipboard manager. + // First we check if there is a clipboard manager. + if (XGetSelectionOwner(X11->display, ATOM(CLIPBOARD_MANAGER)) == XNone + || !owner) + return true; + + Window ownerId = owner->internalWinId(); + Q_ASSERT(ownerId); + // we delete the property so the manager saves all TARGETS. + XDeleteProperty(X11->display, ownerId, ATOM(_QT_SELECTION)); + XConvertSelection(X11->display, ATOM(CLIPBOARD_MANAGER), ATOM(SAVE_TARGETS), + ATOM(_QT_SELECTION), ownerId, X11->time); + XSync(dpy, false); + + XEvent event; + // waiting until the clipboard manager fetches the content. + if (!X11->clipboardWaitForEvent(ownerId, SelectionNotify, &event, 10000)) { + qWarning("QClipboard: Unable to receive an event from the " + "clipboard manager in a reasonable time"); + } + + return true; + } + + switch (xevent->type) { + + case SelectionClear: + // new selection owner + if (xevent->xselectionclear.selection == XA_PRIMARY) { + QClipboardData *d = selectionData(); + + // ignore the event if it was generated before we gained selection ownership + if (d->timestamp != CurrentTime && xevent->xselectionclear.time <= d->timestamp) + break; + + DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)", + XGetSelectionOwner(dpy, XA_PRIMARY), + xevent->xselectionclear.time, d->timestamp); + + if (! waiting_for_data) { + d->clear(); + emitChanged(QClipboard::Selection); + } else { + pending_selection_changed = true; + if (! pending_timer_id) + pending_timer_id = QApplication::clipboard()->startTimer(0); + } + } else if (xevent->xselectionclear.selection == ATOM(CLIPBOARD)) { + QClipboardData *d = clipboardData(); + + // ignore the event if it was generated before we gained selection ownership + if (d->timestamp != CurrentTime && xevent->xselectionclear.time <= d->timestamp) + break; + + DEBUG("QClipboard: new clipboard owner 0x%lx at time %lx (%lx)", + XGetSelectionOwner(dpy, ATOM(CLIPBOARD)), + xevent->xselectionclear.time, d->timestamp); + + if (! waiting_for_data) { + d->clear(); + emitChanged(QClipboard::Clipboard); + } else { + pending_clipboard_changed = true; + if (! pending_timer_id) + pending_timer_id = QApplication::clipboard()->startTimer(0); + } + } else { + qWarning("QClipboard: Unknown SelectionClear event received"); + return false; + } + break; + + case SelectionNotify: + /* + Something has delivered data to us, but this was not caught + by QClipboardWatcher::getDataInFormat() + + Just skip the event to prevent Bad Things (tm) from + happening later on... + */ + break; + + case SelectionRequest: + { + // someone wants our data + XSelectionRequestEvent *req = &xevent->xselectionrequest; + + if (requestor && req->requestor == requestor->internalWinId()) + break; + + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = req->display; + event.xselection.requestor = req->requestor; + event.xselection.selection = req->selection; + event.xselection.target = req->target; + event.xselection.property = XNone; + event.xselection.time = req->time; + + DEBUG("QClipboard: SelectionRequest from %lx\n" + " selection 0x%lx (%s) target 0x%lx (%s)", + req->requestor, + req->selection, + X11->xdndAtomToString(req->selection).data(), + req->target, + X11->xdndAtomToString(req->target).data()); + + QClipboardData *d; + if (req->selection == XA_PRIMARY) { + d = selectionData(); + } else if (req->selection == ATOM(CLIPBOARD)) { + d = clipboardData(); + } else { + qWarning("QClipboard: Unknown selection '%lx'", req->selection); + XSendEvent(dpy, req->requestor, False, NoEventMask, &event); + break; + } + + if (! d->source()) { + qWarning("QClipboard: Cannot transfer data, no data available"); + XSendEvent(dpy, req->requestor, False, NoEventMask, &event); + break; + } + + DEBUG("QClipboard: SelectionRequest at time %lx (ours %lx)", + req->time, d->timestamp); + + if (d->timestamp == CurrentTime // we don't own the selection anymore + || (req->time != CurrentTime && req->time < d->timestamp)) { + DEBUG("QClipboard: SelectionRequest too old"); + XSendEvent(dpy, req->requestor, False, NoEventMask, &event); + break; + } + + Atom xa_targets = ATOM(TARGETS); + Atom xa_multiple = ATOM(MULTIPLE); + Atom xa_timestamp = ATOM(TIMESTAMP); + + struct AtomPair { Atom target; Atom property; } *multi = 0; + Atom multi_type = XNone; + int multi_format = 0; + int nmulti = 0; + int imulti = -1; + bool multi_writeback = false; + + if (req->target == xa_multiple) { + QByteArray multi_data; + if (req->property == XNone + || !X11->clipboardReadProperty(req->requestor, req->property, false, &multi_data, + 0, &multi_type, &multi_format) + || multi_format != 32) { + // MULTIPLE property not formatted correctly + XSendEvent(dpy, req->requestor, False, NoEventMask, &event); + break; + } + nmulti = multi_data.size()/sizeof(*multi); + multi = new AtomPair[nmulti]; + memcpy(multi,multi_data.data(),multi_data.size()); + imulti = 0; + } + + for (; imulti < nmulti; ++imulti) { + Atom target; + Atom property; + + if (multi) { + target = multi[imulti].target; + property = multi[imulti].property; + } else { + target = req->target; + property = req->property; + if (property == XNone) // obsolete client + property = target; + } + + Atom ret = XNone; + if (target == XNone || property == XNone) { + ; + } else if (target == xa_timestamp) { + if (d->timestamp != CurrentTime) { + XChangeProperty(dpy, req->requestor, property, XA_INTEGER, 32, + PropModeReplace, (uchar *) &d->timestamp, 1); + ret = property; + } else { + qWarning("QClipboard: Invalid data timestamp"); + } + } else if (target == xa_targets) { + ret = send_targets_selection(d, req->requestor, property); + } else { + ret = send_selection(d, target, req->requestor, property); + } + + if (nmulti > 0) { + if (ret == XNone) { + multi[imulti].property = XNone; + multi_writeback = true; + } + } else { + event.xselection.property = ret; + break; + } + } + + if (nmulti > 0) { + if (multi_writeback) { + // according to ICCCM 2.6.2 says to put None back + // into the original property on the requestor window + XChangeProperty(dpy, req->requestor, req->property, multi_type, 32, + PropModeReplace, (uchar *) multi, nmulti * 2); + } + + delete [] multi; + event.xselection.property = req->property; + } + + // send selection notify to requestor + XSendEvent(dpy, req->requestor, False, NoEventMask, &event); + + DEBUG("QClipboard: SelectionNotify to 0x%lx\n" + " property 0x%lx (%s)", + req->requestor, event.xselection.property, + X11->xdndAtomToString(event.xselection.property).data()); + } + break; + } + + return true; +} + + + + + + +QClipboardWatcher::QClipboardWatcher(QClipboard::Mode mode) + : QInternalMimeData() +{ + switch (mode) { + case QClipboard::Selection: + atom = XA_PRIMARY; + break; + + case QClipboard::Clipboard: + atom = ATOM(CLIPBOARD); + break; + + default: + qWarning("QClipboardWatcher: Internal error: Unsupported clipboard mode"); + break; + } + + setupOwner(); +} + +QClipboardWatcher::~QClipboardWatcher() +{ + if(selection_watcher == this) + selection_watcher = 0; + if(clipboard_watcher == this) + clipboard_watcher = 0; +} + +bool QClipboardWatcher::empty() const +{ + Display *dpy = X11->display; + Window win = XGetSelectionOwner(dpy, atom); + + if(win == requestor->internalWinId()) { + qWarning("QClipboardWatcher::empty: Internal error: Application owns the selection"); + return true; + } + + return win == XNone; +} + +QStringList QClipboardWatcher::formats_sys() const +{ + if (empty()) + return QStringList(); + + if (!formatList.count()) { + // get the list of targets from the current clipboard owner - we do this + // once so that multiple calls to this function don't require multiple + // server round trips... + + format_atoms = getDataInFormat(ATOM(TARGETS)); + + if (format_atoms.size() > 0) { + Atom *targets = (Atom *) format_atoms.data(); + int size = format_atoms.size() / sizeof(Atom); + + for (int i = 0; i < size; ++i) { + if (targets[i] == 0) + continue; + + QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(targets[i]); + for (int j = 0; j < formatsForAtom.size(); ++j) { + if (!formatList.contains(formatsForAtom.at(j))) + formatList.append(formatsForAtom.at(j)); + } + VDEBUG(" format: %s", X11->xdndAtomToString(targets[i]).data()); + VDEBUG(" data:\n%s\n", getDataInFormat(targets[i]).data()); + } + DEBUG("QClipboardWatcher::format: %d formats available", formatList.count()); + } + } + + return formatList; +} + +bool QClipboardWatcher::hasFormat_sys(const QString &format) const +{ + QStringList list = formats(); + return list.contains(format); +} + +QVariant QClipboardWatcher::retrieveData_sys(const QString &fmt, QVariant::Type requestedType) const +{ + if (fmt.isEmpty() || empty()) + return QByteArray(); + + (void)formats(); // trigger update of format list + DEBUG("QClipboardWatcher::data: fetching format '%s'", fmt.toLatin1().data()); + + QList<Atom> atoms; + Atom *targets = (Atom *) format_atoms.data(); + int size = format_atoms.size() / sizeof(Atom); + for (int i = 0; i < size; ++i) + atoms.append(targets[i]); + + QByteArray encoding; + Atom fmtatom = X11->xdndMimeAtomForFormat(fmt, requestedType, atoms, &encoding); + + if (fmtatom == 0) + return QVariant(); + + return X11->xdndMimeConvertToFormat(fmtatom, getDataInFormat(fmtatom), fmt, requestedType, encoding); +} + +QByteArray QClipboardWatcher::getDataInFormat(Atom fmtatom) const +{ + QByteArray buf; + + Display *dpy = X11->display; + requestor->createWinId(); + Window win = requestor->internalWinId(); + Q_ASSERT(requestor->testAttribute(Qt::WA_WState_Created)); + + DEBUG("QClipboardWatcher::getDataInFormat: selection '%s' format '%s'", + X11->xdndAtomToString(atom).data(), X11->xdndAtomToString(fmtatom).data()); + + XSelectInput(dpy, win, NoEventMask); // don't listen for any events + + XDeleteProperty(dpy, win, ATOM(_QT_SELECTION)); + XConvertSelection(dpy, atom, fmtatom, ATOM(_QT_SELECTION), win, X11->time); + XSync(dpy, false); + + VDEBUG("QClipboardWatcher::getDataInFormat: waiting for SelectionNotify event"); + + XEvent xevent; + if (!X11->clipboardWaitForEvent(win,SelectionNotify,&xevent,clipboard_timeout) || + xevent.xselection.property == XNone) { + DEBUG("QClipboardWatcher::getDataInFormat: format not available"); + return buf; + } + + VDEBUG("QClipboardWatcher::getDataInFormat: fetching data..."); + + Atom type; + XSelectInput(dpy, win, PropertyChangeMask); + + if (X11->clipboardReadProperty(win, ATOM(_QT_SELECTION), true, &buf, 0, &type, 0)) { + if (type == ATOM(INCR)) { + int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0; + buf = X11->clipboardReadIncrementalProperty(win, ATOM(_QT_SELECTION), nbytes, false); + } + } + + XSelectInput(dpy, win, NoEventMask); + + DEBUG("QClipboardWatcher::getDataInFormat: %d bytes received", buf.size()); + + return buf; +} + + +const QMimeData* QClipboard::mimeData(Mode mode) const +{ + QClipboardData *d = 0; + switch (mode) { + case Selection: + d = selectionData(); + break; + case Clipboard: + d = clipboardData(); + break; + default: + qWarning("QClipboard::mimeData: unsupported mode '%d'", mode); + return 0; + } + + if (! d->source() && ! timer_event_clear) { + if (mode == Selection) { + if (! selection_watcher) + selection_watcher = new QClipboardWatcher(mode); + d->setSource(selection_watcher); + } else { + if (! clipboard_watcher) + clipboard_watcher = new QClipboardWatcher(mode); + d->setSource(clipboard_watcher); + } + + if (! timer_id) { + // start a zero timer - we will clear cached data when the timer + // times out, which will be the next time we hit the event loop... + // that way, the data is cached long enough for calls within a single + // loop/function, but the data doesn't linger around in case the selection + // changes + QClipboard *that = ((QClipboard *) this); + timer_id = that->startTimer(0); + } + } + + return d->source(); +} + + +void QClipboard::setMimeData(QMimeData* src, Mode mode) +{ + Atom atom, sentinel_atom; + QClipboardData *d; + switch (mode) { + case Selection: + atom = XA_PRIMARY; + sentinel_atom = ATOM(_QT_SELECTION_SENTINEL); + d = selectionData(); + break; + + case Clipboard: + atom = ATOM(CLIPBOARD); + sentinel_atom = ATOM(_QT_CLIPBOARD_SENTINEL); + d = clipboardData(); + break; + + default: + qWarning("QClipboard::setMimeData: unsupported mode '%d'", mode); + return; + } + + Display *dpy = X11->display; + Window newOwner; + + if (! src) { // no data, clear clipboard contents + newOwner = XNone; + d->clear(); + } else { + setupOwner(); + + newOwner = owner->internalWinId(); + + d->setSource(src); + d->timestamp = X11->time; + } + + Window prevOwner = XGetSelectionOwner(dpy, atom); + // use X11->time, since d->timestamp == CurrentTime when clearing + XSetSelectionOwner(dpy, atom, newOwner, X11->time); + + if (mode == Selection) + emitChanged(QClipboard::Selection); + else + emitChanged(QClipboard::Clipboard); + + if (XGetSelectionOwner(dpy, atom) != newOwner) { + qWarning("QClipboard::setData: Cannot set X11 selection owner for %s", + X11->xdndAtomToString(atom).data()); + d->clear(); + return; + } + + // Signal to other Qt processes that the selection has changed + Window owners[2]; + owners[0] = newOwner; + owners[1] = prevOwner; + XChangeProperty(dpy, QApplication::desktop()->screen(0)->internalWinId(), + sentinel_atom, XA_WINDOW, 32, PropModeReplace, + (unsigned char*)&owners, 2); +} + + +/* + Called by the main event loop in qapplication_x11.cpp when either + the _QT_SELECTION_SENTINEL property has been changed (i.e. when some + Qt process has performed QClipboard::setData()) or when Xfixes told + us that an other application changed the selection. If it returns + true, the QClipBoard dataChanged() signal should be emitted. +*/ + +bool qt_check_selection_sentinel() +{ + bool doIt = true; + if (owner && !X11->use_xfixes) { + /* + Since the X selection mechanism cannot give any signal when + the selection has changed, we emulate it (for Qt processes) here. + The notification should be ignored in case of either + a) This is the process that did setData (because setData() + then has already emitted dataChanged()) + b) This is the process that owned the selection when dataChanged() + was called (because we have then received a SelectionClear event, + and have already emitted dataChanged() as a result of that) + */ + + unsigned char *retval; + Atom actualType; + int actualFormat; + ulong nitems; + ulong bytesLeft; + + if (XGetWindowProperty(X11->display, + QApplication::desktop()->screen(0)->internalWinId(), + ATOM(_QT_SELECTION_SENTINEL), 0, 2, False, XA_WINDOW, + &actualType, &actualFormat, &nitems, + &bytesLeft, &retval) == Success) { + Window *owners = (Window *)retval; + if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) { + Window win = owner->internalWinId(); + if (owners[0] == win || owners[1] == win) + doIt = false; + } + + XFree(owners); + } + } + + if (doIt) { + if (waiting_for_data) { + pending_selection_changed = true; + if (! pending_timer_id) + pending_timer_id = QApplication::clipboard()->startTimer(0); + doIt = false; + } else { + selectionData()->clear(); + } + } + + return doIt; +} + + +bool qt_check_clipboard_sentinel() +{ + bool doIt = true; + if (owner && !X11->use_xfixes) { + unsigned char *retval; + Atom actualType; + int actualFormat; + unsigned long nitems, bytesLeft; + + if (XGetWindowProperty(X11->display, + QApplication::desktop()->screen(0)->internalWinId(), + ATOM(_QT_CLIPBOARD_SENTINEL), 0, 2, False, XA_WINDOW, + &actualType, &actualFormat, &nitems, &bytesLeft, + &retval) == Success) { + Window *owners = (Window *)retval; + if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) { + Window win = owner->internalWinId(); + if (owners[0] == win || owners[1] == win) + doIt = false; + } + + XFree(owners); + } + } + + if (doIt) { + if (waiting_for_data) { + pending_clipboard_changed = true; + if (! pending_timer_id) + pending_timer_id = QApplication::clipboard()->startTimer(0); + doIt = false; + } else { + clipboardData()->clear(); + } + } + + return doIt; +} + +bool qt_xfixes_selection_changed(Window selectionOwner, Time timestamp) +{ + QClipboardData *d = selectionData(); +#ifdef QCLIPBOARD_DEBUG + DEBUG("qt_xfixes_selection_changed: owner = %u; selectionOwner = %u; internal timestamp = %u; external timestamp = %u", + (unsigned int)(owner ? (int)owner->internalWinId() : 0), (unsigned int)selectionOwner, + (unsigned int)(d ? d->timestamp : 0), (unsigned int)timestamp); +#endif + if (!owner || (selectionOwner && selectionOwner != owner->internalWinId()) || + (!selectionOwner && (d->timestamp == CurrentTime || d->timestamp < timestamp))) + return qt_check_selection_sentinel(); + return false; +} + +bool qt_xfixes_clipboard_changed(Window clipboardOwner, Time timestamp) +{ + QClipboardData *d = clipboardData(); +#ifdef QCLIPBOARD_DEBUG + DEBUG("qt_xfixes_clipboard_changed: owner = %u; clipboardOwner = %u; internal timestamp = %u; external timestamp = %u", + (unsigned int)(owner ? (int)owner->internalWinId() : 0), (unsigned int)clipboardOwner, + (unsigned int)(d ? d->timestamp : 0), (unsigned int)timestamp); +#endif + if (!owner || (clipboardOwner && clipboardOwner != owner->internalWinId()) || + (!clipboardOwner && (d->timestamp == CurrentTime || d->timestamp < timestamp))) + return qt_check_clipboard_sentinel(); + return false; +} + +QT_END_NAMESPACE + +#endif // QT_NO_CLIPBOARD diff --git a/src/gui/guikernel/qcursor.cpp b/src/gui/guikernel/qcursor.cpp new file mode 100644 index 0000000000..823f6f1e8c --- /dev/null +++ b/src/gui/guikernel/qcursor.cpp @@ -0,0 +1,573 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcursor.h" + +#ifndef QT_NO_CURSOR + +#include <qapplication.h> +#include <qbitmap.h> +#include <qimage.h> +#include <qdatastream.h> +#include <qvariant.h> +#include <private/qcursor_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QCursor + + \brief The QCursor class provides a mouse cursor with an arbitrary + shape. + + \ingroup appearance + \ingroup shared + + + This class is mainly used to create mouse cursors that are + associated with particular widgets and to get and set the position + of the mouse cursor. + + Qt has a number of standard cursor shapes, but you can also make + custom cursor shapes based on a QBitmap, a mask and a hotspot. + + To associate a cursor with a widget, use QWidget::setCursor(). To + associate a cursor with all widgets (normally for a short period + of time), use QApplication::setOverrideCursor(). + + To set a cursor shape use QCursor::setShape() or use the QCursor + constructor which takes the shape as argument, or you can use one + of the predefined cursors defined in the \l Qt::CursorShape enum. + + If you want to create a cursor with your own bitmap, either use + the QCursor constructor which takes a bitmap and a mask or the + constructor which takes a pixmap as arguments. + + To set or get the position of the mouse cursor use the static + methods QCursor::pos() and QCursor::setPos(). + + \bold{Note:} It is possible to create a QCursor before + QApplication, but it is not useful except as a place-holder for a + real QCursor created after QApplication. Attempting to use a + QCursor that was created before QApplication will result in a + crash. + + \section1 A Note for X11 Users + + On X11, Qt supports the \link + http://www.xfree86.org/4.3.0/Xcursor.3.html Xcursor\endlink + library, which allows for full color icon themes. The table below + shows the cursor name used for each Qt::CursorShape value. If a + cursor cannot be found using the name shown below, a standard X11 + cursor will be used instead. Note: X11 does not provide + appropriate cursors for all possible Qt::CursorShape values. It + is possible that some cursors will be taken from the Xcursor + theme, while others will use an internal bitmap cursor. + + \table + \header \o Shape \o Qt::CursorShape Value \o Cursor Name + \o Shape \o Qt::CursorShape Value \o Cursor Name + \row \o \inlineimage cursor-arrow.png + \o Qt::ArrowCursor \o \c left_ptr + \o \inlineimage cursor-sizev.png + \o Qt::SizeVerCursor \o \c size_ver + \row \o \inlineimage cursor-uparrow.png + \o Qt::UpArrowCursor \o \c up_arrow + \o \inlineimage cursor-sizeh.png + \o Qt::SizeHorCursor \o \c size_hor + \row \o \inlineimage cursor-cross.png + \o Qt::CrossCursor \o \c cross + \o \inlineimage cursor-sizeb.png + \o Qt::SizeBDiagCursor \o \c size_bdiag + \row \o \inlineimage cursor-ibeam.png + \o Qt::IBeamCursor \o \c ibeam + \o \inlineimage cursor-sizef.png + \o Qt::SizeFDiagCursor \o \c size_fdiag + \row \o \inlineimage cursor-wait.png + \o Qt::WaitCursor \o \c wait + \o \inlineimage cursor-sizeall.png + \o Qt::SizeAllCursor \o \c size_all + \row \o \inlineimage cursor-busy.png + \o Qt::BusyCursor \o \c left_ptr_watch + \o \inlineimage cursor-vsplit.png + \o Qt::SplitVCursor \o \c split_v + \row \o \inlineimage cursor-forbidden.png + \o Qt::ForbiddenCursor \o \c forbidden + \o \inlineimage cursor-hsplit.png + \o Qt::SplitHCursor \o \c split_h + \row \o \inlineimage cursor-hand.png + \o Qt::PointingHandCursor \o \c pointing_hand + \o \inlineimage cursor-openhand.png + \o Qt::OpenHandCursor \o \c openhand + \row \o \inlineimage cursor-whatsthis.png + \o Qt::WhatsThisCursor \o \c whats_this + \o \inlineimage cursor-closedhand.png + \o Qt::ClosedHandCursor \o \c closedhand + \row \o + \o Qt::DragMoveCursor \o \c dnd-move or \c move + \o + \o Qt::DragCopyCursor \o \c dnd-copy or \c copy + \row \o + \o Qt::DragLinkCursor \o \c dnd-link or \c link + \endtable + + \sa QWidget, {fowler}{GUI Design Handbook: Cursors} +*/ + +/*! + \fn HCURSOR_or_HANDLE QCursor::handle() const + + Returns a platform-specific cursor handle. The \c + HCURSOR_or_HANDLE type is \c HCURSOR on Windows and Qt::HANDLE on X11 + and Mac OS X. On \l{Qt for Embedded Linux} it is an integer. + + \warning Using the value returned by this function is not + portable. +*/ + +/*! + \fn QCursor::QCursor(HCURSOR cursor) + + Constructs a Qt cursor from the given Windows \a cursor. + + \warning This function is only available on Windows. + + \sa handle() +*/ + +/*! + \fn QCursor::QCursor(Qt::HANDLE handle) + + Constructs a Qt cursor from the given \a handle. + + \warning This function is only available on X11. + + \sa handle() +*/ + +/*! + \fn QPoint QCursor::pos() + + Returns the position of the cursor (hot spot) in global screen + coordinates. + + You can call QWidget::mapFromGlobal() to translate it to widget + coordinates. + + \sa setPos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal() +*/ + +/*! + \fn void QCursor::setPos(int x, int y) + + Moves the cursor (hot spot) to the global screen position (\a x, + \a y). + + You can call QWidget::mapToGlobal() to translate widget + coordinates to global screen coordinates. + + \sa pos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal() +*/ + +/*! + \fn void QCursor::setPos (const QPoint &p) + + \overload + + Moves the cursor (hot spot) to the global screen position at point + \a p. +*/ + +/***************************************************************************** + QCursor stream functions + *****************************************************************************/ + +#ifndef QT_NO_DATASTREAM + + +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QCursor &cursor) + \relates QCursor + + Writes the \a cursor to the \a stream. + + \sa {Serializing Qt Data Types} +*/ + +QDataStream &operator<<(QDataStream &s, const QCursor &c) +{ + s << (qint16)c.shape(); // write shape id to stream + if (c.shape() == Qt::BitmapCursor) { // bitmap cursor + bool isPixmap = false; + if (s.version() >= 7) { + isPixmap = !c.pixmap().isNull(); + s << isPixmap; + } + if (isPixmap) + s << c.pixmap(); + else + s << *c.bitmap() << *c.mask(); + s << c.hotSpot(); + } + return s; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QCursor &cursor) + \relates QCursor + + Reads the \a cursor from the \a stream. + + \sa {Serializing Qt Data Types} +*/ + +QDataStream &operator>>(QDataStream &s, QCursor &c) +{ + qint16 shape; + s >> shape; // read shape id from stream + if (shape == Qt::BitmapCursor) { // read bitmap cursor + bool isPixmap = false; + if (s.version() >= 7) + s >> isPixmap; + if (isPixmap) { + QPixmap pm; + QPoint hot; + s >> pm >> hot; + c = QCursor(pm, hot.x(), hot.y()); + } else { + QBitmap bm, bmm; + QPoint hot; + s >> bm >> bmm >> hot; + c = QCursor(bm, bmm, hot.x(), hot.y()); + } + } else { + c.setShape((Qt::CursorShape)shape); // create cursor with shape + } + return s; +} +#endif // QT_NO_DATASTREAM + + +/*! + Constructs a custom pixmap cursor. + + \a pixmap is the image. It is usual to give it a mask (set using + QPixmap::setMask()). \a hotX and \a hotY define the cursor's hot + spot. + + If \a hotX is negative, it is set to the \c{pixmap().width()/2}. + If \a hotY is negative, it is set to the \c{pixmap().height()/2}. + + Valid cursor sizes depend on the display hardware (or the + underlying window system). We recommend using 32 x 32 cursors, + because this size is supported on all platforms. Some platforms + also support 16 x 16, 48 x 48, and 64 x 64 cursors. + + \note On Windows CE, the cursor size is fixed. If the pixmap + is bigger than the system size, it will be scaled. + + \sa QPixmap::QPixmap(), QPixmap::setMask() +*/ + +QCursor::QCursor(const QPixmap &pixmap, int hotX, int hotY) + : d(0) +{ + QImage img = pixmap.toImage().convertToFormat(QImage::Format_Indexed8, Qt::ThresholdDither|Qt::AvoidDither); + QBitmap bm = QBitmap::fromImage(img, Qt::ThresholdDither|Qt::AvoidDither); + QBitmap bmm = pixmap.mask(); + if (!bmm.isNull()) { + QBitmap nullBm; + bm.setMask(nullBm); + } + else if (!pixmap.mask().isNull()) { + QImage mimg = pixmap.mask().toImage().convertToFormat(QImage::Format_Indexed8, Qt::ThresholdDither|Qt::AvoidDither); + bmm = QBitmap::fromImage(mimg, Qt::ThresholdDither|Qt::AvoidDither); + } + else { + bmm = QBitmap(bm.size()); + bmm.fill(Qt::color1); + } + + d = QCursorData::setBitmap(bm, bmm, hotX, hotY); + d->pixmap = pixmap; +} + + + +/*! + Constructs a custom bitmap cursor. + + \a bitmap and + \a mask make up the bitmap. + \a hotX and + \a hotY define the cursor's hot spot. + + If \a hotX is negative, it is set to the \c{bitmap().width()/2}. + If \a hotY is negative, it is set to the \c{bitmap().height()/2}. + + The cursor \a bitmap (B) and \a mask (M) bits are combined like this: + \list + \o B=1 and M=1 gives black. + \o B=0 and M=1 gives white. + \o B=0 and M=0 gives transparent. + \o B=1 and M=0 gives an XOR'd result under Windows, undefined + results on all other platforms. + \endlist + + Use the global Qt color Qt::color0 to draw 0-pixels and Qt::color1 to + draw 1-pixels in the bitmaps. + + Valid cursor sizes depend on the display hardware (or the + underlying window system). We recommend using 32 x 32 cursors, + because this size is supported on all platforms. Some platforms + also support 16 x 16, 48 x 48, and 64 x 64 cursors. + + \note On Windows CE, the cursor size is fixed. If the pixmap + is bigger than the system size, it will be scaled. + + \sa QBitmap::QBitmap(), QBitmap::setMask() +*/ + +QCursor::QCursor(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) + : d(0) +{ + d = QCursorData::setBitmap(bitmap, mask, hotX, hotY); +} + +QCursorData *qt_cursorTable[Qt::LastCursor + 1]; +bool QCursorData::initialized = false; + +/*! \internal */ +void QCursorData::cleanup() +{ + if(!QCursorData::initialized) + return; + + for (int shape = 0; shape <= Qt::LastCursor; ++shape) { + // In case someone has a static QCursor defined with this shape + if (!qt_cursorTable[shape]->ref.deref()) + delete qt_cursorTable[shape]; + qt_cursorTable[shape] = 0; + } + QCursorData::initialized = false; +} + +/*! \internal */ +void QCursorData::initialize() +{ + if (QCursorData::initialized) + return; +#ifdef Q_WS_MAC + // DRSWAT - Not Needed Cocoa or Carbon + //InitCursor(); +#endif + for (int shape = 0; shape <= Qt::LastCursor; ++shape) + qt_cursorTable[shape] = new QCursorData((Qt::CursorShape)shape); + QCursorData::initialized = true; +} + +/*! + Constructs a cursor with the default arrow shape. +*/ +QCursor::QCursor() +{ + if (!QCursorData::initialized) { + if (QApplication::startingUp()) { + d = 0; + return; + } + QCursorData::initialize(); + } + QCursorData *c = qt_cursorTable[0]; + c->ref.ref(); + d = c; +} + +/*! + Constructs a cursor with the specified \a shape. + + See \l Qt::CursorShape for a list of shapes. + + \sa setShape() +*/ +QCursor::QCursor(Qt::CursorShape shape) + : d(0) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + setShape(shape); +} + + +/*! + Returns the cursor shape identifier. The return value is one of + the \l Qt::CursorShape enum values (cast to an int). + + \sa setShape() +*/ +Qt::CursorShape QCursor::shape() const +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + return d->cshape; +} + +/*! + Sets the cursor to the shape identified by \a shape. + + See \l Qt::CursorShape for the list of cursor shapes. + + \sa shape() +*/ +void QCursor::setShape(Qt::CursorShape shape) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + QCursorData *c = uint(shape) <= Qt::LastCursor ? qt_cursorTable[shape] : 0; + if (!c) + c = qt_cursorTable[0]; + c->ref.ref(); + if (!d) { + d = c; + } else { + if (!d->ref.deref()) + delete d; + d = c; + } +} + +/*! + Returns the cursor bitmap, or 0 if it is one of the standard + cursors. +*/ +const QBitmap *QCursor::bitmap() const +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + return d->bm; +} + +/*! + Returns the cursor bitmap mask, or 0 if it is one of the standard + cursors. +*/ + +const QBitmap *QCursor::mask() const +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + return d->bmm; +} + +/*! + Returns the cursor pixmap. This is only valid if the cursor is a + pixmap cursor. +*/ + +QPixmap QCursor::pixmap() const +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + return d->pixmap; +} + +/*! + Returns the cursor hot spot, or (0, 0) if it is one of the + standard cursors. +*/ + +QPoint QCursor::hotSpot() const +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + return QPoint(d->hx, d->hy); +} + +/*! + Constructs a copy of the cursor \a c. +*/ + +QCursor::QCursor(const QCursor &c) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + d = c.d; + d->ref.ref(); +} + +/*! + Destroys the cursor. +*/ + +QCursor::~QCursor() +{ + if (d && !d->ref.deref()) + delete d; +} + + +/*! + Assigns \a c to this cursor and returns a reference to this + cursor. +*/ + +QCursor &QCursor::operator=(const QCursor &c) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (c.d) + c.d->ref.ref(); + if (d && !d->ref.deref()) + delete d; + d = c.d; + return *this; +} + +/*! + Returns the cursor as a QVariant. +*/ +QCursor::operator QVariant() const +{ + return QVariant(QVariant::Cursor, this); +} +QT_END_NAMESPACE +#endif // QT_NO_CURSOR + diff --git a/src/gui/guikernel/qcursor.h b/src/gui/guikernel/qcursor.h new file mode 100644 index 0000000000..b47ec04d66 --- /dev/null +++ b/src/gui/guikernel/qcursor.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCURSOR_H +#define QCURSOR_H + +#include <QtCore/qpoint.h> +#include <QtGui/qwindowdefs.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QVariant; + +/* + ### The fake cursor has to go first with old qdoc. +*/ +#ifdef QT_NO_CURSOR + +class Q_GUI_EXPORT QCursor +{ +public: + static QPoint pos(); + static void setPos(int x, int y); + inline static void setPos(const QPoint &p) { setPos(p.x(), p.y()); } +private: + QCursor(); +}; + +#endif // QT_NO_CURSOR + +#ifndef QT_NO_CURSOR + +class QCursorData; +class QBitmap; +class QPixmap; + +#if defined(Q_WS_MAC) +void qt_mac_set_cursor(const QCursor *c); +#endif +#if defined(Q_OS_SYMBIAN) +extern void qt_symbian_show_pointer_sprite(); +extern void qt_symbian_hide_pointer_sprite(); +extern void qt_symbian_set_pointer_sprite(const QCursor& cursor); +extern void qt_symbian_move_cursor_sprite(); +#endif + +class Q_GUI_EXPORT QCursor +{ +public: + QCursor(); + QCursor(Qt::CursorShape shape); + QCursor(const QBitmap &bitmap, const QBitmap &mask, int hotX=-1, int hotY=-1); + QCursor(const QPixmap &pixmap, int hotX=-1, int hotY=-1); + QCursor(const QCursor &cursor); + ~QCursor(); + QCursor &operator=(const QCursor &cursor); +#ifdef Q_COMPILER_RVALUE_REFS + inline QCursor &operator=(QCursor &&other) + { qSwap(d, other.d); return *this; } +#endif + operator QVariant() const; + + Qt::CursorShape shape() const; + void setShape(Qt::CursorShape newShape); + + const QBitmap *bitmap() const; + const QBitmap *mask() const; + QPixmap pixmap() const; + QPoint hotSpot() const; + + static QPoint pos(); + static void setPos(int x, int y); + inline static void setPos(const QPoint &p) { setPos(p.x(), p.y()); } + +#ifdef qdoc + HCURSOR_or_HANDLE handle() const; + QCursor(HCURSOR cursor); + QCursor(Qt::HANDLE cursor); +#endif + +#ifndef qdoc +#if defined(Q_WS_WIN) + HCURSOR handle() const; + QCursor(HCURSOR cursor); +#elif defined(Q_WS_X11) + Qt::HANDLE handle() const; + QCursor(Qt::HANDLE cursor); + static int x11Screen(); +#elif defined(Q_WS_MAC) + Qt::HANDLE handle() const; +#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) + int handle() const; +#elif defined(Q_OS_SYMBIAN) + Qt::HANDLE handle() const; +#endif +#endif + +private: + QCursorData *d; +#if defined(Q_WS_MAC) + friend void *qt_mac_nsCursorForQCursor(const QCursor &c); + friend void qt_mac_set_cursor(const QCursor *c); + friend void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); +#endif +#if defined(Q_OS_SYMBIAN) + friend void qt_symbian_show_pointer_sprite(); + friend void qt_symbian_hide_pointer_sprite(); + friend void qt_symbian_set_pointer_sprite(const QCursor& cursor); + friend void qt_symbian_move_cursor_sprite(); +#endif +}; + +/***************************************************************************** + QCursor stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +Q_GUI_EXPORT QDataStream &operator<<(QDataStream &outS, const QCursor &cursor); +Q_GUI_EXPORT QDataStream &operator>>(QDataStream &inS, QCursor &cursor); +#endif +#endif // QT_NO_CURSOR + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QCURSOR_H diff --git a/src/gui/guikernel/qcursor_mac.mm b/src/gui/guikernel/qcursor_mac.mm new file mode 100644 index 0000000000..0afa3ee4f0 --- /dev/null +++ b/src/gui/guikernel/qcursor_mac.mm @@ -0,0 +1,689 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qcursor_p.h> +#include <private/qpixmap_mac_p.h> +#include <qapplication.h> +#include <qbitmap.h> +#include <qcursor.h> +#include <qevent.h> +#include <string.h> +#include <unistd.h> +#include <AppKit/NSCursor.h> +#include <qpainter.h> +#include <private/qt_cocoa_helpers_mac_p.h> +#include <private/qapplication_p.h> + +QT_BEGIN_NAMESPACE + +/***************************************************************************** + Externals + *****************************************************************************/ +extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; +extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp +extern GrafPtr qt_mac_qd_context(const QPaintDevice *); //qpaintdevice_mac.cpp +extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_mac.cpp +extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp + +/***************************************************************************** + Internal QCursorData class + *****************************************************************************/ + +class QMacAnimateCursor : public QObject +{ + int timerId, step; + ThemeCursor curs; +public: + QMacAnimateCursor() : QObject(), timerId(-1) { } + void start(ThemeCursor c) { + step = 1; + if(timerId != -1) + killTimer(timerId); + timerId = startTimer(300); + curs = c; + } + void stop() { + if(timerId != -1) { + killTimer(timerId); + timerId = -1; + } + } +protected: + void timerEvent(QTimerEvent *e) { + if(e->timerId() == timerId) { + /* + if(SetAnimatedThemeCursor(curs, step++) == themeBadCursorIndexErr) + stop(); + */ + } + } +}; + +inline void *qt_mac_nsCursorForQCursor(const QCursor &c) +{ + c.d->update(); + return [[static_cast<NSCursor *>(c.d->curs.cp.nscursor) retain] autorelease]; +} + +static QCursorData *currentCursor = 0; //current cursor + +void qt_mac_set_cursor(const QCursor *c) +{ +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*c)) set]; +#else + if (!c) { + currentCursor = 0; + return; + } + c->handle(); //force the cursor to get loaded, if it's not + + if(currentCursor && currentCursor->type == QCursorData::TYPE_ThemeCursor + && currentCursor->curs.tc.anim) + currentCursor->curs.tc.anim->stop(); + if(c->d->type == QCursorData::TYPE_ImageCursor) { + [static_cast<NSCursor *>(c->d->curs.cp.nscursor) set]; + } else if(c->d->type == QCursorData::TYPE_ThemeCursor) { + if(SetAnimatedThemeCursor(c->d->curs.tc.curs, 0) == themeBadCursorIndexErr) { + SetThemeCursor(c->d->curs.tc.curs); + } else { + if(!c->d->curs.tc.anim) + c->d->curs.tc.anim = new QMacAnimateCursor; + c->d->curs.tc.anim->start(c->d->curs.tc.curs); + } + } + + currentCursor = c->d; +#endif +} + +static QPointer<QWidget> lastWidgetUnderMouse = 0; +static QPointer<QWidget> lastMouseCursorWidget = 0; +static bool qt_button_down_on_prev_call = false; +static QCursor *grabCursor = 0; + +void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse) +{ + QCursor cursor(Qt::ArrowCursor); + if (qt_button_down) { + // The widget that is currently pressed + // grabs the mouse cursor: + widgetUnderMouse = qt_button_down; + qt_button_down_on_prev_call = true; + } else if (qt_button_down_on_prev_call) { + // Grab has been released, so do + // a full check: + qt_button_down_on_prev_call = false; + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + } + + if (QApplication::overrideCursor()) { + cursor = *QApplication::overrideCursor(); + } else if (grabCursor) { + cursor = *grabCursor; + } else if (widgetUnderMouse) { + if (widgetUnderMouse == lastWidgetUnderMouse) { + // Optimization that should hit when the widget under + // the mouse does not change as the mouse moves: + if (lastMouseCursorWidget) + cursor = lastMouseCursorWidget->cursor(); + } else { + QWidget *w = widgetUnderMouse; + for (; w; w = w->parentWidget()) { + if (w->testAttribute(Qt::WA_SetCursor)) { + cursor = w->cursor(); + break; + } + if (w->isWindow()) + break; + } + // One final check in case we ran out of parents in the loop: + if (w && !w->testAttribute(Qt::WA_SetCursor)) + w = 0; + + lastWidgetUnderMouse = widgetUnderMouse; + lastMouseCursorWidget = w; + } + } + +#ifdef QT_MAC_USE_COCOA + cursor.d->update(); + NSCursor *nsCursor = static_cast<NSCursor *>(cursor.d->curs.cp.nscursor); + if ([NSCursor currentCursor] != nsCursor) { + QMacCocoaAutoReleasePool pool; + [nsCursor set]; + } +#else + qt_mac_set_cursor(&cursor); +#endif +} + +void qt_mac_update_cursor() +{ + // This function is similar to qt_mac_updateCursorWithWidgetUnderMouse + // except that is clears the optimization cache, and finds the widget + // under mouse itself. Clearing the cache is useful in cases where the + // application has been deactivated/activated etc. + // NB: since we dont have any true native widget, the call to + // qt_mac_getTargetForMouseEvent will fail when the mouse is over QMacNativeWidgets. +#ifdef QT_MAC_USE_COCOA + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + QWidget *widgetUnderMouse = 0; + + if (qt_button_down) { + widgetUnderMouse = qt_button_down; + } else { + QPoint localPoint; + QPoint globalPoint; + qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, 0, &widgetUnderMouse); + } + qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); +#else + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(QCursor::pos())); +#endif +} + +void qt_mac_setMouseGrabCursor(bool set, QCursor *const cursor = 0) +{ + if (grabCursor) { + delete grabCursor; + grabCursor = 0; + } + if (set) { + if (cursor) + grabCursor = new QCursor(*cursor); + else if (lastMouseCursorWidget) + grabCursor = new QCursor(lastMouseCursorWidget->cursor()); + else + grabCursor = new QCursor(Qt::ArrowCursor); + } + qt_mac_update_cursor(); +} + +#ifndef QT_MAC_USE_COCOA +void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos) +{ + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(globalPos)); +} +#endif + +static int nextCursorId = Qt::BitmapCursor; + +QCursorData::QCursorData(Qt::CursorShape s) + : cshape(s), bm(0), bmm(0), hx(-1), hy(-1), mId(s), type(TYPE_None) +{ + ref = 1; + memset(&curs, '\0', sizeof(curs)); +} + +QCursorData::~QCursorData() +{ + if (type == TYPE_ImageCursor) { + if (curs.cp.my_cursor) { + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(curs.cp.nscursor) release]; + } + } else if(type == TYPE_ThemeCursor) { + delete curs.tc.anim; + } + type = TYPE_None; + + delete bm; + delete bmm; + if(currentCursor == this) + currentCursor = 0; +} + +QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) { + qWarning("Qt: QCursor: Cannot create bitmap cursor; invalid bitmap(s)"); + QCursorData *c = qt_cursorTable[0]; + c->ref.ref(); + return c; + } + // This is silly, but this is apparently called outside the constructor, so we have + // to be ready for that case. + QCursorData *x = new QCursorData; + x->ref = 1; + x->mId = ++nextCursorId; + x->bm = new QBitmap(bitmap); + x->bmm = new QBitmap(mask); + x->cshape = Qt::BitmapCursor; + x->hx = hotX >= 0 ? hotX : bitmap.width() / 2; + x->hy = hotY >= 0 ? hotY : bitmap.height() / 2; + return x; +} + +Qt::HANDLE QCursor::handle() const +{ + if(!QCursorData::initialized) + QCursorData::initialize(); + if(d->type == QCursorData::TYPE_None) + d->update(); + return (Qt::HANDLE)d->mId; +} + +QPoint QCursor::pos() +{ + return flipPoint([NSEvent mouseLocation]).toPoint(); +} + +void QCursor::setPos(int x, int y) +{ +#ifdef QT_MAC_USE_COCOA + CGPoint pos; + pos.x = x; + pos.y = y; + + CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); +#else + CGWarpMouseCursorPosition(CGPointMake(x, y)); + + /* I'm not too keen on doing this, but this makes it a lot easier, so I just + send the event back through the event system and let it get propagated correctly + ideally this would not really need to be faked --Sam + */ + QWidget *widget = 0; + if(QWidget *grb = QWidget::mouseGrabber()) + widget = grb; + else + widget = QApplication::widgetAt(QPoint(x, y)); + if(widget) { + QMouseEvent me(QMouseEvent::MouseMove, widget->mapFromGlobal(QPoint(x, y)), Qt::NoButton, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + qt_sendSpontaneousEvent(widget, &me); + } +#endif +} + +void QCursorData::initCursorFromBitmap() +{ + NSImage *nsimage; + QImage finalCursor(bm->size(), QImage::Format_ARGB32); + QImage bmi = bm->toImage().convertToFormat(QImage::Format_RGB32); + QImage bmmi = bmm->toImage().convertToFormat(QImage::Format_RGB32); + for (int row = 0; row < finalCursor.height(); ++row) { + QRgb *bmData = reinterpret_cast<QRgb *>(bmi.scanLine(row)); + QRgb *bmmData = reinterpret_cast<QRgb *>(bmmi.scanLine(row)); + QRgb *finalData = reinterpret_cast<QRgb *>(finalCursor.scanLine(row)); + for (int col = 0; col < finalCursor.width(); ++col) { + if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) { + finalData[col] = 0xffffffff; + } else if (bmmData[col] == 0xff000000 && bmData[col] == 0xffffffff) { + finalData[col] = 0x7f000000; + } else if (bmmData[col] == 0xffffffff && bmData[col] == 0xffffffff) { + finalData[col] = 0x00000000; + } else { + finalData[col] = 0xff000000; + } + } + } + type = QCursorData::TYPE_ImageCursor; + curs.cp.my_cursor = true; + QPixmap bmCopy = QPixmap::fromImage(finalCursor); + NSPoint hotSpot = { hx, hy }; + nsimage = static_cast<NSImage*>(qt_mac_create_nsimage(bmCopy)); + curs.cp.nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot]; + [nsimage release]; +} + +void QCursorData::initCursorFromPixmap() +{ + type = QCursorData::TYPE_ImageCursor; + curs.cp.my_cursor = true; + NSPoint hotSpot = { hx, hy }; + NSImage *nsimage; + nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap)); + curs.cp.nscursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot]; + [nsimage release]; +} + +void QCursorData::update() +{ + if(!QCursorData::initialized) + QCursorData::initialize(); + if(type != QCursorData::TYPE_None) + return; + + /* Note to self... *** + * mask x data + * 0xFF x 0x00 == fully opaque white + * 0x00 x 0xFF == xor'd black + * 0xFF x 0xFF == fully opaque black + * 0x00 x 0x00 == fully transparent + */ + + if (hx < 0) + hx = 0; + if (hy < 0) + hy = 0; + +#define QT_USE_APPROXIMATE_CURSORS +#ifdef QT_USE_APPROXIMATE_CURSORS + static const uchar cur_ver_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x0f, 0xf0, + 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00 }; + static const uchar mcur_ver_bits[] = { + 0x00, 0x00, 0x03, 0x80, 0x07, 0xc0, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0xf8, + 0x7f, 0xfc, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x7f, 0xfc, 0x3f, 0xf8, + 0x1f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80 }; + + static const uchar cur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30, + 0x38, 0x38, 0x7f, 0xfc, 0x7f, 0xfc, 0x38, 0x38, 0x18, 0x30, 0x08, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar mcur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x0c, 0x60, 0x1c, 0x70, 0x3c, 0x78, + 0x7f, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x3c, 0x78, + 0x1c, 0x70, 0x0c, 0x60, 0x04, 0x40, 0x00, 0x00 }; + + static const uchar cur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0xf8, 0x00, 0x78, + 0x00, 0xf8, 0x01, 0xd8, 0x23, 0x88, 0x37, 0x00, 0x3e, 0x00, 0x3c, 0x00, + 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar mcur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, 0xfc, + 0x41, 0xfc, 0x63, 0xfc, 0x77, 0xdc, 0x7f, 0x8c, 0x7f, 0x04, 0x7e, 0x00, + 0x7f, 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x00, 0x00 }; + + static const uchar cur_bdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, 0x00, + 0x37, 0x00, 0x23, 0x88, 0x01, 0xd8, 0x00, 0xf8, 0x00, 0x78, 0x00, 0xf8, + 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar mcur_bdiag_bits[] = { + 0x00, 0x00, 0x7f, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7e, 0x00, 0x7f, 0x04, + 0x7f, 0x8c, 0x77, 0xdc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, 0xfc, + 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00 }; + + static const unsigned char cur_up_arrow_bits[] = { + 0x00, 0x80, 0x01, 0x40, 0x01, 0x40, 0x02, 0x20, 0x02, 0x20, 0x04, 0x10, + 0x04, 0x10, 0x08, 0x08, 0x0f, 0x78, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, + 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0 }; + static const unsigned char mcur_up_arrow_bits[] = { + 0x00, 0x80, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xe0, 0x03, 0xe0, 0x07, 0xf0, + 0x07, 0xf0, 0x0f, 0xf8, 0x0f, 0xf8, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, + 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0 }; +#endif + const uchar *cursorData = 0; + const uchar *cursorMaskData = 0; +#ifdef QT_MAC_USE_COCOA + switch (cshape) { // map Q cursor to MAC cursor + case Qt::BitmapCursor: { + if (pixmap.isNull()) + initCursorFromBitmap(); + else + initCursorFromPixmap(); + break; } + case Qt::BlankCursor: { + pixmap = QPixmap(16, 16); + pixmap.fill(Qt::transparent); + initCursorFromPixmap(); + break; } + case Qt::ArrowCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor arrowCursor]; + break; } + case Qt::CrossCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor crosshairCursor]; + break; } + case Qt::WaitCursor: { + pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/spincursor.png")); + initCursorFromPixmap(); + break; } + case Qt::IBeamCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor IBeamCursor]; + break; } + case Qt::SizeAllCursor: { + pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/pluscursor.png")); + initCursorFromPixmap(); + break; } + case Qt::WhatsThisCursor: { //for now just use the pointing hand + case Qt::PointingHandCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor pointingHandCursor]; + break; } + case Qt::BusyCursor: { + pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/waitcursor.png")); + initCursorFromPixmap(); + break; } + case Qt::SplitVCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor resizeUpDownCursor]; + break; } + case Qt::SplitHCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor resizeLeftRightCursor]; + break; } + case Qt::ForbiddenCursor: { + pixmap = QPixmap(QLatin1String(":/trolltech/mac/cursors/images/forbiddencursor.png")); + initCursorFromPixmap(); + break; } + case Qt::OpenHandCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor openHandCursor]; + break; + case Qt::ClosedHandCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor closedHandCursor]; + break; + case Qt::DragCopyCursor: + type = QCursorData::TYPE_ThemeCursor; + if ([NSCursor respondsToSelector:@selector(dragCopyCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragCopyCursor)]; + break; + case Qt::DragMoveCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor arrowCursor]; + break; + case Qt::DragLinkCursor: + type = QCursorData::TYPE_ThemeCursor; + if ([NSCursor respondsToSelector:@selector(dragLinkCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragLinkCursor)]; + break; +#define QT_USE_APPROXIMATE_CURSORS +#ifdef QT_USE_APPROXIMATE_CURSORS + case Qt::SizeVerCursor: + cursorData = cur_ver_bits; + cursorMaskData = mcur_ver_bits; + hx = hy = 8; + break; + case Qt::SizeHorCursor: + cursorData = cur_hor_bits; + cursorMaskData = mcur_hor_bits; + hx = hy = 8; + break; + case Qt::SizeBDiagCursor: + cursorData = cur_fdiag_bits; + cursorMaskData = mcur_fdiag_bits; + hx = hy = 8; + break; + case Qt::SizeFDiagCursor: + cursorData = cur_bdiag_bits; + cursorMaskData = mcur_bdiag_bits; + hx = hy = 8; + break; + case Qt::UpArrowCursor: + cursorData = cur_up_arrow_bits; + cursorMaskData = mcur_up_arrow_bits; + hx = 8; + break; +#endif + default: + qWarning("Qt: QCursor::update: Invalid cursor shape %d", cshape); + return; + } +#else + // Carbon + switch (cshape) { // map Q cursor to MAC cursor + case Qt::BitmapCursor: { + if (pixmap.isNull()) + initCursorFromBitmap(); + else + initCursorFromPixmap(); + break; } + case Qt::BlankCursor: { + pixmap = QPixmap(16, 16); + pixmap.fill(Qt::transparent); + initCursorFromPixmap(); + break; } + case Qt::ArrowCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeArrowCursor; + break; } + case Qt::CrossCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeCrossCursor; + break; } + case Qt::WaitCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeWatchCursor; + break; } + case Qt::IBeamCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeIBeamCursor; + break; } + case Qt::SizeAllCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemePlusCursor; + break; } + case Qt::WhatsThisCursor: { //for now just use the pointing hand + case Qt::PointingHandCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemePointingHandCursor; + break; } + case Qt::BusyCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeSpinningCursor; + break; } + case Qt::SplitVCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeResizeUpDownCursor; + break; } + case Qt::SplitHCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeResizeLeftRightCursor; + break; } + case Qt::ForbiddenCursor: { + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeNotAllowedCursor; + break; } + case Qt::OpenHandCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeOpenHandCursor; + break; + case Qt::ClosedHandCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeClosedHandCursor; + break; + case Qt::DragMoveCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeArrowCursor; + break; + case Qt::DragCopyCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeCopyArrowCursor; + break; + case Qt::DragLinkCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeAliasArrowCursor; + break; +#define QT_USE_APPROXIMATE_CURSORS +#ifdef QT_USE_APPROXIMATE_CURSORS + case Qt::SizeVerCursor: + cursorData = cur_ver_bits; + cursorMaskData = mcur_ver_bits; + hx = hy = 8; + break; + case Qt::SizeHorCursor: + cursorData = cur_hor_bits; + cursorMaskData = mcur_hor_bits; + hx = hy = 8; + break; + case Qt::SizeBDiagCursor: + cursorData = cur_fdiag_bits; + cursorMaskData = mcur_fdiag_bits; + hx = hy = 8; + break; + case Qt::SizeFDiagCursor: + cursorData = cur_bdiag_bits; + cursorMaskData = mcur_bdiag_bits; + hx = hy = 8; + break; + case Qt::UpArrowCursor: + cursorData = cur_up_arrow_bits; + cursorMaskData = mcur_up_arrow_bits; + hx = 8; + break; +#endif + default: + qWarning("Qt: QCursor::update: Invalid cursor shape %d", cshape); + return; + } +#endif + + if (cursorData) { + bm = new QBitmap(QBitmap::fromData(QSize(16, 16), cursorData, + QImage::Format_Mono)); + bmm = new QBitmap(QBitmap::fromData(QSize(16, 16), cursorMaskData, + QImage::Format_Mono)); + initCursorFromBitmap(); + } + +#if 0 + if(type == QCursorData::TYPE_CursPtr && curs.cp.hcurs && curs.cp.my_cursor) { + curs.cp.hcurs->hotSpot.h = hx >= 0 ? hx : 8; + curs.cp.hcurs->hotSpot.v = hy >= 0 ? hy : 8; + } +#endif +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qcursor_p.h b/src/gui/guikernel/qcursor_p.h new file mode 100644 index 0000000000..660a2a5e8b --- /dev/null +++ b/src/gui/guikernel/qcursor_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCURSOR_P_H +#define QCURSOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qatomic.h" +#include "QtCore/qglobal.h" +#include "QtCore/qnamespace.h" +#include "QtGui/qpixmap.h" + +# if defined (Q_WS_MAC) +# include "private/qt_mac_p.h" +# elif defined(Q_WS_X11) +# include "private/qt_x11_p.h" +# elif defined(Q_WS_WIN) +# include "QtCore/qt_windows.h" +# elif defined(Q_OS_SYMBIAN) +# include "private/qt_s60_p.h" +#endif + +QT_BEGIN_NAMESPACE + +#if defined (Q_WS_MAC) +void *qt_mac_nsCursorForQCursor(const QCursor &c); +class QMacAnimateCursor; +#endif + +class QBitmap; +class QCursorData { +public: + QCursorData(Qt::CursorShape s = Qt::ArrowCursor); + ~QCursorData(); + + static void initialize(); + static void cleanup(); + + QAtomicInt ref; + Qt::CursorShape cshape; + QBitmap *bm, *bmm; + QPixmap pixmap; + short hx, hy; +#if defined (Q_WS_MAC) + int mId; +#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) + int id; +#endif +#if defined (Q_WS_WIN) + HCURSOR hcurs; +#elif defined (Q_WS_X11) + XColor fg, bg; + Cursor hcurs; + Pixmap pm, pmm; +#elif defined (Q_WS_MAC) + enum { TYPE_None, TYPE_ImageCursor, TYPE_ThemeCursor } type; + union { + struct { + uint my_cursor:1; + void *nscursor; + } cp; + struct { + QMacAnimateCursor *anim; + ThemeCursor curs; + } tc; + } curs; + void initCursorFromBitmap(); + void initCursorFromPixmap(); +#elif defined Q_OS_SYMBIAN + void loadShapeFromResource(RWsSpriteBase& target, QString resource, int hx, int hy, int interval=0); + void constructShapeSprite(RWsSpriteBase& target); + void constructCursorSprite(RWsSpriteBase& target); + RWsPointerCursor pcurs; + RWsSprite scurs; + RPointerArray<TSpriteMember> nativeSpriteMembers; +#endif + static bool initialized; + void update(); + static QCursorData *setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY); +}; + +extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; // qcursor.cpp + +QT_END_NAMESPACE + +#endif // QCURSOR_P_H diff --git a/src/gui/guikernel/qcursor_qpa.cpp b/src/gui/guikernel/qcursor_qpa.cpp new file mode 100644 index 0000000000..a6ae7d30f5 --- /dev/null +++ b/src/gui/guikernel/qcursor_qpa.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qcursor.h> +#include <private/qcursor_p.h> +#include <qbitmap.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +#ifndef QT_NO_CURSOR + +static int nextCursorId = Qt::BitmapCursor; + +/***************************************************************************** + Internal QCursorData class + *****************************************************************************/ + +QCursorData::QCursorData(Qt::CursorShape s) + : cshape(s), bm(0), bmm(0), hx(0), hy(0), id(s) +{ + ref = 1; +} + +QCursorData::~QCursorData() +{ + delete bm; + delete bmm; +} + + +/***************************************************************************** + Global cursors + *****************************************************************************/ + +extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; // qcursor.cpp + +int QCursor::handle() const +{ + return d->id; +} + + +QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) { + qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)"); + QCursorData *c = qt_cursorTable[0]; + c->ref.ref(); + return c; + } + QCursorData *d = new QCursorData; + d->bm = new QBitmap(bitmap); + d->bmm = new QBitmap(mask); + d->cshape = Qt::BitmapCursor; + d->id = ++nextCursorId; + d->hx = hotX >= 0 ? hotX : bitmap.width() / 2; + d->hy = hotY >= 0 ? hotY : bitmap.height() / 2; + + return d; +} + +void QCursorData::update() +{ +} + +#endif //QT_NO_CURSOR + +extern int qt_last_x,qt_last_y; + +QPoint QCursor::pos() +{ + return QPoint(qt_last_x, qt_last_y); +} + +void QCursor::setPos(int x, int y) +{ + // Need to check, since some X servers generate null mouse move + // events, causing looping in applications which call setPos() on + // every mouse move event. + // + if (pos() == QPoint(x, y)) + return; +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qcursor_s60.cpp b/src/gui/guikernel/qcursor_s60.cpp new file mode 100644 index 0000000000..8dfe87ef81 --- /dev/null +++ b/src/gui/guikernel/qcursor_s60.cpp @@ -0,0 +1,533 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qcursor_p.h> +#include <private/qwidget_p.h> +#include <private/qapplication_p.h> +#include <coecntrl.h> +#include <qcursor.h> +#include <private/qt_s60_p.h> +#include <qbitmap.h> +#include <w32std.h> +#include <qapplication.h> +#include <qwidget.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_CURSOR +static QCursor cursorSprite; +static int cursorSpriteVisible; +#endif + +//pos and setpos are required whether cursors are configured or not. +QPoint QCursor::pos() +{ + return S60->lastCursorPos; +} + +void QCursor::setPos(int x, int y) +{ + //clip to screen size (window server allows a sprite hotspot to be outside the screen) + if (x < 0) + x=0; + else if (x >= S60->screenWidthInPixels) + x = S60->screenWidthInPixels - 1; + if (y < 0) + y = 0; + else if (y >= S60->screenHeightInPixels) + y = S60->screenHeightInPixels - 1; + +#ifndef QT_NO_CURSOR +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors && cursorSpriteVisible) + cursorSprite.d->scurs.SetPosition(TPoint(x,y)); + else +#endif + S60->wsSession().SetPointerCursorPosition(TPoint(x, y)); +#endif + S60->lastCursorPos = QPoint(x, y); + //send a fake mouse move event, so that enter/leave events go to the widget hierarchy + QWidget *w = QApplication::topLevelAt(S60->lastCursorPos); + if (w) { + CCoeControl* ctrl = w->effectiveWinId(); + TPoint epos(x, y); + TPoint cpos = epos - ctrl->PositionRelativeToScreen(); + TPointerEvent fakeEvent; + fakeEvent.iType = TPointerEvent::EMove; + fakeEvent.iModifiers = 0U; + fakeEvent.iPosition = cpos; + fakeEvent.iParentPosition = epos; + ctrl->HandlePointerEventL(fakeEvent); + } +} + +#ifndef QT_NO_CURSOR +/* + * Request cursor to be turned on or off. + * Reference counted, so 2 on + 1 off = on, for example + */ +void qt_symbian_set_cursor_visible(bool visible) { + if (visible) + cursorSpriteVisible++; + else + cursorSpriteVisible--; + Q_ASSERT(cursorSpriteVisible >=0); + + if (cursorSpriteVisible && !S60->mouseInteractionEnabled) { +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) + qt_symbian_show_pointer_sprite(); + else +#endif + S60->wsSession().SetPointerCursorMode(EPointerCursorNormal); + } else if (!cursorSpriteVisible && S60->mouseInteractionEnabled) { +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) + qt_symbian_hide_pointer_sprite(); + else +#endif + S60->wsSession().SetPointerCursorMode(EPointerCursorNone); + } + S60->mouseInteractionEnabled = ((cursorSpriteVisible > 0) ? true : false); +} + +/* + * Check if the cursor is on or off + */ +bool qt_symbian_is_cursor_visible() { + return S60->mouseInteractionEnabled; +} + +QCursorData::QCursorData(Qt::CursorShape s) : + cshape(s), bm(0), bmm(0), hx(0), hy(0), pcurs() +{ + ref = 1; +} + +QCursorData::~QCursorData() +{ + for(int i=0;i<nativeSpriteMembers.Count();i++) { + delete nativeSpriteMembers[i]->iBitmap; + delete nativeSpriteMembers[i]->iMaskBitmap; + } + nativeSpriteMembers.ResetAndDestroy(); + pcurs.Close(); + delete bm; + delete bmm; +} + +/* Create a bitmap cursor, this is called by public constructors in the + * generic QCursor code. + */ +QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) { + qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)"); + QCursorData *c = qt_cursorTable[0]; + c->ref.ref(); + return c; + } + QCursorData *d = new QCursorData; + d->bm = new QBitmap(bitmap); + d->bmm = new QBitmap(mask); + d->cshape = Qt::BitmapCursor; + d->hx = hotX >= 0 ? hotX : bitmap.width() / 2; + d->hy = hotY >= 0 ? hotY : bitmap.height() / 2; + return d; +} + +/* + * returns an opaque native handle to a cursor. + * It happens to be the address of the native handle, as window server handles + * are not POD types. Note there is no QCursor(HANDLE) constructor on Symbian, + * Mac or QWS. + */ +Qt::HANDLE QCursor::handle() const +{ + if (d->pcurs.WsHandle()) + return reinterpret_cast<Qt::HANDLE> (&(d->pcurs)); + +#ifdef Q_SYMBIAN_HAS_SYSTEM_CURSORS + // don't construct shape cursors, QApplication_s60 will use the system cursor instead + if (!(d->bm)) + return 0; +#endif + + d->pcurs = RWsPointerCursor(S60->wsSession()); + d->pcurs.Construct(0); + d->constructCursorSprite(d->pcurs); + d->pcurs.Activate(); + + return reinterpret_cast<Qt::HANDLE> (&(d->pcurs)); +} + +#ifndef Q_SYMBIAN_HAS_SYSTEM_CURSORS +/* + * Loads a single cursor shape from resources and appends it to a native sprite. + * Animated cursors (e.g. the busy cursor) have multiple members. + */ +void QCursorData::loadShapeFromResource(RWsSpriteBase& target, QString resource, int hx, int hy, int interval) +{ + QPixmap pix; + CFbsBitmap* native; + QScopedPointer<TSpriteMember> member(new TSpriteMember); + member->iInterval = interval; + member->iInvertMask = false; + member->iMaskBitmap = 0; // all shapes are RGBA + member->iDrawMode = CGraphicsContext::EDrawModePEN; + member->iOffset = TPoint(-hx, -hy); + QString res(QLatin1String(":/trolltech/symbian/cursors/images/%1.png")); + pix.load(res.arg(resource)); + native = pix.toSymbianCFbsBitmap(); + member->iBitmap = native; + qt_symbian_throwIfError(nativeSpriteMembers.Append(member.data())); + target.AppendMember(*(member.take())); +} + +//TODO: after 4.6, connect with style & skins? +/* + * Constructs the native cursor from resources compiled into QtGui + * This is needed only when the platform doesn't have system cursors. + * + * System cursors are higher performance, since they are constructed once + * and shared by all applications by specifying the shape number. + * Due to symbian platform security considerations, and the fact most + * existing phones have a broken RWsPointerCursor, system cursors are not + * being used. + */ +void QCursorData::constructShapeSprite(RWsSpriteBase& target) +{ + int i; + switch (cshape) { + default: + qWarning("QCursorData::constructShapeSprite unknown shape %d", cshape); + //fall through and give arrow cursor + case Qt::ArrowCursor: + loadShapeFromResource(target, QLatin1String("pointer"), 1, 1); + break; + case Qt::UpArrowCursor: + loadShapeFromResource(target, QLatin1String("uparrow"), 4, 0); + break; + case Qt::CrossCursor: + loadShapeFromResource(target, QLatin1String("cross"), 7, 7); + break; + case Qt::WaitCursor: + for (i = 1; i <= 12; i++) { + loadShapeFromResource(target, QString(QLatin1String("wait%1")).arg(i), 7, 7, 1000000); + } + break; + case Qt::IBeamCursor: + loadShapeFromResource(target, QLatin1String("ibeam"), 3, 10); + break; + case Qt::SizeVerCursor: + loadShapeFromResource(target, QLatin1String("sizever"), 4, 8); + break; + case Qt::SizeHorCursor: + loadShapeFromResource(target, QLatin1String("sizehor"), 8, 4); + break; + case Qt::SizeBDiagCursor: + loadShapeFromResource(target, QLatin1String("sizebdiag"), 8, 8); + break; + case Qt::SizeFDiagCursor: + loadShapeFromResource(target, QLatin1String("sizefdiag"), 8, 8); + break; + case Qt::SizeAllCursor: + loadShapeFromResource(target, QLatin1String("sizeall"), 7, 7); + break; + case Qt::BlankCursor: + loadShapeFromResource(target, QLatin1String("blank"), 0, 0); + break; + case Qt::SplitVCursor: + loadShapeFromResource(target, QLatin1String("splitv"), 7, 7); + break; + case Qt::SplitHCursor: + loadShapeFromResource(target, QLatin1String("splith"), 7, 7); + break; + case Qt::PointingHandCursor: + loadShapeFromResource(target, QLatin1String("handpoint"), 5, 0); + break; + case Qt::ForbiddenCursor: + loadShapeFromResource(target, QLatin1String("forbidden"), 7, 7); + break; + case Qt::WhatsThisCursor: + loadShapeFromResource(target, QLatin1String("whatsthis"), 1, 1); + break; + case Qt::BusyCursor: + loadShapeFromResource(target, QLatin1String("busy3"), 1, 1, 1000000); + loadShapeFromResource(target, QLatin1String("busy6"), 1, 1, 1000000); + loadShapeFromResource(target, QLatin1String("busy9"), 1, 1, 1000000); + loadShapeFromResource(target, QLatin1String("busy12"), 1, 1, 1000000); + break; + case Qt::OpenHandCursor: + loadShapeFromResource(target, QLatin1String("openhand"), 7, 7); + break; + case Qt::ClosedHandCursor: + loadShapeFromResource(target, QLatin1String("closehand"), 7, 7); + break; + } +} +#endif + +/* + * Common code between the sprite workaround and standard modes of operation. + * RWsSpriteBase is the base class for both RWsSprite and RWsPointerCursor. + * It is called from both handle() and qt_s60_show_pointer_sprite() + */ +void QCursorData::constructCursorSprite(RWsSpriteBase& target) +{ + int count = nativeSpriteMembers.Count(); + if (count) { + // already constructed + for (int i = 0; i < count; i++) + target.AppendMember(*(nativeSpriteMembers[i])); + + return; + } + if (pixmap.isNull() && !bm) { +#ifndef Q_SYMBIAN_HAS_SYSTEM_CURSORS + //shape cursor + constructShapeSprite(target); +#endif + return; + } + QScopedPointer<TSpriteMember> member(new TSpriteMember); + if (pixmap.isNull()) { + //construct mono cursor + member->iBitmap = bm->toSymbianCFbsBitmap(); + member->iMaskBitmap = bmm->toSymbianCFbsBitmap(); + } + else { + //construct normal cursor + member->iBitmap = pixmap.toSymbianCFbsBitmap(); + if (pixmap.hasAlphaChannel()) { + member->iMaskBitmap = 0; //use alpha blending + } + else if (pixmap.hasAlpha()) { + member->iMaskBitmap = pixmap.mask().toSymbianCFbsBitmap(); + } + else { + member->iMaskBitmap = 0; //opaque rectangle cursor (due to EDrawModePEN) + } + } + + member->iDrawMode = CGraphicsContext::EDrawModePEN; + member->iInvertMask = EFalse; + member->iInterval = 0; + member->iOffset = TPoint(-(hx), -(hy)); //Symbian hotspot coordinates are negative + qt_symbian_throwIfError(nativeSpriteMembers.Append(member.data())); + target.AppendMember(*(member.take())); +} + +/* + * shows the pointer sprite by constructing a native handle, and registering + * it with the window server. + * Only used when the sprite workaround is in use. + */ +void qt_symbian_show_pointer_sprite() +{ + if (cursorSprite.d) { + if (cursorSprite.d->scurs.WsHandle()) + cursorSprite.d->scurs.Close(); + } else { + cursorSprite = QCursor(Qt::ArrowCursor); + } + + cursorSprite.d->scurs = RWsSprite(S60->wsSession()); + QPoint pos = QCursor::pos(); + cursorSprite.d->scurs.Construct(S60->windowGroup(), TPoint(pos.x(), pos.y()), ESpriteNoChildClip | ESpriteNoShadows); + + cursorSprite.d->constructCursorSprite(cursorSprite.d->scurs); + cursorSprite.d->scurs.Activate(); +} + +/* + * hides the pointer sprite by closing the native handle. + * Only used when the sprite workaround is in use. + */ +void qt_symbian_hide_pointer_sprite() +{ + if (cursorSprite.d) { + cursorSprite.d->scurs.Close(); + } +} + +/* + * Changes the cursor sprite to the cursor specified. + * Only used when the sprite workaround is in use. + */ +void qt_symbian_set_pointer_sprite(const QCursor& cursor) +{ + if (S60->mouseInteractionEnabled) + qt_symbian_hide_pointer_sprite(); + cursorSprite = cursor; + if (S60->mouseInteractionEnabled) + qt_symbian_show_pointer_sprite(); +} + +/* + * When using sprites as a workaround on phones that have a broken + * RWsPointerCursor, this function is called in response to pointer events + * and when QCursor::setPos() is called. + * Performance is worse than a real pointer cursor, due to extra context + * switches vs. the window server moving the cursor by itself. + */ +void qt_symbian_move_cursor_sprite() +{ + if (S60->mouseInteractionEnabled) { + cursorSprite.d->scurs.SetPosition(TPoint(S60->lastCursorPos.x(), S60->lastCursorPos.y())); + } +} + +/* + * Translate from Qt::CursorShape to OS system pointer cursor list index. + * Currently we control the implementation of the system pointer cursor list, + * so this function is trivial. That may not always be the case. + */ +TInt qt_symbian_translate_cursor_shape(Qt::CursorShape shape) +{ + return (TInt) shape; +} + +/* + Internal function called from QWidget::setCursor() + force is true if this function is called from dispatchEnterLeave, it means that the + mouse is actually directly under this widget. +*/ +void qt_symbian_set_cursor(QWidget *w, bool force) +{ + static QPointer<QWidget> lastUnderMouse = 0; + if (force) { + lastUnderMouse = w; + } + else if (w->testAttribute(Qt::WA_WState_Created) && lastUnderMouse + && lastUnderMouse->effectiveWinId() == w->effectiveWinId()) { + w = lastUnderMouse; + } + + if (!S60->curWin && w && w->internalWinId()) + return; + QWidget* cW = w && !w->internalWinId() ? w : QWidget::find(S60->curWin); + if (!cW || cW->window() != w->window() || !cW->isVisible() || !cW->underMouse() + || QApplication::overrideCursor()) + return; + +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) + qt_symbian_set_pointer_sprite(cW->cursor()); + else +#endif + qt_symbian_setWindowCursor(cW->cursor(), w->effectiveWinId()); +} + +/* + * Makes the specified cursor appear above a specific native window group + * Called from QSymbianControl and QApplication::restoreOverrideCursor + * + * Window server is needed for this, so there is no equivalent when using + * the sprite workaround. + */ +void qt_symbian_setWindowGroupCursor(const QCursor &cursor, RWindowTreeNode &node) +{ + Qt::HANDLE handle = cursor.handle(); + if (handle) { + RWsPointerCursor *pcurs = reinterpret_cast<RWsPointerCursor *> (handle); + node.SetCustomPointerCursor(*pcurs); + } else +#ifdef Q_SYMBIAN_HAS_SYSTEM_CURSORS + { + TInt shape = qt_symbian_translate_cursor_shape(cursor.shape()); + node.SetPointerCursor(shape); + } +#else + qWarning("qt_s60_setWindowGroupCursor - null handle"); +#endif +} + +/* + * Makes the specified cursor appear above a specific native window + * Called from QSymbianControl and QApplication::restoreOverrideCursor + * + * Window server is needed for this, so there is no equivalent when using + * the sprite workaround. + */ +void qt_symbian_setWindowCursor(const QCursor &cursor, const CCoeControl* wid) +{ + //find the window for this control + while (!wid->OwnsWindow()) { + wid = wid->Parent(); + if (!wid) + return; + } + RWindowTreeNode *node = wid->DrawableWindow(); + qt_symbian_setWindowGroupCursor(cursor, *node); +} + +/* + * Makes the specified cursor appear everywhere. + * Called from QApplication::setOverrideCursor + */ +void qt_symbian_setGlobalCursor(const QCursor &cursor) +{ +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) { + qt_symbian_set_pointer_sprite(cursor); + } else +#endif + { + //because of the internals of window server, we need to force the cursor + //to be set in all child windows too, otherwise when the cursor is over + //the child window it may show a widget cursor or arrow cursor instead, + //depending on construction order. + QListIterator<WId> iter(QWidgetPrivate::mapper->uniqueKeys()); + while(iter.hasNext()) + { + CCoeControl *ctrl = iter.next(); + if(ctrl->OwnsWindow()) { + RWindowTreeNode *node = ctrl->DrawableWindow(); + qt_symbian_setWindowGroupCursor(cursor, *node); + } + } + } +} +QT_END_NAMESPACE +#endif // QT_NO_CURSOR diff --git a/src/gui/guikernel/qcursor_win.cpp b/src/gui/guikernel/qcursor_win.cpp new file mode 100644 index 0000000000..8a9362ebfc --- /dev/null +++ b/src/gui/guikernel/qcursor_win.cpp @@ -0,0 +1,492 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qcursor_p.h> +#include <qbitmap.h> +#include <qcursor.h> + +#ifndef QT_NO_CURSOR + +#include <qimage.h> +#include <qt_windows.h> +#include <private/qapplication_p.h> + +QT_BEGIN_NAMESPACE + +/***************************************************************************** + Internal QCursorData class + *****************************************************************************/ + +QCursorData::QCursorData(Qt::CursorShape s) + : cshape(s), bm(0), bmm(0), hx(0), hy(0), hcurs(0) +{ + ref = 1; +} + +QCursorData::~QCursorData() +{ + delete bm; + delete bmm; +#if !defined(Q_WS_WINCE) || defined(GWES_ICONCURS) + if (hcurs) + DestroyCursor(hcurs); +#endif +} + + +QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) { + qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)"); + QCursorData *c = qt_cursorTable[0]; + c->ref.ref(); + return c; + } + QCursorData *d = new QCursorData; + d->bm = new QBitmap(bitmap); + d->bmm = new QBitmap(mask); + d->hcurs = 0; + d->cshape = Qt::BitmapCursor; + d->hx = hotX >= 0 ? hotX : bitmap.width()/2; + d->hy = hotY >= 0 ? hotY : bitmap.height()/2; + return d; +} + +HCURSOR QCursor::handle() const +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (!d->hcurs) + d->update(); + return d->hcurs; +} + +QCursor::QCursor(HCURSOR handle) +{ + d = new QCursorData(Qt::CustomCursor); + d->hcurs = handle; +} + +#endif //QT_NO_CURSOR + +QPoint QCursor::pos() +{ + POINT p; + GetCursorPos(&p); + return QPoint(p.x, p.y); +} + +void QCursor::setPos(int x, int y) +{ + SetCursorPos(x, y); +} + +#ifndef QT_NO_CURSOR + +extern HBITMAP qt_createIconMask(const QBitmap &bitmap); + +static HCURSOR create32BitCursor(const QPixmap &pixmap, int hx, int hy) +{ + HCURSOR cur = 0; +#if !defined(Q_WS_WINCE) + QBitmap mask = pixmap.mask(); + if (mask.isNull()) { + mask = QBitmap(pixmap.size()); + mask.fill(Qt::color1); + } + + HBITMAP ic = pixmap.toWinHBITMAP(QPixmap::Alpha); + HBITMAP im = qt_createIconMask(mask); + + ICONINFO ii; + ii.fIcon = 0; + ii.xHotspot = hx; + ii.yHotspot = hy; + ii.hbmMask = im; + ii.hbmColor = ic; + + cur = CreateIconIndirect(&ii); + + DeleteObject(ic); + DeleteObject(im); +#elif defined(GWES_ICONCURS) + QImage bbits, mbits; + bool invb, invm; + bbits = pixmap.toImage().convertToFormat(QImage::Format_Mono); + mbits = pixmap.toImage().convertToFormat(QImage::Format_Mono); + invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); + invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1)); + + int sysW = GetSystemMetrics(SM_CXCURSOR); + int sysH = GetSystemMetrics(SM_CYCURSOR); + int sysN = qMax(1, sysW / 8); + int n = qMax(1, bbits.width() / 8); + int h = bbits.height(); + + uchar* xBits = new uchar[sysH * sysN]; + uchar* xMask = new uchar[sysH * sysN]; + int x = 0; + for (int i = 0; i < sysH; ++i) { + if (i >= h) { + memset(&xBits[x] , 255, sysN); + memset(&xMask[x] , 0, sysN); + x += sysN; + } else { + int fillWidth = n > sysN ? sysN : n; + uchar *bits = bbits.scanLine(i); + uchar *mask = mbits.scanLine(i); + for (int j = 0; j < fillWidth; ++j) { + uchar b = bits[j]; + uchar m = mask[j]; + if (invb) + b ^= 0xFF; + if (invm) + m ^= 0xFF; + xBits[x] = ~m; + xMask[x] = b ^ m; + ++x; + } + for (int j = fillWidth; j < sysN; ++j ) { + xBits[x] = 255; + xMask[x] = 0; + ++x; + } + } + } + + cur = CreateCursor(qWinAppInst(), hx, hy, sysW, sysH, + xBits, xMask); +#else + Q_UNUSED(pixmap); + Q_UNUSED(hx); + Q_UNUSED(hy); +#endif + return cur; +} + +void QCursorData::update() +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (hcurs) + return; + + if (cshape == Qt::BitmapCursor && !pixmap.isNull()) { + hcurs = create32BitCursor(pixmap, hx, hy); + if (hcurs) + return; + } + + + // Non-standard Windows cursors are created from bitmaps + + static const uchar vsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar vsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar hsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03, + 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar hsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00, + 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07, + 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00, + 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar phand_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, + 0x80, 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, + 0x80, 0x1c, 0x00, 0x00, 0x80, 0xe4, 0x00, 0x00, 0x80, 0x24, 0x03, 0x00, + 0x80, 0x24, 0x05, 0x00, 0xb8, 0x24, 0x09, 0x00, 0xc8, 0x00, 0x09, 0x00, + 0x88, 0x00, 0x08, 0x00, 0x90, 0x00, 0x08, 0x00, 0xa0, 0x00, 0x08, 0x00, + 0x20, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x04, 0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x02, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + static const uchar phandm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, + 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, + 0x80, 0x1f, 0x00, 0x00, 0x80, 0xff, 0x00, 0x00, 0x80, 0xff, 0x03, 0x00, + 0x80, 0xff, 0x07, 0x00, 0xb8, 0xff, 0x0f, 0x00, 0xf8, 0xff, 0x0f, 0x00, + 0xf8, 0xff, 0x0f, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0xe0, 0xff, 0x0f, 0x00, + 0xe0, 0xff, 0x0f, 0x00, 0xc0, 0xff, 0x0f, 0x00, 0xc0, 0xff, 0x07, 0x00, + 0x80, 0xff, 0x07, 0x00, 0x80, 0xff, 0x07, 0x00, 0x00, 0xff, 0x03, 0x00, + 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + static const uchar openhand_bits[] = { + 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92, + 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20, + 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00}; + static const uchar openhandm_bits[] = { + 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff, + 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f, + 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00}; + static const uchar closedhand_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50, + 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10, + 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00}; + static const uchar closedhandm_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f, + 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f, + 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00}; + + static const uchar * const cursor_bits32[] = { + vsplit_bits, vsplitm_bits, hsplit_bits, hsplitm_bits, + phand_bits, phandm_bits + }; + + wchar_t *sh = 0; + switch (cshape) { // map to windows cursor + case Qt::ArrowCursor: + sh = IDC_ARROW; + break; + case Qt::UpArrowCursor: + sh = IDC_UPARROW; + break; + case Qt::CrossCursor: + sh = IDC_CROSS; + break; + case Qt::WaitCursor: + sh = IDC_WAIT; + break; + case Qt::IBeamCursor: + sh = IDC_IBEAM; + break; + case Qt::SizeVerCursor: + sh = IDC_SIZENS; + break; + case Qt::SizeHorCursor: + sh = IDC_SIZEWE; + break; + case Qt::SizeBDiagCursor: + sh = IDC_SIZENESW; + break; + case Qt::SizeFDiagCursor: + sh = IDC_SIZENWSE; + break; + case Qt::SizeAllCursor: + sh = IDC_SIZEALL; + break; + case Qt::ForbiddenCursor: + sh = IDC_NO; + break; + case Qt::WhatsThisCursor: + sh = IDC_HELP; + break; + case Qt::BusyCursor: + sh = IDC_APPSTARTING; + break; + case Qt::PointingHandCursor: + sh = IDC_HAND; + break; + case Qt::BlankCursor: + case Qt::SplitVCursor: + case Qt::SplitHCursor: + case Qt::OpenHandCursor: + case Qt::ClosedHandCursor: + case Qt::BitmapCursor: { + QImage bbits, mbits; + bool invb, invm; + if (cshape == Qt::BlankCursor) { + bbits = QImage(32, 32, QImage::Format_Mono); + bbits.fill(0); // ignore color table + mbits = bbits.copy(); + hx = hy = 16; + invb = invm = false; + } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) { + bool open = cshape == Qt::OpenHandCursor; + QBitmap cb = QBitmap::fromData(QSize(16, 16), open ? openhand_bits : closedhand_bits); + QBitmap cm = QBitmap::fromData(QSize(16, 16), open ? openhandm_bits : closedhandm_bits); + bbits = cb.toImage().convertToFormat(QImage::Format_Mono); + mbits = cm.toImage().convertToFormat(QImage::Format_Mono); + hx = hy = 8; + invb = invm = false; + } else if (cshape != Qt::BitmapCursor) { + int i = cshape - Qt::SplitVCursor; + QBitmap cb = QBitmap::fromData(QSize(32, 32), cursor_bits32[i * 2]); + QBitmap cm = QBitmap::fromData(QSize(32, 32), cursor_bits32[i * 2 + 1]); + bbits = cb.toImage().convertToFormat(QImage::Format_Mono); + mbits = cm.toImage().convertToFormat(QImage::Format_Mono); + if (cshape == Qt::PointingHandCursor) { + hx = 7; + hy = 0; + } else + hx = hy = 16; + invb = invm = false; + } else { + bbits = bm->toImage().convertToFormat(QImage::Format_Mono); + mbits = bmm->toImage().convertToFormat(QImage::Format_Mono); + invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); + invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1)); + } + int n = qMax(1, bbits.width() / 8); + int h = bbits.height(); +#if !defined(Q_WS_WINCE) + uchar* xBits = new uchar[h * n]; + uchar* xMask = new uchar[h * n]; + int x = 0; + for (int i = 0; i < h; ++i) { + uchar *bits = bbits.scanLine(i); + uchar *mask = mbits.scanLine(i); + for (int j = 0; j < n; ++j) { + uchar b = bits[j]; + uchar m = mask[j]; + if (invb) + b ^= 0xff; + if (invm) + m ^= 0xff; + xBits[x] = ~m; + xMask[x] = b ^ m; + ++x; + } + } + hcurs = CreateCursor(qWinAppInst(), hx, hy, bbits.width(), bbits.height(), + xBits, xMask); + delete [] xBits; + delete [] xMask; +#elif defined(GWES_ICONCURS) // Q_WS_WINCE + // Windows CE only supports fixed cursor size. + int sysW = GetSystemMetrics(SM_CXCURSOR); + int sysH = GetSystemMetrics(SM_CYCURSOR); + int sysN = qMax(1, sysW / 8); + uchar* xBits = new uchar[sysH * sysN]; + uchar* xMask = new uchar[sysH * sysN]; + int x = 0; + for (int i = 0; i < sysH; ++i) { + if (i >= h) { + memset(&xBits[x] , 255, sysN); + memset(&xMask[x] , 0, sysN); + x += sysN; + } else { + int fillWidth = n > sysN ? sysN : n; + uchar *bits = bbits.scanLine(i); + uchar *mask = mbits.scanLine(i); + for (int j = 0; j < fillWidth; ++j) { + uchar b = bits[j]; + uchar m = mask[j]; + if (invb) + b ^= 0xFF; + if (invm) + m ^= 0xFF; + xBits[x] = ~m; + xMask[x] = b ^ m; + ++x; + } + for (int j = fillWidth; j < sysN; ++j ) { + xBits[x] = 255; + xMask[x] = 0; + ++x; + } + } + } + + hcurs = CreateCursor(qWinAppInst(), hx, hy, sysW, sysH, + xBits, xMask); + delete [] xBits; + delete [] xMask; +#else + Q_UNUSED(n); + Q_UNUSED(h); +#endif + return; + } + case Qt::DragCopyCursor: + case Qt::DragMoveCursor: + case Qt::DragLinkCursor: { + QPixmap pixmap = QApplicationPrivate::instance()->getPixmapCursor(cshape); + hcurs = create32BitCursor(pixmap, hx, hy); + } + default: + qWarning("QCursor::update: Invalid cursor shape %d", cshape); + return; + } +#ifdef Q_WS_WINCE + hcurs = LoadCursor(0, sh); +#else + hcurs = (HCURSOR)LoadImage(0, sh, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); +#endif +} + +QT_END_NAMESPACE +#endif // QT_NO_CURSOR diff --git a/src/gui/guikernel/qcursor_x11.cpp b/src/gui/guikernel/qcursor_x11.cpp new file mode 100644 index 0000000000..d0ed98e1fe --- /dev/null +++ b/src/gui/guikernel/qcursor_x11.cpp @@ -0,0 +1,637 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qdebug.h> +#include <qdatastream.h> +#include <private/qcursor_p.h> +#include <private/qt_x11_p.h> +#include <private/qapplication_p.h> +#include <qbitmap.h> +#include <qcursor.h> +#include <X11/cursorfont.h> + +#include <qlibrary.h> + +#ifndef QT_NO_XCURSOR +# include <X11/Xcursor/Xcursor.h> +#endif // QT_NO_XCURSOR + +#ifndef QT_NO_XFIXES +# include <X11/extensions/Xfixes.h> +#endif // QT_NO_XFIXES + +#include "qx11info_x11.h" +#include <private/qpixmap_x11_p.h> + +QT_BEGIN_NAMESPACE + +// Define QT_USE_APPROXIMATE_CURSORS when compiling if you REALLY want to +// use the ugly X11 cursors. + +/***************************************************************************** + Internal QCursorData class + *****************************************************************************/ + +QCursorData::QCursorData(Qt::CursorShape s) + : cshape(s), bm(0), bmm(0), hx(0), hy(0), hcurs(0), pm(0), pmm(0) +{ + ref = 1; +} + +QCursorData::~QCursorData() +{ + Display *dpy = X11 ? X11->display : (Display*)0; + + // Add in checking for the display too as on HP-UX + // we seem to get a core dump as the cursor data is + // deleted again from main() on exit... + if (hcurs && dpy) + XFreeCursor(dpy, hcurs); + if (pm && dpy) + XFreePixmap(dpy, pm); + if (pmm && dpy) + XFreePixmap(dpy, pmm); + delete bm; + delete bmm; +} + +#ifndef QT_NO_CURSOR +QCursor::QCursor(Qt::HANDLE cursor) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + d = new QCursorData(Qt::CustomCursor); + d->hcurs = cursor; +} + +#endif + +QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) { + qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)"); + QCursorData *c = qt_cursorTable[0]; + c->ref.ref(); + return c; + } + QCursorData *d = new QCursorData; + d->ref = 1; + + extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp + d->bm = new QBitmap(qt_toX11Pixmap(bitmap)); + d->bmm = new QBitmap(qt_toX11Pixmap(mask)); + + d->hcurs = 0; + d->cshape = Qt::BitmapCursor; + d->hx = hotX >= 0 ? hotX : bitmap.width() / 2; + d->hy = hotY >= 0 ? hotY : bitmap.height() / 2; + d->fg.red = 0x0000; + d->fg.green = 0x0000; + d->fg.blue = 0x0000; + d->bg.red = 0xffff; + d->bg.green = 0xffff; + d->bg.blue = 0xffff; + return d; +} + + + +#ifndef QT_NO_CURSOR +Qt::HANDLE QCursor::handle() const +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (!d->hcurs) + d->update(); + return d->hcurs; +} +#endif + +QPoint QCursor::pos() +{ + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint buttons; + Display* dpy = X11->display; + for (int i = 0; i < ScreenCount(dpy); ++i) { + if (XQueryPointer(dpy, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y, + &win_x, &win_y, &buttons)) + + return QPoint(root_x, root_y); + } + return QPoint(); +} + +/*! \internal +*/ +#ifndef QT_NO_CURSOR +int QCursor::x11Screen() +{ + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint buttons; + Display* dpy = X11->display; + for (int i = 0; i < ScreenCount(dpy); ++i) { + if (XQueryPointer(dpy, QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y, + &win_x, &win_y, &buttons)) + return i; + } + return -1; +} +#endif + +void QCursor::setPos(int x, int y) +{ + QPoint current, target(x, y); + + // this is copied from pos(), since we need the screen number for the correct + // root window in the XWarpPointer call + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint buttons; + Display* dpy = X11->display; + int screen; + for (screen = 0; screen < ScreenCount(dpy); ++screen) { + if (XQueryPointer(dpy, QX11Info::appRootWindow(screen), &root, &child, &root_x, &root_y, + &win_x, &win_y, &buttons)) { + current = QPoint(root_x, root_y); + break; + } + } + + if (screen >= ScreenCount(dpy)) + return; + + // Need to check, since some X servers generate null mouse move + // events, causing looping in applications which call setPos() on + // every mouse move event. + // + if (current == target) + return; + + XWarpPointer(X11->display, XNone, QX11Info::appRootWindow(screen), 0, 0, 0, 0, x, y); +} + + +/*! + \internal + + Creates the cursor. +*/ + +void QCursorData::update() +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (hcurs) + return; + + Display *dpy = X11->display; + Window rootwin = QX11Info::appRootWindow(); + + if (cshape == Qt::BitmapCursor) { + extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap); // qpixmap_x11.cpp +#ifndef QT_NO_XRENDER + if (!pixmap.isNull() && X11->use_xrender) { + pixmap = qt_toX11Pixmap(pixmap); + hcurs = XRenderCreateCursor (X11->display, pixmap.x11PictureHandle(), hx, hy); + } else +#endif + { + hcurs = XCreatePixmapCursor(dpy, bm->handle(), bmm->handle(), &fg, &bg, hx, hy); + } + return; + } + + static const char *cursorNames[] = { + "left_ptr", + "up_arrow", + "cross", + "wait", + "ibeam", + "size_ver", + "size_hor", + "size_bdiag", + "size_fdiag", + "size_all", + "blank", + "split_v", + "split_h", + "pointing_hand", + "forbidden", + "whats_this", + "left_ptr_watch", + "openhand", + "closedhand", + "copy", + "move", + "link" + }; + +#ifndef QT_NO_XCURSOR + if (X11->ptrXcursorLibraryLoadCursor) { + // special case for non-standard dnd-* cursors + switch (cshape) { + case Qt::DragCopyCursor: + hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); + break; + case Qt::DragMoveCursor: + hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); + break; + case Qt::DragLinkCursor: + hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); + break; + default: + break; + } + if (!hcurs) + hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); + } + if (hcurs) + return; +#endif // QT_NO_XCURSOR + + static const uchar cur_blank_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + // Non-standard X11 cursors are created from bitmaps + +#ifndef QT_USE_APPROXIMATE_CURSORS + static const uchar cur_ver_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f, + 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00 }; + static const uchar mcur_ver_bits[] = { + 0x00, 0x00, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, + 0xfc, 0x7f, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xfc, 0x7f, 0xf8, 0x3f, + 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03 }; + static const uchar cur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x30, 0x18, + 0x38, 0x38, 0xfc, 0x7f, 0xfc, 0x7f, 0x38, 0x38, 0x30, 0x18, 0x20, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar mcur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x60, 0x0c, 0x70, 0x1c, 0x78, 0x3c, + 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0x78, 0x3c, + 0x70, 0x1c, 0x60, 0x0c, 0x40, 0x04, 0x00, 0x00 }; + static const uchar cur_bdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, + 0x00, 0x37, 0x88, 0x23, 0xd8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar mcur_bdiag_bits[] = { + 0x00, 0x00, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x7e, 0x04, 0x7f, + 0x8c, 0x7f, 0xdc, 0x77, 0xfc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, + 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 }; + static const uchar cur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, + 0xf8, 0x00, 0xd8, 0x01, 0x88, 0x23, 0x00, 0x37, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00 }; + static const uchar mcur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, + 0xfc, 0x41, 0xfc, 0x63, 0xdc, 0x77, 0x8c, 0x7f, 0x04, 0x7f, 0x00, 0x7e, + 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x7f, 0x00, 0x00 }; + static const uchar *cursor_bits16[] = { + cur_ver_bits, mcur_ver_bits, cur_hor_bits, mcur_hor_bits, + cur_bdiag_bits, mcur_bdiag_bits, cur_fdiag_bits, mcur_fdiag_bits, + 0, 0, cur_blank_bits, cur_blank_bits }; + + static const uchar vsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar vsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar hsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03, + 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar hsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00, + 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07, + 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00, + 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar whatsthis_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x07, 0x00, + 0x09, 0x18, 0x0e, 0x00, 0x11, 0x1c, 0x0e, 0x00, 0x21, 0x1c, 0x0e, 0x00, + 0x41, 0x1c, 0x0e, 0x00, 0x81, 0x1c, 0x0e, 0x00, 0x01, 0x01, 0x07, 0x00, + 0x01, 0x82, 0x03, 0x00, 0xc1, 0xc7, 0x01, 0x00, 0x49, 0xc0, 0x01, 0x00, + 0x95, 0xc0, 0x01, 0x00, 0x93, 0xc0, 0x01, 0x00, 0x21, 0x01, 0x00, 0x00, + 0x20, 0xc1, 0x01, 0x00, 0x40, 0xc2, 0x01, 0x00, 0x40, 0x02, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + static const uchar whatsthism_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0x00, 0x07, 0xf8, 0x0f, 0x00, + 0x0f, 0xfc, 0x1f, 0x00, 0x1f, 0x3e, 0x1f, 0x00, 0x3f, 0x3e, 0x1f, 0x00, + 0x7f, 0x3e, 0x1f, 0x00, 0xff, 0x3e, 0x1f, 0x00, 0xff, 0x9d, 0x0f, 0x00, + 0xff, 0xc3, 0x07, 0x00, 0xff, 0xe7, 0x03, 0x00, 0x7f, 0xe0, 0x03, 0x00, + 0xf7, 0xe0, 0x03, 0x00, 0xf3, 0xe0, 0x03, 0x00, 0xe1, 0xe1, 0x03, 0x00, + 0xe0, 0xe1, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + static const uchar busy_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x41, 0xe0, 0xff, 0x00, 0x81, 0x20, 0x80, 0x00, 0x01, 0xe1, 0xff, 0x00, + 0x01, 0x42, 0x40, 0x00, 0xc1, 0x47, 0x40, 0x00, 0x49, 0x40, 0x55, 0x00, + 0x95, 0x80, 0x2a, 0x00, 0x93, 0x00, 0x15, 0x00, 0x21, 0x01, 0x0a, 0x00, + 0x20, 0x01, 0x11, 0x00, 0x40, 0x82, 0x20, 0x00, 0x40, 0x42, 0x44, 0x00, + 0x80, 0x41, 0x4a, 0x00, 0x00, 0x40, 0x55, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0x20, 0x80, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + static const uchar busym_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x7f, 0xe0, 0xff, 0x00, 0xff, 0xe0, 0xff, 0x00, 0xff, 0xe1, 0xff, 0x00, + 0xff, 0xc3, 0x7f, 0x00, 0xff, 0xc7, 0x7f, 0x00, 0x7f, 0xc0, 0x7f, 0x00, + 0xf7, 0x80, 0x3f, 0x00, 0xf3, 0x00, 0x1f, 0x00, 0xe1, 0x01, 0x0e, 0x00, + 0xe0, 0x01, 0x1f, 0x00, 0xc0, 0x83, 0x3f, 0x00, 0xc0, 0xc3, 0x7f, 0x00, + 0x80, 0xc1, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + static const uchar * const cursor_bits32[] = { + vsplit_bits, vsplitm_bits, hsplit_bits, hsplitm_bits, + 0, 0, 0, 0, whatsthis_bits, whatsthism_bits, busy_bits, busym_bits + }; + + static const uchar forbidden_bits[] = { + 0x00,0x00,0x00,0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xf0,0x00,0x38,0xc0,0x01, + 0x7c,0x80,0x03,0xec,0x00,0x03,0xce,0x01,0x07,0x86,0x03,0x06,0x06,0x07,0x06, + 0x06,0x0e,0x06,0x06,0x1c,0x06,0x0e,0x38,0x07,0x0c,0x70,0x03,0x1c,0xe0,0x03, + 0x38,0xc0,0x01,0xf0,0xe0,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00,0x00,0x00,0x00 }; + + static const uchar forbiddenm_bits[] = { + 0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xff,0x00,0xf8,0xff,0x01,0xfc,0xf0,0x03, + 0xfe,0xc0,0x07,0xfe,0x81,0x07,0xff,0x83,0x0f,0xcf,0x07,0x0f,0x8f,0x0f,0x0f, + 0x0f,0x1f,0x0f,0x0f,0x3e,0x0f,0x1f,0xfc,0x0f,0x1e,0xf8,0x07,0x3e,0xf0,0x07, + 0xfc,0xe0,0x03,0xf8,0xff,0x01,0xf0,0xff,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00}; + + static const uchar openhand_bits[] = { + 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92, + 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20, + 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00}; + static const uchar openhandm_bits[] = { + 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff, + 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f, + 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00}; + static const uchar closedhand_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50, + 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10, + 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00}; + static const uchar closedhandm_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f, + 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f, + 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00}; + + static const uchar * const cursor_bits20[] = { + forbidden_bits, forbiddenm_bits + }; + + if ((cshape >= Qt::SizeVerCursor && cshape < Qt::SizeAllCursor) + || cshape == Qt::BlankCursor) { + XColor bg, fg; + bg.red = 255 << 8; + bg.green = 255 << 8; + bg.blue = 255 << 8; + fg.red = 0; + fg.green = 0; + fg.blue = 0; + int i = (cshape - Qt::SizeVerCursor) * 2; + pm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char*>(cursor_bits16[i]), 16, 16); + pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char*>(cursor_bits16[i + 1]), 16, 16); + hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8); + } else if ((cshape >= Qt::SplitVCursor && cshape <= Qt::SplitHCursor) + || cshape == Qt::WhatsThisCursor || cshape == Qt::BusyCursor) { + XColor bg, fg; + bg.red = 255 << 8; + bg.green = 255 << 8; + bg.blue = 255 << 8; + fg.red = 0; + fg.green = 0; + fg.blue = 0; + int i = (cshape - Qt::SplitVCursor) * 2; + pm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(cursor_bits32[i]), 32, 32); + pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(cursor_bits32[i + 1]), 32, 32); + int hs = (cshape == Qt::PointingHandCursor || cshape == Qt::WhatsThisCursor + || cshape == Qt::BusyCursor) ? 0 : 16; + hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, hs, hs); + } else if (cshape == Qt::ForbiddenCursor) { + XColor bg, fg; + bg.red = 255 << 8; + bg.green = 255 << 8; + bg.blue = 255 << 8; + fg.red = 0; + fg.green = 0; + fg.blue = 0; + int i = (cshape - Qt::ForbiddenCursor) * 2; + pm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(cursor_bits20[i]), 20, 20); + pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(cursor_bits20[i + 1]), 20, 20); + hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 10, 10); + } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) { + XColor bg, fg; + bg.red = 255 << 8; + bg.green = 255 << 8; + bg.blue = 255 << 8; + fg.red = 0; + fg.green = 0; + fg.blue = 0; + bool open = cshape == Qt::OpenHandCursor; + pm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(open ? openhand_bits : closedhand_bits), 16, 16); + pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(open ? openhandm_bits : closedhandm_bits), 16, 16); + hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8); + } else if (cshape == Qt::DragCopyCursor || cshape == Qt::DragMoveCursor + || cshape == Qt::DragLinkCursor) { + XColor bg, fg; + bg.red = 255 << 8; + bg.green = 255 << 8; + bg.blue = 255 << 8; + fg.red = 0; + fg.green = 0; + fg.blue = 0; + QImage image = QApplicationPrivate::instance()->getPixmapCursor(cshape).toImage(); + pm = QX11PixmapData::createBitmapFromImage(image); + pmm = QX11PixmapData::createBitmapFromImage(image.createAlphaMask().convertToFormat(QImage::Format_MonoLSB)); + hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8); + } + + if (hcurs) + { +#ifndef QT_NO_XFIXES + if (X11->use_xfixes && X11->ptrXFixesSetCursorName) + X11->ptrXFixesSetCursorName(dpy, hcurs, cursorNames[cshape]); +#endif /* ! QT_NO_XFIXES */ + return; + } + +#endif /* ! QT_USE_APPROXIMATE_CURSORS */ + + uint sh; + switch (cshape) { // map Q cursor to X cursor + case Qt::ArrowCursor: + sh = XC_left_ptr; + break; + case Qt::UpArrowCursor: + sh = XC_center_ptr; + break; + case Qt::CrossCursor: + sh = XC_crosshair; + break; + case Qt::WaitCursor: + sh = XC_watch; + break; + case Qt::IBeamCursor: + sh = XC_xterm; + break; + case Qt::SizeAllCursor: + sh = XC_fleur; + break; + case Qt::PointingHandCursor: + sh = XC_hand2; + break; +#ifdef QT_USE_APPROXIMATE_CURSORS + case Qt::SizeBDiagCursor: + sh = XC_top_right_corner; + break; + case Qt::SizeFDiagCursor: + sh = XC_bottom_right_corner; + break; + case Qt::BlankCursor: + XColor bg, fg; + bg.red = 255 << 8; + bg.green = 255 << 8; + bg.blue = 255 << 8; + fg.red = 0; + fg.green = 0; + fg.blue = 0; + pm = XCreateBitmapFromData(dpy, rootwin, cur_blank_bits, 16, 16); + pmm = XCreateBitmapFromData(dpy, rootwin, cur_blank_bits, 16, 16); + hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8); + return; + break; + case Qt::SizeVerCursor: + case Qt::SplitVCursor: + sh = XC_sb_v_double_arrow; + break; + case Qt::SizeHorCursor: + case Qt::SplitHCursor: + sh = XC_sb_h_double_arrow; + break; + case Qt::WhatsThisCursor: + sh = XC_question_arrow; + break; + case Qt::ForbiddenCursor: + sh = XC_circle; + break; + case Qt::BusyCursor: + sh = XC_watch; + break; + case Qt::DragCopyCursor: + sh = XC_tcross; + break; + case Qt::DragLinkCursor: + sh = XC_center_ptr; + break; + case Qt::DragMoveCursor: + sh = XC_top_left_arrow; + break; +#endif /* QT_USE_APPROXIMATE_CURSORS */ + default: + qWarning("QCursor::update: Invalid cursor shape %d", cshape); + return; + } + hcurs = XCreateFontCursor(dpy, sh); + +#ifndef QT_NO_XFIXES + if (X11->use_xfixes && X11->ptrXFixesSetCursorName) + X11->ptrXFixesSetCursorName(dpy, hcurs, cursorNames[cshape]); +#endif /* ! QT_NO_XFIXES */ +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qdnd.cpp b/src/gui/guikernel/qdnd.cpp new file mode 100644 index 0000000000..7063828610 --- /dev/null +++ b/src/gui/guikernel/qdnd.cpp @@ -0,0 +1,491 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#include "qbitmap.h" +#include "qdrag.h" +#include "qpixmap.h" +#include "qevent.h" +#include "qfile.h" +#include "qtextcodec.h" +#include "qapplication.h" +#include "qpoint.h" +#include "qwidget.h" +#include "qbuffer.h" +#include "qimage.h" +#include "qregexp.h" +#include "qdir.h" +#include "qdnd_p.h" +#include "qimagereader.h" +#include "qimagewriter.h" +#include "qdebug.h" +#include <ctype.h> + +#include <private/qapplication_p.h> + +#ifndef QT_NO_DRAGANDDROP + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DRAGANDDROP + +//#define QDND_DEBUG + +#ifdef QDND_DEBUG +QString dragActionsToString(Qt::DropActions actions) +{ + QString str; + if (actions == Qt::IgnoreAction) { + if (!str.isEmpty()) + str += " | "; + str += "IgnoreAction"; + } + if (actions & Qt::LinkAction) { + if (!str.isEmpty()) + str += " | "; + str += "LinkAction"; + } + if (actions & Qt::CopyAction) { + if (!str.isEmpty()) + str += " | "; + str += "CopyAction"; + } + if (actions & Qt::MoveAction) { + if (!str.isEmpty()) + str += " | "; + str += "MoveAction"; + } + if ((actions & Qt::TargetMoveAction) == Qt::TargetMoveAction ) { + if (!str.isEmpty()) + str += " | "; + str += "TargetMoveAction"; + } + return str; +} + +QString KeyboardModifiersToString(Qt::KeyboardModifiers moderfies) +{ + QString str; + if (moderfies & Qt::ControlModifier) { + if (!str.isEmpty()) + str += " | "; + str += Qt::ControlModifier; + } + if (moderfies & Qt::AltModifier) { + if (!str.isEmpty()) + str += " | "; + str += Qt::AltModifier; + } + if (moderfies & Qt::ShiftModifier) { + if (!str.isEmpty()) + str += " | "; + str += Qt::ShiftModifier; + } + return str; +} +#endif + + +// the universe's only drag manager +QDragManager *QDragManager::instance = 0; + + +QDragManager::QDragManager() + : QObject(qApp) +{ + Q_ASSERT(!instance); + +#ifdef Q_WS_QWS + currentActionForOverrideCursor = Qt::IgnoreAction; +#endif + object = 0; + beingCancelled = false; + restoreCursor = false; + willDrop = false; + eventLoop = 0; + dropData = new QDropData(); + currentDropTarget = 0; +#ifdef Q_WS_X11 + xdndMimeTransferedPixmapIndex = 0; +#endif +} + + +QDragManager::~QDragManager() +{ +#ifndef QT_NO_CURSOR + if (restoreCursor) + QApplication::restoreOverrideCursor(); +#endif + instance = 0; + delete dropData; +} + +QDragManager *QDragManager::self() +{ + if (!instance && !QApplication::closingDown()) + instance = new QDragManager; + return instance; +} + +QPixmap QDragManager::dragCursor(Qt::DropAction action) const +{ + QDragPrivate * d = dragPrivate(); + if (d && d->customCursors.contains(action)) + return d->customCursors[action]; + else if (action == Qt::MoveAction) + return QApplicationPrivate::instance()->getPixmapCursor(Qt::DragMoveCursor); + else if (action == Qt::CopyAction) + return QApplicationPrivate::instance()->getPixmapCursor(Qt::DragCopyCursor); + else if (action == Qt::LinkAction) + return QApplicationPrivate::instance()->getPixmapCursor(Qt::DragLinkCursor); +#ifdef Q_WS_WIN + else if (action == Qt::IgnoreAction) + return QApplicationPrivate::instance()->getPixmapCursor(Qt::ForbiddenCursor); +#endif + return QPixmap(); +} + +bool QDragManager::hasCustomDragCursors() const +{ + QDragPrivate * d = dragPrivate(); + return d && !d->customCursors.isEmpty(); +} + +Qt::DropAction QDragManager::defaultAction(Qt::DropActions possibleActions, + Qt::KeyboardModifiers modifiers) const +{ +#ifdef QDND_DEBUG + qDebug("QDragManager::defaultAction(Qt::DropActions possibleActions)"); + qDebug("keyboard modifiers : %s", KeyboardModifiersToString(modifiers).latin1()); +#endif + + QDragPrivate *d = dragPrivate(); + Qt::DropAction defaultAction = d ? d->defaultDropAction : Qt::IgnoreAction; + + if (defaultAction == Qt::IgnoreAction) { + //This means that the drag was initiated by QDrag::start and we need to + //preserve the old behavior +#ifdef Q_WS_MAC + defaultAction = Qt::MoveAction; +#else + defaultAction = Qt::CopyAction; +#endif + } + +#ifdef Q_WS_MAC + if (modifiers & Qt::ControlModifier && modifiers & Qt::AltModifier) + defaultAction = Qt::LinkAction; + else if (modifiers & Qt::AltModifier) + defaultAction = Qt::CopyAction; + else if (modifiers & Qt::ControlModifier) + defaultAction = Qt::MoveAction; +#else + if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier) + defaultAction = Qt::LinkAction; + else if (modifiers & Qt::ControlModifier) + defaultAction = Qt::CopyAction; + else if (modifiers & Qt::ShiftModifier) + defaultAction = Qt::MoveAction; + else if (modifiers & Qt::AltModifier) + defaultAction = Qt::LinkAction; +#endif + + // if the object is set take the list of possibles from it + if (object) + possibleActions = object->d_func()->possible_actions; + +#ifdef QDND_DEBUG + qDebug("possible actions : %s", dragActionsToString(possibleActions).latin1()); +#endif + + // Check if the action determined is allowed + if (!(possibleActions & defaultAction)) { + if (possibleActions & Qt::CopyAction) + defaultAction = Qt::CopyAction; + else if (possibleActions & Qt::MoveAction) + defaultAction = Qt::MoveAction; + else if (possibleActions & Qt::LinkAction) + defaultAction = Qt::LinkAction; + else + defaultAction = Qt::IgnoreAction; + } + +#ifdef QDND_DEBUG + qDebug("default action : %s", dragActionsToString(defaultAction).latin1()); +#endif + + return defaultAction; +} + +void QDragManager::setCurrentTarget(QWidget *target, bool dropped) +{ + if (currentDropTarget == target) + return; + + currentDropTarget = target; + if (!dropped && object) { + object->d_func()->target = target; + emit object->targetChanged(target); + } + +} + +QWidget *QDragManager::currentTarget() +{ + return currentDropTarget; +} + +#endif + +QDropData::QDropData() + : QInternalMimeData() +{ +} + +QDropData::~QDropData() +{ +} +#endif // QT_NO_DRAGANDDROP + +#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD)) + +static QStringList imageReadMimeFormats() +{ + QStringList formats; + QList<QByteArray> imageFormats = QImageReader::supportedImageFormats(); + for (int i = 0; i < imageFormats.size(); ++i) { + QString format = QLatin1String("image/"); + format += QString::fromLatin1(imageFormats.at(i).toLower()); + formats.append(format); + } + + //put png at the front because it is best + int pngIndex = formats.indexOf(QLatin1String("image/png")); + if (pngIndex != -1 && pngIndex != 0) + formats.move(pngIndex, 0); + + return formats; +} + + +static QStringList imageWriteMimeFormats() +{ + QStringList formats; + QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats(); + for (int i = 0; i < imageFormats.size(); ++i) { + QString format = QLatin1String("image/"); + format += QString::fromLatin1(imageFormats.at(i).toLower()); + formats.append(format); + } + + //put png at the front because it is best + int pngIndex = formats.indexOf(QLatin1String("image/png")); + if (pngIndex != -1 && pngIndex != 0) + formats.move(pngIndex, 0); + + return formats; +} + +QInternalMimeData::QInternalMimeData() + : QMimeData() +{ +} + +QInternalMimeData::~QInternalMimeData() +{ +} + +bool QInternalMimeData::hasFormat(const QString &mimeType) const +{ + bool foundFormat = hasFormat_sys(mimeType); + if (!foundFormat && mimeType == QLatin1String("application/x-qt-image")) { + QStringList imageFormats = imageReadMimeFormats(); + for (int i = 0; i < imageFormats.size(); ++i) { + if ((foundFormat = hasFormat_sys(imageFormats.at(i)))) + break; + } + } + return foundFormat; +} + +QStringList QInternalMimeData::formats() const +{ + QStringList realFormats = formats_sys(); + if (!realFormats.contains(QLatin1String("application/x-qt-image"))) { + QStringList imageFormats = imageReadMimeFormats(); + for (int i = 0; i < imageFormats.size(); ++i) { + if (realFormats.contains(imageFormats.at(i))) { + realFormats += QLatin1String("application/x-qt-image"); + break; + } + } + } + return realFormats; +} + +QVariant QInternalMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const +{ + QVariant data = retrieveData_sys(mimeType, type); + if (mimeType == QLatin1String("application/x-qt-image")) { + if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty())) { + // try to find an image + QStringList imageFormats = imageReadMimeFormats(); + for (int i = 0; i < imageFormats.size(); ++i) { + data = retrieveData_sys(imageFormats.at(i), type); + if (data.isNull() || (data.type() == QVariant::ByteArray && data.toByteArray().isEmpty())) + continue; + break; + } + } + // we wanted some image type, but all we got was a byte array. Convert it to an image. + if (data.type() == QVariant::ByteArray + && (type == QVariant::Image || type == QVariant::Pixmap || type == QVariant::Bitmap)) + data = QImage::fromData(data.toByteArray()); + + } else if (mimeType == QLatin1String("application/x-color") && data.type() == QVariant::ByteArray) { + QColor c; + QByteArray ba = data.toByteArray(); + if (ba.size() == 8) { + ushort * colBuf = (ushort *)ba.data(); + c.setRgbF(qreal(colBuf[0]) / qreal(0xFFFF), + qreal(colBuf[1]) / qreal(0xFFFF), + qreal(colBuf[2]) / qreal(0xFFFF), + qreal(colBuf[3]) / qreal(0xFFFF)); + data = c; + } else { + qWarning("Qt: Invalid color format"); + } + } else if (data.type() != type && data.type() == QVariant::ByteArray) { + // try to use mime data's internal conversion stuf. + QInternalMimeData *that = const_cast<QInternalMimeData *>(this); + that->setData(mimeType, data.toByteArray()); + data = QMimeData::retrieveData(mimeType, type); + that->clear(); + } + return data; +} + +bool QInternalMimeData::canReadData(const QString &mimeType) +{ + return imageReadMimeFormats().contains(mimeType); +} + +// helper functions for rendering mimedata to the system, this is needed because QMimeData is in core. +QStringList QInternalMimeData::formatsHelper(const QMimeData *data) +{ + QStringList realFormats = data->formats(); + if (realFormats.contains(QLatin1String("application/x-qt-image"))) { + // add all supported image formats + QStringList imageFormats = imageWriteMimeFormats(); + for (int i = 0; i < imageFormats.size(); ++i) { + if (!realFormats.contains(imageFormats.at(i))) + realFormats.append(imageFormats.at(i)); + } + } + return realFormats; +} + +bool QInternalMimeData::hasFormatHelper(const QString &mimeType, const QMimeData *data) +{ + + bool foundFormat = data->hasFormat(mimeType); + if (!foundFormat) { + if (mimeType == QLatin1String("application/x-qt-image")) { + // check all supported image formats + QStringList imageFormats = imageWriteMimeFormats(); + for (int i = 0; i < imageFormats.size(); ++i) { + if ((foundFormat = data->hasFormat(imageFormats.at(i)))) + break; + } + } else if (mimeType.startsWith(QLatin1String("image/"))) { + return data->hasImage() && imageWriteMimeFormats().contains(mimeType); + } + } + return foundFormat; +} + +QByteArray QInternalMimeData::renderDataHelper(const QString &mimeType, const QMimeData *data) +{ + QByteArray ba; + if (mimeType == QLatin1String("application/x-color")) { + /* QMimeData can only provide colors as QColor or the name + of a color as a QByteArray or a QString. So we need to do + the conversion to application/x-color here. + The application/x-color format is : + type: application/x-color + format: 16 + data[0]: red + data[1]: green + data[2]: blue + data[3]: opacity + */ + ba.resize(8); + ushort * colBuf = (ushort *)ba.data(); + QColor c = qvariant_cast<QColor>(data->colorData()); + colBuf[0] = ushort(c.redF() * 0xFFFF); + colBuf[1] = ushort(c.greenF() * 0xFFFF); + colBuf[2] = ushort(c.blueF() * 0xFFFF); + colBuf[3] = ushort(c.alphaF() * 0xFFFF); + } else { + ba = data->data(mimeType); + if (ba.isEmpty()) { + if (mimeType == QLatin1String("application/x-qt-image") && data->hasImage()) { + QImage image = qvariant_cast<QImage>(data->imageData()); + QBuffer buf(&ba); + buf.open(QBuffer::WriteOnly); + // would there not be PNG ?? + image.save(&buf, "PNG"); + } else if (mimeType.startsWith(QLatin1String("image/")) && data->hasImage()) { + QImage image = qvariant_cast<QImage>(data->imageData()); + QBuffer buf(&ba); + buf.open(QBuffer::WriteOnly); + image.save(&buf, mimeType.mid(mimeType.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper()); + } + } + } + return ba; +} + +#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qdnd_mac.mm b/src/gui/guikernel/qdnd_mac.mm new file mode 100644 index 0000000000..3af2ba007c --- /dev/null +++ b/src/gui/guikernel/qdnd_mac.mm @@ -0,0 +1,753 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qapplication.h" +#ifndef QT_NO_DRAGANDDROP +#include "qbitmap.h" +#include "qcursor.h" +#include "qevent.h" +#include "qpainter.h" +#include "qurl.h" +#include "qwidget.h" +#include "qfile.h" +#include "qfileinfo.h" +#include <stdlib.h> +#include <string.h> +#ifndef QT_NO_ACCESSIBILITY +# include "qaccessible.h" +#endif + +#include <private/qapplication_p.h> +#include <private/qwidget_p.h> +#include <private/qdnd_p.h> +#include <private/qt_mac_p.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +QMacDndAnswerRecord qt_mac_dnd_answer_rec; + +/***************************************************************************** + QDnD debug facilities + *****************************************************************************/ +//#define DEBUG_DRAG_EVENTS +//#define DEBUG_DRAG_PROMISES + +/***************************************************************************** + QDnD globals + *****************************************************************************/ +bool qt_mac_in_drag = false; +#ifndef QT_MAC_USE_COCOA +static DragReference qt_mac_current_dragRef = 0; +#endif + +/***************************************************************************** + Externals + *****************************************************************************/ +extern void qt_mac_send_modifiers_changed(quint32, QObject *); //qapplication_mac.cpp +extern uint qGlobalPostedEventsCount(); //qapplication.cpp +extern RgnHandle qt_mac_get_rgn(); // qregion_mac.cpp +extern void qt_mac_dispose_rgn(RgnHandle); // qregion_mac.cpp +/***************************************************************************** + QDnD utility functions + *****************************************************************************/ + +//default pixmap +static const int default_pm_hotx = -2; +static const int default_pm_hoty = -16; +#ifndef QT_MAC_USE_COCOA +static const char * const default_pm[] = { + "13 9 3 1", + ". c None", + " c #000000", + "X c #FFFFFF", + "X X X X X X X", + " X X X X X X ", + "X ......... X", + " X.........X ", + "X ......... X", + " X.........X ", + "X ......... X", + " X X X X X X ", + "X X X X X X X", +}; +#endif + +//action management +#ifdef DEBUG_DRAG_EVENTS +# define MAP_MAC_ENUM(x) x, #x +#else +# define MAP_MAC_ENUM(x) x +#endif +struct mac_enum_mapper +{ + int mac_code; + int qt_code; +#ifdef DEBUG_DRAG_EVENTS + char *qt_desc; +#endif +}; +#ifndef QT_MAC_USE_COCOA +static mac_enum_mapper dnd_action_symbols[] = { + { kDragActionAlias, MAP_MAC_ENUM(Qt::LinkAction) }, + { kDragActionMove, MAP_MAC_ENUM(Qt::MoveAction) }, + { kDragActionGeneric, MAP_MAC_ENUM(Qt::CopyAction) }, + { kDragActionCopy, MAP_MAC_ENUM(Qt::CopyAction) }, + { 0, MAP_MAC_ENUM(0) } +}; +static DragActions qt_mac_dnd_map_qt_actions(Qt::DropActions qActions) +{ + DragActions ret = kDragActionNothing; + for(int i = 0; dnd_action_symbols[i].qt_code; ++i) { + if(qActions & dnd_action_symbols[i].qt_code) + ret |= dnd_action_symbols[i].mac_code; + } + return ret; +} +static Qt::DropActions qt_mac_dnd_map_mac_actions(DragActions macActions) +{ +#ifdef DEBUG_DRAG_EVENTS + qDebug("Converting DND ActionList: 0x%lx", macActions); +#endif + Qt::DropActions ret = Qt::IgnoreAction; + for(int i = 0; dnd_action_symbols[i].qt_code; ++i) { +#ifdef DEBUG_DRAG_EVENTS + qDebug(" %d) [%s] : %s", i, dnd_action_symbols[i].qt_desc, + (macActions & dnd_action_symbols[i].mac_code) ? "true" : "false"); +#endif + if(macActions & dnd_action_symbols[i].mac_code) + ret |= Qt::DropAction(dnd_action_symbols[i].qt_code); + } + return ret; +} +static Qt::DropAction qt_mac_dnd_map_mac_default_action(DragActions macActions) +{ + static Qt::DropAction preferred_actions[] = { Qt::CopyAction, Qt::LinkAction, //in order + Qt::MoveAction, Qt::IgnoreAction }; + Qt::DropAction ret = Qt::IgnoreAction; + const Qt::DropActions qtActions = qt_mac_dnd_map_mac_actions(macActions); + for(int i = 0; preferred_actions[i] != Qt::IgnoreAction; ++i) { + if(qtActions & preferred_actions[i]) { + ret = preferred_actions[i]; + break; + } + } +#ifdef DEBUG_DRAG_EVENTS + for(int i = 0; dnd_action_symbols[i].qt_code; ++i) { + if(dnd_action_symbols[i].qt_code == ret) { + qDebug("Got default action: %s [0x%lx]", dnd_action_symbols[i].qt_desc, macActions); + break; + } + } +#endif + return ret; +} +static void qt_mac_dnd_update_action(DragReference dragRef) { + SInt16 modifiers; + GetDragModifiers(dragRef, &modifiers, 0, 0); + qt_mac_send_modifiers_changed(modifiers, qApp); + + Qt::DropAction qtAction = Qt::IgnoreAction; + { + DragActions macAllowed = kDragActionNothing; + GetDragDropAction(dragRef, &macAllowed); + Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(macAllowed); + qtAction = QDragManager::self()->defaultAction(qtAllowed, QApplication::keyboardModifiers()); +#if 1 + if(!(qtAllowed & qtAction)) + qtAction = qt_mac_dnd_map_mac_default_action(macAllowed); +#endif + } + DragActions macAction = qt_mac_dnd_map_qt_actions(qtAction); + + DragActions currentActions; + GetDragDropAction(dragRef, ¤tActions); + if(currentActions != macAction) { + SetDragDropAction(dragRef, macAction); + QDragManager::self()->emitActionChanged(qtAction); + } +} +#endif // !QT_MAC_USE_COCOA + +/***************************************************************************** + DnD functions + *****************************************************************************/ +bool QDropData::hasFormat_sys(const QString &mime) const +{ +#ifndef QT_MAC_USE_COCOA + OSPasteboardRef board; + if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) { + qDebug("DnD: Cannot get PasteBoard!"); + return false; + } + return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mime); +#else + Q_UNUSED(mime); + return false; +#endif // !QT_MAC_USE_COCOA +} + +QVariant QDropData::retrieveData_sys(const QString &mime, QVariant::Type type) const +{ +#ifndef QT_MAC_USE_COCOA + OSPasteboardRef board; + if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) { + qDebug("DnD: Cannot get PasteBoard!"); + return QVariant(); + } + return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mime, type); +#else + Q_UNUSED(mime); + Q_UNUSED(type); + return QVariant(); +#endif // !QT_MAC_USE_COCOA +} + +QStringList QDropData::formats_sys() const +{ +#ifndef QT_MAC_USE_COCOA + OSPasteboardRef board; + if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) { + qDebug("DnD: Cannot get PasteBoard!"); + return QStringList(); + } + return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats(); +#else + return QStringList(); +#endif +} + +void QDragManager::timerEvent(QTimerEvent*) +{ +} + +bool QDragManager::eventFilter(QObject *, QEvent *) +{ + return false; +} + +void QDragManager::updateCursor() +{ +} + +void QDragManager::cancel(bool) +{ + if(object) { + beingCancelled = true; + object = 0; + } +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd); +#endif +} + +void QDragManager::move(const QPoint &) +{ +} + +void QDragManager::drop() +{ +} + +/** + If a drop action is already set on the carbon event + (from e.g. an earlier enter event), we insert the same + action on the new Qt event that has yet to be sendt. +*/ +static inline bool qt_mac_set_existing_drop_action(const DragRef &dragRef, QDropEvent &event) +{ +#ifndef QT_MAC_USE_COCOA + DragActions currentAction = kDragActionNothing; + OSStatus err = GetDragDropAction(dragRef, ¤tAction); + if (err == noErr && currentAction != kDragActionNothing) { + // This looks a bit evil, but we only ever set one action, so it's OK. + event.setDropAction(Qt::DropAction(int(qt_mac_dnd_map_mac_actions(currentAction)))); + return true; + } +#else + Q_UNUSED(dragRef); + Q_UNUSED(event); +#endif + return false; +} + +/** + If an answer rect has been set on the event (after being sent + to the global event processor), we store that rect so we can + check if the mouse is in the same area upon next drag move event. +*/ +void qt_mac_copy_answer_rect(const QDragMoveEvent &event) +{ + if (!event.answerRect().isEmpty()) { + qt_mac_dnd_answer_rec.rect = event.answerRect(); + qt_mac_dnd_answer_rec.buttons = event.mouseButtons(); + qt_mac_dnd_answer_rec.modifiers = event.keyboardModifiers(); + qt_mac_dnd_answer_rec.lastAction = event.dropAction(); + } +} + +bool qt_mac_mouse_inside_answer_rect(QPoint mouse) +{ + if (!qt_mac_dnd_answer_rec.rect.isEmpty() + && qt_mac_dnd_answer_rec.rect.contains(mouse) + && QApplication::mouseButtons() == qt_mac_dnd_answer_rec.buttons + && QApplication::keyboardModifiers() == qt_mac_dnd_answer_rec.modifiers) + return true; + else + return false; +} + +bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef) +{ +#ifndef QT_MAC_USE_COCOA + Q_Q(QWidget); + qt_mac_current_dragRef = dragRef; + if (kind != kEventControlDragLeave) + qt_mac_dnd_update_action(dragRef); + + Point mouse; + GetDragMouse(dragRef, &mouse, 0L); + if(!mouse.h && !mouse.v) + GetGlobalMouse(&mouse); + + if(QApplicationPrivate::modalState()) { + for(QWidget *modal = q; modal; modal = modal->parentWidget()) { + if(modal->isWindow()) { + if(modal != QApplication::activeModalWidget()) + return noErr; + break; + } + } + } + + //lookup the possible actions + DragActions allowed = kDragActionNothing; + GetDragAllowableActions(dragRef, &allowed); + Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(allowed); + + //lookup the source dragAccepted + QMimeData *dropdata = QDragManager::self()->dropData; + if(QDragManager::self()->source()) + dropdata = QDragManager::self()->dragPrivate()->data; + + // 'interrestedInDrag' should end up being 'true' if a later drop + // will be accepted by the widget for the current mouse position + bool interrestedInDrag = true; + + //Dispatch events + if (kind == kEventControlDragWithin) { + if (qt_mac_mouse_inside_answer_rect(q->mapFromGlobal(QPoint(mouse.h, mouse.v)))) + return qt_mac_dnd_answer_rec.lastAction == Qt::IgnoreAction; + else + qt_mac_dnd_answer_rec.clear(); + + QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + + // Accept the event by default if a + // drag action exists on the carbon event + if (qt_mac_set_existing_drop_action(dragRef, qDMEvent)) + qDMEvent.accept(); + + QApplication::sendEvent(q, &qDMEvent); + if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) + interrestedInDrag = false; + + qt_mac_copy_answer_rect(qDMEvent); + SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction())); + + } else if (kind == kEventControlDragEnter) { + qt_mac_dnd_answer_rec.clear(); + + QDragEnterEvent qDEEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + qt_mac_set_existing_drop_action(dragRef, qDEEvent); + QApplication::sendEvent(q, &qDEEvent); + SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDEEvent.dropAction())); + + if (!qDEEvent.isAccepted()) + // The widget is simply not interested in this + // drag. So tell carbon this by returning 'false'. We will then + // not receive any further move, drop or leave events for this widget. + return false; + else { + // Documentation states that a drag move event is sent immediately after + // a drag enter event. So we do that. This will honor widgets overriding + // 'dragMoveEvent' only, and not 'dragEnterEvent' + QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + qDMEvent.accept(); // accept by default, since enter event was accepted. + qDMEvent.setDropAction(qDEEvent.dropAction()); + QApplication::sendEvent(q, &qDMEvent); + if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) + interrestedInDrag = false; + + qt_mac_copy_answer_rect(qDMEvent); + SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction())); + } + + } else if(kind == kEventControlDragLeave) { + QDragLeaveEvent de; + QApplication::sendEvent(q, &de); + } else if(kind == kEventControlDragReceive) { + QDropEvent de(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + if(QDragManager::self()->object) + QDragManager::self()->dragPrivate()->target = q; + QApplication::sendEvent(q, &de); + if(!de.isAccepted()) { + interrestedInDrag = false; + SetDragDropAction(dragRef, kDragActionNothing); + } else { + if(QDragManager::self()->object) + QDragManager::self()->dragPrivate()->executed_action = de.dropAction(); + SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(de.dropAction())); + } + } + +#ifdef DEBUG_DRAG_EVENTS + { + const char *desc = 0; + switch(kind) { + case kEventControlDragWithin: desc = "DragMove"; break; + case kEventControlDragEnter: desc = "DragEnter"; break; + case kEventControlDragLeave: desc = "DragLeave"; break; + case kEventControlDragReceive: desc = "DragDrop"; break; + } + if(desc) { + QPoint pos(q->mapFromGlobal(QPoint(mouse.h, mouse.v))); + qDebug("Sending <%s>(%d, %d) event to %p(%s::%s) [%d] (%p)", + desc, pos.x(), pos.y(), q, q->metaObject()->className(), + q->objectName().toLocal8Bit().constData(), ret, dragRef); + } + } +#endif + + //set the cursor + bool found_cursor = false; + if(kind == kEventControlDragWithin || kind == kEventControlDragEnter) { + ThemeCursor cursor = kThemeNotAllowedCursor; + found_cursor = true; + if (interrestedInDrag) { + DragActions action = kDragActionNothing; + GetDragDropAction(dragRef, &action); + switch(qt_mac_dnd_map_mac_default_action(action)) { + case Qt::IgnoreAction: + cursor = kThemeNotAllowedCursor; + break; + case Qt::MoveAction: + cursor = kThemeArrowCursor; + break; + case Qt::CopyAction: + cursor = kThemeCopyArrowCursor; + break; + case Qt::LinkAction: + cursor = kThemeAliasArrowCursor; + break; + default: + cursor = kThemeNotAllowedCursor; + break; + } + } + SetThemeCursor(cursor); + } + if(found_cursor) { + qt_mac_set_cursor(0); //just use our's + } else { + QCursor cursor(Qt::ArrowCursor); + if(qApp && qApp->overrideCursor()) { + cursor = *qApp->overrideCursor(); + } else if(q) { + for(QWidget *p = q; p; p = p->parentWidget()) { + if(p->isEnabled() && p->testAttribute(Qt::WA_SetCursor)) { + cursor = p->cursor(); + break; + } + } + } + qt_mac_set_cursor(&cursor); + } + + //idle things + if(qGlobalPostedEventsCount()) { + QApplication::sendPostedEvents(); + QApplication::flush(); + } + + // If this was not a drop, tell carbon that we will be interresed in receiving more + // events for the current drag. We do that by returning true. + if (kind == kEventControlDragReceive) + return interrestedInDrag; + else + return true; +#else + Q_UNUSED(kind); + Q_UNUSED(dragRef); + return false; +#endif // !QT_MAC_USE_COCOA +} + +#ifndef QT_MAC_USE_COCOA +Qt::DropAction QDragManager::drag(QDrag *o) +{ + + if(qt_mac_in_drag) { //just make sure.. + qWarning("Qt: Internal error: WH0A, unexpected condition reached"); + return Qt::IgnoreAction; + } + if(object == o) + return Qt::IgnoreAction; + /* At the moment it seems clear that Mac OS X does not want to drag with a non-left button + so we just bail early to prevent it */ + if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary)) + return Qt::IgnoreAction; + + if(object) { + dragPrivate()->source->removeEventFilter(this); + cancel(); + beingCancelled = false; + } + + object = o; + dragPrivate()->target = 0; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart); +#endif + + //setup the data + QMacPasteboard dragBoard(QMacPasteboardMime::MIME_DND); + dragBoard.setMimeData(dragPrivate()->data); + + //create the drag + OSErr result; + DragRef dragRef; + if((result = NewDragWithPasteboard(dragBoard.pasteBoard(), &dragRef))) + return Qt::IgnoreAction; + //setup the actions + DragActions possibleActions = qt_mac_dnd_map_qt_actions(dragPrivate()->possible_actions); + SetDragAllowableActions(dragRef, //local + possibleActions, + true); + SetDragAllowableActions(dragRef, //remote (same as local) + possibleActions, + false); + + + QPoint hotspot; + QPixmap pix = dragPrivate()->pixmap; + if(pix.isNull()) { + if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) { + //get the string + QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text() + : dragPrivate()->data->urls().first().toString(); + if(s.length() > 26) + s = s.left(23) + QChar(0x2026); + if(!s.isEmpty()) { + //draw it + QFont f(qApp->font()); + f.setPointSize(12); + QFontMetrics fm(f); + const int width = fm.width(s); + const int height = fm.height(); + if(width > 0 && height > 0) { + QPixmap tmp(width, height); + QPainter p(&tmp); + p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0); + p.setPen(Qt::color1); + p.setFont(f); + p.drawText(0, fm.ascent(), s); + p.end(); + //save it + pix = tmp; + hotspot = QPoint(tmp.width() / 2, tmp.height() / 2); + } + } + } else { + pix = QPixmap(default_pm); + hotspot = QPoint(default_pm_hotx, default_pm_hoty); + } + } else { + hotspot = dragPrivate()->hotspot; + } + + //so we must fake an event + EventRecord fakeEvent; + GetGlobalMouse(&(fakeEvent.where)); + fakeEvent.message = 0; + fakeEvent.what = mouseDown; + fakeEvent.when = EventTimeToTicks(GetCurrentEventTime()); + fakeEvent.modifiers = GetCurrentKeyModifiers(); + if(GetCurrentEventButtonState() & 2) + fakeEvent.modifiers |= controlKey; + + //find the hotspot in relation to the pixmap + Point boundsPoint; + boundsPoint.h = fakeEvent.where.h - hotspot.x(); + boundsPoint.v = fakeEvent.where.v - hotspot.y(); + Rect boundsRect; + SetRect(&boundsRect, boundsPoint.h, boundsPoint.v, boundsPoint.h + pix.width(), boundsPoint.v + pix.height()); + + //set the drag image + QRegion dragRegion(boundsPoint.h, boundsPoint.v, pix.width(), pix.height()), pixRegion; + if(!pix.isNull()) { + HIPoint hipoint = { -hotspot.x(), -hotspot.y() }; + CGImageRef img = (CGImageRef)pix.macCGHandle(); + SetDragImageWithCGImage(dragRef, img, &hipoint, 0); + CGImageRelease(img); + } + + SetDragItemBounds(dragRef, (ItemReference)1 , &boundsRect); + { //do the drag + qt_mac_in_drag = true; + qt_mac_dnd_update_action(dragRef); + result = TrackDrag(dragRef, &fakeEvent, QMacSmartQuickDrawRegion(dragRegion.toQDRgn())); + qt_mac_in_drag = false; + } + object = 0; + if(result == noErr) { + // Check if the receiver points us to + // a file location. If so, we need to do + // the file copy/move ourselves: + QCFType<CFURLRef> pasteLocation = 0; + PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation); + if (pasteLocation){ + Qt::DropAction action = o->d_func()->defaultDropAction; + if (action == Qt::IgnoreAction){ + if (o->d_func()->possible_actions & Qt::MoveAction) + action = Qt::MoveAction; + else if (o->d_func()->possible_actions & Qt::CopyAction) + action = Qt::CopyAction; + + } + bool atleastOne = false; + QList<QUrl> urls = o->mimeData()->urls(); + for (int i = 0; i < urls.size(); ++i){ + QUrl fromUrl = urls.at(i); + QString filename = QFileInfo(fromUrl.path()).fileName(); + QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename); + if (action == Qt::MoveAction){ + if (QFile::rename(fromUrl.path(), toUrl.path())) + atleastOne = true; + } else if (action == Qt::CopyAction){ + if (QFile::copy(fromUrl.path(), toUrl.path())) + atleastOne = true; + } + } + if (atleastOne){ + DisposeDrag(dragRef); + o->setMimeData(0); + o->deleteLater(); + return action; + } + } + + DragActions ret = kDragActionNothing; + GetDragDropAction(dragRef, &ret); + DisposeDrag(dragRef); //cleanup + o->setMimeData(0); + o->deleteLater(); + return qt_mac_dnd_map_mac_default_action(ret); + } + DisposeDrag(dragRef); //cleanup + return Qt::IgnoreAction; +} +#endif + +void QDragManager::updatePixmap() +{ +} + +QCocoaDropData::QCocoaDropData(CFStringRef pasteboard) + : QInternalMimeData() +{ + NSString* pasteboardName = (NSString*)pasteboard; + [pasteboardName retain]; + dropPasteboard = pasteboard; +} + +QCocoaDropData::~QCocoaDropData() +{ + NSString* pasteboardName = (NSString*)dropPasteboard; + [pasteboardName release]; +} + +QStringList QCocoaDropData::formats_sys() const +{ + QStringList formats; + OSPasteboardRef board; + if (PasteboardCreate(dropPasteboard, &board) != noErr) { + qDebug("DnD: Cannot get PasteBoard!"); + return formats; + } + formats = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats(); + return formats; +} + +QVariant QCocoaDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const +{ + QVariant data; + OSPasteboardRef board; + if (PasteboardCreate(dropPasteboard, &board) != noErr) { + qDebug("DnD: Cannot get PasteBoard!"); + return data; + } + data = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mimeType, type); + CFRelease(board); + return data; +} + +bool QCocoaDropData::hasFormat_sys(const QString &mimeType) const +{ + bool has = false; + OSPasteboardRef board; + if (PasteboardCreate(dropPasteboard, &board) != noErr) { + qDebug("DnD: Cannot get PasteBoard!"); + return has; + } + has = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mimeType); + CFRelease(board); + return has; +} + +#endif // QT_NO_DRAGANDDROP +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qdnd_p.h b/src/gui/guikernel/qdnd_p.h new file mode 100644 index 0000000000..754366637c --- /dev/null +++ b/src/gui/guikernel/qdnd_p.h @@ -0,0 +1,336 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDND_P_H +#define QDND_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qobject.h" +#include "QtCore/qmap.h" +#include "QtGui/qmime.h" +#include "QtGui/qdrag.h" +#include "QtGui/qpixmap.h" +#include "QtGui/qcursor.h" +#include "QtCore/qpoint.h" +#include "private/qobject_p.h" +#ifdef Q_WS_MAC +# include "private/qt_mac_p.h" +#endif + +#if defined(Q_WS_WIN) +# include <qt_windows.h> +# include <objidl.h> +#endif + +QT_BEGIN_NAMESPACE + +class QEventLoop; + +#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD)) + +class Q_GUI_EXPORT QInternalMimeData : public QMimeData +{ + Q_OBJECT +public: + QInternalMimeData(); + ~QInternalMimeData(); + + bool hasFormat(const QString &mimeType) const; + QStringList formats() const; + static bool canReadData(const QString &mimeType); + + + static QStringList formatsHelper(const QMimeData *data); + static bool hasFormatHelper(const QString &mimeType, const QMimeData *data); + static QByteArray renderDataHelper(const QString &mimeType, const QMimeData *data); + +protected: + QVariant retrieveData(const QString &mimeType, QVariant::Type type) const; + + virtual bool hasFormat_sys(const QString &mimeType) const = 0; + virtual QStringList formats_sys() const = 0; + virtual QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const = 0; +}; + +#ifdef Q_WS_WIN +class QOleDataObject : public IDataObject +{ +public: + explicit QOleDataObject(QMimeData *mimeData); + virtual ~QOleDataObject(); + + void releaseQt(); + const QMimeData *mimeData() const; + DWORD reportedPerformedEffect() const; + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj); + STDMETHOD_(ULONG,AddRef)(void); + STDMETHOD_(ULONG,Release)(void); + + // IDataObject methods + STDMETHOD(GetData)(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium); + STDMETHOD(GetDataHere)(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium); + STDMETHOD(QueryGetData)(LPFORMATETC pformatetc); + STDMETHOD(GetCanonicalFormatEtc)(LPFORMATETC pformatetc, LPFORMATETC pformatetcOut); + STDMETHOD(SetData)(LPFORMATETC pformatetc, STGMEDIUM FAR * pmedium, + BOOL fRelease); + STDMETHOD(EnumFormatEtc)(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc); + STDMETHOD(DAdvise)(FORMATETC FAR* pFormatetc, DWORD advf, + LPADVISESINK pAdvSink, DWORD FAR* pdwConnection); + STDMETHOD(DUnadvise)(DWORD dwConnection); + STDMETHOD(EnumDAdvise)(LPENUMSTATDATA FAR* ppenumAdvise); + +private: + ULONG m_refs; + QPointer<QMimeData> data; + int CF_PERFORMEDDROPEFFECT; + DWORD performedEffect; +}; + +class QOleEnumFmtEtc : public IEnumFORMATETC +{ +public: + explicit QOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs); + explicit QOleEnumFmtEtc(const QVector<LPFORMATETC> &lpfmtetcs); + virtual ~QOleEnumFmtEtc(); + + bool isNull() const; + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj); + STDMETHOD_(ULONG,AddRef)(void); + STDMETHOD_(ULONG,Release)(void); + + // IEnumFORMATETC methods + STDMETHOD(Next)(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched); + STDMETHOD(Skip)(ULONG celt); + STDMETHOD(Reset)(void); + STDMETHOD(Clone)(LPENUMFORMATETC FAR* newEnum); + +private: + bool copyFormatEtc(LPFORMATETC dest, LPFORMATETC src) const; + + ULONG m_dwRefs; + ULONG m_nIndex; + QVector<LPFORMATETC> m_lpfmtetcs; + bool m_isNull; +}; + +#endif + +#endif //QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD + +#ifndef QT_NO_DRAGANDDROP + +class QDragPrivate : public QObjectPrivate +{ +public: + QWidget *source; + QWidget *target; + QMimeData *data; + QPixmap pixmap; + QPoint hotspot; + Qt::DropActions possible_actions; + Qt::DropAction executed_action; + QMap<Qt::DropAction, QPixmap> customCursors; + Qt::DropAction defaultDropAction; +}; + +class QDropData : public QInternalMimeData +{ + Q_OBJECT +public: + QDropData(); + ~QDropData(); + +protected: + bool hasFormat_sys(const QString &mimeType) const; + QStringList formats_sys() const; + QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const; + +#if defined(Q_WS_WIN) +public: + LPDATAOBJECT currentDataObject; +#endif +}; + +class QDragManager: public QObject { + Q_OBJECT + + QDragManager(); + ~QDragManager(); + // only friend classes can use QDragManager. + friend class QDrag; + friend class QDragMoveEvent; + friend class QDropEvent; + friend class QApplication; +#ifdef Q_WS_MAC + friend class QWidgetPrivate; //dnd is implemented here +#endif + + bool eventFilter(QObject *, QEvent *); + void timerEvent(QTimerEvent*); + +public: + Qt::DropAction drag(QDrag *); + + void cancel(bool deleteSource = true); + void move(const QPoint &); + void drop(); + void updatePixmap(); + QWidget *source() const { return object ? object->d_func()->source : 0; } + QDragPrivate *dragPrivate() const { return object ? object->d_func() : 0; } + static QDragPrivate *dragPrivate(QDrag *drag) { return drag ? drag->d_func() : 0; } + + static QDragManager *self(); + Qt::DropAction defaultAction(Qt::DropActions possibleActions, + Qt::KeyboardModifiers modifiers) const; + + QDrag *object; + + void updateCursor(); + + bool beingCancelled; + bool restoreCursor; + bool willDrop; + QEventLoop *eventLoop; + + QPixmap dragCursor(Qt::DropAction action) const; + + bool hasCustomDragCursors() const; + + QDropData *dropData; + + void emitActionChanged(Qt::DropAction newAction) { if (object) emit object->actionChanged(newAction); } + + void setCurrentTarget(QWidget *target, bool dropped = false); + QWidget *currentTarget(); + +#ifdef Q_WS_X11 + QPixmap xdndMimeTransferedPixmap[2]; + int xdndMimeTransferedPixmapIndex; +#endif + +private: +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) + Qt::DropAction currentActionForOverrideCursor; +#endif +#ifdef Q_OS_SYMBIAN +#ifndef QT_NO_CURSOR + QCursor overrideCursor; +#endif +#endif + QWidget *currentDropTarget; + + static QDragManager *instance; + Q_DISABLE_COPY(QDragManager) +}; + + +#if defined(Q_WS_WIN) + +class QOleDropTarget : public IDropTarget +{ +public: + QOleDropTarget(QWidget* w); + virtual ~QOleDropTarget() {} + + void releaseQt(); + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + + // IDropTarget methods + STDMETHOD(DragEnter)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); + STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); + STDMETHOD(DragLeave)(); + STDMETHOD(Drop)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); + +private: + ULONG m_refs; + QWidget* widget; + QPointer<QWidget> currentWidget; + QRect answerRect; + QPoint lastPoint; + DWORD chosenEffect; + DWORD lastKeyState; + + void sendDragEnterEvent(QWidget *to, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); +}; + +#endif + +#if defined (Q_WS_MAC) +class QCocoaDropData : public QInternalMimeData +{ + Q_OBJECT +public: + QCocoaDropData(CFStringRef pasteboard); + ~QCocoaDropData(); + +protected: + bool hasFormat_sys(const QString &mimeType) const; + QStringList formats_sys() const; + QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const; +public: + CFStringRef dropPasteboard; +}; +#endif + +#endif // !QT_NO_DRAGANDDROP + + +QT_END_NAMESPACE + +#endif // QDND_P_H diff --git a/src/gui/guikernel/qdnd_qpa.cpp b/src/gui/guikernel/qdnd_qpa.cpp new file mode 100644 index 0000000000..b744c2f085 --- /dev/null +++ b/src/gui/guikernel/qdnd_qpa.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qapplication.h" + +#ifndef QT_NO_DRAGANDDROP + +#include "qwidget.h" +#include "qdatetime.h" +#include "qbitmap.h" +#include "qcursor.h" +#include "qevent.h" +#include "qpainter.h" +#include "qdnd_p.h" + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +static QPixmap *defaultPm = 0; +static const int default_pm_hotx = -2; +static const int default_pm_hoty = -16; +static const char *const default_pm[] = { +"13 9 3 1", +". c None", +" c #000000", +"X c #FFFFFF", +"X X X X X X X", +" X X X X X X ", +"X ......... X", +" X.........X ", +"X ......... X", +" X.........X ", +"X ......... X", +" X X X X X X ", +"X X X X X X X", +}; + +// Shift/Ctrl handling, and final drop status +static Qt::DropAction global_accepted_action = Qt::CopyAction; +static Qt::DropActions possible_actions = Qt::IgnoreAction; + + +// static variables in place of a proper cross-process solution +static QDrag *drag_object; +static bool qt_qws_dnd_dragging = false; + + +static Qt::KeyboardModifiers oldstate; + +class QShapedPixmapWidget : public QWidget { + QPixmap pixmap; +public: + QShapedPixmapWidget() : + QWidget(0, Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint) + { + // ### Temporary workaround for 4.2-rc1!!! To prevent flickering when + // using drag'n drop in a client application. (task 126956) + // setAttribute() should be done unconditionally! + if (QApplication::type() == QApplication::GuiServer) + setAttribute(Qt::WA_TransparentForMouseEvents); + } + + void setPixmap(QPixmap pm) + { + pixmap = pm; + if (!pixmap.mask().isNull()) { + setMask(pixmap.mask()); + } else { + clearMask(); + } + resize(pm.width(),pm.height()); + } + + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.drawPixmap(0,0,pixmap); + } +}; + + +static QShapedPixmapWidget *qt_qws_dnd_deco = 0; + + +void QDragManager::updatePixmap() +{ + if (qt_qws_dnd_deco) { + QPixmap pm; + QPoint pm_hot(default_pm_hotx,default_pm_hoty); + if (drag_object) { + pm = drag_object->pixmap(); + if (!pm.isNull()) + pm_hot = drag_object->hotSpot(); + } + if (pm.isNull()) { + if (!defaultPm) + defaultPm = new QPixmap(default_pm); + pm = *defaultPm; + } + qt_qws_dnd_deco->setPixmap(pm); + qt_qws_dnd_deco->move(QCursor::pos()-pm_hot); + if (willDrop) { + qt_qws_dnd_deco->show(); + } else { + qt_qws_dnd_deco->hide(); + } + } +} + +void QDragManager::timerEvent(QTimerEvent *) { } + +void QDragManager::move(const QPoint &) { } + +void QDragManager::updateCursor() +{ +#ifndef QT_NO_CURSOR + if (willDrop) { + if (qt_qws_dnd_deco) + qt_qws_dnd_deco->show(); + if (currentActionForOverrideCursor != global_accepted_action) { + QApplication::changeOverrideCursor(QCursor(dragCursor(global_accepted_action), 0, 0)); + currentActionForOverrideCursor = global_accepted_action; + } + } else { + QCursor *overrideCursor = QApplication::overrideCursor(); + if (!overrideCursor || overrideCursor->shape() != Qt::ForbiddenCursor) { + QApplication::changeOverrideCursor(QCursor(Qt::ForbiddenCursor)); + currentActionForOverrideCursor = Qt::IgnoreAction; + } + if (qt_qws_dnd_deco) + qt_qws_dnd_deco->hide(); + } +#endif +} + + +bool QDragManager::eventFilter(QObject *o, QEvent *e) +{ + if (beingCancelled) { + if (e->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) { + qApp->removeEventFilter(this); + Q_ASSERT(object == 0); + beingCancelled = false; + eventLoop->exit(); + return true; // block the key release + } + return false; + } + + + + if (!o->isWidgetType()) + return false; + + switch(e->type()) { + case QEvent::ShortcutOverride: + // prevent accelerators from firing while dragging + e->accept(); + return true; + + case QEvent::KeyPress: + case QEvent::KeyRelease: + { + QKeyEvent *ke = ((QKeyEvent*)e); + if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) { + cancel(); + qApp->removeEventFilter(this); + beingCancelled = false; + eventLoop->exit(); + } else { + updateCursor(); + } + return true; // Eat all key events + } + + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + { + if (!object) { //#### this should not happen + qWarning("QDragManager::eventFilter: No object"); + return true; + } + + QDragManager *manager = QDragManager::self(); + QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData; + if (manager->object) + possible_actions = manager->dragPrivate()->possible_actions; + else + possible_actions = Qt::IgnoreAction; + + QMouseEvent *me = (QMouseEvent *)e; + if (me->buttons()) { + Qt::DropAction prevAction = global_accepted_action; + QWidget *cw = QApplication::widgetAt(me->globalPos()); + + // Fix for when we move mouse on to the deco widget + if (qt_qws_dnd_deco && cw == qt_qws_dnd_deco) + cw = object->target(); + + while (cw && !cw->acceptDrops() && !cw->isWindow()) + cw = cw->parentWidget(); + + if (object->target() != cw) { + if (object->target()) { + QDragLeaveEvent dle; + QApplication::sendEvent(object->target(), &dle); + willDrop = false; + global_accepted_action = Qt::IgnoreAction; + updateCursor(); + restoreCursor = true; + object->d_func()->target = 0; + } + if (cw && cw->acceptDrops()) { + object->d_func()->target = cw; + QDragEnterEvent dee(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData, + me->buttons(), me->modifiers()); + QApplication::sendEvent(object->target(), &dee); + willDrop = dee.isAccepted() && dee.dropAction() != Qt::IgnoreAction; + global_accepted_action = willDrop ? dee.dropAction() : Qt::IgnoreAction; + updateCursor(); + restoreCursor = true; + } + } else if (cw) { + QDragMoveEvent dme(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData, + me->buttons(), me->modifiers()); + if (global_accepted_action != Qt::IgnoreAction) { + dme.setDropAction(global_accepted_action); + dme.accept(); + } + QApplication::sendEvent(cw, &dme); + willDrop = dme.isAccepted(); + global_accepted_action = willDrop ? dme.dropAction() : Qt::IgnoreAction; + updatePixmap(); + updateCursor(); + } + if (global_accepted_action != prevAction) + emitActionChanged(global_accepted_action); + } + return true; // Eat all mouse events + } + + case QEvent::MouseButtonRelease: + { + qApp->removeEventFilter(this); + if (restoreCursor) { + willDrop = false; +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + restoreCursor = false; + } + if (object && object->target()) { + QMouseEvent *me = (QMouseEvent *)e; + + QDragManager *manager = QDragManager::self(); + QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData; + + QDropEvent de(object->target()->mapFromGlobal(me->globalPos()), possible_actions, dropData, + me->buttons(), me->modifiers()); + QApplication::sendEvent(object->target(), &de); + if (de.isAccepted()) + global_accepted_action = de.dropAction(); + else + global_accepted_action = Qt::IgnoreAction; + + if (object) + object->deleteLater(); + drag_object = object = 0; + } + eventLoop->exit(); + return true; // Eat all mouse events + } + + default: + break; + } + + return false; +} + +Qt::DropAction QDragManager::drag(QDrag *o) +{ + if (object == o || !o || !o->source()) + return Qt::IgnoreAction; + + if (object) { + cancel(); + qApp->removeEventFilter(this); + beingCancelled = false; + } + + object = drag_object = o; + qt_qws_dnd_deco = new QShapedPixmapWidget(); + oldstate = Qt::NoModifier; // #### Should use state that caused the drag +// drag_mode = mode; + + willDrop = false; + updatePixmap(); + updateCursor(); + restoreCursor = true; + object->d_func()->target = 0; + qApp->installEventFilter(this); + + global_accepted_action = Qt::CopyAction; +#ifndef QT_NO_CURSOR + qApp->setOverrideCursor(Qt::ArrowCursor); + restoreCursor = true; + updateCursor(); +#endif + + qt_qws_dnd_dragging = true; + + eventLoop = new QEventLoop; + (void) eventLoop->exec(); + delete eventLoop; + eventLoop = 0; + + delete qt_qws_dnd_deco; + qt_qws_dnd_deco = 0; + qt_qws_dnd_dragging = false; + + + return global_accepted_action; +} + + +void QDragManager::cancel(bool deleteSource) +{ +// qDebug("QDragManager::cancel"); + beingCancelled = true; + + if (object->target()) { + QDragLeaveEvent dle; + QApplication::sendEvent(object->target(), &dle); + } + +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + restoreCursor = false; + } +#endif + + if (drag_object) { + if (deleteSource) + object->deleteLater(); + drag_object = object = 0; + } + + delete qt_qws_dnd_deco; + qt_qws_dnd_deco = 0; + + global_accepted_action = Qt::IgnoreAction; +} + + +void QDragManager::drop() +{ +} + +QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type type) const +{ + if (!drag_object) + return QVariant(); + QByteArray data = drag_object->mimeData()->data(mimetype); + if (type == QVariant::String) + return QString::fromUtf8(data); + return data; +} + +bool QDropData::hasFormat_sys(const QString &format) const +{ + return formats().contains(format); +} + +QStringList QDropData::formats_sys() const +{ + if (drag_object) + return drag_object->mimeData()->formats(); + return QStringList(); +} + + +#endif // QT_NO_DRAGANDDROP + + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qdnd_s60.cpp b/src/gui/guikernel/qdnd_s60.cpp new file mode 100644 index 0000000000..a9847a98f8 --- /dev/null +++ b/src/gui/guikernel/qdnd_s60.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qapplication.h" + +#ifndef QT_NO_DRAGANDDROP + +#include "qwidget.h" +#include "qdatetime.h" +#include "qbitmap.h" +#include "qcursor.h" +#include "qevent.h" +#include "qpainter.h" +#include "qdnd_p.h" +#include "qt_s60_p.h" + +#include <coecntrl.h> +// pointer cursor +#include <w32std.h> +#include <gdi.h> +#include <QCursor> + +QT_BEGIN_NAMESPACE +//### artistic impression of Symbians default DnD cursor ? + +static QPixmap *defaultPm = 0; +static const int default_pm_hotx = -50; +static const int default_pm_hoty = -50; +static const char *const default_pm[] = { +"13 9 3 1", +". c None", +" c #000000", +"X c #FFFFFF", +"X X X X X X X", +" X X X X X X ", +"X ......... X", +" X.........X ", +"X ......... X", +" X.........X ", +"X ......... X", +" X X X X X X ", +"X X X X X X X", +}; +//### actions need to be redefined for S60 +// Shift/Ctrl handling, and final drop status +static Qt::DropAction global_accepted_action = Qt::MoveAction; +static Qt::DropActions possible_actions = Qt::IgnoreAction; + + +// static variables in place of a proper cross-process solution +static QDrag *drag_object; +static bool qt_symbian_dnd_dragging = false; + + +static Qt::KeyboardModifiers oldstate; + +void QDragManager::updatePixmap() +{ + QPixmap pm; + QPoint pm_hot(default_pm_hotx,default_pm_hoty); + if (drag_object) { + pm = drag_object->pixmap(); + if (!pm.isNull()) + pm_hot = drag_object->hotSpot(); + } + if (pm.isNull()) { + if (!defaultPm) + defaultPm = new QPixmap(default_pm); + pm = *defaultPm; + } +#ifndef QT_NO_CURSOR + QCursor cursor(pm, pm_hot.x(), pm_hot.y()); + overrideCursor = cursor; +#endif +} + +void QDragManager::timerEvent(QTimerEvent *) { } + +void QDragManager::move(const QPoint&) { +} + +void QDragManager::updateCursor() +{ +#ifndef QT_NO_CURSOR + QCursor cursor = willDrop ? overrideCursor : Qt::ForbiddenCursor; + if (!restoreCursor) { + QApplication::setOverrideCursor(cursor); + restoreCursor = true; + } + else { + QApplication::changeOverrideCursor(cursor); + } +#endif +} + + +bool QDragManager::eventFilter(QObject *o, QEvent *e) +{ + if (beingCancelled) { + return false; + } + if (!o->isWidgetType()) + return false; + + switch(e->type()) { + case QEvent::MouseButtonPress: + { + } + case QEvent::MouseMove: + { + if (!object) { //#### this should not happen + qWarning("QDragManager::eventFilter: No object"); + return true; + } + QDragManager *manager = QDragManager::self(); + QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData; + if (manager->object) + possible_actions = manager->dragPrivate()->possible_actions; + else + possible_actions = Qt::IgnoreAction; + + QMouseEvent *me = (QMouseEvent *)e; + + if (me->buttons()) { + Qt::DropAction prevAction = global_accepted_action; + QWidget *cw = QApplication::widgetAt(me->globalPos()); + // map the Coords relative to the window. + if (!cw) + return true; + + while (cw && !cw->acceptDrops() && !cw->isWindow()) + cw = cw->parentWidget(); + + bool oldWillDrop = willDrop; + if (object->target() != cw) { + if (object->target()) { + QDragLeaveEvent dle; + QApplication::sendEvent(object->target(), &dle); + willDrop = false; + global_accepted_action = Qt::IgnoreAction; + if (oldWillDrop != willDrop) + updateCursor(); + object->d_func()->target = 0; + } + if (cw && cw->acceptDrops()) { + object->d_func()->target = cw; + QDragEnterEvent dee(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData, + me->buttons(), me->modifiers()); + QApplication::sendEvent(object->target(), &dee); + willDrop = dee.isAccepted() && dee.dropAction() != Qt::IgnoreAction; + global_accepted_action = willDrop ? dee.dropAction() : Qt::IgnoreAction; + if (oldWillDrop != willDrop) + updateCursor(); + } + } else if (cw) { + QDragMoveEvent dme(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData, + me->buttons(), me->modifiers()); + if (global_accepted_action != Qt::IgnoreAction) { + dme.setDropAction(global_accepted_action); + dme.accept(); + } + QApplication::sendEvent(cw, &dme); + willDrop = dme.isAccepted(); + global_accepted_action = willDrop ? dme.dropAction() : Qt::IgnoreAction; + if (oldWillDrop != willDrop) { + updatePixmap(); + updateCursor(); + } + } + if (global_accepted_action != prevAction) + emitActionChanged(global_accepted_action); + } + return true; // Eat all mouse events + } + + case QEvent::MouseButtonRelease: + { + qApp->removeEventFilter(this); +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + willDrop = false; + restoreCursor = false; + } +#endif + if (object && object->target()) { + + QMouseEvent *me = (QMouseEvent *)e; + + QDragManager *manager = QDragManager::self(); + QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData; + + QDropEvent de(object->target()->mapFromGlobal(me->globalPos()), possible_actions, dropData, + me->buttons(), me->modifiers()); + QApplication::sendEvent(object->target(), &de); + if (de.isAccepted()) + global_accepted_action = de.dropAction(); + else + global_accepted_action = Qt::IgnoreAction; + + if (object) + object->deleteLater(); + drag_object = object = 0; + } + eventLoop->exit(); + return true; // Eat all mouse events + } + + default: + break; + } + return false; +} + +Qt::DropAction QDragManager::drag(QDrag *o) +{ + Q_ASSERT(!qt_symbian_dnd_dragging); + if (object == o || !o || !o->source()) + return Qt::IgnoreAction; + + if (object) { + cancel(); + qApp->removeEventFilter(this); + beingCancelled = false; + } + + object = drag_object = o; + + oldstate = Qt::NoModifier; // #### Should use state that caused the drag + willDrop = false; + updatePixmap(); + updateCursor(); + +#ifndef QT_NO_CURSOR + qt_symbian_set_cursor_visible(true); //force cursor on even for touch phone +#endif + + object->d_func()->target = 0; + + qApp->installEventFilter(this); + + global_accepted_action = defaultAction(dragPrivate()->possible_actions, Qt::NoModifier); + qt_symbian_dnd_dragging = true; + + eventLoop = new QEventLoop; + // block + (void) eventLoop->exec(QEventLoop::AllEvents); + delete eventLoop; + eventLoop = 0; + +#ifndef QT_NO_CURSOR + qt_symbian_set_cursor_visible(false); + + overrideCursor = QCursor(); //deref the cursor data + qt_symbian_dnd_dragging = false; +#endif + + return global_accepted_action; +} + + +void QDragManager::cancel(bool deleteSource) +{ + beingCancelled = true; + + if (object->target()) { + QDragLeaveEvent dle; + QApplication::sendEvent(object->target(), &dle); + } + + if (drag_object) { + if (deleteSource) + object->deleteLater(); + drag_object = object = 0; + } + +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + restoreCursor = false; + } +#endif + + global_accepted_action = Qt::IgnoreAction; +} + + +void QDragManager::drop() +{ +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + restoreCursor = false; + } +#endif +} + +QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type type) const +{ + if (!drag_object) + return QVariant(); + QByteArray data = drag_object->mimeData()->data(mimetype); + if (type == QVariant::String) + return QString::fromUtf8(data); + return data; +} + +bool QDropData::hasFormat_sys(const QString &format) const +{ + return formats().contains(format); +} + +QStringList QDropData::formats_sys() const +{ + if (drag_object) + return drag_object->mimeData()->formats(); + return QStringList(); +} + +QT_END_NAMESPACE +#endif // QT_NO_DRAGANDDROP diff --git a/src/gui/guikernel/qdnd_win.cpp b/src/gui/guikernel/qdnd_win.cpp new file mode 100644 index 0000000000..176e3cef7f --- /dev/null +++ b/src/gui/guikernel/qdnd_win.cpp @@ -0,0 +1,1027 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qapplication.h" + +#include "qapplication_p.h" +#include "qevent.h" +#include "qpainter.h" +#include "qwidget.h" +#include "qbuffer.h" +#include "qdatastream.h" +#include "qcursor.h" +#include "qt_windows.h" +#include <shlobj.h> +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible.h" +#endif +#include "qdnd_p.h" +#include "qdebug.h" + +#if defined(Q_OS_WINCE) +#include "qguifunctions_wince.h" +#endif + +// support for xbuttons +#ifndef MK_XBUTTON1 +#define MK_XBUTTON1 0x0020 +#define MK_XBUTTON2 0x0040 +#endif + +QT_BEGIN_NAMESPACE + +#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD)) + +//--------------------------------------------------------------------- +// QOleDataObject Constructor +//--------------------------------------------------------------------- + +QOleDataObject::QOleDataObject(QMimeData *mimeData) +{ + m_refs = 1; + data = mimeData; + CF_PERFORMEDDROPEFFECT = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT); + performedEffect = DROPEFFECT_NONE; +} + +QOleDataObject::~QOleDataObject() +{ +} + +void QOleDataObject::releaseQt() +{ + data = 0; +} + +const QMimeData *QOleDataObject::mimeData() const +{ + return data; +} + +DWORD QOleDataObject::reportedPerformedEffect() const +{ + return performedEffect; +} + +//--------------------------------------------------------------------- +// IUnknown Methods +//--------------------------------------------------------------------- + +STDMETHODIMP +QOleDataObject::QueryInterface(REFIID iid, void FAR* FAR* ppv) +{ + if (iid == IID_IUnknown || iid == IID_IDataObject) { + *ppv = this; + AddRef(); + return NOERROR; + } + *ppv = NULL; + return ResultFromScode(E_NOINTERFACE); +} + +STDMETHODIMP_(ULONG) +QOleDataObject::AddRef(void) +{ + return ++m_refs; +} + +STDMETHODIMP_(ULONG) +QOleDataObject::Release(void) +{ + if (--m_refs == 0) { + releaseQt(); + delete this; + return 0; + } + return m_refs; +} + +//--------------------------------------------------------------------- +// IDataObject Methods +// +// The following methods are NOT supported for data transfer using the +// clipboard or drag-drop: +// +// IDataObject::SetData -- return E_NOTIMPL +// IDataObject::DAdvise -- return OLE_E_ADVISENOTSUPPORTED +// ::DUnadvise +// ::EnumDAdvise +// IDataObject::GetCanonicalFormatEtc -- return E_NOTIMPL +// (NOTE: must set pformatetcOut->ptd = NULL) +//--------------------------------------------------------------------- + +STDMETHODIMP +QOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) +{ +#ifdef QDND_DEBUG + qDebug("QOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)"); +#ifndef Q_OS_WINCE + wchar_t buf[256] = {0}; + GetClipboardFormatName(pformatetc->cfFormat, buf, 255); + qDebug("CF = %d : %s", pformatetc->cfFormat, QString::fromWCharArray(buf)); +#endif +#endif + + if (!data) + return ResultFromScode(DATA_E_FORMATETC); + + QWindowsMime *converter = QWindowsMime::converterFromMime(*pformatetc, data); + + if (converter && converter->convertFromMime(*pformatetc, data, pmedium)) + return ResultFromScode(S_OK); + else + return ResultFromScode(DATA_E_FORMATETC); +} + +STDMETHODIMP +QOleDataObject::GetDataHere(LPFORMATETC, LPSTGMEDIUM) +{ + return ResultFromScode(DATA_E_FORMATETC); +} + +STDMETHODIMP +QOleDataObject::QueryGetData(LPFORMATETC pformatetc) +{ +#ifdef QDND_DEBUG + qDebug("QOleDataObject::QueryGetData(LPFORMATETC pformatetc)"); +#endif + + if (!data) + return ResultFromScode(DATA_E_FORMATETC); + + if (QWindowsMime::converterFromMime(*pformatetc, data)) + return ResultFromScode(S_OK); + return ResultFromScode(S_FALSE); +} + +STDMETHODIMP +QOleDataObject::GetCanonicalFormatEtc(LPFORMATETC, LPFORMATETC pformatetcOut) +{ + pformatetcOut->ptd = NULL; + return ResultFromScode(E_NOTIMPL); +} + +STDMETHODIMP +QOleDataObject::SetData(LPFORMATETC pFormatetc, STGMEDIUM *pMedium, BOOL fRelease) +{ + if (pFormatetc->cfFormat == CF_PERFORMEDDROPEFFECT && pMedium->tymed == TYMED_HGLOBAL) { + DWORD * val = (DWORD*)GlobalLock(pMedium->hGlobal); + performedEffect = *val; + GlobalUnlock(pMedium->hGlobal); + if (fRelease) + ReleaseStgMedium(pMedium); + return ResultFromScode(S_OK); + } + return ResultFromScode(E_NOTIMPL); +} + + +STDMETHODIMP +QOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc) +{ +#ifdef QDND_DEBUG + qDebug("QOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc)"); +#endif + + if (!data) + return ResultFromScode(DATA_E_FORMATETC); + + SCODE sc = S_OK; + + QVector<FORMATETC> fmtetcs; + if (dwDirection == DATADIR_GET) { + fmtetcs = QWindowsMime::allFormatsForMime(data); + } else { + FORMATETC formatetc; + formatetc.cfFormat = CF_PERFORMEDDROPEFFECT; + formatetc.dwAspect = DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.ptd = NULL; + formatetc.tymed = TYMED_HGLOBAL; + fmtetcs.append(formatetc); + } + + QOleEnumFmtEtc *enumFmtEtc = new QOleEnumFmtEtc(fmtetcs); + *ppenumFormatEtc = enumFmtEtc; + if (enumFmtEtc->isNull()) { + delete enumFmtEtc; + *ppenumFormatEtc = NULL; + sc = E_OUTOFMEMORY; + } + + return ResultFromScode(sc); +} + +STDMETHODIMP +QOleDataObject::DAdvise(FORMATETC FAR*, DWORD, + LPADVISESINK, DWORD FAR*) +{ + return ResultFromScode(OLE_E_ADVISENOTSUPPORTED); +} + + +STDMETHODIMP +QOleDataObject::DUnadvise(DWORD) +{ + return ResultFromScode(OLE_E_ADVISENOTSUPPORTED); +} + +STDMETHODIMP +QOleDataObject::EnumDAdvise(LPENUMSTATDATA FAR*) +{ + return ResultFromScode(OLE_E_ADVISENOTSUPPORTED); +} + +#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD + +#ifndef QT_NO_DRAGANDDROP + +//#define QDND_DEBUG + +#ifdef QDND_DEBUG +extern QString dragActionsToString(Qt::DropActions actions); +#endif + +Qt::DropActions translateToQDragDropActions(DWORD pdwEffects) +{ + Qt::DropActions actions = Qt::IgnoreAction; + if (pdwEffects & DROPEFFECT_LINK) + actions |= Qt::LinkAction; + if (pdwEffects & DROPEFFECT_COPY) + actions |= Qt::CopyAction; + if (pdwEffects & DROPEFFECT_MOVE) + actions |= Qt::MoveAction; + return actions; +} + +Qt::DropAction translateToQDragDropAction(DWORD pdwEffect) +{ + if (pdwEffect & DROPEFFECT_LINK) + return Qt::LinkAction; + if (pdwEffect & DROPEFFECT_COPY) + return Qt::CopyAction; + if (pdwEffect & DROPEFFECT_MOVE) + return Qt::MoveAction; + return Qt::IgnoreAction; +} + +DWORD translateToWinDragEffects(Qt::DropActions action) +{ + DWORD effect = DROPEFFECT_NONE; + if (action & Qt::LinkAction) + effect |= DROPEFFECT_LINK; + if (action & Qt::CopyAction) + effect |= DROPEFFECT_COPY; + if (action & Qt::MoveAction) + effect |= DROPEFFECT_MOVE; + return effect; +} + +Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState) +{ + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + if (keyState & MK_SHIFT) + modifiers |= Qt::ShiftModifier; + if (keyState & MK_CONTROL) + modifiers |= Qt::ControlModifier; + if (keyState & MK_ALT) + modifiers |= Qt::AltModifier; + + return modifiers; +} + +Qt::MouseButtons toQtMouseButtons(DWORD keyState) +{ + Qt::MouseButtons buttons = Qt::NoButton; + + if (keyState & MK_LBUTTON) + buttons |= Qt::LeftButton; + if (keyState & MK_RBUTTON) + buttons |= Qt::RightButton; + if (keyState & MK_MBUTTON) + buttons |= Qt::MidButton; + + return buttons; +} + +class QOleDropSource : public IDropSource +{ +public: + QOleDropSource(); + virtual ~QOleDropSource(); + + void createCursors(); + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj); + STDMETHOD_(ULONG,AddRef)(void); + STDMETHOD_(ULONG,Release)(void); + + // IDropSource methods + STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState); + STDMETHOD(GiveFeedback)(DWORD dwEffect); + +private: + Qt::MouseButtons currentButtons; + Qt::DropAction currentAction; + QMap <Qt::DropAction, QCursor> cursors; + + ULONG m_refs; +}; + + +QOleDropSource::QOleDropSource() +{ + currentButtons = Qt::NoButton; + m_refs = 1; + currentAction = Qt::IgnoreAction; +} + +QOleDropSource::~QOleDropSource() +{ +} + +void QOleDropSource::createCursors() +{ + QDragManager *manager = QDragManager::self(); + if (manager && manager->object + && (!manager->object->pixmap().isNull() + || manager->hasCustomDragCursors())) { + QPixmap pm = manager->object->pixmap(); + QList<Qt::DropAction> actions; + actions << Qt::MoveAction << Qt::CopyAction << Qt::LinkAction; + if (!manager->object->pixmap().isNull()) + actions << Qt::IgnoreAction; + QPoint hotSpot = manager->object->hotSpot(); + for (int cnum = 0; cnum < actions.size(); ++cnum) { + QPixmap cpm = manager->dragCursor(actions.at(cnum)); + int w = cpm.width(); + int h = cpm.height(); + + if (!pm.isNull()) { + int x1 = qMin(-hotSpot.x(), 0); + int x2 = qMax(pm.width() - hotSpot.x(), cpm.width()); + int y1 = qMin(-hotSpot.y(), 0); + int y2 = qMax(pm.height() - hotSpot.y(), cpm.height()); + + w = x2 - x1 + 1; + h = y2 - y1 + 1; + } + + QRect srcRect = pm.rect(); + QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y())); + QPoint newHotSpot = hotSpot; + +#if defined(Q_OS_WINCE) + // Limited cursor size + int reqw = GetSystemMetrics(SM_CXCURSOR); + int reqh = GetSystemMetrics(SM_CYCURSOR); + + QPoint hotspotInPM = newHotSpot - pmDest; + if (reqw < w) { + // Not wide enough - move objectpm right + qreal r = qreal(newHotSpot.x()) / w; + newHotSpot = QPoint(int(r * reqw), newHotSpot.y()); + if (newHotSpot.x() + cpm.width() > reqw) + newHotSpot.setX(reqw - cpm.width()); + + srcRect = QRect(QPoint(hotspotInPM.x() - newHotSpot.x(), srcRect.top()), QSize(reqw, srcRect.height())); + } + if (reqh < h) { + qreal r = qreal(newHotSpot.y()) / h; + newHotSpot = QPoint(newHotSpot.x(), int(r * reqh)); + if (newHotSpot.y() + cpm.height() > reqh) + newHotSpot.setY(reqh - cpm.height()); + + srcRect = QRect(QPoint(srcRect.left(), hotspotInPM.y() - newHotSpot.y()), QSize(srcRect.width(), reqh)); + } + // Always use system cursor size + w = reqw; + h = reqh; +#endif + + QPixmap newCursor(w, h); + if (!pm.isNull()) { + newCursor.fill(QColor(0, 0, 0, 0)); + QPainter p(&newCursor); + p.drawPixmap(pmDest, pm, srcRect); + p.drawPixmap(qMax(0,newHotSpot.x()),qMax(0,newHotSpot.y()),cpm); + } else { + newCursor = cpm; + } + +#ifndef QT_NO_CURSOR + cursors[actions.at(cnum)] = QCursor(newCursor, pm.isNull() ? 0 : qMax(0,newHotSpot.x()), + pm.isNull() ? 0 : qMax(0,newHotSpot.y())); +#endif + } + } +} + + + +//--------------------------------------------------------------------- +// IUnknown Methods +//--------------------------------------------------------------------- + + +STDMETHODIMP +QOleDropSource::QueryInterface(REFIID iid, void FAR* FAR* ppv) +{ + if(iid == IID_IUnknown || iid == IID_IDropSource) + { + *ppv = this; + ++m_refs; + return NOERROR; + } + *ppv = NULL; + return ResultFromScode(E_NOINTERFACE); +} + + +STDMETHODIMP_(ULONG) +QOleDropSource::AddRef(void) +{ + return ++m_refs; +} + + +STDMETHODIMP_(ULONG) +QOleDropSource::Release(void) +{ + if(--m_refs == 0) + { + delete this; + return 0; + } + return m_refs; +} + +static inline Qt::MouseButtons keystate_to_mousebutton(DWORD grfKeyState) +{ + Qt::MouseButtons result; + if (grfKeyState & MK_LBUTTON) + result |= Qt::LeftButton; + if (grfKeyState & MK_MBUTTON) + result |= Qt::MidButton; + if (grfKeyState & MK_RBUTTON) + result |= Qt::RightButton; + if (grfKeyState & MK_XBUTTON1) + result |= Qt::XButton1; + if (grfKeyState & MK_XBUTTON2) + result |= Qt::XButton2; + return result; +} + +//--------------------------------------------------------------------- +// IDropSource Methods +//--------------------------------------------------------------------- +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) +{ +#ifdef QDND_DEBUG + qDebug("QOleDropSource::QueryContinueDrag(fEscapePressed %d, grfKeyState %d)", fEscapePressed, grfKeyState); +#endif + + if (fEscapePressed) { + return ResultFromScode(DRAGDROP_S_CANCEL); + } else if ((GetAsyncKeyState(VK_LBUTTON) == 0) + && (GetAsyncKeyState(VK_MBUTTON) == 0) + && (GetAsyncKeyState(VK_RBUTTON) == 0)) { + // grfKeyState is broken on CE & some Windows XP versions, + // therefore we need to check the state manually + return ResultFromScode(DRAGDROP_S_DROP); + } else { +#if !defined(Q_OS_WINCE) + if (currentButtons == Qt::NoButton) { + currentButtons = keystate_to_mousebutton(grfKeyState); + } else { + Qt::MouseButtons buttons = keystate_to_mousebutton(grfKeyState); + if (!(currentButtons & buttons)) + return ResultFromScode(DRAGDROP_S_DROP); + } +#endif + QApplication::processEvents(); + return NOERROR; + } +} + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QOleDropSource::GiveFeedback(DWORD dwEffect) +{ + Qt::DropAction action = translateToQDragDropAction(dwEffect); + +#ifdef QDND_DEBUG + qDebug("QOleDropSource::GiveFeedback(DWORD dwEffect)"); + qDebug("dwEffect = %s", dragActionsToString(action).toLatin1().data()); +#endif + + if (currentAction != action) { + currentAction = action; + QDragManager::self()->emitActionChanged(currentAction); + } + + if (cursors.contains(currentAction)) { +#ifndef QT_NO_CURSOR + SetCursor(cursors[currentAction].handle()); +#endif + return ResultFromScode(S_OK); + } + + return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS); +} + +//--------------------------------------------------------------------- +// QOleDropTarget +//--------------------------------------------------------------------- + +QOleDropTarget::QOleDropTarget(QWidget* w) +: widget(w) +{ + m_refs = 1; +} + +void QOleDropTarget::releaseQt() +{ + widget = 0; +} + +//--------------------------------------------------------------------- +// IUnknown Methods +//--------------------------------------------------------------------- + + +STDMETHODIMP +QOleDropTarget::QueryInterface(REFIID iid, void FAR* FAR* ppv) +{ + if(iid == IID_IUnknown || iid == IID_IDropTarget) + { + *ppv = this; + AddRef(); + return NOERROR; + } + *ppv = NULL; + return ResultFromScode(E_NOINTERFACE); +} + + +STDMETHODIMP_(ULONG) +QOleDropTarget::AddRef(void) +{ + return ++m_refs; +} + + +STDMETHODIMP_(ULONG) +QOleDropTarget::Release(void) +{ + if(--m_refs == 0) + { + delete this; + return 0; + } + return m_refs; +} + +//--------------------------------------------------------------------- +// IDropTarget Methods +//--------------------------------------------------------------------- + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) +{ +#ifdef QDND_DEBUG + qDebug("QOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)"); +#endif + + if (!QApplicationPrivate::tryModalHelper(widget)) { + *pdwEffect = DROPEFFECT_NONE; + return NOERROR; + } + + QDragManager *manager = QDragManager::self(); + manager->dropData->currentDataObject = pDataObj; + manager->dropData->currentDataObject->AddRef(); + sendDragEnterEvent(widget, grfKeyState, pt, pdwEffect); + *pdwEffect = chosenEffect; + + return NOERROR; +} + +void QOleDropTarget::sendDragEnterEvent(QWidget *dragEnterWidget, DWORD grfKeyState, + POINTL pt, LPDWORD pdwEffect) +{ + Q_ASSERT(dragEnterWidget); + lastPoint = dragEnterWidget->mapFromGlobal(QPoint(pt.x,pt.y)); + lastKeyState = grfKeyState; + + chosenEffect = DROPEFFECT_NONE; + currentWidget = dragEnterWidget; + + QDragManager *manager = QDragManager::self(); + QMimeData * md = manager->source() ? manager->dragPrivate()->data : manager->dropData; + QDragEnterEvent enterEvent(lastPoint, translateToQDragDropActions(*pdwEffect), md, + toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState)); + QApplication::sendEvent(dragEnterWidget, &enterEvent); + answerRect = enterEvent.answerRect(); + + if (enterEvent.isAccepted()) { + chosenEffect = translateToWinDragEffects(enterEvent.dropAction()); + } + + // Documentation states that a drag move event is sendt immidiatly after + // a drag enter event. This will honor widgets overriding dragMoveEvent only: + if (enterEvent.isAccepted()) { + QDragMoveEvent moveEvent(lastPoint, translateToQDragDropActions(*pdwEffect), md, + toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState)); + answerRect = enterEvent.answerRect(); + moveEvent.setDropAction(enterEvent.dropAction()); + moveEvent.accept(); // accept by default, since enter event was accepted. + + QApplication::sendEvent(dragEnterWidget, &moveEvent); + if (moveEvent.isAccepted()) { + answerRect = moveEvent.answerRect(); + chosenEffect = translateToWinDragEffects(moveEvent.dropAction()); + } else { + chosenEffect = DROPEFFECT_NONE; + } + } + +} + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) +{ +#ifdef QDND_DEBUG + qDebug("QOleDropTarget::DragOver(grfKeyState %d, pt (%d,%d), pdwEffect %d)", grfKeyState, pt.x, pt.y, pdwEffect); +#endif + + QWidget *dragOverWidget = widget->childAt(widget->mapFromGlobal(QPoint(pt.x, pt.y))); + if (!dragOverWidget) + dragOverWidget = widget; + + + if (!QApplicationPrivate::tryModalHelper(dragOverWidget) + || !dragOverWidget->testAttribute(Qt::WA_DropSiteRegistered)) { + *pdwEffect = DROPEFFECT_NONE; + return NOERROR; + } + + QPoint tmpPoint = dragOverWidget->mapFromGlobal(QPoint(pt.x, pt.y)); + // see if we should compress this event + if ((tmpPoint == lastPoint || answerRect.contains(tmpPoint)) && lastKeyState == grfKeyState) { + *pdwEffect = chosenEffect; + return NOERROR; + } + + if (!dragOverWidget->internalWinId() && dragOverWidget != currentWidget) { + QPointer<QWidget> dragOverWidgetGuard(dragOverWidget); + // Send drag leave event to the previous drag widget. + QDragLeaveEvent dragLeave; + if (currentWidget) + QApplication::sendEvent(currentWidget, &dragLeave); + if (!dragOverWidgetGuard) { + dragOverWidget = widget->childAt(widget->mapFromGlobal(QPoint(pt.x, pt.y))); + if (!dragOverWidget) + dragOverWidget = widget; + } + // Send drag enter event to the current drag widget. + sendDragEnterEvent(dragOverWidget, grfKeyState, pt, pdwEffect); + } + + QDragManager *manager = QDragManager::self(); + QMimeData *md = manager->source() ? manager->dragPrivate()->data : manager->dropData; + + QDragMoveEvent oldEvent(lastPoint, translateToQDragDropActions(*pdwEffect), md, + toQtMouseButtons(lastKeyState), toQtKeyboardModifiers(lastKeyState)); + + + lastPoint = tmpPoint; + lastKeyState = grfKeyState; + + QDragMoveEvent e(lastPoint, translateToQDragDropActions(*pdwEffect), md, + toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState)); + if (chosenEffect != DROPEFFECT_NONE) { + if (oldEvent.dropAction() == e.dropAction() && + oldEvent.keyboardModifiers() == e.keyboardModifiers()) + e.setDropAction(translateToQDragDropAction(chosenEffect)); + e.accept(); + } + QApplication::sendEvent(dragOverWidget, &e); + + answerRect = e.answerRect(); + if (e.isAccepted()) + chosenEffect = translateToWinDragEffects(e.dropAction()); + else + chosenEffect = DROPEFFECT_NONE; + *pdwEffect = chosenEffect; + + return NOERROR; +} + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QOleDropTarget::DragLeave() +{ +#ifdef QDND_DEBUG + qDebug("QOleDropTarget::DragLeave()"); +#endif + + if (!QApplicationPrivate::tryModalHelper(widget)) { + return NOERROR; + } + + currentWidget = 0; + QDragLeaveEvent e; + QApplication::sendEvent(widget, &e); + + QDragManager *manager = QDragManager::self(); + + if (manager->dropData->currentDataObject) { // Sanity + manager->dropData->currentDataObject->Release(); + manager->dropData->currentDataObject = 0; + } + + return NOERROR; +} + +#define KEY_STATE_BUTTON_MASK (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) +{ +#ifdef QDND_DEBUG + qDebug("QOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, grfKeyState %d, POINTL pt, LPDWORD pdwEffect)", grfKeyState); +#endif + + QWidget *dropWidget = widget->childAt(widget->mapFromGlobal(QPoint(pt.x, pt.y))); + if (!dropWidget) + dropWidget = widget; + + if (!QApplicationPrivate::tryModalHelper(dropWidget) + || !dropWidget->testAttribute(Qt::WA_DropSiteRegistered)) { + *pdwEffect = DROPEFFECT_NONE; + return NOERROR; + } + + lastPoint = dropWidget->mapFromGlobal(QPoint(pt.x,pt.y)); + // grfKeyState does not all ways contain button state in the drop so if + // it doesn't then use the last known button state; + if ((grfKeyState & KEY_STATE_BUTTON_MASK) == 0) + grfKeyState |= lastKeyState & KEY_STATE_BUTTON_MASK; + lastKeyState = grfKeyState; + + QDragManager *manager = QDragManager::self(); + QMimeData *md = manager->source() ? manager->dragPrivate()->data : manager->dropData; + QDropEvent e(lastPoint, translateToQDragDropActions(*pdwEffect), md, + toQtMouseButtons(grfKeyState), toQtKeyboardModifiers(grfKeyState)); + if (chosenEffect != DROPEFFECT_NONE) { + e.setDropAction(translateToQDragDropAction(chosenEffect)); + } + QApplication::sendEvent(dropWidget, &e); + + if (chosenEffect != DROPEFFECT_NONE) { + e.accept(); + } + + + if (e.isAccepted()) { + if (e.dropAction() == Qt::MoveAction || e.dropAction() == Qt::TargetMoveAction) { + if (e.dropAction() == Qt::MoveAction) + chosenEffect = DROPEFFECT_MOVE; + else + chosenEffect = DROPEFFECT_COPY; + HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD)); + if (hData) { + DWORD *moveEffect = (DWORD *)GlobalLock(hData);; + *moveEffect = DROPEFFECT_MOVE; + GlobalUnlock(hData); + STGMEDIUM medium; + memset(&medium, 0, sizeof(STGMEDIUM)); + medium.tymed = TYMED_HGLOBAL; + medium.hGlobal = hData; + FORMATETC format; + format.cfFormat = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT); + format.tymed = TYMED_HGLOBAL; + format.ptd = 0; + format.dwAspect = 1; + format.lindex = -1; + manager->dropData->currentDataObject->SetData(&format, &medium, true); + } + } else { + chosenEffect = translateToWinDragEffects(e.dropAction()); + } + } else { + chosenEffect = DROPEFFECT_NONE; + } + *pdwEffect = chosenEffect; + + + if (manager->dropData->currentDataObject) { + manager->dropData->currentDataObject->Release(); + manager->dropData->currentDataObject = 0; + } + + return NOERROR; + + // We won't get any mouserelease-event, so manually adjust qApp state: +///### test this QApplication::winMouseButtonUp(); +} + +//--------------------------------------------------------------------- +// QDropData +//--------------------------------------------------------------------- + +bool QDropData::hasFormat_sys(const QString &mimeType) const +{ + if (!currentDataObject) // Sanity + return false; + + return QWindowsMime::converterToMime(mimeType, currentDataObject) != 0; +} + +QStringList QDropData::formats_sys() const +{ + QStringList fmts; + if (!currentDataObject) // Sanity + return fmts; + + fmts = QWindowsMime::allMimesForFormats(currentDataObject); + + return fmts; +} + +QVariant QDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const +{ + QVariant result; + + if (!currentDataObject) // Sanity + return result; + + QWindowsMime *converter = QWindowsMime::converterToMime(mimeType, currentDataObject); + + if (converter) + result = converter->convertToMime(mimeType, currentDataObject, type); + + return result; +} + +Qt::DropAction QDragManager::drag(QDrag *o) + +{ +#ifdef QDND_DEBUG + qDebug("QDragManager::drag(QDrag *drag)"); +#endif + + if (object == o || !o || !o->d_func()->source) + return Qt::IgnoreAction; + + if (object) { + cancel(); + qApp->removeEventFilter(this); + beingCancelled = false; + } + + object = o; + +#ifdef QDND_DEBUG + qDebug("actions = %s", dragActionsToString(dragPrivate()->possible_actions).toLatin1().data()); +#endif + + dragPrivate()->target = 0; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart); +#endif + + DWORD resultEffect; + QOleDropSource *src = new QOleDropSource(); + src->createCursors(); + QOleDataObject *obj = new QOleDataObject(o->mimeData()); + DWORD allowedEffects = translateToWinDragEffects(dragPrivate()->possible_actions); + +#if !defined(Q_OS_WINCE) || defined(GWES_ICONCURS) + HRESULT r = DoDragDrop(obj, src, allowedEffects, &resultEffect); +#else + HRESULT r = DRAGDROP_S_CANCEL; + resultEffect = DROPEFFECT_MOVE; +#endif + + Qt::DropAction ret = Qt::IgnoreAction; + if (r == DRAGDROP_S_DROP) { + if (obj->reportedPerformedEffect() == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) { + ret = Qt::TargetMoveAction; + resultEffect = DROPEFFECT_MOVE; + } else { + ret = translateToQDragDropAction(resultEffect); + } + // Force it to be a copy if an unsupported operation occurred. + // This indicates a bug in the drop target. + if (resultEffect != DROPEFFECT_NONE && !(resultEffect & allowedEffects)) + ret = Qt::CopyAction; + } else { + dragPrivate()->target = 0; + } + + // clean up + obj->releaseQt(); + obj->Release(); // Will delete obj if refcount becomes 0 + src->Release(); // Will delete src if refcount becomes 0 + object = 0; + o->setMimeData(0); + o->deleteLater(); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd); +#endif + + return ret; +} + +void QDragManager::cancel(bool /* deleteSource */) +{ + if (object) { + beingCancelled = true; + object = 0; + } + +#ifndef QT_NO_CURSOR + // insert cancel code here ######## todo + + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + restoreCursor = false; + } +#endif +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd); +#endif +} + +void QDragManager::updatePixmap() +{ + // not used in windows implementation +} + +bool QDragManager::eventFilter(QObject *, QEvent *) +{ + // not used in windows implementation + return false; +} + +void QDragManager::timerEvent(QTimerEvent*) +{ + // not used in windows implementation +} + +void QDragManager::move(const QPoint &) +{ + // not used in windows implementation +} + +void QDragManager::drop() +{ + // not used in windows implementation +} + +QT_END_NAMESPACE + +#endif // QT_NO_DRAGANDDROP diff --git a/src/gui/guikernel/qdnd_x11.cpp b/src/gui/guikernel/qdnd_x11.cpp new file mode 100644 index 0000000000..9ff1543e51 --- /dev/null +++ b/src/gui/guikernel/qdnd_x11.cpp @@ -0,0 +1,2072 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#include "qapplication.h" + +#ifndef QT_NO_DRAGANDDROP + +#include "qwidget.h" +#include "qpainter.h" +#include "qpixmap.h" +#include "qbitmap.h" +#include "qdesktopwidget.h" +#include "qevent.h" +#include "qiodevice.h" +#include "qpointer.h" +#include "qcursor.h" +#include "qelapsedtimer.h" +#include "qvariant.h" +#include "qvector.h" +#include "qurl.h" +#include "qdebug.h" +#include "qimagewriter.h" +#include "qbuffer.h" +#include "qtextcodec.h" + +#include "qdnd_p.h" +#include "qapplication_p.h" +#include "qt_x11_p.h" +#include "qx11info_x11.h" + +#include "qwidget_p.h" +#include "qcursor_p.h" + +QT_BEGIN_NAMESPACE + +// #define DND_DEBUG +#ifdef DND_DEBUG +#define DEBUG qDebug +#else +#define DEBUG if(0) qDebug +#endif + +#ifdef DND_DEBUG +#define DNDDEBUG qDebug() +#else +#define DNDDEBUG if(0) qDebug() +#endif + +static int findXdndDropTransactionByWindow(Window window) +{ + int at = -1; + for (int i = 0; i < X11->dndDropTransactions.count(); ++i) { + const QXdndDropTransaction &t = X11->dndDropTransactions.at(i); + if (t.target == window || t.proxy_target == window) { + at = i; + break; + } + } + return at; +} + +static int findXdndDropTransactionByTime(Time timestamp) +{ + int at = -1; + for (int i = 0; i < X11->dndDropTransactions.count(); ++i) { + const QXdndDropTransaction &t = X11->dndDropTransactions.at(i); + if (t.timestamp == timestamp) { + at = i; + break; + } + } + return at; +} + +// timer used to discard old XdndDrop transactions +static int transaction_expiry_timer = -1; +enum { XdndDropTransactionTimeout = 5000 }; // 5 seconds + +static void restartXdndDropExpiryTimer() +{ + if (transaction_expiry_timer != -1) + QDragManager::self()->killTimer(transaction_expiry_timer); + transaction_expiry_timer = QDragManager::self()->startTimer(XdndDropTransactionTimeout); +} + + +// find an ancestor with XdndAware on it +static Window findXdndAwareParent(Window window) +{ + Window target = 0; + forever { + // check if window has XdndAware + Atom type = 0; + int f; + unsigned long n, a; + unsigned char *data = 0; + if (XGetWindowProperty(X11->display, window, ATOM(XdndAware), 0, 0, False, + AnyPropertyType, &type, &f,&n,&a,&data) == Success) { + if (data) + XFree(data); + if (type) { + target = window; + break; + } + } + + // try window's parent + Window root; + Window parent; + Window *children; + uint unused; + if (!XQueryTree(X11->display, window, &root, &parent, &children, &unused)) + break; + if (children) + XFree(children); + if (window == root) + break; + window = parent; + } + return target; +} + + + + +// and all this stuff is copied -into- qapp_x11.cpp + +static void handle_xdnd_position(QWidget *, const XEvent *, bool); +static void handle_xdnd_status(QWidget * w, const XEvent * xe, bool /*passive*/); + +const int xdnd_version = 5; + +static Qt::DropAction xdndaction_to_qtaction(Atom atom) +{ + if (atom == ATOM(XdndActionCopy) || atom == 0) + return Qt::CopyAction; + if (atom == ATOM(XdndActionLink)) + return Qt::LinkAction; + if (atom == ATOM(XdndActionMove)) + return Qt::MoveAction; + return Qt::CopyAction; +} + +static int qtaction_to_xdndaction(Qt::DropAction a) +{ + switch (a) { + case Qt::CopyAction: + return ATOM(XdndActionCopy); + case Qt::LinkAction: + return ATOM(XdndActionLink); + case Qt::MoveAction: + case Qt::TargetMoveAction: + return ATOM(XdndActionMove); + case Qt::IgnoreAction: + return XNone; + default: + return ATOM(XdndActionCopy); + } +} + +// clean up the stuff used. +static void qt_xdnd_cleanup(); + +static void qt_xdnd_send_leave(); + +// real variables: +// xid of current drag source +static Atom qt_xdnd_dragsource_xid = 0; + +// the types in this drop. 100 is no good, but at least it's big. +const int qt_xdnd_max_type = 100; +static Atom qt_xdnd_types[qt_xdnd_max_type + 1]; + +// timer used when target wants "continuous" move messages (eg. scroll) +static int heartbeat = -1; +// rectangle in which the answer will be the same +static QRect qt_xdnd_source_sameanswer; +// top-level window we sent position to last. +static Window qt_xdnd_current_target; +// window to send events to (always valid if qt_xdnd_current_target) +static Window qt_xdnd_current_proxy_target; +static Time qt_xdnd_source_current_time; + +// widget we forwarded position to last, and local position +static QPointer<QWidget> qt_xdnd_current_widget; +static QPoint qt_xdnd_current_position; +// timestamp from the XdndPosition and XdndDrop +static Time qt_xdnd_target_current_time; +// screen number containing the pointer... -1 means default +static int qt_xdnd_current_screen = -1; +// state of dragging... true if dragging, false if not +bool qt_xdnd_dragging = false; + +static bool waiting_for_status = false; + +// used to preset each new QDragMoveEvent +static Qt::DropAction last_target_accepted_action = Qt::IgnoreAction; + +// Shift/Ctrl handling, and final drop status +static Qt::DropAction global_accepted_action = Qt::CopyAction; +static Qt::DropActions possible_actions = Qt::IgnoreAction; + +// for embedding only +static QWidget* current_embedding_widget = 0; +static XEvent last_enter_event; + +// cursors +static QCursor *noDropCursor = 0; +static QCursor *moveCursor = 0; +static QCursor *copyCursor = 0; +static QCursor *linkCursor = 0; + +static QPixmap *defaultPm = 0; + +static const int default_pm_hotx = -2; +static const int default_pm_hoty = -16; +static const char* const default_pm[] = { +"13 9 3 1", +". c None", +" c #000000", +"X c #FFFFFF", +"X X X X X X X", +" X X X X X X ", +"X ......... X", +" X.........X ", +"X ......... X", +" X.........X ", +"X ......... X", +" X X X X X X ", +"X X X X X X X" +}; + +class QShapedPixmapWidget : public QWidget +{ + Q_OBJECT +public: + QShapedPixmapWidget(QWidget* w) : + QWidget(w, + Qt::Tool | Qt::FramelessWindowHint + | Qt::X11BypassWindowManagerHint + | Qt::BypassGraphicsProxyWidget) + { + setAttribute(Qt::WA_X11NetWmWindowTypeDND); + } + + void setPixmap(const QPixmap &pm) + { + QBitmap mask = pm.mask(); + if (!mask.isNull()) { + setMask(mask); + } else { + clearMask(); + } + resize(pm.width(),pm.height()); + pixmap = pm; + update(); + } + QPoint pm_hot; + +protected: + QPixmap pixmap; + void paintEvent(QPaintEvent*) + { + QPainter p(this); + p.drawPixmap(0, 0, pixmap); + } +}; + +#include "qdnd_x11.moc" + +struct XdndData { + QShapedPixmapWidget *deco; + QWidget* desktop_proxy; +}; + +static XdndData xdnd_data = { 0, 0 }; + +class QExtraWidget : public QWidget +{ + Q_DECLARE_PRIVATE(QWidget) +public: + inline QWExtra* extraData(); + inline QTLWExtra* topData(); +}; + +inline QWExtra* QExtraWidget::extraData() { return d_func()->extraData(); } +inline QTLWExtra* QExtraWidget::topData() { return d_func()->topData(); } + + +static WId xdndProxy(WId w) +{ + Atom type = XNone; + int f; + unsigned long n, a; + unsigned char *retval = 0; + XGetWindowProperty(X11->display, w, ATOM(XdndProxy), 0, 1, False, + XA_WINDOW, &type, &f,&n,&a,&retval); + WId *proxy_id_ptr = (WId *)retval; + WId proxy_id = 0; + if (type == XA_WINDOW && proxy_id_ptr) { + proxy_id = *proxy_id_ptr; + XFree(proxy_id_ptr); + proxy_id_ptr = 0; + // Already exists. Real? + X11->ignoreBadwindow(); + XGetWindowProperty(X11->display, proxy_id, ATOM(XdndProxy), 0, 1, False, + XA_WINDOW, &type, &f,&n,&a,&retval); + proxy_id_ptr = (WId *)retval; + if (X11->badwindow() || type != XA_WINDOW || !proxy_id_ptr || *proxy_id_ptr != proxy_id) + // Bogus - we will overwrite. + proxy_id = 0; + } + if (proxy_id_ptr) + XFree(proxy_id_ptr); + return proxy_id; +} + +static bool xdndEnable(QWidget* w, bool on) +{ + DNDDEBUG << "xdndEnable" << w << on; + if (on) { + QWidget * xdnd_widget = 0; + if ((w->windowType() == Qt::Desktop)) { + if (xdnd_data.desktop_proxy) // *WE* already have one. + return false; + + // As per Xdnd4, use XdndProxy + XGrabServer(X11->display); + Q_ASSERT(w->testAttribute(Qt::WA_WState_Created)); + WId proxy_id = xdndProxy(w->effectiveWinId()); + + if (!proxy_id) { + xdnd_widget = xdnd_data.desktop_proxy = new QWidget; + proxy_id = xdnd_data.desktop_proxy->effectiveWinId(); + XChangeProperty (X11->display, w->effectiveWinId(), ATOM(XdndProxy), + XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1); + XChangeProperty (X11->display, proxy_id, ATOM(XdndProxy), + XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1); + } + + XUngrabServer(X11->display); + } else { + xdnd_widget = w->window(); + } + if (xdnd_widget) { + DNDDEBUG << "setting XdndAware for" << xdnd_widget << xdnd_widget->effectiveWinId(); + Atom atm = (Atom)xdnd_version; + Q_ASSERT(xdnd_widget->testAttribute(Qt::WA_WState_Created)); + XChangeProperty(X11->display, xdnd_widget->effectiveWinId(), ATOM(XdndAware), + XA_ATOM, 32, PropModeReplace, (unsigned char *)&atm, 1); + return true; + } else { + return false; + } + } else { + if ((w->windowType() == Qt::Desktop)) { + XDeleteProperty(X11->display, w->internalWinId(), ATOM(XdndProxy)); + delete xdnd_data.desktop_proxy; + xdnd_data.desktop_proxy = 0; + } else { + DNDDEBUG << "not deleting XDndAware"; + } + return true; + } +} + +QByteArray QX11Data::xdndAtomToString(Atom a) +{ + if (!a) return 0; + + if (a == XA_STRING || a == ATOM(UTF8_STRING)) { + return "text/plain"; // some Xdnd clients are dumb + } + char *atom = XGetAtomName(display, a); + QByteArray result = atom; + XFree(atom); + return result; +} + +Atom QX11Data::xdndStringToAtom(const char *mimeType) +{ + if (!mimeType || !*mimeType) + return 0; + return XInternAtom(display, mimeType, False); +} + +//$$$ +QString QX11Data::xdndMimeAtomToString(Atom a) +{ + QString atomName; + if (a) { + char *atom = XGetAtomName(display, a); + atomName = QString::fromLatin1(atom); + XFree(atom); + } + return atomName; +} + +//$$$ +Atom QX11Data::xdndMimeStringToAtom(const QString &mimeType) +{ + if (mimeType.isEmpty()) + return 0; + return XInternAtom(display, mimeType.toLatin1().constData(), False); +} + +//$$$ replace ccxdndAtomToString() +QStringList QX11Data::xdndMimeFormatsForAtom(Atom a) +{ + QStringList formats; + if (a) { + QString atomName = xdndMimeAtomToString(a); + formats.append(atomName); + + // special cases for string type + if (a == ATOM(UTF8_STRING) || a == XA_STRING + || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT)) + formats.append(QLatin1String("text/plain")); + + // special cases for uris + if (atomName == QLatin1String("text/x-moz-url")) + formats.append(QLatin1String("text/uri-list")); + + // special case for images + if (a == XA_PIXMAP) + formats.append(QLatin1String("image/ppm")); + } + return formats; +} + +//$$$ +bool QX11Data::xdndMimeDataForAtom(Atom a, QMimeData *mimeData, QByteArray *data, Atom *atomFormat, int *dataFormat) +{ + bool ret = false; + *atomFormat = a; + *dataFormat = 8; + QString atomName = xdndMimeAtomToString(a); + if (QInternalMimeData::hasFormatHelper(atomName, mimeData)) { + *data = QInternalMimeData::renderDataHelper(atomName, mimeData); + if (atomName == QLatin1String("application/x-color")) + *dataFormat = 16; + ret = true; + } else { + if ((a == ATOM(UTF8_STRING) || a == XA_STRING + || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT)) + && QInternalMimeData::hasFormatHelper(QLatin1String("text/plain"), mimeData)) { + if (a == ATOM(UTF8_STRING)){ + *data = QInternalMimeData::renderDataHelper(QLatin1String("text/plain"), mimeData); + ret = true; + } else if (a == XA_STRING) { + *data = QString::fromUtf8(QInternalMimeData::renderDataHelper( + QLatin1String("text/plain"), mimeData)).toLocal8Bit(); + ret = true; + } else if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT)) { + // the ICCCM states that TEXT and COMPOUND_TEXT are in the + // encoding of choice, so we choose the encoding of the locale + QByteArray strData = QString::fromUtf8(QInternalMimeData::renderDataHelper( + QLatin1String("text/plain"), mimeData)).toLocal8Bit(); + char *list[] = { strData.data(), NULL }; + + XICCEncodingStyle style = (a == ATOM(COMPOUND_TEXT)) + ? XCompoundTextStyle : XStdICCTextStyle; + XTextProperty textprop; + if (list[0] != NULL + && XmbTextListToTextProperty(X11->display, list, 1, style, + &textprop) == Success) { + *atomFormat = textprop.encoding; + *dataFormat = textprop.format; + *data = QByteArray((const char *) textprop.value, textprop.nitems * textprop.format / 8); + ret = true; + + DEBUG(" textprop type %lx\n" + " textprop name '%s'\n" + " format %d\n" + " %ld items\n" + " %d bytes\n", + textprop.encoding, + X11->xdndMimeAtomToString(textprop.encoding).toLatin1().data(), + textprop.format, textprop.nitems, data->size()); + + XFree(textprop.value); + } + } + } else if (atomName == QLatin1String("text/x-moz-url") && + QInternalMimeData::hasFormatHelper(QLatin1String("text/uri-list"), mimeData)) { + QByteArray uri = QInternalMimeData::renderDataHelper( + QLatin1String("text/uri-list"), mimeData).split('\n').first(); + QString mozUri = QString::fromLatin1(uri, uri.size()); + mozUri += QLatin1Char('\n'); + *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()), mozUri.length() * 2); + ret = true; + } else if ((a == XA_PIXMAP || a == XA_BITMAP) && mimeData->hasImage()) { + QPixmap pm = qvariant_cast<QPixmap>(mimeData->imageData()); + if (a == XA_BITMAP && pm.depth() != 1) { + QImage img = pm.toImage(); + img = img.convertToFormat(QImage::Format_MonoLSB); + pm = QPixmap::fromImage(img); + } + QDragManager *dm = QDragManager::self(); + if (dm) { + Pixmap handle = pm.handle(); + *data = QByteArray((const char *) &handle, sizeof(Pixmap)); + dm->xdndMimeTransferedPixmap[dm->xdndMimeTransferedPixmapIndex] = pm; + dm->xdndMimeTransferedPixmapIndex = + (dm->xdndMimeTransferedPixmapIndex + 1) % 2; + ret = true; + } + } else { + DEBUG("QClipboard: xdndMimeDataForAtom(): converting to type '%s' is not supported", qPrintable(atomName)); + } + } + return ret && data != 0; +} + +//$$$ +QList<Atom> QX11Data::xdndMimeAtomsForFormat(const QString &format) +{ + QList<Atom> atoms; + atoms.append(xdndMimeStringToAtom(format)); + + // special cases for strings + if (format == QLatin1String("text/plain")) { + atoms.append(ATOM(UTF8_STRING)); + atoms.append(XA_STRING); + atoms.append(ATOM(TEXT)); + atoms.append(ATOM(COMPOUND_TEXT)); + } + + // special cases for uris + if (format == QLatin1String("text/uri-list")) { + atoms.append(xdndMimeStringToAtom(QLatin1String("text/x-moz-url"))); + } + + //special cases for images + if (format == QLatin1String("image/ppm")) + atoms.append(XA_PIXMAP); + if (format == QLatin1String("image/pbm")) + atoms.append(XA_BITMAP); + + return atoms; +} + +//$$$ +QVariant QX11Data::xdndMimeConvertToFormat(Atom a, const QByteArray &data, const QString &format, QVariant::Type requestedType, const QByteArray &encoding) +{ + QString atomName = xdndMimeAtomToString(a); + if (atomName == format) + return data; + + if (!encoding.isEmpty() + && atomName == format + QLatin1String(";charset=") + QString::fromLatin1(encoding)) { + + if (requestedType == QVariant::String) { + QTextCodec *codec = QTextCodec::codecForName(encoding); + if (codec) + return codec->toUnicode(data); + } + + return data; + } + + // special cases for string types + if (format == QLatin1String("text/plain")) { + if (a == ATOM(UTF8_STRING)) + return QString::fromUtf8(data); + if (a == XA_STRING) + return QString::fromLatin1(data); + if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT)) + // #### might be wrong for COMPUND_TEXT + return QString::fromLocal8Bit(data, data.size()); + } + + // special case for uri types + if (format == QLatin1String("text/uri-list")) { + if (atomName == QLatin1String("text/x-moz-url")) { + // we expect this as utf16 <url><space><title> + // the first part is a url that should only contain ascci char + // so it should be safe to check that the second char is 0 + // to verify that it is utf16 + if (data.size() > 1 && data.at(1) == 0) + return QString::fromRawData((const QChar *)data.constData(), + data.size() / 2).split(QLatin1Char('\n')).first().toLatin1(); + } + } + + // special cas for images + if (format == QLatin1String("image/ppm")) { + if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) { + Pixmap xpm = *((Pixmap*)data.data()); + if (!xpm) + return QByteArray(); + QPixmap qpm = QPixmap::fromX11Pixmap(xpm); + QImageWriter imageWriter; + imageWriter.setFormat("PPMRAW"); + QImage imageToWrite = qpm.toImage(); + QBuffer buf; + buf.open(QIODevice::WriteOnly); + imageWriter.setDevice(&buf); + imageWriter.write(imageToWrite); + return buf.buffer(); + } + } + return QVariant(); +} + +//$$$ middle of xdndObtainData +Atom QX11Data::xdndMimeAtomForFormat(const QString &format, QVariant::Type requestedType, const QList<Atom> &atoms, QByteArray *encoding) +{ + encoding->clear(); + + // find matches for string types + if (format == QLatin1String("text/plain")) { + if (atoms.contains(ATOM(UTF8_STRING))) + return ATOM(UTF8_STRING); + if (atoms.contains(ATOM(COMPOUND_TEXT))) + return ATOM(COMPOUND_TEXT); + if (atoms.contains(ATOM(TEXT))) + return ATOM(TEXT); + if (atoms.contains(XA_STRING)) + return XA_STRING; + } + + // find matches for uri types + if (format == QLatin1String("text/uri-list")) { + Atom a = xdndMimeStringToAtom(format); + if (a && atoms.contains(a)) + return a; + a = xdndMimeStringToAtom(QLatin1String("text/x-moz-url")); + if (a && atoms.contains(a)) + return a; + } + + // find match for image + if (format == QLatin1String("image/ppm")) { + if (atoms.contains(XA_PIXMAP)) + return XA_PIXMAP; + } + + // for string/text requests try to use a format with a well-defined charset + // first to avoid encoding problems + if (requestedType == QVariant::String + && format.startsWith(QLatin1String("text/")) + && !format.contains(QLatin1String("charset="))) { + + QString formatWithCharset = format; + formatWithCharset.append(QLatin1String(";charset=utf-8")); + + Atom a = xdndMimeStringToAtom(formatWithCharset); + if (a && atoms.contains(a)) { + *encoding = "utf-8"; + return a; + } + } + + Atom a = xdndMimeStringToAtom(format); + if (a && atoms.contains(a)) + return a; + + return 0; +} + +void QX11Data::xdndSetup() { + QCursorData::initialize(); + qAddPostRoutine(qt_xdnd_cleanup); +} + + +void qt_xdnd_cleanup() +{ + delete noDropCursor; + noDropCursor = 0; + delete copyCursor; + copyCursor = 0; + delete moveCursor; + moveCursor = 0; + delete linkCursor; + linkCursor = 0; + delete defaultPm; + defaultPm = 0; + delete xdnd_data.desktop_proxy; + xdnd_data.desktop_proxy = 0; + delete xdnd_data.deco; + xdnd_data.deco = 0; +} + + +static QWidget *find_child(QWidget *tlw, QPoint & p) +{ + QWidget *widget = tlw; + + p = widget->mapFromGlobal(p); + bool done = false; + while (!done) { + done = true; + if (((QExtraWidget*)widget)->extraData() && + ((QExtraWidget*)widget)->extraData()->xDndProxy != 0) + break; // stop searching for widgets under the mouse cursor if found widget is a proxy. + QObjectList children = widget->children(); + if (!children.isEmpty()) { + for(int i = children.size(); i > 0;) { + --i; + QWidget *w = qobject_cast<QWidget *>(children.at(i)); + if (!w) + continue; + if (w->testAttribute(Qt::WA_TransparentForMouseEvents)) + continue; + if (w->isVisible() && + w->geometry().contains(p) && + !w->isWindow()) { + widget = w; + done = false; + p = widget->mapFromParent(p); + break; + } + } + } + } + return widget; +} + + +static bool checkEmbedded(QWidget* w, const XEvent* xe) +{ + if (!w) + return false; + + if (current_embedding_widget != 0 && current_embedding_widget != w) { + qt_xdnd_current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy; + qt_xdnd_current_proxy_target = qt_xdnd_current_target; + qt_xdnd_send_leave(); + qt_xdnd_current_target = 0; + qt_xdnd_current_proxy_target = 0; + current_embedding_widget = 0; + } + + QWExtra* extra = ((QExtraWidget*)w)->extraData(); + if (extra && extra->xDndProxy != 0) { + + if (current_embedding_widget != w) { + + last_enter_event.xany.window = extra->xDndProxy; + XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event); + current_embedding_widget = w; + } + + ((XEvent*)xe)->xany.window = extra->xDndProxy; + XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe); + if (qt_xdnd_current_widget != w) { + qt_xdnd_current_widget = w; + } + return true; + } + current_embedding_widget = 0; + return false; +} + +void QX11Data::xdndHandleEnter(QWidget *, const XEvent * xe, bool /*passive*/) +{ + motifdnd_active = false; + + last_enter_event.xclient = xe->xclient; + + const long *l = xe->xclient.data.l; + int version = (int)(((unsigned long)(l[1])) >> 24); + + if (version > xdnd_version) + return; + + qt_xdnd_dragsource_xid = l[0]; + + int j = 0; + if (l[1] & 1) { + // get the types from XdndTypeList + Atom type = XNone; + int f; + unsigned long n, a; + unsigned char *retval = 0; + XGetWindowProperty(X11->display, qt_xdnd_dragsource_xid, ATOM(XdndTypelist), 0, + qt_xdnd_max_type, False, XA_ATOM, &type, &f,&n,&a,&retval); + if (retval) { + Atom *data = (Atom *)retval; + for (; j<qt_xdnd_max_type && j < (int)n; j++) { + qt_xdnd_types[j] = data[j]; + } + XFree((uchar*)data); + } + } else { + // get the types from the message + int i; + for(i=2; i < 5; i++) { + qt_xdnd_types[j++] = l[i]; + } + } + qt_xdnd_types[j] = 0; +} + +static void handle_xdnd_position(QWidget *w, const XEvent * xe, bool passive) +{ + const unsigned long *l = (const unsigned long *)xe->xclient.data.l; + + QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff); + QWidget * c = find_child(w, p); // changes p to to c-local coordinates + + if (!passive && checkEmbedded(c, xe)) + return; + + if (!c || (!c->acceptDrops() && (c->windowType() == Qt::Desktop))) + return; + + if (l[0] != qt_xdnd_dragsource_xid) { + DEBUG("xdnd drag position from unexpected source (%08lx not %08lx)", l[0], qt_xdnd_dragsource_xid); + return; + } + + // timestamp from the source + if (l[3] != 0) { + // Some X server/client combination swallow the first 32 bit and + // interpret a set bit 31 as negative sign. + qt_xdnd_target_current_time = X11->userTime = + ((sizeof(Time) == 8 && xe->xclient.data.l[3] < 0) + ? uint(l[3]) + : l[3]); + } + + QDragManager *manager = QDragManager::self(); + QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData; + + XClientMessageEvent response; + response.type = ClientMessage; + response.window = qt_xdnd_dragsource_xid; + response.format = 32; + response.message_type = ATOM(XdndStatus); + response.data.l[0] = w->effectiveWinId(); + response.data.l[1] = 0; // flags + response.data.l[2] = 0; // x, y + response.data.l[3] = 0; // w, h + response.data.l[4] = 0; // action + + if (!passive) { // otherwise just reject + while (c && !c->acceptDrops() && !c->isWindow()) { + p = c->mapToParent(p); + c = c->parentWidget(); + } + QWidget *target_widget = c && c->acceptDrops() ? c : 0; + + QRect answerRect(c->mapToGlobal(p), QSize(1,1)); + + if (manager->object) { + possible_actions = manager->dragPrivate()->possible_actions; + } else { + possible_actions = Qt::DropActions(xdndaction_to_qtaction(l[4])); +// possible_actions |= Qt::CopyAction; + } + QDragMoveEvent me(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers()); + + Qt::DropAction accepted_action = Qt::IgnoreAction; + + + if (target_widget != qt_xdnd_current_widget) { + if (qt_xdnd_current_widget) { + QDragLeaveEvent e; + QApplication::sendEvent(qt_xdnd_current_widget, &e); + } + if (qt_xdnd_current_widget != target_widget) { + qt_xdnd_current_widget = target_widget; + } + if (target_widget) { + qt_xdnd_current_position = p; + + last_target_accepted_action = Qt::IgnoreAction; + QDragEnterEvent de(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers()); + QApplication::sendEvent(target_widget, &de); + if (de.isAccepted() && de.dropAction() != Qt::IgnoreAction) + last_target_accepted_action = de.dropAction(); + } + } + + DEBUG() << "qt_handle_xdnd_position action=" << X11->xdndAtomToString(l[4]); + if (!target_widget) { + answerRect = QRect(p, QSize(1, 1)); + } else { + qt_xdnd_current_widget = c; + qt_xdnd_current_position = p; + + if (last_target_accepted_action != Qt::IgnoreAction) { + me.setDropAction(last_target_accepted_action); + me.accept(); + } + QApplication::sendEvent(c, &me); + if (me.isAccepted()) { + response.data.l[1] = 1; // yes + accepted_action = me.dropAction(); + last_target_accepted_action = accepted_action; + } else { + response.data.l[0] = 0; + last_target_accepted_action = Qt::IgnoreAction; + } + answerRect = me.answerRect().intersected(c->rect()); + } + answerRect = QRect(c->mapToGlobal(answerRect.topLeft()), answerRect.size()); + + if (answerRect.left() < 0) + answerRect.setLeft(0); + if (answerRect.right() > 4096) + answerRect.setRight(4096); + if (answerRect.top() < 0) + answerRect.setTop(0); + if (answerRect.bottom() > 4096) + answerRect.setBottom(4096); + if (answerRect.width() < 0) + answerRect.setWidth(0); + if (answerRect.height() < 0) + answerRect.setHeight(0); + + response.data.l[2] = (answerRect.x() << 16) + answerRect.y(); + response.data.l[3] = (answerRect.width() << 16) + answerRect.height(); + response.data.l[4] = qtaction_to_xdndaction(accepted_action); + } + + // reset + qt_xdnd_target_current_time = CurrentTime; + + QWidget * source = QWidget::find(qt_xdnd_dragsource_xid); + if (source && (source->windowType() == Qt::Desktop) && !source->acceptDrops()) + source = 0; + + DEBUG() << "sending XdndStatus"; + if (source) + handle_xdnd_status(source, (const XEvent *)&response, passive); + else + XSendEvent(X11->display, qt_xdnd_dragsource_xid, False, NoEventMask, (XEvent*)&response); +} + +static Bool xdnd_position_scanner(Display *, XEvent *event, XPointer) +{ + if (event->type != ClientMessage) + return false; + XClientMessageEvent *ev = &event->xclient; + + if (ev->message_type == ATOM(XdndPosition)) + return true; + + return false; +} + +void QX11Data::xdndHandlePosition(QWidget * w, const XEvent * xe, bool passive) +{ + DEBUG("xdndHandlePosition"); + while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_position_scanner, 0)) + ; + + handle_xdnd_position(w, xe, passive); +} + + +static void handle_xdnd_status(QWidget *, const XEvent * xe, bool) +{ + const unsigned long *l = (const unsigned long *)xe->xclient.data.l; + // ignore late status messages + if (l[0] && l[0] != qt_xdnd_current_proxy_target) + return; + Qt::DropAction newAction = (l[1] & 0x1) ? xdndaction_to_qtaction(l[4]) : Qt::IgnoreAction; + + if ((int)(l[1] & 2) == 0) { + QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff); + QSize s((l[3] & 0xffff0000) >> 16, l[3] & 0x0000ffff); + qt_xdnd_source_sameanswer = QRect(p, s); + } else { + qt_xdnd_source_sameanswer = QRect(); + } + QDragManager *manager = QDragManager::self(); + manager->willDrop = (l[1] & 0x1); + if (global_accepted_action != newAction) + manager->emitActionChanged(newAction); + global_accepted_action = newAction; + manager->updateCursor(); + waiting_for_status = false; +} + +static Bool xdnd_status_scanner(Display *, XEvent *event, XPointer) +{ + if (event->type != ClientMessage) + return false; + XClientMessageEvent *ev = &event->xclient; + + if (ev->message_type == ATOM(XdndStatus)) + return true; + + return false; +} + +void QX11Data::xdndHandleStatus(QWidget * w, const XEvent * xe, bool passive) +{ + DEBUG("xdndHandleStatus"); + while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_status_scanner, 0)) + ; + + handle_xdnd_status(w, xe, passive); + DEBUG("xdndHandleStatus end"); +} + +void QX11Data::xdndHandleLeave(QWidget *w, const XEvent * xe, bool /*passive*/) +{ + DEBUG("xdnd leave"); + if (!qt_xdnd_current_widget || + w->window() != qt_xdnd_current_widget->window()) { + return; // sanity + } + + if (checkEmbedded(current_embedding_widget, xe)) { + current_embedding_widget = 0; + qt_xdnd_current_widget = 0; + return; + } + + const unsigned long *l = (const unsigned long *)xe->xclient.data.l; + + QDragLeaveEvent e; + QApplication::sendEvent(qt_xdnd_current_widget, &e); + + if (l[0] != qt_xdnd_dragsource_xid) { + // This often happens - leave other-process window quickly + DEBUG("xdnd drag leave from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid); + qt_xdnd_current_widget = 0; + return; + } + + qt_xdnd_dragsource_xid = 0; + qt_xdnd_types[0] = 0; + qt_xdnd_current_widget = 0; +} + + +void qt_xdnd_send_leave() +{ + if (!qt_xdnd_current_target) + return; + + QDragManager *manager = QDragManager::self(); + + XClientMessageEvent leave; + leave.type = ClientMessage; + leave.window = qt_xdnd_current_target; + leave.format = 32; + leave.message_type = ATOM(XdndLeave); + leave.data.l[0] = manager->dragPrivate()->source->effectiveWinId(); + leave.data.l[1] = 0; // flags + leave.data.l[2] = 0; // x, y + leave.data.l[3] = 0; // w, h + leave.data.l[4] = 0; // just null + + QWidget * w = QWidget::find(qt_xdnd_current_proxy_target); + + if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops()) + w = 0; + + if (w) + X11->xdndHandleLeave(w, (const XEvent *)&leave, false); + else + XSendEvent(X11->display, qt_xdnd_current_proxy_target, False, + NoEventMask, (XEvent*)&leave); + + // reset the drag manager state + manager->willDrop = false; + if (global_accepted_action != Qt::IgnoreAction) + manager->emitActionChanged(Qt::IgnoreAction); + global_accepted_action = Qt::IgnoreAction; + manager->updateCursor(); + qt_xdnd_current_target = 0; + qt_xdnd_current_proxy_target = 0; + qt_xdnd_source_current_time = 0; + waiting_for_status = false; +} + +// TODO: remove and use QApplication::currentKeyboardModifiers() in Qt 4.8. +static Qt::KeyboardModifiers currentKeyboardModifiers() +{ + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint keybstate; + for (int i = 0; i < ScreenCount(X11->display); ++i) { + if (XQueryPointer(X11->display, QX11Info::appRootWindow(i), &root, &child, + &root_x, &root_y, &win_x, &win_y, &keybstate)) + return X11->translateModifiers(keybstate & 0x00ff); + } + return 0; +} + +void QX11Data::xdndHandleDrop(QWidget *, const XEvent * xe, bool passive) +{ + DEBUG("xdndHandleDrop"); + if (!qt_xdnd_current_widget) { + qt_xdnd_dragsource_xid = 0; + return; // sanity + } + + if (!passive && checkEmbedded(qt_xdnd_current_widget, xe)){ + current_embedding_widget = 0; + qt_xdnd_dragsource_xid = 0; + qt_xdnd_current_widget = 0; + return; + } + const unsigned long *l = (const unsigned long *)xe->xclient.data.l; + + QDragManager *manager = QDragManager::self(); + DEBUG("xdnd drop"); + + if (l[0] != qt_xdnd_dragsource_xid) { + DEBUG("xdnd drop from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid); + return; + } + + // update the "user time" from the timestamp in the event. + if (l[2] != 0) { + // Some X server/client combination swallow the first 32 bit and + // interpret a set bit 31 as negative sign. + qt_xdnd_target_current_time = X11->userTime = + ((sizeof(Time) == 8 && xe->xclient.data.l[2] < 0) + ? uint(l[2]) + : l[2]); + } + + if (!passive) { + // this could be a same-application drop, just proxied due to + // some XEMBEDding, so try to find the real QMimeData used + // based on the timestamp for this drop. + QMimeData *dropData = 0; + int at = findXdndDropTransactionByTime(qt_xdnd_target_current_time); + if (at != -1) + dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data; + // if we can't find it, then use the data in the drag manager + if (!dropData) + dropData = (manager->object) ? manager->dragPrivate()->data : manager->dropData; + + // Drop coming from another app? Update keyboard modifiers. + if (!qt_xdnd_dragging) { + QApplicationPrivate::modifier_buttons = currentKeyboardModifiers(); + } + + QDropEvent de(qt_xdnd_current_position, possible_actions, dropData, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + QApplication::sendEvent(qt_xdnd_current_widget, &de); + if (!de.isAccepted()) { + // Ignore a failed drag + global_accepted_action = Qt::IgnoreAction; + } else { + global_accepted_action = de.dropAction(); + } + XClientMessageEvent finished; + finished.type = ClientMessage; + finished.window = qt_xdnd_dragsource_xid; + finished.format = 32; + finished.message_type = ATOM(XdndFinished); + DNDDEBUG << "xdndHandleDrop" + << "qt_xdnd_current_widget" << qt_xdnd_current_widget + << (qt_xdnd_current_widget ? qt_xdnd_current_widget->effectiveWinId() : 0) + << "t_xdnd_current_widget->window()" + << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window() : 0) + << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window()->internalWinId() : 0); + finished.data.l[0] = qt_xdnd_current_widget?qt_xdnd_current_widget->window()->internalWinId():0; + finished.data.l[1] = de.isAccepted() ? 1 : 0; // flags + finished.data.l[2] = qtaction_to_xdndaction(global_accepted_action); + XSendEvent(X11->display, qt_xdnd_dragsource_xid, False, + NoEventMask, (XEvent*)&finished); + } else { + QDragLeaveEvent e; + QApplication::sendEvent(qt_xdnd_current_widget, &e); + } + qt_xdnd_dragsource_xid = 0; + qt_xdnd_current_widget = 0; + waiting_for_status = false; + + // reset + qt_xdnd_target_current_time = CurrentTime; +} + + +void QX11Data::xdndHandleFinished(QWidget *, const XEvent * xe, bool passive) +{ + DEBUG("xdndHandleFinished"); + const unsigned long *l = (const unsigned long *)xe->xclient.data.l; + + DNDDEBUG << "xdndHandleFinished, l[0]" << l[0] + << "qt_xdnd_current_target" << qt_xdnd_current_target + << "qt_xdnd_current_proxy_targe" << qt_xdnd_current_proxy_target; + + if (l[0]) { + int at = findXdndDropTransactionByWindow(l[0]); + if (at != -1) { + restartXdndDropExpiryTimer(); + + QXdndDropTransaction t = X11->dndDropTransactions.takeAt(at); + QDragManager *manager = QDragManager::self(); + + Window target = qt_xdnd_current_target; + Window proxy_target = qt_xdnd_current_proxy_target; + QWidget *embedding_widget = current_embedding_widget; + QDrag *currentObject = manager->object; + + qt_xdnd_current_target = t.target; + qt_xdnd_current_proxy_target = t.proxy_target; + current_embedding_widget = t.embedding_widget; + manager->object = t.object; + + if (!passive) + (void) checkEmbedded(qt_xdnd_current_widget, xe); + + current_embedding_widget = 0; + qt_xdnd_current_target = 0; + qt_xdnd_current_proxy_target = 0; + + if (t.object) + t.object->deleteLater(); + + qt_xdnd_current_target = target; + qt_xdnd_current_proxy_target = proxy_target; + current_embedding_widget = embedding_widget; + manager->object = currentObject; + } + } + waiting_for_status = false; +} + + +void QDragManager::timerEvent(QTimerEvent* e) +{ + if (e->timerId() == heartbeat && qt_xdnd_source_sameanswer.isNull()) { + move(QCursor::pos()); + } else if (e->timerId() == transaction_expiry_timer) { + for (int i = 0; i < X11->dndDropTransactions.count(); ++i) { + const QXdndDropTransaction &t = X11->dndDropTransactions.at(i); + if (t.targetWidget) { + // dnd within the same process, don't delete these + continue; + } + t.object->deleteLater(); + X11->dndDropTransactions.removeAt(i--); + } + + killTimer(transaction_expiry_timer); + transaction_expiry_timer = -1; + } +} + +bool QDragManager::eventFilter(QObject * o, QEvent * e) +{ + if (beingCancelled) { + if (e->type() == QEvent::KeyRelease && ((QKeyEvent*)e)->key() == Qt::Key_Escape) { + qApp->removeEventFilter(this); + Q_ASSERT(object == 0); + beingCancelled = false; + eventLoop->exit(); + return true; // block the key release + } + return false; + } + + Q_ASSERT(object != 0); + + if (!o->isWidgetType()) + return false; + + if (e->type() == QEvent::MouseMove) { + QMouseEvent* me = (QMouseEvent *)e; + move(me->globalPos()); + return true; + } else if (e->type() == QEvent::MouseButtonRelease) { + DEBUG("pre drop"); + qApp->removeEventFilter(this); + if (willDrop) + drop(); + else + cancel(); + DEBUG("drop, resetting object"); + beingCancelled = false; + eventLoop->exit(); + return true; + } + + if (e->type() == QEvent::ShortcutOverride) { + // prevent accelerators from firing while dragging + e->accept(); + return true; + } + + if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) { + QKeyEvent *ke = ((QKeyEvent*)e); + if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) { + cancel(); + qApp->removeEventFilter(this); + beingCancelled = false; + eventLoop->exit(); + } else { + qt_xdnd_source_sameanswer = QRect(); // force move + move(QCursor::pos()); + } + return true; // Eat all key events + } + + // ### We bind modality to widgets, so we have to do this + // ### "manually". + // DnD is modal - eat all other interactive events + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::Wheel: + case QEvent::ShortcutOverride: + return true; + default: + return false; + } +} + +void QDragManager::updateCursor() +{ + if (!noDropCursor) { +#ifndef QT_NO_CURSOR + noDropCursor = new QCursor(Qt::ForbiddenCursor); + moveCursor = new QCursor(Qt::DragMoveCursor); + copyCursor = new QCursor(Qt::DragCopyCursor); + linkCursor = new QCursor(Qt::DragLinkCursor); +#endif + } + + QCursor *c; + if (willDrop) { + if (global_accepted_action == Qt::CopyAction) { + c = copyCursor; + } else if (global_accepted_action == Qt::LinkAction) { + c = linkCursor; + } else { + c = moveCursor; + } + if (xdnd_data.deco) { + xdnd_data.deco->show(); + xdnd_data.deco->raise(); + } + } else { + c = noDropCursor; + //if (qt_xdnd_deco) + // qt_xdnd_deco->hide(); + } +#ifndef QT_NO_CURSOR + if (c) + qApp->changeOverrideCursor(*c); +#endif +} + + +void QDragManager::cancel(bool deleteSource) +{ + DEBUG("QDragManager::cancel"); + Q_ASSERT(heartbeat != -1); + killTimer(heartbeat); + heartbeat = -1; + beingCancelled = true; + qt_xdnd_dragging = false; + + if (qt_xdnd_current_target) + qt_xdnd_send_leave(); + +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + restoreCursor = false; + } +#endif + + if (deleteSource && object) + object->deleteLater(); + object = 0; + qDeleteInEventHandler(xdnd_data.deco); + xdnd_data.deco = 0; + + global_accepted_action = Qt::IgnoreAction; +} + +static +Window findRealWindow(const QPoint & pos, Window w, int md) +{ + if (xdnd_data.deco && w == xdnd_data.deco->effectiveWinId()) + return 0; + + if (md) { + X11->ignoreBadwindow(); + XWindowAttributes attr; + XGetWindowAttributes(X11->display, w, &attr); + if (X11->badwindow()) + return 0; + + if (attr.map_state == IsViewable + && QRect(attr.x,attr.y,attr.width,attr.height).contains(pos)) { + { + Atom type = XNone; + int f; + unsigned long n, a; + unsigned char *data; + + XGetWindowProperty(X11->display, w, ATOM(XdndAware), 0, 0, False, + AnyPropertyType, &type, &f,&n,&a,&data); + if (data) XFree(data); + if (type) + return w; + } + + Window r, p; + Window* c; + uint nc; + if (XQueryTree(X11->display, w, &r, &p, &c, &nc)) { + r=0; + for (uint i=nc; !r && i--;) { + r = findRealWindow(pos-QPoint(attr.x,attr.y), + c[i], md-1); + } + XFree(c); + if (r) + return r; + + // We didn't find a client window! Just use the + // innermost window. + } + + // No children! + return w; + } + } + return 0; +} + +void QDragManager::move(const QPoint & globalPos) +{ +#ifdef QT_NO_CURSOR + Q_UNUSED(globalPos); + return; +#else + DEBUG() << "QDragManager::move enter"; + if (!object) { + // perhaps the target crashed? + return; + } + + int screen = QCursor::x11Screen(); + if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) { + // recreate the pixmap on the new screen... + delete xdnd_data.deco; + QWidget* parent = object->source()->window()->x11Info().screen() == screen + ? object->source()->window() : QApplication::desktop()->screen(screen); + xdnd_data.deco = new QShapedPixmapWidget(parent); + if (!QWidget::mouseGrabber()) { + updatePixmap(); + xdnd_data.deco->grabMouse(); + } + } + xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot); + + if (qt_xdnd_source_sameanswer.contains(globalPos) && qt_xdnd_source_sameanswer.isValid()) + return; + + qt_xdnd_current_screen = screen; + Window rootwin = QX11Info::appRootWindow(qt_xdnd_current_screen); + Window target = 0; + int lx = 0, ly = 0; + if (!XTranslateCoordinates(X11->display, rootwin, rootwin, globalPos.x(), globalPos.y(), &lx, &ly, &target)) + // some weird error... + return; + + if (target == rootwin) { + // Ok. + } else if (target) { + //me + Window src = rootwin; + while (target != 0) { + DNDDEBUG << "checking target for XdndAware" << QWidget::find(target) << target; + int lx2, ly2; + Window t; + // translate coordinates + if (!XTranslateCoordinates(X11->display, src, target, lx, ly, &lx2, &ly2, &t)) { + target = 0; + break; + } + lx = lx2; + ly = ly2; + src = target; + + // check if it has XdndAware + Atom type = 0; + int f; + unsigned long n, a; + unsigned char *data = 0; + XGetWindowProperty(X11->display, target, ATOM(XdndAware), 0, 0, False, + AnyPropertyType, &type, &f,&n,&a,&data); + if (data) + XFree(data); + if (type) { + DNDDEBUG << "Found XdndAware on " << QWidget::find(target) << target; + break; + } + + // find child at the coordinates + if (!XTranslateCoordinates(X11->display, src, src, lx, ly, &lx2, &ly2, &target)) { + target = 0; + break; + } + } + if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) { + DNDDEBUG << "need to find real window"; + target = findRealWindow(globalPos, rootwin, 6); + DNDDEBUG << "real window found" << QWidget::find(target) << target; + } + } + + QWidget* w; + if (target) { + w = QWidget::find((WId)target); + if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops()) + w = 0; + } else { + w = 0; + target = rootwin; + } + + DNDDEBUG << "and the final target is " << QWidget::find(target) << target; + DNDDEBUG << "the widget w is" << w; + + WId proxy_target = xdndProxy(target); + if (!proxy_target) + proxy_target = target; + int target_version = 1; + + if (proxy_target) { + Atom type = XNone; + int r, f; + unsigned long n, a; + unsigned char *retval; + X11->ignoreBadwindow(); + r = XGetWindowProperty(X11->display, proxy_target, ATOM(XdndAware), 0, + 1, False, AnyPropertyType, &type, &f,&n,&a,&retval); + int *tv = (int *)retval; + if (r != Success || X11->badwindow()) { + target = 0; + } else { + target_version = qMin(xdnd_version,tv ? *tv : 1); + if (tv) + XFree(tv); +// if (!(!X11->badwindow() && type)) +// target = 0; + } + } + + if (target != qt_xdnd_current_target) { + if (qt_xdnd_current_target) + qt_xdnd_send_leave(); + + qt_xdnd_current_target = target; + qt_xdnd_current_proxy_target = proxy_target; + if (target) { + QVector<Atom> types; + int flags = target_version << 24; + QStringList fmts = QInternalMimeData::formatsHelper(dragPrivate()->data); + for (int i = 0; i < fmts.size(); ++i) { + QList<Atom> atoms = X11->xdndMimeAtomsForFormat(fmts.at(i)); + for (int j = 0; j < atoms.size(); ++j) { + if (!types.contains(atoms.at(j))) + types.append(atoms.at(j)); + } + } + if (types.size() > 3) { + XChangeProperty(X11->display, + dragPrivate()->source->effectiveWinId(), ATOM(XdndTypelist), + XA_ATOM, 32, PropModeReplace, + (unsigned char *)types.data(), + types.size()); + flags |= 0x0001; + } + XClientMessageEvent enter; + enter.type = ClientMessage; + enter.window = target; + enter.format = 32; + enter.message_type = ATOM(XdndEnter); + enter.data.l[0] = dragPrivate()->source->effectiveWinId(); + enter.data.l[1] = flags; + enter.data.l[2] = types.size()>0 ? types.at(0) : 0; + enter.data.l[3] = types.size()>1 ? types.at(1) : 0; + enter.data.l[4] = types.size()>2 ? types.at(2) : 0; + // provisionally set the rectangle to 5x5 pixels... + qt_xdnd_source_sameanswer = QRect(globalPos.x() - 2, + globalPos.y() -2 , 5, 5); + + DEBUG("sending Xdnd enter"); + if (w) + X11->xdndHandleEnter(w, (const XEvent *)&enter, false); + else if (target) + XSendEvent(X11->display, proxy_target, False, NoEventMask, (XEvent*)&enter); + waiting_for_status = false; + } + } + if (waiting_for_status) + return; + + if (target) { + waiting_for_status = true; + + XClientMessageEvent move; + move.type = ClientMessage; + move.window = target; + move.format = 32; + move.message_type = ATOM(XdndPosition); + move.window = target; + move.data.l[0] = dragPrivate()->source->effectiveWinId(); + move.data.l[1] = 0; // flags + move.data.l[2] = (globalPos.x() << 16) + globalPos.y(); + move.data.l[3] = X11->time; + move.data.l[4] = qtaction_to_xdndaction(defaultAction(dragPrivate()->possible_actions, QApplication::keyboardModifiers())); + DEBUG("sending Xdnd position"); + + qt_xdnd_source_current_time = X11->time; + + if (w) + handle_xdnd_position(w, (const XEvent *)&move, false); + else + XSendEvent(X11->display, proxy_target, False, NoEventMask, + (XEvent*)&move); + } else { + if (willDrop) { + willDrop = false; + updateCursor(); + } + } + DEBUG() << "QDragManager::move leave"; +#endif +} + + +void QDragManager::drop() +{ + Q_ASSERT(heartbeat != -1); + killTimer(heartbeat); + heartbeat = -1; + qt_xdnd_dragging = false; + + if (!qt_xdnd_current_target) + return; + + qDeleteInEventHandler(xdnd_data.deco); + xdnd_data.deco = 0; + + XClientMessageEvent drop; + drop.type = ClientMessage; + drop.window = qt_xdnd_current_target; + drop.format = 32; + drop.message_type = ATOM(XdndDrop); + drop.data.l[0] = dragPrivate()->source->effectiveWinId(); + drop.data.l[1] = 0; // flags + drop.data.l[2] = X11->time; + + drop.data.l[3] = 0; + drop.data.l[4] = 0; + + QWidget * w = QWidget::find(qt_xdnd_current_proxy_target); + + if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops()) + w = 0; + + QXdndDropTransaction t = { + X11->time, + qt_xdnd_current_target, + qt_xdnd_current_proxy_target, + w, + current_embedding_widget, + object + }; + X11->dndDropTransactions.append(t); + restartXdndDropExpiryTimer(); + + if (w) + X11->xdndHandleDrop(w, (const XEvent *)&drop, false); + else + XSendEvent(X11->display, qt_xdnd_current_proxy_target, False, + NoEventMask, (XEvent*)&drop); + + qt_xdnd_current_target = 0; + qt_xdnd_current_proxy_target = 0; + qt_xdnd_source_current_time = 0; + current_embedding_widget = 0; + object = 0; + +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + restoreCursor = false; + } +#endif +} + + + +bool QX11Data::xdndHandleBadwindow() +{ + if (qt_xdnd_current_target) { + QDragManager *manager = QDragManager::self(); + if (manager->object) { + qt_xdnd_current_target = 0; + qt_xdnd_current_proxy_target = 0; + manager->object->deleteLater(); + manager->object = 0; + delete xdnd_data.deco; + xdnd_data.deco = 0; + return true; + } + } + if (qt_xdnd_dragsource_xid) { + qt_xdnd_dragsource_xid = 0; + if (qt_xdnd_current_widget) { + QApplication::postEvent(qt_xdnd_current_widget, new QDragLeaveEvent); + qt_xdnd_current_widget = 0; + } + return true; + } + return false; +} + +void QX11Data::xdndHandleSelectionRequest(const XSelectionRequestEvent * req) +{ + if (!req) + return; + XEvent evt; + evt.xselection.type = SelectionNotify; + evt.xselection.display = req->display; + evt.xselection.requestor = req->requestor; + evt.xselection.selection = req->selection; + evt.xselection.target = XNone; + evt.xselection.property = XNone; + evt.xselection.time = req->time; + + QDragManager *manager = QDragManager::self(); + QDrag *currentObject = manager->object; + + // which transaction do we use? (note: -2 means use current manager->object) + int at = -1; + + // figure out which data the requestor is really interested in + if (manager->object && req->time == qt_xdnd_source_current_time) { + // requestor wants the current drag data + at = -2; + } else { + // if someone has requested data in response to XdndDrop, find the corresponding transaction. the + // spec says to call XConvertSelection() using the timestamp from the XdndDrop + at = findXdndDropTransactionByTime(req->time); + if (at == -1) { + // no dice, perhaps the client was nice enough to use the same window id in XConvertSelection() + // that we sent the XdndDrop event to. + at = findXdndDropTransactionByWindow(req->requestor); + } + if (at == -1 && req->time == CurrentTime) { + // previous Qt versions always requested the data on a child of the target window + // using CurrentTime... but it could be asking for either drop data or the current drag's data + Window target = findXdndAwareParent(req->requestor); + if (target) { + if (qt_xdnd_current_target && qt_xdnd_current_target == target) + at = -2; + else + at = findXdndDropTransactionByWindow(target); + } + } + } + if (at >= 0) { + restartXdndDropExpiryTimer(); + + // use the drag object from an XdndDrop tansaction + manager->object = X11->dndDropTransactions.at(at).object; + } else if (at != -2) { + // no transaction found, we'll have to reject the request + manager->object = 0; + } + if (manager->object) { + Atom atomFormat = req->target; + int dataFormat = 0; + QByteArray data; + if (X11->xdndMimeDataForAtom(req->target, manager->dragPrivate()->data, + &data, &atomFormat, &dataFormat)) { + int dataSize = data.size() / (dataFormat / 8); + XChangeProperty (X11->display, req->requestor, req->property, + atomFormat, dataFormat, PropModeReplace, + (unsigned char *)data.data(), dataSize); + evt.xselection.property = req->property; + evt.xselection.target = atomFormat; + } + } + + // reset manager->object in case we modified it above + manager->object = currentObject; + + // ### this can die if req->requestor crashes at the wrong + // ### moment + XSendEvent(X11->display, req->requestor, False, 0, &evt); +} + +static QVariant xdndObtainData(const char *format, QVariant::Type requestedType) +{ + QByteArray result; + + QWidget* w; + QDragManager *manager = QDragManager::self(); + if (qt_xdnd_dragsource_xid && manager->object && + (w=QWidget::find(qt_xdnd_dragsource_xid)) + && (!(w->windowType() == Qt::Desktop) || w->acceptDrops())) + { + QDragPrivate * o = QDragManager::self()->dragPrivate(); + if (o->data->hasFormat(QLatin1String(format))) + result = o->data->data(QLatin1String(format)); + return result; + } + + QList<Atom> atoms; + int i = 0; + while ((qt_xdnd_types[i])) { + atoms.append(qt_xdnd_types[i]); + ++i; + } + QByteArray encoding; + Atom a = X11->xdndMimeAtomForFormat(QLatin1String(format), requestedType, atoms, &encoding); + if (!a) + return result; + + if (XGetSelectionOwner(X11->display, ATOM(XdndSelection)) == XNone) + return result; // should never happen? + + QWidget* tw = qt_xdnd_current_widget; + if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop)) + tw = new QWidget; + + XConvertSelection(X11->display, ATOM(XdndSelection), a, ATOM(XdndSelection), tw->effectiveWinId(), + qt_xdnd_target_current_time); + XFlush(X11->display); + + XEvent xevent; + bool got=X11->clipboardWaitForEvent(tw->effectiveWinId(), SelectionNotify, &xevent, 5000); + if (got) { + Atom type; + + if (X11->clipboardReadProperty(tw->effectiveWinId(), ATOM(XdndSelection), true, &result, 0, &type, 0)) { + if (type == ATOM(INCR)) { + int nbytes = result.size() >= 4 ? *((int*)result.data()) : 0; + result = X11->clipboardReadIncrementalProperty(tw->effectiveWinId(), ATOM(XdndSelection), nbytes, false); + } else if (type != a && type != XNone) { + DEBUG("Qt clipboard: unknown atom %ld", type); + } + } + } + if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop)) + delete tw; + + return X11->xdndMimeConvertToFormat(a, result, QLatin1String(format), requestedType, encoding); +} + + +/* + Enable drag and drop for widget w by installing the proper + properties on w's toplevel widget. +*/ +bool QX11Data::dndEnable(QWidget* w, bool on) +{ + w = w->window(); + + if (bool(((QExtraWidget*)w)->topData()->dnd) == on) + return true; // been there, done that + ((QExtraWidget*)w)->topData()->dnd = on ? 1 : 0; + + motifdndEnable(w, on); + return xdndEnable(w, on); +} + +Qt::DropAction QDragManager::drag(QDrag * o) +{ + if (object == o || !o || !o->d_func()->source) + return Qt::IgnoreAction; + + if (object) { + cancel(); + qApp->removeEventFilter(this); + beingCancelled = false; + } + + if (object) { + // the last drag and drop operation hasn't finished, so we are going to wait + // for one second to see if it does... if the finish message comes after this, + // then we could still have problems, but this is highly unlikely + QApplication::flush(); + + QElapsedTimer timer; + timer.start(); + do { + XEvent event; + if (XCheckTypedEvent(X11->display, ClientMessage, &event)) + qApp->x11ProcessEvent(&event); + + // sleep 50 ms, so we don't use up CPU cycles all the time. + struct timeval usleep_tv; + usleep_tv.tv_sec = 0; + usleep_tv.tv_usec = 50000; + select(0, 0, 0, 0, &usleep_tv); + } while (object && timer.hasExpired(1000)); + } + + object = o; + object->d_func()->target = 0; + xdnd_data.deco = new QShapedPixmapWidget(object->source()->window()); + + willDrop = false; + + updatePixmap(); + + qApp->installEventFilter(this); + XSetSelectionOwner(X11->display, ATOM(XdndSelection), dragPrivate()->source->window()->internalWinId(), X11->time); + global_accepted_action = Qt::CopyAction; + qt_xdnd_source_sameanswer = QRect(); +#ifndef QT_NO_CURSOR + // set the override cursor (must be done here, since it is updated + // in the call to move() below) + qApp->setOverrideCursor(Qt::ArrowCursor); + restoreCursor = true; +#endif + move(QCursor::pos()); + heartbeat = startTimer(200); + + qt_xdnd_dragging = true; + + if (!QWidget::mouseGrabber()) + xdnd_data.deco->grabMouse(); + + eventLoop = new QEventLoop; + (void) eventLoop->exec(); + delete eventLoop; + eventLoop = 0; + +#ifndef QT_NO_CURSOR + if (restoreCursor) { + qApp->restoreOverrideCursor(); + restoreCursor = false; + } +#endif + + // delete cursors as they may be different next drag. + delete noDropCursor; + noDropCursor = 0; + delete copyCursor; + copyCursor = 0; + delete moveCursor; + moveCursor = 0; + delete linkCursor; + linkCursor = 0; + + delete xdnd_data.deco; + xdnd_data.deco = 0; + if (heartbeat != -1) + killTimer(heartbeat); + heartbeat = -1; + qt_xdnd_current_screen = -1; + qt_xdnd_dragging = false; + + return global_accepted_action; + // object persists until we get an xdnd_finish message +} + +void QDragManager::updatePixmap() +{ + if (xdnd_data.deco) { + QPixmap pm; + QPoint pm_hot(default_pm_hotx,default_pm_hoty); + if (object) { + pm = dragPrivate()->pixmap; + if (!pm.isNull()) + pm_hot = dragPrivate()->hotspot; + } + if (pm.isNull()) { + if (!defaultPm) + defaultPm = new QPixmap(default_pm); + pm = *defaultPm; + } + xdnd_data.deco->pm_hot = pm_hot; + xdnd_data.deco->setPixmap(pm); + xdnd_data.deco->move(QCursor::pos()-pm_hot); + xdnd_data.deco->show(); + } +} + +QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type requestedType) const +{ + QByteArray mime = mimetype.toLatin1(); + QVariant data = X11->motifdnd_active + ? X11->motifdndObtainData(mime) + : xdndObtainData(mime, requestedType); + return data; +} + +bool QDropData::hasFormat_sys(const QString &format) const +{ + return formats().contains(format); +} + +QStringList QDropData::formats_sys() const +{ + QStringList formats; + if (X11->motifdnd_active) { + int i = 0; + QByteArray fmt; + while (!(fmt = X11->motifdndFormat(i)).isEmpty()) { + formats.append(QLatin1String(fmt)); + ++i; + } + } else { + int i = 0; + while ((qt_xdnd_types[i])) { + QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(qt_xdnd_types[i]); + for (int j = 0; j < formatsForAtom.size(); ++j) { + if (!formats.contains(formatsForAtom.at(j))) + formats.append(formatsForAtom.at(j)); + } + ++i; + } + } + return formats; +} + +QT_END_NAMESPACE + +#endif // QT_NO_DRAGANDDROP diff --git a/src/gui/guikernel/qdrag.cpp b/src/gui/guikernel/qdrag.cpp new file mode 100644 index 0000000000..d8d14cb45c --- /dev/null +++ b/src/gui/guikernel/qdrag.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qwidget.h> +#include <qdrag.h> +#include <qpixmap.h> +#include <qpoint.h> +#include "qdnd_p.h" + +#ifndef QT_NO_DRAGANDDROP + +QT_BEGIN_NAMESPACE + +/*! + \class QDrag + \brief The QDrag class provides support for MIME-based drag and drop data + transfer. + + Drag and drop is an intuitive way for users to copy or move data around in an + application, and is used in many desktop environments as a mechanism for copying + data between applications. Drag and drop support in Qt is centered around the + QDrag class that handles most of the details of a drag and drop operation. + + The data to be transferred by the drag and drop operation is contained in a + QMimeData object. This is specified with the setMimeData() function in the + following way: + + \snippet doc/src/snippets/dragging/mainwindow.cpp 1 + + Note that setMimeData() assigns ownership of the QMimeData object to the + QDrag object. The QDrag must be constructed on the heap with a parent QWidget + to ensure that Qt can clean up after the drag and drop operation has been + completed. + + A pixmap can be used to represent the data while the drag is in + progress, and will move with the cursor to the drop target. This + pixmap typically shows an icon that represents the MIME type of + the data being transferred, but any pixmap can be set with + setPixmap(). The cursor's hot spot can be given a position + relative to the top-left corner of the pixmap with the + setHotSpot() function. The following code positions the pixmap so + that the cursor's hot spot points to the center of its bottom + edge: + + \snippet doc/src/snippets/separations/finalwidget.cpp 2 + + \note On X11, the pixmap may not be able to keep up with the mouse + movements if the hot spot causes the pixmap to be displayed + directly under the cursor. + + The source and target widgets can be found with source() and target(). + These functions are often used to determine whether drag and drop operations + started and finished at the same widget, so that special behavior can be + implemented. + + QDrag only deals with the drag and drop operation itself. It is up to the + developer to decide when a drag operation begins, and how a QDrag object should + be constructed and used. For a given widget, it is often necessary to + reimplement \l{QWidget::mousePressEvent()}{mousePressEvent()} to determine + whether the user has pressed a mouse button, and reimplement + \l{QWidget::mouseMoveEvent()}{mouseMoveEvent()} to check whether a QDrag is + required. + + \sa {Drag and Drop}, QClipboard, QMimeData, QWindowsMime, QMacPasteboardMime, + {Draggable Icons Example}, {Draggable Text Example}, {Drop Site Example}, + {Fridge Magnets Example} +*/ + +/*! + Constructs a new drag object for the widget specified by \a dragSource. +*/ +QDrag::QDrag(QWidget *dragSource) + : QObject(*new QDragPrivate, dragSource) +{ + Q_D(QDrag); + d->source = dragSource; + d->target = 0; + d->data = 0; + d->hotspot = QPoint(-10, -10); + d->possible_actions = Qt::CopyAction; + d->executed_action = Qt::IgnoreAction; + d->defaultDropAction = Qt::IgnoreAction; +} + +/*! + Destroys the drag object. +*/ +QDrag::~QDrag() +{ + Q_D(QDrag); + delete d->data; + QDragManager *manager = QDragManager::self(); + if (manager && manager->object == this) + manager->cancel(false); +} + +/*! + Sets the data to be sent to the given MIME \a data. Ownership of the data is + transferred to the QDrag object. +*/ +void QDrag::setMimeData(QMimeData *data) +{ + Q_D(QDrag); + if (d->data == data) + return; + if (d->data != 0) + delete d->data; + d->data = data; +} + +/*! + Returns the MIME data that is encapsulated by the drag object. +*/ +QMimeData *QDrag::mimeData() const +{ + Q_D(const QDrag); + return d->data; +} + +/*! + Sets \a pixmap as the pixmap used to represent the data in a drag + and drop operation. You can only set a pixmap before the drag is + started. +*/ +void QDrag::setPixmap(const QPixmap &pixmap) +{ + Q_D(QDrag); + d->pixmap = pixmap; +} + +/*! + Returns the pixmap used to represent the data in a drag and drop operation. +*/ +QPixmap QDrag::pixmap() const +{ + Q_D(const QDrag); + return d->pixmap; +} + +/*! + Sets the position of the hot spot relative to the top-left corner of the + pixmap used to the point specified by \a hotspot. + + \bold{Note:} on X11, the pixmap may not be able to keep up with the mouse + movements if the hot spot causes the pixmap to be displayed + directly under the cursor. +*/ +void QDrag::setHotSpot(const QPoint& hotspot) +{ + Q_D(QDrag); + d->hotspot = hotspot; +} + +/*! + Returns the position of the hot spot relative to the top-left corner of the + cursor. +*/ +QPoint QDrag::hotSpot() const +{ + Q_D(const QDrag); + return d->hotspot; +} + +/*! + Returns the source of the drag object. This is the widget where the drag + and drop operation originated. +*/ +QWidget *QDrag::source() const +{ + Q_D(const QDrag); + return d->source; +} + +/*! + Returns the target of the drag and drop operation. This is the widget where + the drag object was dropped. +*/ +QWidget *QDrag::target() const +{ + Q_D(const QDrag); + return d->target; +} + +/*! + \since 4.3 + + Starts the drag and drop operation and returns a value indicating the requested + drop action when it is completed. The drop actions that the user can choose + from are specified in \a supportedActions. The default proposed action will be selected + among the allowed actions in the following order: Move, Copy and Link. + + \bold{Note:} On Linux and Mac OS X, the drag and drop operation + can take some time, but this function does not block the event + loop. Other events are still delivered to the application while + the operation is performed. On Windows, the Qt event loop is + blocked while during the operation. +*/ + +Qt::DropAction QDrag::exec(Qt::DropActions supportedActions) +{ + return exec(supportedActions, Qt::IgnoreAction); +} + +/*! + \since 4.3 + + Starts the drag and drop operation and returns a value indicating the requested + drop action when it is completed. The drop actions that the user can choose + from are specified in \a supportedActions. + + The \a defaultDropAction determines which action will be proposed when the user performs a + drag without using modifier keys. + + \bold{Note:} On Linux and Mac OS X, the drag and drop operation + can take some time, but this function does not block the event + loop. Other events are still delivered to the application while + the operation is performed. On Windows, the Qt event loop is + blocked during the operation. However, QDrag::exec() on + Windows causes processEvents() to be called frequently to keep the GUI responsive. + If any loops or operations are called while a drag operation is active, it will block the drag operation. +*/ + +Qt::DropAction QDrag::exec(Qt::DropActions supportedActions, Qt::DropAction defaultDropAction) +{ + Q_D(QDrag); + if (!d->data) { + qWarning("QDrag: No mimedata set before starting the drag"); + return d->executed_action; + } + QDragManager *manager = QDragManager::self(); + d->defaultDropAction = Qt::IgnoreAction; + d->possible_actions = supportedActions; + + if (manager) { + if (defaultDropAction == Qt::IgnoreAction) { + if (supportedActions & Qt::MoveAction) { + d->defaultDropAction = Qt::MoveAction; + } else if (supportedActions & Qt::CopyAction) { + d->defaultDropAction = Qt::CopyAction; + } else if (supportedActions & Qt::LinkAction) { + d->defaultDropAction = Qt::LinkAction; + } + } else { + d->defaultDropAction = defaultDropAction; + } + d->executed_action = manager->drag(this); + } + + return d->executed_action; +} + +/*! + \obsolete + + \bold{Note:} It is recommended to use exec() instead of this function. + + Starts the drag and drop operation and returns a value indicating the requested + drop action when it is completed. The drop actions that the user can choose + from are specified in \a request. Qt::CopyAction is always allowed. + + \bold{Note:} Although the drag and drop operation can take some time, this function + does not block the event loop. Other events are still delivered to the application + while the operation is performed. + + \sa exec() +*/ +Qt::DropAction QDrag::start(Qt::DropActions request) +{ + Q_D(QDrag); + if (!d->data) { + qWarning("QDrag: No mimedata set before starting the drag"); + return d->executed_action; + } + QDragManager *manager = QDragManager::self(); + d->defaultDropAction = Qt::IgnoreAction; + d->possible_actions = request | Qt::CopyAction; + if (manager) + d->executed_action = manager->drag(this); + return d->executed_action; +} + +/*! + Sets the drag \a cursor for the \a action. This allows you + to override the default native cursors. To revert to using the + native cursor for \a action pass in a null QPixmap as \a cursor. + + The \a action can only be CopyAction, MoveAction or LinkAction. + All other values of DropAction are ignored. +*/ +void QDrag::setDragCursor(const QPixmap &cursor, Qt::DropAction action) +{ + Q_D(QDrag); + if (action != Qt::CopyAction && action != Qt::MoveAction && action != Qt::LinkAction) + return; + if (cursor.isNull()) + d->customCursors.remove(action); + else + d->customCursors[action] = cursor; +} + +/*! + \fn void QDrag::actionChanged(Qt::DropAction action) + + This signal is emitted when the \a action associated with the + drag changes. + + \sa targetChanged() +*/ + +/*! + \fn void QDrag::targetChanged(QWidget *newTarget) + + This signal is emitted when the target of the drag and drop + operation changes, with \a newTarget the new target. + + \sa target(), actionChanged() +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_DRAGANDDROP diff --git a/src/gui/guikernel/qdrag.h b/src/gui/guikernel/qdrag.h new file mode 100644 index 0000000000..da847898b2 --- /dev/null +++ b/src/gui/guikernel/qdrag.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDRAG_H +#define QDRAG_H + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_DRAGANDDROP +class QMimeData; +class QDragPrivate; +class QWidget; +class QPixmap; +class QPoint; +class QDragManager; + +class Q_GUI_EXPORT QDrag : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDrag) +public: + explicit QDrag(QWidget *dragSource); + ~QDrag(); + + void setMimeData(QMimeData *data); + QMimeData *mimeData() const; + + void setPixmap(const QPixmap &); + QPixmap pixmap() const; + + void setHotSpot(const QPoint &hotspot); + QPoint hotSpot() const; + + QWidget *source() const; + QWidget *target() const; + + Qt::DropAction start(Qt::DropActions supportedActions = Qt::CopyAction); + Qt::DropAction exec(Qt::DropActions supportedActions = Qt::MoveAction); + Qt::DropAction exec(Qt::DropActions supportedActions, Qt::DropAction defaultAction); + + void setDragCursor(const QPixmap &cursor, Qt::DropAction action); + +Q_SIGNALS: + void actionChanged(Qt::DropAction action); + void targetChanged(QWidget *newTarget); + +private: +#ifdef Q_WS_MAC + friend class QWidgetPrivate; +#endif + friend class QDragManager; + Q_DISABLE_COPY(QDrag) +}; + +#endif // QT_NO_DRAGANDDROP + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDRAG_H diff --git a/src/gui/guikernel/qevent.cpp b/src/gui/guikernel/qevent.cpp new file mode 100644 index 0000000000..b78a2632aa --- /dev/null +++ b/src/gui/guikernel/qevent.cpp @@ -0,0 +1,4619 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qevent.h" +#include "qcursor.h" +#include "qapplication.h" +#include "private/qapplication_p.h" +#include "private/qevent_p.h" +#include "private/qkeysequence_p.h" +#include "qwidget.h" +#include "qgraphicsview.h" +#include "qdebug.h" +#include "qmime.h" +#include "qdnd_p.h" +#include "qevent_p.h" +#include "qgesture.h" +#include "private/qgesture_p.h" + +#ifdef Q_OS_SYMBIAN +#include "private/qcore_symbian_p.h" +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QInputEvent + \ingroup events + + \brief The QInputEvent class is the base class for events that + describe user input. +*/ + +/*! + \internal +*/ +QInputEvent::QInputEvent(Type type, Qt::KeyboardModifiers modifiers) + : QEvent(type), modState(modifiers) +{} + +/*! + \internal +*/ +QInputEvent::~QInputEvent() +{ +} + +/*! + \fn Qt::KeyboardModifiers QInputEvent::modifiers() const + + Returns the keyboard modifier flags that existed immediately + before the event occurred. + + \sa QApplication::keyboardModifiers() +*/ + +/*! \fn void QInputEvent::setModifiers(Qt::KeyboardModifiers modifiers) + + \internal + + Sets the keyboard modifiers flags for this event. +*/ + +/*! + \class QMouseEvent + \ingroup events + + \brief The QMouseEvent class contains parameters that describe a mouse event. + + Mouse events occur when a mouse button is pressed or released + inside a widget, or when the mouse cursor is moved. + + Mouse move events will occur only when a mouse button is pressed + down, unless mouse tracking has been enabled with + QWidget::setMouseTracking(). + + Qt automatically grabs the mouse when a mouse button is pressed + inside a widget; the widget will continue to receive mouse events + until the last mouse button is released. + + A mouse event contains a special accept flag that indicates + whether the receiver wants the event. You should call ignore() if + the mouse event is not handled by your widget. A mouse event is + propagated up the parent widget chain until a widget accepts it + with accept(), or an event filter consumes it. + + \note If a mouse event is propagated to a \l{QWidget}{widget} for + which Qt::WA_NoMousePropagation has been set, that mouse event + will not be propagated further up the parent widget chain. + + The state of the keyboard modifier keys can be found by calling the + \l{QInputEvent::modifiers()}{modifiers()} function, inherited from + QInputEvent. + + The functions pos(), x(), and y() give the cursor position + relative to the widget that receives the mouse event. If you + move the widget as a result of the mouse event, use the global + position returned by globalPos() to avoid a shaking motion. + + The QWidget::setEnabled() function can be used to enable or + disable mouse and keyboard events for a widget. + + Reimplement the QWidget event handlers, QWidget::mousePressEvent(), + QWidget::mouseReleaseEvent(), QWidget::mouseDoubleClickEvent(), + and QWidget::mouseMoveEvent() to receive mouse events in your own + widgets. + + \sa QWidget::setMouseTracking() QWidget::grabMouse() + QCursor::pos() +*/ + +/*! + Constructs a mouse event object. + + The \a type parameter must be one of QEvent::MouseButtonPress, + QEvent::MouseButtonRelease, QEvent::MouseButtonDblClick, + or QEvent::MouseMove. + + The \a position is the mouse cursor's position relative to the + receiving widget. + The \a button that caused the event is given as a value from + the Qt::MouseButton enum. If the event \a type is + \l MouseMove, the appropriate button for this event is Qt::NoButton. + The mouse and keyboard states at the time of the event are specified by + \a buttons and \a modifiers. + + The globalPos() is initialized to QCursor::pos(), which may not + be appropriate. Use the other constructor to specify the global + position explicitly. +*/ + +QMouseEvent::QMouseEvent(Type type, const QPoint &position, Qt::MouseButton button, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) + : QInputEvent(type, modifiers), p(position), b(button), mouseState(buttons) +{ + g = QCursor::pos(); +} + +/*! + \internal +*/ +QMouseEvent::~QMouseEvent() +{ +} + + +/*! + Constructs a mouse event object. + + The \a type parameter must be QEvent::MouseButtonPress, + QEvent::MouseButtonRelease, QEvent::MouseButtonDblClick, + or QEvent::MouseMove. + + The \a pos is the mouse cursor's position relative to the + receiving widget. The cursor's position in global coordinates is + specified by \a globalPos. The \a button that caused the event is + given as a value from the \l Qt::MouseButton enum. If the event \a + type is \l MouseMove, the appropriate button for this event is + Qt::NoButton. \a buttons is the state of all buttons at the + time of the event, \a modifiers the state of all keyboard + modifiers. + +*/ +QMouseEvent::QMouseEvent(Type type, const QPoint &pos, const QPoint &globalPos, + Qt::MouseButton button, Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers) + : QInputEvent(type, modifiers), p(pos), g(globalPos), b(button), mouseState(buttons) +{} + +/*! + \internal +*/ +QMouseEvent *QMouseEvent::createExtendedMouseEvent(Type type, const QPointF &pos, + const QPoint &globalPos, Qt::MouseButton button, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) +{ + return new QMouseEventEx(type, pos, globalPos, button, buttons, modifiers); +} + +/*! + \fn bool QMouseEvent::hasExtendedInfo() const + \internal +*/ + +/*! + \since 4.4 + + Returns the position of the mouse cursor as a QPointF, relative to the + widget that received the event. + + If you move the widget as a result of the mouse event, use the + global position returned by globalPos() to avoid a shaking + motion. + + \sa x() y() pos() globalPos() +*/ +QPointF QMouseEvent::posF() const +{ + return hasExtendedInfo() ? reinterpret_cast<const QMouseEventEx *>(this)->posF : QPointF(pos()); +} + +/*! + \internal +*/ +QMouseEventEx::QMouseEventEx(Type type, const QPointF &pos, const QPoint &globalPos, + Qt::MouseButton button, Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers) + : QMouseEvent(type, pos.toPoint(), globalPos, button, buttons, modifiers), posF(pos) +{ + d = reinterpret_cast<QEventPrivate *>(this); +} + +/*! + \internal +*/ +QMouseEventEx::~QMouseEventEx() +{ +} + +/*! + \fn const QPoint &QMouseEvent::pos() const + + Returns the position of the mouse cursor, relative to the widget + that received the event. + + If you move the widget as a result of the mouse event, use the + global position returned by globalPos() to avoid a shaking + motion. + + \sa x() y() globalPos() +*/ + +/*! + \fn const QPoint &QMouseEvent::globalPos() const + + Returns the global position of the mouse cursor \e{at the time + of the event}. This is important on asynchronous window systems + like X11. Whenever you move your widgets around in response to + mouse events, globalPos() may differ a lot from the current + pointer position QCursor::pos(), and from + QWidget::mapToGlobal(pos()). + + \sa globalX() globalY() +*/ + +/*! + \fn int QMouseEvent::x() const + + Returns the x position of the mouse cursor, relative to the + widget that received the event. + + \sa y() pos() +*/ + +/*! + \fn int QMouseEvent::y() const + + Returns the y position of the mouse cursor, relative to the + widget that received the event. + + \sa x() pos() +*/ + +/*! + \fn int QMouseEvent::globalX() const + + Returns the global x position of the mouse cursor at the time of + the event. + + \sa globalY() globalPos() +*/ + +/*! + \fn int QMouseEvent::globalY() const + + Returns the global y position of the mouse cursor at the time of + the event. + + \sa globalX() globalPos() +*/ + +/*! + \fn Qt::MouseButton QMouseEvent::button() const + + Returns the button that caused the event. + + Note that the returned value is always Qt::NoButton for mouse + move events. + + \sa buttons() Qt::MouseButton +*/ + +/*! + \fn Qt::MouseButton QMouseEvent::buttons() const + + Returns the button state when the event was generated. The button + state is a combination of Qt::LeftButton, Qt::RightButton, + Qt::MidButton using the OR operator. For mouse move events, + this is all buttons that are pressed down. For mouse press and + double click events this includes the button that caused the + event. For mouse release events this excludes the button that + caused the event. + + \sa button() Qt::MouseButton +*/ + + +/*! + \fn Qt::ButtonState QMouseEvent::state() const + + Returns the button state immediately before the event was + generated. The button state is a combination of mouse buttons + (see Qt::ButtonState) and keyboard modifiers (Qt::MouseButtons). + + Use buttons() and/or modifiers() instead. Be aware that buttons() + return the state immediately \e after the event was generated. +*/ + +/*! + \fn Qt::ButtonState QMouseEvent::stateAfter() const + + Returns the button state immediately after the event was + generated. The button state is a combination of mouse buttons + (see Qt::ButtonState) and keyboard modifiers (Qt::MouseButtons). + + Use buttons() and/or modifiers() instead. +*/ + +/*! + \class QHoverEvent + \ingroup events + + \brief The QHoverEvent class contains parameters that describe a mouse event. + + Mouse events occur when a mouse cursor is moved into, out of, or within a + widget, and if the widget has the Qt::WA_Hover attribute. + + The function pos() gives the current cursor position, while oldPos() gives + the old mouse position. + + There are a few similarities between the events QEvent::HoverEnter + and QEvent::HoverLeave, and the events QEvent::Enter and QEvent::Leave. + However, they are slightly different because we do an update() in the event + handler of HoverEnter and HoverLeave. + + QEvent::HoverMove is also slightly different from QEvent::MouseMove. Let us + consider a top-level window A containing a child B which in turn contains a + child C (all with mouse tracking enabled): + + \image hoverevents.png + + Now, if you move the cursor from the top to the bottom in the middle of A, + you will get the following QEvent::MouseMove events: + + \list 1 + \o A::MouseMove + \o B::MouseMove + \o C::MouseMove + \endlist + + You will get the same events for QEvent::HoverMove, except that the event + always propagates to the top-level regardless whether the event is accepted + or not. It will only stop propagating with the Qt::WA_NoMousePropagation + attribute. + + In this case the events will occur in the following way: + + \list 1 + \o A::HoverMove + \o A::HoverMove, B::HoverMove + \o A::HoverMove, B::HoverMove, C::HoverMove + \endlist + +*/ + +/*! + \fn const QPoint &QHoverEvent::pos() const + + Returns the position of the mouse cursor, relative to the widget + that received the event. + + On QEvent::HoverLeave events, this position will always be + QPoint(-1, -1). + + \sa oldPos() +*/ + +/*! + \fn const QPoint &QHoverEvent::oldPos() const + + Returns the previous position of the mouse cursor, relative to the widget + that received the event. If there is no previous position, oldPos() will + return the same position as pos(). + + On QEvent::HoverEnter events, this position will always be + QPoint(-1, -1). + + \sa pos() +*/ + +/*! + Constructs a hover event object. + + The \a type parameter must be QEvent::HoverEnter, + QEvent::HoverLeave, or QEvent::HoverMove. + + The \a pos is the current mouse cursor's position relative to the + receiving widget, while \a oldPos is the previous mouse cursor's + position relative to the receiving widget. +*/ +QHoverEvent::QHoverEvent(Type type, const QPoint &pos, const QPoint &oldPos) + : QEvent(type), p(pos), op(oldPos) +{ +} + +/*! + \internal +*/ +QHoverEvent::~QHoverEvent() +{ +} + + +/*! + \class QWheelEvent + \brief The QWheelEvent class contains parameters that describe a wheel event. + + \ingroup events + + Wheel events are sent to the widget under the mouse cursor, but + if that widget does not handle the event they are sent to the + focus widget. The rotation distance is provided by delta(). + The functions pos() and globalPos() return the mouse cursor's + location at the time of the event. + + A wheel event contains a special accept flag that indicates + whether the receiver wants the event. You should call ignore() if + you do not handle the wheel event; this ensures that it will be + sent to the parent widget. + + The QWidget::setEnabled() function can be used to enable or + disable mouse and keyboard events for a widget. + + The event handler QWidget::wheelEvent() receives wheel events. + + \sa QMouseEvent QWidget::grabMouse() +*/ + +/*! + \fn Qt::MouseButtons QWheelEvent::buttons() const + + Returns the mouse state when the event occurred. +*/ + +/*! + \fn Qt::Orientation QWheelEvent::orientation() const + + Returns the wheel's orientation. +*/ + +/*! + Constructs a wheel event object. + + The position, \a pos, is the location of the mouse cursor within + the widget. The globalPos() is initialized to QCursor::pos() + which is usually, but not always, correct. + Use the other constructor if you need to specify the global + position explicitly. + + The \a buttons describe the state of the mouse buttons at the time + of the event, \a delta contains the rotation distance, + \a modifiers holds the keyboard modifier flags at the time of the + event, and \a orient holds the wheel's orientation. + + \sa pos() delta() state() +*/ +#ifndef QT_NO_WHEELEVENT +QWheelEvent::QWheelEvent(const QPoint &pos, int delta, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, + Qt::Orientation orient) + : QInputEvent(Wheel, modifiers), p(pos), d(delta), mouseState(buttons), o(orient) +{ + g = QCursor::pos(); +} + +/*! + \internal +*/ +QWheelEvent::~QWheelEvent() +{ +} + +/*! + Constructs a wheel event object. + + The \a pos provides the location of the mouse cursor + within the widget. The position in global coordinates is specified + by \a globalPos. \a delta contains the rotation distance, \a modifiers + holds the keyboard modifier flags at the time of the event, and + \a orient holds the wheel's orientation. + + \sa pos() globalPos() delta() state() +*/ +QWheelEvent::QWheelEvent(const QPoint &pos, const QPoint& globalPos, int delta, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, + Qt::Orientation orient) + : QInputEvent(Wheel, modifiers), p(pos), g(globalPos), d(delta), mouseState(buttons), o(orient) +{} + +#endif // QT_NO_WHEELEVENT + +/*! + \fn int QWheelEvent::delta() const + + Returns the distance that the wheel is rotated, in eighths of a + degree. A positive value indicates that the wheel was rotated + forwards away from the user; a negative value indicates that the + wheel was rotated backwards toward the user. + + Most mouse types work in steps of 15 degrees, in which case the + delta value is a multiple of 120; i.e., 120 units * 1/8 = 15 degrees. + + However, some mice have finer-resolution wheels and send delta values + that are less than 120 units (less than 15 degrees). To support this + possibility, you can either cumulatively add the delta values from events + until the value of 120 is reached, then scroll the widget, or you can + partially scroll the widget in response to each wheel event. + + Example: + + \snippet doc/src/snippets/code/src_gui_kernel_qevent.cpp 0 +*/ + +/*! + \fn const QPoint &QWheelEvent::pos() const + + Returns the position of the mouse cursor relative to the widget + that received the event. + + If you move your widgets around in response to mouse events, + use globalPos() instead of this function. + + \sa x() y() globalPos() +*/ + +/*! + \fn int QWheelEvent::x() const + + Returns the x position of the mouse cursor, relative to the + widget that received the event. + + \sa y() pos() +*/ + +/*! + \fn int QWheelEvent::y() const + + Returns the y position of the mouse cursor, relative to the + widget that received the event. + + \sa x() pos() +*/ + + +/*! + \fn const QPoint &QWheelEvent::globalPos() const + + Returns the global position of the mouse pointer \e{at the time + of the event}. This is important on asynchronous window systems + such as X11; whenever you move your widgets around in response to + mouse events, globalPos() can differ a lot from the current + cursor position returned by QCursor::pos(). + + \sa globalX() globalY() +*/ + +/*! + \fn int QWheelEvent::globalX() const + + Returns the global x position of the mouse cursor at the time of + the event. + + \sa globalY() globalPos() +*/ + +/*! + \fn int QWheelEvent::globalY() const + + Returns the global y position of the mouse cursor at the time of + the event. + + \sa globalX() globalPos() +*/ + + +/*! \obsolete + \fn Qt::ButtonState QWheelEvent::state() const + + Returns the keyboard modifier flags at the time of the event. + + The returned value is a selection of the following values, + combined using the OR operator: Qt::ShiftButton, + Qt::ControlButton, and Qt::AltButton. +*/ + + +/*! + \class QKeyEvent + \brief The QKeyEvent class describes a key event. + + \ingroup events + + Key events are sent to the widget with keyboard input focus + when keys are pressed or released. + + A key event contains a special accept flag that indicates whether + the receiver will handle the key event. You should call ignore() + if the key press or release event is not handled by your widget. + A key event is propagated up the parent widget chain until a + widget accepts it with accept() or an event filter consumes it. + Key events for multimedia keys are ignored by default. You should + call accept() if your widget handles those events. + + The QWidget::setEnable() function can be used to enable or disable + mouse and keyboard events for a widget. + + The event handlers QWidget::keyPressEvent(), QWidget::keyReleaseEvent(), + QGraphicsItem::keyPressEvent() and QGraphicsItem::keyReleaseEvent() + receive key events. + + \sa QFocusEvent, QWidget::grabKeyboard() +*/ + +/*! + Constructs a key event object. + + The \a type parameter must be QEvent::KeyPress, QEvent::KeyRelease, + or QEvent::ShortcutOverride. + + Int \a key is the code for the Qt::Key that the event loop should listen + for. If \a key is 0, the event is not a result of a known key; for + example, it may be the result of a compose sequence or keyboard macro. + The \a modifiers holds the keyboard modifiers, and the given \a text + is the Unicode text that the key generated. If \a autorep is true, + isAutoRepeat() will be true. \a count is the number of keys involved + in the event. +*/ +QKeyEvent::QKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, const QString& text, + bool autorep, ushort count) + : QInputEvent(type, modifiers), txt(text), k(key), c(count), autor(autorep) +{ +} + +/*! + \internal +*/ +QKeyEvent::~QKeyEvent() +{ +} + +/*! + \internal +*/ +QKeyEvent *QKeyEvent::createExtendedKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text, bool autorep, ushort count) +{ + return new QKeyEventEx(type, key, modifiers, text, autorep, count, + nativeScanCode, nativeVirtualKey, nativeModifiers); +} + +/*! + \fn bool QKeyEvent::hasExtendedInfo() const + \internal +*/ + +/*! + \since 4.2 + + Returns the native scan code of the key event. If the key event + does not contain this data 0 is returned. + + Note: The native scan code may be 0, even if the key event contains + extended information. + + Note: On Mac OS/X, this function is not useful, because there is no + way to get the scan code from Carbon or Cocoa. The function always + returns 1 (or 0 in the case explained above). +*/ +quint32 QKeyEvent::nativeScanCode() const +{ + return (reinterpret_cast<const QKeyEvent*>(d) != this + ? 0 : reinterpret_cast<const QKeyEventEx*>(this)->nScanCode); +} + +/*! + \since 4.2 + + Returns the native virtual key, or key sym of the key event. + If the key event does not contain this data 0 is returned. + + Note: The native virtual key may be 0, even if the key event contains extended information. +*/ +quint32 QKeyEvent::nativeVirtualKey() const +{ + return (reinterpret_cast<const QKeyEvent*>(d) != this + ? 0 : reinterpret_cast<const QKeyEventEx*>(this)->nVirtualKey); +} + +/*! + \since 4.2 + + Returns the native modifiers of a key event. + If the key event does not contain this data 0 is returned. + + Note: The native modifiers may be 0, even if the key event contains extended information. +*/ +quint32 QKeyEvent::nativeModifiers() const +{ + return (reinterpret_cast<const QKeyEvent*>(d) != this + ? 0 : reinterpret_cast<const QKeyEventEx*>(this)->nModifiers); +} + +/*! + \internal + Creates an extended key event object, which in addition to the normal key event data, also + contains the native scan code, virtual key and modifiers. This extra data is used by the + shortcut system, to determine which shortcuts to trigger. +*/ +QKeyEventEx::QKeyEventEx(Type type, int key, Qt::KeyboardModifiers modifiers, + const QString &text, bool autorep, ushort count, + quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers) + : QKeyEvent(type, key, modifiers, text, autorep, count), + nScanCode(nativeScanCode), nVirtualKey(nativeVirtualKey), nModifiers(nativeModifiers) +{ + d = reinterpret_cast<QEventPrivate*>(this); +} + +/*! + \internal + Creates a copy of an other extended key event. +*/ +QKeyEventEx::QKeyEventEx(const QKeyEventEx &other) + : QKeyEvent(QEvent::Type(other.t), other.k, other.modState, other.txt, other.autor, other.c), + nScanCode(other.nScanCode), nVirtualKey(other.nVirtualKey), nModifiers(other.nModifiers) +{ + d = reinterpret_cast<QEventPrivate*>(this); +} + +/*! + \internal +*/ +QKeyEventEx::~QKeyEventEx() +{ +} + +/*! + \fn int QKeyEvent::key() const + + Returns the code of the key that was pressed or released. + + See \l Qt::Key for the list of keyboard codes. These codes are + independent of the underlying window system. Note that this + function does not distinguish between capital and non-capital + letters, use the text() function (returning the Unicode text the + key generated) for this purpose. + + A value of either 0 or Qt::Key_unknown means that the event is not + the result of a known key; for example, it may be the result of + a compose sequence, a keyboard macro, or due to key event + compression. + + \sa Qt::WA_KeyCompression +*/ + +/*! + \fn QString QKeyEvent::text() const + + Returns the Unicode text that this key generated. The text + returned can be an empty string in cases + where modifier keys, such as Shift, Control, Alt, and Meta, + are being pressed or released. In such cases key() will contain + a valid value. + + \sa Qt::WA_KeyCompression +*/ + +/*! + Returns the keyboard modifier flags that existed immediately + after the event occurred. + + \warning This function cannot always be trusted. The user can + confuse it by pressing both \key{Shift} keys simultaneously and + releasing one of them, for example. + + \sa QApplication::keyboardModifiers() +*/ +//###### We must check with XGetModifierMapping +Qt::KeyboardModifiers QKeyEvent::modifiers() const +{ + if (key() == Qt::Key_Shift) + return Qt::KeyboardModifiers(QInputEvent::modifiers()^Qt::ShiftModifier); + if (key() == Qt::Key_Control) + return Qt::KeyboardModifiers(QInputEvent::modifiers()^Qt::ControlModifier); + if (key() == Qt::Key_Alt) + return Qt::KeyboardModifiers(QInputEvent::modifiers()^Qt::AltModifier); + if (key() == Qt::Key_Meta) + return Qt::KeyboardModifiers(QInputEvent::modifiers()^Qt::MetaModifier); + return QInputEvent::modifiers(); +} + +#ifndef QT_NO_SHORTCUT +/*! + \fn bool QKeyEvent::matches(QKeySequence::StandardKey key) const + \since 4.2 + + Returns true if the key event matches the given standard \a key; + otherwise returns false. +*/ +bool QKeyEvent::matches(QKeySequence::StandardKey matchKey) const +{ + uint searchkey = (modifiers() | key()) & ~(Qt::KeypadModifier); //The keypad modifier should not make a difference + uint platform = QApplicationPrivate::currentPlatform(); + +#ifdef Q_WS_MAC + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + uint oldSearchKey = searchkey; + searchkey &= ~(Qt::ControlModifier | Qt::MetaModifier); + if (oldSearchKey & Qt::ControlModifier) + searchkey |= Qt::MetaModifier; + if (oldSearchKey & Qt::MetaModifier) + searchkey |= Qt::ControlModifier; + } +#endif + + uint N = QKeySequencePrivate::numberOfKeyBindings; + int first = 0; + int last = N - 1; + + while (first <= last) { + int mid = (first + last) / 2; + QKeyBinding midVal = QKeySequencePrivate::keyBindings[mid]; + + if (searchkey > midVal.shortcut){ + first = mid + 1; // Search in top half + } + else if (searchkey < midVal.shortcut){ + last = mid - 1; // Search in bottom half + } + else { + //found correct shortcut value, now we must check for platform match + if ((midVal.platform & platform) && (midVal.standardKey == matchKey)) { + return true; + } else { //We may have several equal values for different platforms, so we must search in both directions + + //search forward + for ( unsigned int i = mid + 1 ; i < N - 1 ; ++i) { + QKeyBinding current = QKeySequencePrivate::keyBindings[i]; + if (current.shortcut != searchkey) + break; + else if (current.platform & platform && current.standardKey == matchKey) + return true; + } + + //search back + for ( int i = mid - 1 ; i >= 0 ; --i) { + QKeyBinding current = QKeySequencePrivate::keyBindings[i]; + if (current.shortcut != searchkey) + break; + else if (current.platform & platform && current.standardKey == matchKey) + return true; + } + return false; //we could not find it among the matching keySequences + } + } + } + return false; //we could not find matching keySequences at all +} +#endif // QT_NO_SHORTCUT + + +/*! + \fn bool QKeyEvent::isAutoRepeat() const + + Returns true if this event comes from an auto-repeating key; + returns false if it comes from an initial key press. + + Note that if the event is a multiple-key compressed event that is + partly due to auto-repeat, this function could return either true + or false indeterminately. +*/ + +/*! + \fn int QKeyEvent::count() const + + Returns the number of keys involved in this event. If text() + is not empty, this is simply the length of the string. + + \sa Qt::WA_KeyCompression +*/ + +/*! + \class QFocusEvent + \brief The QFocusEvent class contains event parameters for widget focus + events. + + \ingroup events + + Focus events are sent to widgets when the keyboard input focus + changes. Focus events occur due to mouse actions, key presses + (such as \gui{Tab} or \gui{Backtab}), the window system, popup + menus, keyboard shortcuts, or other application-specific reasons. + The reason for a particular focus event is returned by reason() + in the appropriate event handler. + + The event handlers QWidget::focusInEvent(), + QWidget::focusOutEvent(), QGraphicsItem::focusInEvent and + QGraphicsItem::focusOutEvent() receive focus events. + + \sa QWidget::setFocus(), QWidget::setFocusPolicy(), {Keyboard Focus} +*/ + +/*! + Constructs a focus event object. + + The \a type parameter must be either QEvent::FocusIn or + QEvent::FocusOut. The \a reason describes the cause of the change + in focus. +*/ +QFocusEvent::QFocusEvent(Type type, Qt::FocusReason reason) + : QEvent(type), m_reason(reason) +{} + +/*! + \internal +*/ +QFocusEvent::~QFocusEvent() +{ +} + +// ### Qt 5: remove +/*! + \internal + */ +Qt::FocusReason QFocusEvent::reason() +{ + return m_reason; +} + +/*! + Returns the reason for this focus event. + */ +Qt::FocusReason QFocusEvent::reason() const +{ + return m_reason; +} + +/*! + \fn bool QFocusEvent::gotFocus() const + + Returns true if type() is QEvent::FocusIn; otherwise returns + false. +*/ + +/*! + \fn bool QFocusEvent::lostFocus() const + + Returns true if type() is QEvent::FocusOut; otherwise returns + false. +*/ + + +/*! + \class QPaintEvent + \brief The QPaintEvent class contains event parameters for paint events. + + \ingroup events + + Paint events are sent to widgets that need to update themselves, + for instance when part of a widget is exposed because a covering + widget was moved. + + The event contains a region() that needs to be updated, and a + rect() that is the bounding rectangle of that region. Both are + provided because many widgets can't make much use of region(), + and rect() can be much faster than region().boundingRect(). + + \section1 Automatic Clipping + + Painting is clipped to region() during the processing of a paint + event. This clipping is performed by Qt's paint system and is + independent of any clipping that may be applied to a QPainter used to + draw on the paint device. + + As a result, the value returned by QPainter::clipRegion() on + a newly-constructed QPainter will not reflect the clip region that is + used by the paint system. + + \sa QPainter, QWidget::update(), QWidget::repaint(), + QWidget::paintEvent() +*/ + +/*! + \fn bool QPaintEvent::erased() const + \compat + + Returns true if the paint event region (or rectangle) has been + erased with the widget's background; otherwise returns false. + + Qt 4 \e always erases regions that require painting. The exception + to this rule is if the widget sets the Qt::WA_OpaquePaintEvent or + Qt::WA_NoSystemBackground attributes. If either one of those + attributes is set \e and the window system does not make use of + subwidget alpha composition (currently X11 and Windows, but this + may change), then the region is not erased. +*/ + +/*! + \fn void QPaintEvent::setErased(bool b) { m_erased = b; } + \internal +*/ + +/*! + Constructs a paint event object with the region that needs to + be updated. The region is specified by \a paintRegion. +*/ +QPaintEvent::QPaintEvent(const QRegion& paintRegion) + : QEvent(Paint), m_rect(paintRegion.boundingRect()), m_region(paintRegion), m_erased(false) +{} + +/*! + Constructs a paint event object with the rectangle that needs + to be updated. The region is specified by \a paintRect. +*/ +QPaintEvent::QPaintEvent(const QRect &paintRect) + : QEvent(Paint), m_rect(paintRect),m_region(paintRect), m_erased(false) +{} + + +/*! + \internal +*/ +QPaintEvent::~QPaintEvent() +{ +} + +/*! + \fn const QRect &QPaintEvent::rect() const + + Returns the rectangle that needs to be updated. + + \sa region() QPainter::setClipRect() +*/ + +/*! + \fn const QRegion &QPaintEvent::region() const + + Returns the region that needs to be updated. + + \sa rect() QPainter::setClipRegion() +*/ + + +QUpdateLaterEvent::QUpdateLaterEvent(const QRegion& paintRegion) + : QEvent(UpdateLater), m_region(paintRegion) +{ +} + +QUpdateLaterEvent::~QUpdateLaterEvent() +{ +} + +/*! + \class QMoveEvent + \brief The QMoveEvent class contains event parameters for move events. + + \ingroup events + + Move events are sent to widgets that have been moved to a new + position relative to their parent. + + The event handler QWidget::moveEvent() receives move events. + + \sa QWidget::move(), QWidget::setGeometry() +*/ + +/*! + Constructs a move event with the new and old widget positions, + \a pos and \a oldPos respectively. +*/ +QMoveEvent::QMoveEvent(const QPoint &pos, const QPoint &oldPos) + : QEvent(Move), p(pos), oldp(oldPos) +{} + +/*! + \internal +*/ +QMoveEvent::~QMoveEvent() +{ +} + +/*! + \fn const QPoint &QMoveEvent::pos() const + + Returns the new position of the widget. This excludes the window + frame for top level widgets. +*/ + +/*! + \fn const QPoint &QMoveEvent::oldPos() const + + Returns the old position of the widget. +*/ + + +/*! + \class QResizeEvent + \brief The QResizeEvent class contains event parameters for resize events. + + \ingroup events + + Resize events are sent to widgets that have been resized. + + The event handler QWidget::resizeEvent() receives resize events. + + \sa QWidget::resize() QWidget::setGeometry() +*/ + +/*! + Constructs a resize event with the new and old widget sizes, \a + size and \a oldSize respectively. +*/ +QResizeEvent::QResizeEvent(const QSize &size, const QSize &oldSize) + : QEvent(Resize), s(size), olds(oldSize) +{} + +/*! + \internal +*/ +QResizeEvent::~QResizeEvent() +{ +} + +/*! + \fn const QSize &QResizeEvent::size() const + + Returns the new size of the widget. This is the same as + QWidget::size(). +*/ + +/*! + \fn const QSize &QResizeEvent::oldSize() const + + Returns the old size of the widget. +*/ + + +/*! + \class QCloseEvent + \brief The QCloseEvent class contains parameters that describe a close event. + + \ingroup events + + Close events are sent to widgets that the user wants to close, + usually by choosing "Close" from the window menu, or by clicking + the \gui{X} title bar button. They are also sent when you call + QWidget::close() to close a widget programmatically. + + Close events contain a flag that indicates whether the receiver + wants the widget to be closed or not. When a widget accepts the + close event, it is hidden (and destroyed if it was created with + the Qt::WA_DeleteOnClose flag). If it refuses to accept the close + event nothing happens. (Under X11 it is possible that the window + manager will forcibly close the window; but at the time of writing + we are not aware of any window manager that does this.) + + The event handler QWidget::closeEvent() receives close events. The + default implementation of this event handler accepts the close + event. If you do not want your widget to be hidden, or want some + special handing, you should reimplement the event handler and + ignore() the event. + + The \l{mainwindows/application#close event handler}{closeEvent() in the + Application example} shows a close event handler that + asks whether to save a document before closing. + + If you want the widget to be deleted when it is closed, create it + with the Qt::WA_DeleteOnClose flag. This is very useful for + independent top-level windows in a multi-window application. + + \l{QObject}s emits the \l{QObject::destroyed()}{destroyed()} + signal when they are deleted. + + If the last top-level window is closed, the + QApplication::lastWindowClosed() signal is emitted. + + The isAccepted() function returns true if the event's receiver has + agreed to close the widget; call accept() to agree to close the + widget and call ignore() if the receiver of this event does not + want the widget to be closed. + + \sa QWidget::close(), QWidget::hide(), QObject::destroyed(), + QCoreApplication::exec(), QCoreApplication::quit(), + QApplication::lastWindowClosed() +*/ + +/*! + Constructs a close event object. + + \sa accept() +*/ +QCloseEvent::QCloseEvent() + : QEvent(Close) +{} + +/*! \internal +*/ +QCloseEvent::~QCloseEvent() +{ +} + +/*! + \class QIconDragEvent + \brief The QIconDragEvent class indicates that a main icon drag has begun. + + \ingroup events + + Icon drag events are sent to widgets when the main icon of a window + has been dragged away. On Mac OS X, this happens when the proxy + icon of a window is dragged off the title bar. + + It is normal to begin using drag and drop in response to this + event. + + \sa {Drag and Drop}, QMimeData, QDrag +*/ + +/*! + Constructs an icon drag event object with the accept flag set to + false. + + \sa accept() +*/ +QIconDragEvent::QIconDragEvent() + : QEvent(IconDrag) +{ ignore(); } + +/*! \internal */ +QIconDragEvent::~QIconDragEvent() +{ +} + +/*! + \class QContextMenuEvent + \brief The QContextMenuEvent class contains parameters that describe a context menu event. + + \ingroup events + + Context menu events are sent to widgets when a user performs + an action associated with opening a context menu. + The actions required to open context menus vary between platforms; + for example, on Windows, pressing the menu button or clicking the + right mouse button will cause this event to be sent. + + When this event occurs it is customary to show a QMenu with a + context menu, if this is relevant to the context. + + Context menu events contain a special accept flag that indicates + whether the receiver accepted the event. If the event handler does + not accept the event then, if possible, whatever triggered the event will be + handled as a regular input event. +*/ + +#ifndef QT_NO_CONTEXTMENU +/*! + Constructs a context menu event object with the accept parameter + flag set to false. + + The \a reason parameter must be QContextMenuEvent::Mouse or + QContextMenuEvent::Keyboard. + + The \a pos parameter specifies the mouse position relative to the + receiving widget. \a globalPos is the mouse position in absolute + coordinates. +*/ +QContextMenuEvent::QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos) + : QInputEvent(ContextMenu), p(pos), gp(globalPos), reas(reason) +{} + +/*! + Constructs a context menu event object with the accept parameter + flag set to false. + + The \a reason parameter must be QContextMenuEvent::Mouse or + QContextMenuEvent::Keyboard. + + The \a pos parameter specifies the mouse position relative to the + receiving widget. \a globalPos is the mouse position in absolute + coordinates. The \a modifiers holds the keyboard modifiers. +*/ +QContextMenuEvent::QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos, + Qt::KeyboardModifiers modifiers) + : QInputEvent(ContextMenu, modifiers), p(pos), gp(globalPos), reas(reason) +{} + + +/*! \internal */ +QContextMenuEvent::~QContextMenuEvent() +{ +} +/*! + Constructs a context menu event object with the accept parameter + flag set to false. + + The \a reason parameter must be QContextMenuEvent::Mouse or + QContextMenuEvent::Keyboard. + + The \a pos parameter specifies the mouse position relative to the + receiving widget. + + The globalPos() is initialized to QCursor::pos(), which may not be + appropriate. Use the other constructor to specify the global + position explicitly. +*/ +QContextMenuEvent::QContextMenuEvent(Reason reason, const QPoint &pos) + : QInputEvent(ContextMenu), p(pos), reas(reason) +{ + gp = QCursor::pos(); +} + +/*! + \fn const QPoint &QContextMenuEvent::pos() const + + Returns the position of the mouse pointer relative to the widget + that received the event. + + \sa x(), y(), globalPos() +*/ + +/*! + \fn int QContextMenuEvent::x() const + + Returns the x position of the mouse pointer, relative to the + widget that received the event. + + \sa y(), pos() +*/ + +/*! + \fn int QContextMenuEvent::y() const + + Returns the y position of the mouse pointer, relative to the + widget that received the event. + + \sa x(), pos() +*/ + +/*! + \fn const QPoint &QContextMenuEvent::globalPos() const + + Returns the global position of the mouse pointer at the time of + the event. + + \sa x(), y(), pos() +*/ + +/*! + \fn int QContextMenuEvent::globalX() const + + Returns the global x position of the mouse pointer at the time of + the event. + + \sa globalY(), globalPos() +*/ + +/*! + \fn int QContextMenuEvent::globalY() const + + Returns the global y position of the mouse pointer at the time of + the event. + + \sa globalX(), globalPos() +*/ +#endif // QT_NO_CONTEXTMENU + +/*! + \fn Qt::ButtonState QContextMenuEvent::state() const + + Returns the button state (a combination of mouse buttons + and keyboard modifiers) immediately before the event was + generated. + + The returned value is a selection of the following values, + combined with the OR operator: + Qt::LeftButton, Qt::RightButton, Qt::MidButton, + Qt::ShiftButton, Qt::ControlButton, and Qt::AltButton. +*/ + +/*! + \enum QContextMenuEvent::Reason + + This enum describes the reason why the event was sent. + + \value Mouse The mouse caused the event to be sent. Normally this + means the right mouse button was clicked, but this is platform + dependent. + + \value Keyboard The keyboard caused this event to be sent. On + Windows, this means the menu button was pressed. + + \value Other The event was sent by some other means (i.e. not by + the mouse or keyboard). +*/ + + +/*! + \fn QContextMenuEvent::Reason QContextMenuEvent::reason() const + + Returns the reason for this context event. +*/ + + +/*! + \class QInputMethodEvent + \brief The QInputMethodEvent class provides parameters for input method events. + + \ingroup events + + Input method events are sent to widgets when an input method is + used to enter text into a widget. Input methods are widely used + to enter text for languages with non-Latin alphabets. + + Note that when creating custom text editing widgets, the + Qt::WA_InputMethodEnabled window attribute must be set explicitly + (using the QWidget::setAttribute() function) in order to receive + input method events. + + The events are of interest to authors of keyboard entry widgets + who want to be able to correctly handle languages with complex + character input. Text input in such languages is usually a three + step process: + + \list 1 + \o \bold{Starting to Compose} + + When the user presses the first key on a keyboard, an input + context is created. This input context will contain a string + of the typed characters. + + \o \bold{Composing} + + With every new key pressed, the input method will try to create a + matching string for the text typed so far called preedit + string. While the input context is active, the user can only move + the cursor inside the string belonging to this input context. + + \o \bold{Completing} + + At some point, the user will activate a user interface component + (perhaps using a particular key) where they can choose from a + number of strings matching the text they have typed so far. The + user can either confirm their choice cancel the input; in either + case the input context will be closed. + \endlist + + QInputMethodEvent models these three stages, and transfers the + information needed to correctly render the intermediate result. A + QInputMethodEvent has two main parameters: preeditString() and + commitString(). The preeditString() parameter gives the currently + active preedit string. The commitString() parameter gives a text + that should get added to (or replace parts of) the text of the + editor widget. It usually is a result of the input operations and + has to be inserted to the widgets text directly before the preedit + string. + + If the commitString() should replace parts of the of the text in + the editor, replacementLength() will contain the number of + characters to be replaced. replacementStart() contains the position + at which characters are to be replaced relative from the start of + the preedit string. + + A number of attributes control the visual appearance of the + preedit string (the visual appearance of text outside the preedit + string is controlled by the widget only). The AttributeType enum + describes the different attributes that can be set. + + A class implementing QWidget::inputMethodEvent() or + QGraphicsItem::inputMethodEvent() should at least understand and + honor the \l TextFormat and \l Cursor attributes. + + Since input methods need to be able to query certain properties + from the widget or graphics item, subclasses must also implement + QWidget::inputMethodQuery() and QGraphicsItem::inputMethodQuery(), + respectively. + + When receiving an input method event, the text widget has to performs the + following steps: + + \list 1 + \o If the widget has selected text, the selected text should get + removed. + + \o Remove the text starting at replacementStart() with length + replacementLength() and replace it by the commitString(). If + replacementLength() is 0, replacementStart() gives the insertion + position for the commitString(). + + When doing replacement the area of the preedit + string is ignored, thus a replacement starting at -1 with a length + of 2 will remove the last character before the preedit string and + the first character afterwards, and insert the commit string + directly before the preedit string. + + If the widget implements undo/redo, this operation gets added to + the undo stack. + + \o If there is no current preedit string, insert the + preeditString() at the current cursor position; otherwise replace + the previous preeditString with the one received from this event. + + If the widget implements undo/redo, the preeditString() should not + influence the undo/redo stack in any way. + + The widget should examine the list of attributes to apply to the + preedit string. It has to understand at least the TextFormat and + Cursor attributes and render them as specified. + \endlist + + \sa QInputContext +*/ + +/*! + \enum QInputMethodEvent::AttributeType + + \value TextFormat + A QTextCharFormat for the part of the preedit string specified by + start and length. value contains a QVariant of type QTextFormat + specifying rendering of this part of the preedit string. There + should be at most one format for every part of the preedit + string. If several are specified for any character in the string the + behaviour is undefined. A conforming implementation has to at least + honor the backgroundColor, textColor and fontUnderline properties + of the format. + + \value Cursor If set, a cursor should be shown inside the preedit + string at position start. The length variable determines whether + the cursor is visible or not. If the length is 0 the cursor is + invisible. If value is a QVariant of type QColor this color will + be used for rendering the cursor, otherwise the color of the + surrounding text will be used. There should be at most one Cursor + attribute per event. If several are specified the behaviour is + undefined. + + \value Language + The variant contains a QLocale object specifying the language of a + certain part of the preedit string. There should be at most one + language set for every part of the preedit string. If several are + specified for any character in the string the behavior is undefined. + + \value Ruby + The ruby text for a part of the preedit string. There should be at + most one ruby text set for every part of the preedit string. If + several are specified for any character in the string the behaviour + is undefined. + + \value Selection + If set, the edit cursor should be moved to the specified position + in the editor text contents. In contrast with \c Cursor, this + attribute does not work on the preedit text, but on the surrounding + text. The cursor will be moved after the commit string has been + committed, and the preedit string will be located at the new edit + position. + The start position specifies the new position and the length + variable can be used to set a selection starting from that point. + The value is unused. + + \sa Attribute +*/ + +/*! + \class QInputMethodEvent::Attribute + \brief The QInputMethodEvent::Attribute class stores an input method attribute. +*/ + +/*! + \fn QInputMethodEvent::Attribute::Attribute(AttributeType type, int start, int length, QVariant value) + + Constructs an input method attribute. \a type specifies the type + of attribute, \a start and \a length the position of the + attribute, and \a value the value of the attribute. +*/ + +/*! + Constructs an event of type QEvent::InputMethod. The + attributes(), preeditString(), commitString(), replacementStart(), + and replacementLength() are initialized to default values. + + \sa setCommitString() +*/ +QInputMethodEvent::QInputMethodEvent() + : QEvent(QEvent::InputMethod), replace_from(0), replace_length(0) +{ +} + +/*! + Construcs an event of type QEvent::InputMethod. The + preedit text is set to \a preeditText, the attributes to + \a attributes. + + The commitString(), replacementStart(), and replacementLength() + values can be set using setCommitString(). + + \sa preeditString(), attributes() +*/ +QInputMethodEvent::QInputMethodEvent(const QString &preeditText, const QList<Attribute> &attributes) + : QEvent(QEvent::InputMethod), preedit(preeditText), attrs(attributes), + replace_from(0), replace_length(0) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QInputMethodEvent::QInputMethodEvent(const QInputMethodEvent &other) + : QEvent(QEvent::InputMethod), preedit(other.preedit), attrs(other.attrs), + commit(other.commit), replace_from(other.replace_from), replace_length(other.replace_length) +{ +} + +/*! + Sets the commit string to \a commitString. + + The commit string is the text that should get added to (or + replace parts of) the text of the editor widget. It usually is a + result of the input operations and has to be inserted to the + widgets text directly before the preedit string. + + If the commit string should replace parts of the of the text in + the editor, \a replaceLength specifies the number of + characters to be replaced. \a replaceFrom specifies the position + at which characters are to be replaced relative from the start of + the preedit string. + + \sa commitString(), replacementStart(), replacementLength() +*/ +void QInputMethodEvent::setCommitString(const QString &commitString, int replaceFrom, int replaceLength) +{ + commit = commitString; + replace_from = replaceFrom; + replace_length = replaceLength; +} + +/*! + \fn const QList<Attribute> &QInputMethodEvent::attributes() const + + Returns the list of attributes passed to the QInputMethodEvent + constructor. The attributes control the visual appearance of the + preedit string (the visual appearance of text outside the preedit + string is controlled by the widget only). + + \sa preeditString(), Attribute +*/ + +/*! + \fn const QString &QInputMethodEvent::preeditString() const + + Returns the preedit text, i.e. the text before the user started + editing it. + + \sa commitString(), attributes() +*/ + +/*! + \fn const QString &QInputMethodEvent::commitString() const + + Returns the text that should get added to (or replace parts of) + the text of the editor widget. It usually is a result of the + input operations and has to be inserted to the widgets text + directly before the preedit string. + + \sa setCommitString(), preeditString(), replacementStart(), replacementLength() +*/ + +/*! + \fn int QInputMethodEvent::replacementStart() const + + Returns the position at which characters are to be replaced relative + from the start of the preedit string. + + \sa replacementLength(), setCommitString() +*/ + +/*! + \fn int QInputMethodEvent::replacementLength() const + + Returns the number of characters to be replaced in the preedit + string. + + \sa replacementStart(), setCommitString() +*/ + +#ifndef QT_NO_TABLETEVENT + +/*! + \class QTabletEvent + \brief The QTabletEvent class contains parameters that describe a Tablet event. + + \ingroup events + + Tablet Events are generated from a Wacom tablet. Most of the time you will + want to deal with events from the tablet as if they were events from a + mouse; for example, you would retrieve the cursor position with x(), y(), + pos(), globalX(), globalY(), and globalPos(). In some situations you may + wish to retrieve the extra information provided by the tablet device + driver; for example, you might want to do subpixeling with higher + resolution coordinates or you may want to adjust color brightness based on + pressure. QTabletEvent allows you to read the pressure(), the xTilt(), and + yTilt(), as well as the type of device being used with device() (see + \l{TabletDevice}). It can also give you the minimum and maximum values for + each device's pressure and high resolution coordinates. + + A tablet event contains a special accept flag that indicates whether the + receiver wants the event. You should call QTabletEvent::accept() if you + handle the tablet event; otherwise it will be sent to the parent widget. + The exception are TabletEnterProximity and TabletLeaveProximity events, + these are only sent to QApplication and don't check whether or not they are + accepted. + + The QWidget::setEnabled() function can be used to enable or + disable mouse and keyboard events for a widget. + + The event handler QWidget::tabletEvent() receives all three types of + tablet events. Qt will first send a tabletEvent then, if it is not + accepted, it will send a mouse event. This allows applications that + don't utilize tablets to use a tablet like a mouse, while also + enabling those who want to use both tablets and mouses differently. + + \section1 Notes for X11 Users + + Qt uses the following hard-coded names to identify tablet + devices from the xorg.conf file on X11 (apart from IRIX): + 'stylus', 'pen', and 'eraser'. If the devices have other names, + they will not be picked up Qt. +*/ + +/*! + \enum QTabletEvent::TabletDevice + + This enum defines what type of device is generating the event. + + \value NoDevice No device, or an unknown device. + \value Puck A Puck (a device that is similar to a flat mouse with + a transparent circle with cross-hairs). + \value Stylus A Stylus. + \value Airbrush An airbrush + \value FourDMouse A 4D Mouse. + \value RotationStylus A special stylus that also knows about rotation + (a 6D stylus). \since 4.1 + \omitvalue XFreeEraser +*/ + +/*! + \enum QTabletEvent::PointerType + + This enum defines what type of point is generating the event. + + \value UnknownPointer An unknown device. + \value Pen Tip end of a stylus-like device (the narrow end of the pen). + \value Cursor Any puck-like device. + \value Eraser Eraser end of a stylus-like device (the broad end of the pen). + + \sa pointerType() +*/ + +/*! + Construct a tablet event of the given \a type. + + The \a pos parameter indicates where the event occurred in the + widget; \a globalPos is the corresponding position in absolute + coordinates. The \a hiResGlobalPos contains a high resolution + measurement of the position. + + \a pressure contains the pressure exerted on the \a device. + + \a pointerType describes the type of pen that is being used. + + \a xTilt and \a yTilt contain the device's degree of tilt from the + x and y axes respectively. + + \a keyState specifies which keyboard modifiers are pressed (e.g., + \key{Ctrl}). + + The \a uniqueID parameter contains the unique ID for the current device. + + The \a z parameter contains the coordinate of the device on the tablet, this + is usually given by a wheel on 4D mouse. If the device does not support a + Z-axis, pass zero here. + + The \a tangentialPressure parameter contins the tangential pressure of an air + brush. If the device does not support tangential pressure, pass 0 here. + + \a rotation contains the device's rotation in degrees. 4D mice support + rotation. If the device does not support rotation, pass 0 here. + + \sa pos() globalPos() device() pressure() xTilt() yTilt() uniqueId(), rotation(), tangentialPressure(), z() +*/ + +QTabletEvent::QTabletEvent(Type type, const QPoint &pos, const QPoint &globalPos, + const QPointF &hiResGlobalPos, int device, int pointerType, + qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, + qreal rotation, int z, Qt::KeyboardModifiers keyState, qint64 uniqueID) + : QInputEvent(type, keyState), + mPos(pos), + mGPos(globalPos), + mHiResGlobalPos(hiResGlobalPos), + mDev(device), + mPointerType(pointerType), + mXT(xTilt), + mYT(yTilt), + mZ(z), + mPress(pressure), + mTangential(tangentialPressure), + mRot(rotation), + mUnique(uniqueID), + mExtra(0) +{ +} + +/*! + \internal +*/ +QTabletEvent::~QTabletEvent() +{ +} + +/*! + \fn TabletDevices QTabletEvent::device() const + + Returns the type of device that generated the event. + + \sa TabletDevice +*/ + +/*! + \fn PointerType QTabletEvent::pointerType() const + + Returns the type of point that generated the event. +*/ + +/*! + \fn qreal QTabletEvent::tangentialPressure() const + + Returns the tangential pressure for the device. This is typically given by a finger + wheel on an airbrush tool. The range is from -1.0 to 1.0. 0.0 indicates a + neutral position. Current airbrushes can only move in the positive + direction from the neutrual position. If the device does not support + tangential pressure, this value is always 0.0. + + \sa pressure() +*/ + +/*! + \fn qreal QTabletEvent::rotation() const + + Returns the rotation of the current device in degress. This is usually + given by a 4D Mouse. If the device doesn't support rotation this value is + always 0.0. + +*/ + +/*! + \fn qreal QTabletEvent::pressure() const + + Returns the pressure for the device. 0.0 indicates that the stylus is not + on the tablet, 1.0 indicates the maximum amount of pressure for the stylus. + + \sa tangentialPressure() +*/ + +/*! + \fn int QTabletEvent::xTilt() const + + Returns the angle between the device (a pen, for example) and the + perpendicular in the direction of the x axis. + Positive values are towards the tablet's physical right. The angle + is in the range -60 to +60 degrees. + + \img qtabletevent-tilt.png + + \sa yTilt() +*/ + +/*! + \fn int QTabletEvent::yTilt() const + + Returns the angle between the device (a pen, for example) and the + perpendicular in the direction of the y axis. + Positive values are towards the bottom of the tablet. The angle is + within the range -60 to +60 degrees. + + \sa xTilt() +*/ + +/*! + \fn const QPoint &QTabletEvent::pos() const + + Returns the position of the device, relative to the widget that + received the event. + + If you move widgets around in response to mouse events, use + globalPos() instead of this function. + + \sa x() y() globalPos() +*/ + +/*! + \fn int QTabletEvent::x() const + + Returns the x position of the device, relative to the widget that + received the event. + + \sa y() pos() +*/ + +/*! + \fn int QTabletEvent::y() const + + Returns the y position of the device, relative to the widget that + received the event. + + \sa x() pos() +*/ + +/*! + \fn int QTabletEvent::z() const + + Returns the z position of the device. Typically this is represented by a + wheel on a 4D Mouse. If the device does not support a Z-axis, this value is + always zero. This is \bold not the same as pressure. + + \sa pressure() +*/ + +/*! + \fn const QPoint &QTabletEvent::globalPos() const + + Returns the global position of the device \e{at the time of the + event}. This is important on asynchronous windows systems like X11; + whenever you move your widgets around in response to mouse events, + globalPos() can differ significantly from the current position + QCursor::pos(). + + \sa globalX() globalY() hiResGlobalPos() +*/ + +/*! + \fn int QTabletEvent::globalX() const + + Returns the global x position of the mouse pointer at the time of + the event. + + \sa globalY() globalPos() hiResGlobalX() +*/ + +/*! + \fn int QTabletEvent::globalY() const + + Returns the global y position of the tablet device at the time of + the event. + + \sa globalX() globalPos() hiResGlobalY() +*/ + +/*! + \fn qint64 QTabletEvent::uniqueId() const + + Returns a unique ID for the current device, making it possible + to differentiate between multiple devices being used at the same + time on the tablet. + + Support of this feature is dependent on the tablet. + + Values for the same device may vary from OS to OS. + + Later versions of the Wacom driver for Linux will now report + the ID information. If you have a tablet that supports unique ID + and are not getting the information on Linux, consider upgrading + your driver. + + As of Qt 4.2, the unique ID is the same regardless of the orientation + of the pen. Earlier versions would report a different value when using + the eraser-end versus the pen-end of the stylus on some OS's. + + \sa pointerType() +*/ + +/*! + \fn const QPointF &QTabletEvent::hiResGlobalPos() const + + The high precision coordinates delivered from the tablet expressed. + Sub pixeling information is in the fractional part of the QPointF. + + \sa globalPos() hiResGlobalX() hiResGlobalY() +*/ + +/*! + \fn qreal &QTabletEvent::hiResGlobalX() const + + The high precision x position of the tablet device. +*/ + +/*! + \fn qreal &QTabletEvent::hiResGlobalY() const + + The high precision y position of the tablet device. +*/ + +#endif // QT_NO_TABLETEVENT + +#ifndef QT_NO_DRAGANDDROP +/*! + Creates a QDragMoveEvent of the required \a type indicating + that the mouse is at position \a pos given within a widget. + + The mouse and keyboard states are specified by \a buttons and + \a modifiers, and the \a actions describe the types of drag + and drop operation that are possible. + The drag data is passed as MIME-encoded information in \a data. + + \warning Do not attempt to create a QDragMoveEvent yourself. + These objects rely on Qt's internal state. +*/ +QDragMoveEvent::QDragMoveEvent(const QPoint& pos, Qt::DropActions actions, const QMimeData *data, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Type type) + : QDropEvent(pos, actions, data, buttons, modifiers, type) + , rect(pos, QSize(1, 1)) +{} + +/*! + Destroys the event. +*/ +QDragMoveEvent::~QDragMoveEvent() +{ +} + +/*! + \fn void QDragMoveEvent::accept(bool y) + + Calls setAccepted(\a y) instead. +*/ + +/*! + \fn void QDragMoveEvent::accept(const QRect &rectangle) + + The same as accept(), but also notifies that future moves will + also be acceptable if they remain within the \a rectangle + given on the widget. This can improve performance, but may + also be ignored by the underlying system. + + If the rectangle is empty, drag move events will be sent + continuously. This is useful if the source is scrolling in a + timer event. +*/ + +/*! + \fn void QDragMoveEvent::accept() + + \overload + + Calls QDropEvent::accept(). +*/ + +/*! + \fn void QDragMoveEvent::ignore() + + \overload + + Calls QDropEvent::ignore(). +*/ + +/*! + \fn void QDragMoveEvent::ignore(const QRect &rectangle) + + The opposite of the accept(const QRect&) function. + Moves within the \a rectangle are not acceptable, and will be + ignored. +*/ + +/*! + \fn QRect QDragMoveEvent::answerRect() const + + Returns the rectangle in the widget where the drop will occur if accepted. + You can use this information to restrict drops to certain places on the + widget. +*/ + + +/*! + \class QDropEvent + \ingroup events + \ingroup draganddrop + + \brief The QDropEvent class provides an event which is sent when a + drag and drop action is completed. + + When a widget \l{QWidget::setAcceptDrops()}{accepts drop events}, it will + receive this event if it has accepted the most recent QDragEnterEvent or + QDragMoveEvent sent to it. + + The drop event contains a proposed action, available from proposedAction(), for + the widget to either accept or ignore. If the action can be handled by the + widget, you should call the acceptProposedAction() function. Since the + proposed action can be a combination of \l Qt::DropAction values, it may be + useful to either select one of these values as a default action or ask + the user to select their preferred action. + + If the proposed drop action is not suitable, perhaps because your custom + widget does not support that action, you can replace it with any of the + \l{possibleActions()}{possible drop actions} by calling setDropAction() + with your preferred action. If you set a value that is not present in the + bitwise OR combination of values returned by possibleActions(), the default + copy action will be used. Once a replacement drop action has been set, call + accept() instead of acceptProposedAction() to complete the drop operation. + + The mimeData() function provides the data dropped on the widget in a QMimeData + object. This contains information about the MIME type of the data in addition to + the data itself. + + \sa QMimeData, QDrag, {Drag and Drop} +*/ + +/*! + \fn const QMimeData *QDropEvent::mimeData() const + + Returns the data that was dropped on the widget and its associated MIME + type information. +*/ + +/*! + Constructs a drop event of a certain \a type corresponding to a + drop at the point specified by \a pos in the destination widget's + coordinate system. + + The \a actions indicate which types of drag and drop operation can + be performed, and the drag data is stored as MIME-encoded data in \a data. + + The states of the mouse buttons and keyboard modifiers at the time of + the drop are specified by \a buttons and \a modifiers. +*/ // ### pos is in which coordinate system? +QDropEvent::QDropEvent(const QPoint& pos, Qt::DropActions actions, const QMimeData *data, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Type type) + : QEvent(type), p(pos), mouseState(buttons), + modState(modifiers), act(actions), + mdata(data) +{ + default_action = QDragManager::self()->defaultAction(act, modifiers); + drop_action = default_action; + ignore(); +} + +/*! \internal */ +QDropEvent::~QDropEvent() +{ +} + +/*! + \compat + Returns a byte array containing the drag's data, in \a format. + + data() normally needs to get the data from the drag source, which + is potentially very slow, so it's advisable to call this function + only if you're sure that you will need the data in that + particular \a format. + + The resulting data will have a size of 0 if the format was not + available. + + \sa format() QByteArray::size() +*/ + +QByteArray QDropEvent::encodedData(const char *format) const +{ + return mdata->data(QLatin1String(format)); +} + +/*! + \compat + Returns a string describing one of the available data types for + this drag. Common examples are "text/plain" and "image/gif". + If \a n is less than zero or greater than the number of available + data types, format() returns 0. + + This function is provided mainly for debugging. Most drop targets + will use provides(). + + \sa data() provides() +*/ + +const char* QDropEvent::format(int n) const +{ + if (fmts.isEmpty()) { + QStringList formats = mdata->formats(); + for (int i = 0; i < formats.size(); ++i) + fmts.append(formats.at(i).toLatin1()); + } + if (n < 0 || n >= fmts.size()) + return 0; + return fmts.at(n).constData(); +} + +/*! + \compat + Returns true if this event provides format \a mimeType; otherwise + returns false. + + \sa data() +*/ + +bool QDropEvent::provides(const char *mimeType) const +{ + return mdata->formats().contains(QLatin1String(mimeType)); +} + +/*! + If the source of the drag operation is a widget in this + application, this function returns that source; otherwise it + returns 0. The source of the operation is the first parameter to + the QDrag object used instantiate the drag. + + This is useful if your widget needs special behavior when dragging + to itself. + + \sa QDrag::QDrag() +*/ +QWidget* QDropEvent::source() const +{ + QDragManager *manager = QDragManager::self(); + return manager ? manager->source() : 0; +} + + +void QDropEvent::setDropAction(Qt::DropAction action) +{ + if (!(action & act) && action != Qt::IgnoreAction) + action = default_action; + drop_action = action; +} + +/*! + \fn const QPoint& QDropEvent::pos() const + + Returns the position where the drop was made. +*/ + +/*! + \fn Qt::MouseButtons QDropEvent::mouseButtons() const + + Returns the mouse buttons that are pressed.. +*/ + +/*! + \fn Qt::KeyboardModifiers QDropEvent::keyboardModifiers() const + + Returns the modifier keys that are pressed. +*/ + +/*! + \fn void QDropEvent::accept() + \internal +*/ + +/*! + \fn void QDropEvent::accept(bool accept) + + Call setAccepted(\a accept) instead. +*/ + +/*! + \fn void QDropEvent::acceptAction(bool accept = true) + + Call this to indicate that the action described by action() is + accepted (i.e. if \a accept is true, which is the default), not merely + the default copy action. If you call acceptAction(true), there is + no need to also call accept(true). +*/ + +/*! + \enum QDropEvent::Action + \compat + + When a drag and drop action is completed, the target is expected + to perform an action on the data provided by the source. This + will be one of the following: + + \value Copy The default action. The source simply uses the data + provided in the operation. + \value Link The source should somehow create a link to the + location specified by the data. + \value Move The source should somehow move the object from the + location specified by the data to a new location. + \value Private The target has special knowledge of the MIME type, + which the source should respond to in a similar way to + a Copy. + \value UserAction The source and target can co-operate using + special actions. This feature is not currently + supported. + + The Link and Move actions only makes sense if the data is a + reference, for example, text/uri-list file lists (see QUriDrag). +*/ + +/*! + \fn void QDropEvent::setDropAction(Qt::DropAction action) + + Sets the \a action to be performed on the data by the target. + Use this to override the \l{proposedAction()}{proposed action} + with one of the \l{possibleActions()}{possible actions}. + + If you set a drop action that is not one of the possible actions, the + drag and drop operation will default to a copy operation. + + Once you have supplied a replacement drop action, call accept() + instead of acceptProposedAction(). + + \sa dropAction() +*/ + +/*! + \fn Qt::DropAction QDropEvent::dropAction() const + + Returns the action to be performed on the data by the target. This may be + different from the action supplied in proposedAction() if you have called + setDropAction() to explicitly choose a drop action. + + \sa setDropAction() +*/ + +/*! + \fn Qt::DropActions QDropEvent::possibleActions() const + + Returns an OR-combination of possible drop actions. + + \sa dropAction() +*/ + +/*! + \fn Qt::DropAction QDropEvent::proposedAction() const + + Returns the proposed drop action. + + \sa dropAction() +*/ + +/*! + \fn void QDropEvent::acceptProposedAction() + + Sets the drop action to be the proposed action. + + \sa setDropAction(), proposedAction(), {QEvent::accept()}{accept()} +*/ + +/*! + \fn void QDropEvent::setPoint(const QPoint &point) + \compat + + Sets the drop to happen at the given \a point. You do not normally + need to use this as it will be set internally before your widget + receives the drop event. +*/ // ### here too - what coordinate system? + + +/*! + \class QDragEnterEvent + \brief The QDragEnterEvent class provides an event which is sent + to a widget when a drag and drop action enters it. + + \ingroup events + \ingroup draganddrop + + A widget must accept this event in order to receive the \l + {QDragMoveEvent}{drag move events} that are sent while the drag + and drop action is in progress. The drag enter event is always + immediately followed by a drag move event. + + QDragEnterEvent inherits most of its functionality from + QDragMoveEvent, which in turn inherits most of its functionality + from QDropEvent. + + \sa QDragLeaveEvent, QDragMoveEvent, QDropEvent +*/ + +/*! + Constructs a QDragEnterEvent that represents a drag entering a + widget at the given \a point with mouse and keyboard states specified by + \a buttons and \a modifiers. + + The drag data is passed as MIME-encoded information in \a data, and the + specified \a actions describe the possible types of drag and drop + operation that can be performed. + + \warning Do not create a QDragEnterEvent yourself since these + objects rely on Qt's internal state. +*/ +QDragEnterEvent::QDragEnterEvent(const QPoint& point, Qt::DropActions actions, const QMimeData *data, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) + : QDragMoveEvent(point, actions, data, buttons, modifiers, DragEnter) +{} + +/*! \internal +*/ +QDragEnterEvent::~QDragEnterEvent() +{ +} + +/*! + Constructs a drag response event containing the \a accepted value, + indicating whether the drag and drop operation was accepted by the + recipient. +*/ +QDragResponseEvent::QDragResponseEvent(bool accepted) + : QEvent(DragResponse), a(accepted) +{} + +/*! \internal +*/ +QDragResponseEvent::~QDragResponseEvent() +{ +} + +/*! + \class QDragMoveEvent + \brief The QDragMoveEvent class provides an event which is sent while a drag and drop action is in progress. + + \ingroup events + \ingroup draganddrop + + A widget will receive drag move events repeatedly while the drag + is within its boundaries, if it accepts + \l{QWidget::setAcceptDrops()}{drop events} and \l + {QWidget::dragEnterEvent()}{enter events}. The widget should + examine the event to see what kind of data it + \l{QDragMoveEvent::provides()}{provides}, and call the accept() + function to accept the drop if appropriate. + + The rectangle supplied by the answerRect() function can be used to restrict + drops to certain parts of the widget. For example, we can check whether the + rectangle intersects with the geometry of a certain child widget and only + call \l{QDropEvent::acceptProposedAction()}{acceptProposedAction()} if that + is the case. + + Note that this class inherits most of its functionality from + QDropEvent. + + \sa QDragEnterEvent, QDragLeaveEvent, QDropEvent +*/ + +/*! + \class QDragLeaveEvent + \brief The QDragLeaveEvent class provides an event that is sent to a widget when a drag and drop action leaves it. + + \ingroup events + \ingroup draganddrop + + This event is always preceded by a QDragEnterEvent and a series + of \l{QDragMoveEvent}s. It is not sent if a QDropEvent is sent + instead. + + \sa QDragEnterEvent, QDragMoveEvent, QDropEvent +*/ + +/*! + Constructs a QDragLeaveEvent. + + \warning Do not create a QDragLeaveEvent yourself since these + objects rely on Qt's internal state. +*/ +QDragLeaveEvent::QDragLeaveEvent() + : QEvent(DragLeave) +{} + +/*! \internal +*/ +QDragLeaveEvent::~QDragLeaveEvent() +{ +} +#endif // QT_NO_DRAGANDDROP + +/*! + \class QHelpEvent + \brief The QHelpEvent class provides an event that is used to request helpful information + about a particular point in a widget. + + \ingroup events + \ingroup helpsystem + + This event can be intercepted in applications to provide tooltips + or "What's This?" help for custom widgets. The type() can be + either QEvent::ToolTip or QEvent::WhatsThis. + + \sa QToolTip, QWhatsThis, QStatusTipEvent, QWhatsThisClickedEvent +*/ + +/*! + Constructs a help event with the given \a type corresponding to the + widget-relative position specified by \a pos and the global position + specified by \a globalPos. + + \a type must be either QEvent::ToolTip or QEvent::WhatsThis. + + \sa pos(), globalPos() +*/ +QHelpEvent::QHelpEvent(Type type, const QPoint &pos, const QPoint &globalPos) + : QEvent(type), p(pos), gp(globalPos) +{} + +/*! + \fn int QHelpEvent::x() const + + Same as pos().x(). + + \sa y(), pos(), globalPos() +*/ + +/*! + \fn int QHelpEvent::y() const + + Same as pos().y(). + + \sa x(), pos(), globalPos() +*/ + +/*! + \fn int QHelpEvent::globalX() const + + Same as globalPos().x(). + + \sa x(), globalY(), globalPos() +*/ + +/*! + \fn int QHelpEvent::globalY() const + + Same as globalPos().y(). + + \sa y(), globalX(), globalPos() +*/ + +/*! + \fn const QPoint &QHelpEvent::pos() const + + Returns the mouse cursor position when the event was generated, + relative to the widget to which the event is dispatched. + + \sa globalPos(), x(), y() +*/ + +/*! + \fn const QPoint &QHelpEvent::globalPos() const + + Returns the mouse cursor position when the event was generated + in global coordinates. + + \sa pos(), globalX(), globalY() +*/ + +/*! \internal +*/ +QHelpEvent::~QHelpEvent() +{ +} + +#ifndef QT_NO_STATUSTIP + +/*! + \class QStatusTipEvent + \brief The QStatusTipEvent class provides an event that is used to show messages in a status bar. + + \ingroup events + \ingroup helpsystem + + Status tips can be set on a widget using the + QWidget::setStatusTip() function. They are shown in the status + bar when the mouse cursor enters the widget. For example: + + \table 100% + \row + \o + \snippet doc/src/snippets/qstatustipevent/main.cpp 1 + \dots + \snippet doc/src/snippets/qstatustipevent/main.cpp 3 + \o + \image qstatustipevent-widget.png Widget with status tip. + \endtable + + Status tips can also be set on actions using the + QAction::setStatusTip() function: + + \table 100% + \row + \o + \snippet doc/src/snippets/qstatustipevent/main.cpp 0 + \snippet doc/src/snippets/qstatustipevent/main.cpp 2 + \dots + \snippet doc/src/snippets/qstatustipevent/main.cpp 3 + \o + \image qstatustipevent-action.png Action with status tip. + \endtable + + Finally, status tips are supported for the item view classes + through the Qt::StatusTipRole enum value. + + \sa QStatusBar, QHelpEvent, QWhatsThisClickedEvent +*/ + +/*! + Constructs a status tip event with the text specified by \a tip. + + \sa tip() +*/ +QStatusTipEvent::QStatusTipEvent(const QString &tip) + : QEvent(StatusTip), s(tip) +{} + +/*! \internal +*/ +QStatusTipEvent::~QStatusTipEvent() +{ +} + +/*! + \fn QString QStatusTipEvent::tip() const + + Returns the message to show in the status bar. + + \sa QStatusBar::showMessage() +*/ + +#endif // QT_NO_STATUSTIP + +#ifndef QT_NO_WHATSTHIS + +/*! + \class QWhatsThisClickedEvent + \brief The QWhatsThisClickedEvent class provides an event that + can be used to handle hyperlinks in a "What's This?" text. + + \ingroup events + \ingroup helpsystem + + \sa QWhatsThis, QHelpEvent, QStatusTipEvent +*/ + +/*! + Constructs an event containing a URL specified by \a href when a link + is clicked in a "What's This?" message. + + \sa href() +*/ +QWhatsThisClickedEvent::QWhatsThisClickedEvent(const QString &href) + : QEvent(WhatsThisClicked), s(href) +{} + +/*! \internal +*/ +QWhatsThisClickedEvent::~QWhatsThisClickedEvent() +{ +} + +/*! + \fn QString QWhatsThisClickedEvent::href() const + + Returns the URL that was clicked by the user in the "What's + This?" text. +*/ + +#endif // QT_NO_WHATSTHIS + +#ifndef QT_NO_ACTION + +/*! + \class QActionEvent + \brief The QActionEvent class provides an event that is generated + when a QAction is added, removed, or changed. + + \ingroup events + + Actions can be added to widgets using QWidget::addAction(). This + generates an \l ActionAdded event, which you can handle to provide + custom behavior. For example, QToolBar reimplements + QWidget::actionEvent() to create \l{QToolButton}s for the + actions. + + \sa QAction, QWidget::addAction(), QWidget::removeAction(), QWidget::actions() +*/ + +/*! + Constructs an action event. The \a type can be \l ActionChanged, + \l ActionAdded, or \l ActionRemoved. + + \a action is the action that is changed, added, or removed. If \a + type is ActionAdded, the action is to be inserted before the + action \a before. If \a before is 0, the action is appended. +*/ +QActionEvent::QActionEvent(int type, QAction *action, QAction *before) + : QEvent(static_cast<QEvent::Type>(type)), act(action), bef(before) +{} + +/*! \internal +*/ +QActionEvent::~QActionEvent() +{ +} + +/*! + \fn QAction *QActionEvent::action() const + + Returns the action that is changed, added, or removed. + + \sa before() +*/ + +/*! + \fn QAction *QActionEvent::before() const + + If type() is \l ActionAdded, returns the action that should + appear before action(). If this function returns 0, the action + should be appended to already existing actions on the same + widget. + + \sa action(), QWidget::actions() +*/ + +#endif // QT_NO_ACTION + +/*! + \class QHideEvent + \brief The QHideEvent class provides an event which is sent after a widget is hidden. + + \ingroup events + + This event is sent just before QWidget::hide() returns, and also + when a top-level window has been hidden (iconified) by the user. + + If spontaneous() is true, the event originated outside the + application. In this case, the user hid the window using the + window manager controls, either by iconifying the window or by + switching to another virtual desktop where the window isn't + visible. The window will become hidden but not withdrawn. If the + window was iconified, QWidget::isMinimized() returns true. + + \sa QShowEvent +*/ + +/*! + Constructs a QHideEvent. +*/ +QHideEvent::QHideEvent() + : QEvent(Hide) +{} + +/*! \internal +*/ +QHideEvent::~QHideEvent() +{ +} + +/*! + \class QShowEvent + \brief The QShowEvent class provides an event that is sent when a widget is shown. + + \ingroup events + + There are two kinds of show events: show events caused by the + window system (spontaneous), and internal show events. Spontaneous (QEvent::spontaneous()) + show events are sent just after the window system shows the + window; they are also sent when a top-level window is redisplayed + after being iconified. Internal show events are delivered just + before the widget becomes visible. + + \sa QHideEvent +*/ + +/*! + Constructs a QShowEvent. +*/ +QShowEvent::QShowEvent() + : QEvent(Show) +{} + +/*! \internal +*/ +QShowEvent::~QShowEvent() +{ +} + +/*! + \fn QByteArray QDropEvent::data(const char* f) const + + \obsolete + + The encoded data is in \a f. + Use QDropEvent::encodedData(). +*/ + +/*! + \class QFileOpenEvent + \brief The QFileOpenEvent class provides an event that will be + sent when there is a request to open a file or a URL. + + \ingroup events + + File open events will be sent to the QApplication::instance() + when the operating system requests that a file or URL should be opened. + This is a high-level event that can be caused by different user actions + depending on the user's desktop environment; for example, double + clicking on an file icon in the Finder on Mac OS X. + + This event is only used to notify the application of a request. + It may be safely ignored. + + \note This class is currently supported for Mac OS X and Symbian only. +*/ + +QFileOpenEventPrivate::~QFileOpenEventPrivate() +{ +#ifdef Q_OS_SYMBIAN + file.Close(); +#endif +} + +/*! + \internal + + Constructs a file open event for the given \a file. +*/ +QFileOpenEvent::QFileOpenEvent(const QString &file) + : QEvent(FileOpen), f(file) +{ + d = reinterpret_cast<QEventPrivate *>(new QFileOpenEventPrivate(QUrl::fromLocalFile(file))); +} + +/*! + \internal + + Constructs a file open event for the given \a url. +*/ +QFileOpenEvent::QFileOpenEvent(const QUrl &url) + : QEvent(FileOpen) +{ + d = reinterpret_cast<QEventPrivate *>(new QFileOpenEventPrivate(url)); + f = url.toLocalFile(); +} + +#ifdef Q_OS_SYMBIAN +/*! \internal +*/ +QFileOpenEvent::QFileOpenEvent(const RFile &fileHandle) + : QEvent(FileOpen) +{ + TFileName fullName; + fileHandle.FullName(fullName); + f = qt_TDesC2QString(fullName); + QScopedPointer<QFileOpenEventPrivate> priv(new QFileOpenEventPrivate(QUrl::fromLocalFile(f))); + // Duplicate here allows the file handle to be valid after S60 app construction is complete. + qt_symbian_throwIfError(priv->file.Duplicate(fileHandle)); + d = reinterpret_cast<QEventPrivate *>(priv.take()); +} +#endif + +/*! \internal +*/ +QFileOpenEvent::~QFileOpenEvent() +{ + delete reinterpret_cast<QFileOpenEventPrivate *>(d); +} + +/*! + \fn QString QFileOpenEvent::file() const + + Returns the file that is being opened. +*/ + +/*! + \fn QUrl QFileOpenEvent::url() const + + Returns the url that is being opened. + + \since 4.6 +*/ +QUrl QFileOpenEvent::url() const +{ + return reinterpret_cast<const QFileOpenEventPrivate *>(d)->url; +} + +/*! + \fn bool openFile(QFile &file, QIODevice::OpenMode flags) const + + Opens a QFile on the file referenced by this event. + Returns true if successful; otherwise returns false. + + This is necessary as some files cannot be opened by name, but require specific + information stored in this event. + For example, if this QFileOpenEvent contains a request to open a Symbian data caged file, + the QFile could only be opened from the Symbian RFile used in the construction of this event. + + \since 4.8 +*/ +bool QFileOpenEvent::openFile(QFile &file, QIODevice::OpenMode flags) const +{ + file.setFileName(f); +#ifdef Q_OS_SYMBIAN + const QFileOpenEventPrivate *priv = reinterpret_cast<const QFileOpenEventPrivate *>(d); + if (priv->file.SubSessionHandle()) { + RFile dup; + // Duplicate here means that the opened QFile will continue to be valid beyond the lifetime of this QFileOpenEvent. + // It also allows openFile to be used in threads other than the thread in which the QFileOpenEvent was created. + if (dup.Duplicate(priv->file) == KErrNone) { + QScopedPointer<RFile, QScopedPointerRCloser<RFile> > dupCloser(&dup); + bool open = file.open(dup, flags, QFile::AutoCloseHandle); + dupCloser.take(); + return open; + } + } +#endif + return file.open(flags); +} + +#ifndef QT_NO_TOOLBAR +/*! + \internal + \class QToolBarChangeEvent + \brief The QToolBarChangeEvent class provides an event that is + sent whenever a the toolbar button is clicked on Mac OS X. + + \ingroup events + + The QToolBarChangeEvent is sent when the toolbar button is clicked. On Mac + OS X, this is the long oblong button on the right side of the window + title bar. The default implementation is to toggle the appearance (hidden or + shown) of the associated toolbars for the window. +*/ + +/*! + \internal + + Construct a QToolBarChangeEvent given the current button state in \a state. +*/ +QToolBarChangeEvent::QToolBarChangeEvent(bool t) + : QEvent(ToolBarChange), tog(t) +{} + +/*! \internal +*/ +QToolBarChangeEvent::~QToolBarChangeEvent() +{ +} + +/*! + \fn bool QToolBarChangeEvent::toggle() const + \internal +*/ + +/* + \fn Qt::ButtonState QToolBarChangeEvent::state() const + + Returns the keyboard modifier flags at the time of the event. + + The returned value is a selection of the following values, + combined using the OR operator: + Qt::ShiftButton, Qt::ControlButton, Qt::MetaButton, and Qt::AltButton. +*/ + +#endif // QT_NO_TOOLBAR + +#ifndef QT_NO_SHORTCUT + +/*! + Constructs a shortcut event for the given \a key press, + associated with the QShortcut ID \a id. + + \a ambiguous specifies whether there is more than one QShortcut + for the same key sequence. +*/ +QShortcutEvent::QShortcutEvent(const QKeySequence &key, int id, bool ambiguous) + : QEvent(Shortcut), sequence(key), ambig(ambiguous), sid(id) +{ +} + +/*! + Destroys the event object. +*/ +QShortcutEvent::~QShortcutEvent() +{ +} + +#endif // QT_NO_SHORTCUT + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QEvent *e) { +#ifndef Q_BROKEN_DEBUG_STREAM + // More useful event output could be added here + if (!e) + return dbg << "QEvent(this = 0x0)"; + const char *n = 0; + switch (e->type()) { + case QEvent::Timer: + n = "Timer"; + break; + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + { + const QMouseEvent *me = static_cast<const QMouseEvent*>(e); + switch(me->type()) { + case QEvent::MouseButtonPress: + n = "MouseButtonPress"; + break; + case QEvent::MouseMove: + n = "MouseMove"; + break; + case QEvent::MouseButtonRelease: + n = "MouseButtonRelease"; + break; + case QEvent::MouseButtonDblClick: + default: + n = "MouseButtonDblClick"; + break; + } + dbg.nospace() << "QMouseEvent(" << n + << ", " << me->button() + << ", " << hex << (int)me->buttons() + << ", " << hex << (int)me->modifiers() + << ')'; + } + return dbg.space(); + +#ifndef QT_NO_TOOLTIP + case QEvent::ToolTip: + n = "ToolTip"; + break; +#endif + case QEvent::WindowActivate: + n = "WindowActivate"; + break; + case QEvent::WindowDeactivate: + n = "WindowDeactivate"; + break; + case QEvent::ActivationChange: + n = "ActivationChange"; + break; +#ifndef QT_NO_WHEELEVENT + case QEvent::Wheel: + dbg.nospace() << "QWheelEvent(" << static_cast<const QWheelEvent *>(e)->delta() + << ')'; + return dbg.space(); +#endif + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::ShortcutOverride: + { + const QKeyEvent *ke = static_cast<const QKeyEvent*>(e); + switch(ke->type()) { + case QEvent::ShortcutOverride: + n = "ShortcutOverride"; + break; + case QEvent::KeyRelease: + n = "KeyRelease"; + break; + case QEvent::KeyPress: + default: + n = "KeyPress"; + break; + } + dbg.nospace() << "QKeyEvent(" << n + << ", " << hex << ke->key() + << ", " << hex << (int)ke->modifiers() + << ", \"" << ke->text() + << "\", " << ke->isAutoRepeat() + << ", " << ke->count() + << ')'; + } + return dbg.space(); + case QEvent::FocusIn: + n = "FocusIn"; + break; + case QEvent::FocusOut: + n = "FocusOut"; + break; + case QEvent::Enter: + n = "Enter"; + break; + case QEvent::Leave: + n = "Leave"; + break; + case QEvent::PaletteChange: + n = "PaletteChange"; + break; + case QEvent::PolishRequest: + n = "PolishRequest"; + break; + case QEvent::Polish: + n = "Polish"; + break; + case QEvent::UpdateRequest: + n = "UpdateRequest"; + break; + case QEvent::Paint: + n = "Paint"; + break; + case QEvent::Move: + n = "Move"; + break; + case QEvent::Resize: + n = "Resize"; + break; + case QEvent::Create: + n = "Create"; + break; + case QEvent::Destroy: + n = "Destroy"; + break; + case QEvent::Close: + n = "Close"; + break; + case QEvent::Quit: + n = "Quit"; + break; + case QEvent::FileOpen: + n = "FileOpen"; + break; + case QEvent::Show: + n = "Show"; + break; + case QEvent::ShowToParent: + n = "ShowToParent"; + break; + case QEvent::Hide: + n = "Hide"; + break; + case QEvent::HideToParent: + n = "HideToParent"; + break; + case QEvent::None: + n = "None"; + break; + case QEvent::ParentChange: + n = "ParentChange"; + break; + case QEvent::ParentAboutToChange: + n = "ParentAboutToChange"; + break; + case QEvent::HoverEnter: + n = "HoverEnter"; + break; + case QEvent::HoverMove: + n = "HoverMove"; + break; + case QEvent::HoverLeave: + n = "HoverLeave"; + break; + case QEvent::ZOrderChange: + n = "ZOrderChange"; + break; + case QEvent::StyleChange: + n = "StyleChange"; + break; + case QEvent::DragEnter: + n = "DragEnter"; + break; + case QEvent::DragMove: + n = "DragMove"; + break; + case QEvent::DragLeave: + n = "DragLeave"; + break; + case QEvent::Drop: + n = "Drop"; + break; + case QEvent::GraphicsSceneMouseMove: + n = "GraphicsSceneMouseMove"; + break; + case QEvent::GraphicsSceneMousePress: + n = "GraphicsSceneMousePress"; + break; + case QEvent::GraphicsSceneMouseRelease: + n = "GraphicsSceneMouseRelease"; + break; + case QEvent::GraphicsSceneMouseDoubleClick: + n = "GraphicsSceneMouseDoubleClick"; + break; + case QEvent::GraphicsSceneContextMenu: + n = "GraphicsSceneContextMenu"; + break; + case QEvent::GraphicsSceneHoverEnter: + n = "GraphicsSceneHoverEnter"; + break; + case QEvent::GraphicsSceneHoverMove: + n = "GraphicsSceneHoverMove"; + break; + case QEvent::GraphicsSceneHoverLeave: + n = "GraphicsSceneHoverLeave"; + break; + case QEvent::GraphicsSceneHelp: + n = "GraphicsSceneHelp"; + break; + case QEvent::GraphicsSceneDragEnter: + n = "GraphicsSceneDragEnter"; + break; + case QEvent::GraphicsSceneDragMove: + n = "GraphicsSceneDragMove"; + break; + case QEvent::GraphicsSceneDragLeave: + n = "GraphicsSceneDragLeave"; + break; + case QEvent::GraphicsSceneDrop: + n = "GraphicsSceneDrop"; + break; + case QEvent::GraphicsSceneWheel: + n = "GraphicsSceneWheel"; + break; + case QEvent::GraphicsSceneResize: + n = "GraphicsSceneResize"; + break; + case QEvent::GraphicsSceneMove: + n = "GraphicsSceneMove"; + break; + case QEvent::CursorChange: + n = "CursorChange"; + break; + case QEvent::ToolTipChange: + n = "ToolTipChange"; + break; + case QEvent::StatusTip: + n = "StatusTip"; + break; + case QEvent::WhatsThis: + n = "WhatsThis"; + break; + case QEvent::FontChange: + n = "FontChange"; + break; + case QEvent::Style: + n = "Style"; + break; + case QEvent::KeyboardLayoutChange: + n = "KeyboardLayoutChange"; + break; + case QEvent::DynamicPropertyChange: + n = "DynamicPropertyChange"; + break; + case QEvent::GrabMouse: + n = "GrabMouse"; + break; + case QEvent::UngrabMouse: + n = "UngrabMouse"; + break; + case QEvent::GrabKeyboard: + n = "GrabKeyboard"; + break; + case QEvent::UngrabKeyboard: + n = "UngrabKeyboard"; + break; + case QEvent::ChildAdded: n = n ? n : "ChildAdded"; + case QEvent::ChildPolished: n = n ? n : "ChildPolished"; + case QEvent::ChildRemoved: n = n ? n : "ChildRemoved"; + dbg.nospace() << "QChildEvent(" << n << ", " << (static_cast<const QChildEvent*>(e))->child(); + return dbg.space(); +#ifndef QT_NO_GESTURES + case QEvent::Gesture: + n = "Gesture"; + break; +#endif + default: + dbg.nospace() << "QEvent(" << (const void *)e << ", type = " << e->type() << ')'; + return dbg.space(); + } + + dbg.nospace() << 'Q' << n << "Event(" << (const void *)e << ')'; + return dbg.space(); +#else + qWarning("This compiler doesn't support streaming QEvent to QDebug"); + return dbg; + Q_UNUSED(e); +#endif +} +#endif + +#ifndef QT_NO_CLIPBOARD +/*! + \class QClipboardEvent + \ingroup events + \internal + + \brief The QClipboardEvent class provides the parameters used in a clipboard event. + + This class is for internal use only, and exists to aid the clipboard on various + platforms to get all the information it needs. Use QEvent::Clipboard instead. + + \sa QClipboard +*/ + +QClipboardEvent::QClipboardEvent(QEventPrivate *data) + : QEvent(QEvent::Clipboard) +{ + d = data; +} + +QClipboardEvent::~QClipboardEvent() +{ +} +#endif // QT_NO_CLIPBOARD + +/*! + \class QShortcutEvent + \brief The QShortcutEvent class provides an event which is generated when + the user presses a key combination. + + \ingroup events + + Normally you don't need to use this class directly; QShortcut + provides a higher-level interface to handle shortcut keys. + + \sa QShortcut +*/ + +/*! + \fn const QKeySequence &QShortcutEvent::key() const + + Returns the key sequence that triggered the event. +*/ + +// ### Qt 5: remove +/*! + \fn const QKeySequence &QShortcutEvent::key() + + \internal +*/ + +/*! + \fn int QShortcutEvent::shortcutId() const + + Returns the ID of the QShortcut object for which this event was + generated. + + \sa QShortcut::id() +*/ + +// ### Qt 5: remove +/*! + \fn int QShortcutEvent::shortcutId() + \overload + + \internal +*/ + +/*! + \fn bool QShortcutEvent::isAmbiguous() const + + Returns true if the key sequence that triggered the event is + ambiguous. + + \sa QShortcut::activatedAmbiguously() +*/ + +// ### Qt 5: remove +/*! + \fn bool QShortcutEvent::isAmbiguous() + + \internal +*/ + +/*! + \class QWindowStateChangeEvent + \ingroup events + + \brief The QWindowStateChangeEvent class provides the window state before a + window state change. +*/ + +/*! \fn Qt::WindowStates QWindowStateChangeEvent::oldState() const + + Returns the state of the window before the change. +*/ + +/*! \internal + */ +QWindowStateChangeEvent::QWindowStateChangeEvent(Qt::WindowStates s) + : QEvent(WindowStateChange), ostate(s) +{ +} + +/*! \internal + */ +QWindowStateChangeEvent::QWindowStateChangeEvent(Qt::WindowStates s, bool isOverride) + : QEvent(WindowStateChange), ostate(s) +{ + if (isOverride) + d = (QEventPrivate*)(this); +} + +/*! \internal + */ +bool QWindowStateChangeEvent::isOverride() const +{ + return (d != 0); +} + +/*! \internal +*/ +QWindowStateChangeEvent::~QWindowStateChangeEvent() +{ +} + + +/*! + \class QTouchEvent + \brief The QTouchEvent class contains parameters that describe a touch event. + \since 4.6 + \ingroup events + \ingroup touch + + \section1 Enabling Touch Events + + Touch events occur when pressing, releasing, or moving one or more touch points on a touch + device (such as a touch-screen or track-pad). To receive touch events, widgets have to have the + Qt::WA_AcceptTouchEvents attribute set and graphics items need to have the + \l{QGraphicsItem::setAcceptTouchEvents()}{acceptTouchEvents} attribute set to true. + + When using QAbstractScrollArea based widgets, you should enable the Qt::WA_AcceptTouchEvents + attribute on the scroll area's \l{QAbstractScrollArea::viewport()}{viewport}. + + Similarly to QMouseEvent, Qt automatically grabs each touch point on the first press inside a + widget, and the widget will receive all updates for the touch point until it is released. + Note that it is possible for a widget to receive events for numerous touch points, and that + multiple widgets may be receiving touch events at the same time. + + \section1 Event Handling + + All touch events are of type QEvent::TouchBegin, QEvent::TouchUpdate, or QEvent::TouchEnd. + Reimplement QWidget::event() or QAbstractScrollArea::viewportEvent() for widgets and + QGraphicsItem::sceneEvent() for items in a graphics view to receive touch events. + + The QEvent::TouchUpdate and QEvent::TouchEnd events are sent to the widget or item that + accepted the QEvent::TouchBegin event. If the QEvent::TouchBegin event is not accepted and not + filtered by an event filter, then no further touch events are sent until the next + QEvent::TouchBegin. + + The touchPoints() function returns a list of all touch points contained in the event. + Information about each touch point can be retrieved using the QTouchEvent::TouchPoint class. + The Qt::TouchPointState enum describes the different states that a touch point may have. + + \section1 Event Delivery and Propagation + + By default, QWidget::event() translates the first non-primary touch point in a QTouchEvent into + a QMouseEvent. This makes it possible to enable touch events on existing widgets that do not + normally handle QTouchEvent. See below for information on some special considerations needed + when doing this. + + QEvent::TouchBegin is the first touch event sent to a widget. The QEvent::TouchBegin event + contains a special accept flag that indicates whether the receiver wants the event. By default, + the event is accepted. You should call ignore() if the touch event is not handled by your + widget. The QEvent::TouchBegin event is propagated up the parent widget chain until a widget + accepts it with accept(), or an event filter consumes it. For QGraphicsItems, the + QEvent::TouchBegin event is propagated to items under the mouse (similar to mouse event + propagation for QGraphicsItems). + + \section1 Touch Point Grouping + + As mentioned above, it is possible that several widgets can be receiving QTouchEvents at the + same time. However, Qt makes sure to never send duplicate QEvent::TouchBegin events to the same + widget, which could theoretically happen during propagation if, for example, the user touched 2 + separate widgets in a QGroupBox and both widgets ignored the QEvent::TouchBegin event. + + To avoid this, Qt will group new touch points together using the following rules: + + \list + + \i When the first touch point is detected, the destination widget is determined firstly by the + location on screen and secondly by the propagation rules. + + \i When additional touch points are detected, Qt first looks to see if there are any active + touch points on any ancestor or descendent of the widget under the new touch point. If there + are, the new touch point is grouped with the first, and the new touch point will be sent in a + single QTouchEvent to the widget that handled the first touch point. (The widget under the new + touch point will not receive an event). + + \endlist + + This makes it possible for sibling widgets to handle touch events independently while making + sure that the sequence of QTouchEvents is always correct. + + \section1 Mouse Events and the Primary Touch Point + + QTouchEvent delivery is independent from that of QMouseEvent. On some windowing systems, mouse + events are also sent for the \l{QTouchEvent::TouchPoint::isPrimary()}{primary touch point}. + This means it is possible for your widget to receive both QTouchEvent and QMouseEvent for the + same user interaction point. You can use the QTouchEvent::TouchPoint::isPrimary() function to + identify the primary touch point. + + Note that on some systems, it is possible to receive touch events without a primary touch + point. All this means is that there will be no mouse event generated for the touch points in + the QTouchEvent. + + \section1 Caveats + + \list + + \i As mentioned above, enabling touch events means multiple widgets can be receiving touch + events simultaneously. Combined with the default QWidget::event() handling for QTouchEvents, + this gives you great flexibility in designing touch user interfaces. Be aware of the + implications. For example, it is possible that the user is moving a QSlider with one finger and + pressing a QPushButton with another. The signals emitted by these widgets will be + interleaved. + + \i Recursion into the event loop using one of the exec() methods (e.g., QDialog::exec() or + QMenu::exec()) in a QTouchEvent event handler is not supported. Since there are multiple event + recipients, recursion may cause problems, including but not limited to lost events + and unexpected infinite recursion. + + \i QTouchEvents are not affected by a \l{QWidget::grabMouse()}{mouse grab} or an + \l{QApplication::activePopupWidget()}{active pop-up widget}. The behavior of QTouchEvents is + undefined when opening a pop-up or grabbing the mouse while there are more than one active touch + points. + + \endlist + + \sa QTouchEvent::TouchPoint, Qt::TouchPointState, Qt::WA_AcceptTouchEvents, + QGraphicsItem::acceptTouchEvents() +*/ + +/*! \enum Qt::TouchPointState + \since 4.6 + + This enum represents the state of a touch point at the time the + QTouchEvent occurred. + + \value TouchPointPressed The touch point is now pressed. + \value TouchPointMoved The touch point moved. + \value TouchPointStationary The touch point did not move. + \value TouchPointReleased The touch point was released. + + \omitvalue TouchPointStateMask + \omitvalue TouchPointPrimary +*/ + +/*! \enum QTouchEvent::DeviceType + + This enum represents the type of device that generated a QTouchEvent. + + \value TouchScreen In this type of device, the touch surface and display are integrated. This + means the surface and display typically have the same size, such that there + is a direct relationship between the touch points' physical positions and the + coordinate reported by QTouchEvent::TouchPoint. As a result, Qt allows the + user to interact directly with multiple QWidgets and QGraphicsItems at the + same time. + + \value TouchPad In this type of device, the touch surface is separate from the display. There + is not a direct relationship between the physical touch location and the + on-screen coordinates. Instead, they are calculated relative to the current + mouse position, and the user must use the touch-pad to move this reference + point. Unlike touch-screens, Qt allows users to only interact with a single + QWidget or QGraphicsItem at a time. +*/ + +/*! + Constructs a QTouchEvent with the given \a eventType, \a deviceType, and \a touchPoints. + The \a touchPointStates and \a modifiers are the current touch point states and keyboard + modifiers at the time of the event. +*/ +QTouchEvent::QTouchEvent(QEvent::Type eventType, + QTouchEvent::DeviceType deviceType, + Qt::KeyboardModifiers modifiers, + Qt::TouchPointStates touchPointStates, + const QList<QTouchEvent::TouchPoint> &touchPoints) + : QInputEvent(eventType, modifiers), + _widget(0), + _deviceType(deviceType), + _touchPointStates(touchPointStates), + _touchPoints(touchPoints) +{ } + +/*! + Destroys the QTouchEvent. +*/ +QTouchEvent::~QTouchEvent() +{ } + +/*! \fn QWidget *QTouchEvent::widget() const + + Returns the widget on which the event occurred. +*/ + + +/*! \fn Qt::TouchPointStates QTouchEvent::touchPointStates() const + + Returns a bitwise OR of all the touch point states for this event. +*/ + +/*! \fn const QList<QTouchEvent::TouchPoint> &QTouchEvent::touchPoints() const + + Returns the list of touch points contained in the touch event. +*/ + +/*! \fn QTouchEvent::DeviceType QTouchEvent::deviceType() const + + Returns the touch device Type, which is of type \l {QTouchEvent::DeviceType} {DeviceType}. +*/ + +/*! \fn void QTouchEvent::setWidget(QWidget *widget) + + \internal + + Sets the widget for this event. +*/ + +/*! \fn void QTouchEvent::setTouchPointStates(Qt::TouchPointStates touchPointStates) + + \internal + + Sets a bitwise OR of all the touch point states for this event. +*/ + +/*! \fn void QTouchEvent::setTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints) + + \internal + + Sets the list of touch points for this event. +*/ + +/*! \fn void QTouchEvent::setDeviceType(DeviceType deviceType) + + \internal + + Sets the device type to \a deviceType, which is of type \l {QTouchEvent::DeviceType} + {DeviceType}. +*/ + +/*! \class QTouchEvent::TouchPoint + \brief The TouchPoint class provides information about a touch point in a QTouchEvent. + \since 4.6 +*/ + +/*! \internal + + Constructs a QTouchEvent::TouchPoint for use in a QTouchEvent. +*/ +QTouchEvent::TouchPoint::TouchPoint(int id) + : d(new QTouchEventTouchPointPrivate(id)) +{ } + +/*! \internal + + Constructs a copy of \a other. +*/ +QTouchEvent::TouchPoint::TouchPoint(const QTouchEvent::TouchPoint &other) + : d(other.d) +{ + d->ref.ref(); +} + +/*! \internal + + Destroys the QTouchEvent::TouchPoint. +*/ +QTouchEvent::TouchPoint::~TouchPoint() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + Returns the id number of this touch point. + + Id numbers are globally sequential, starting at zero, meaning the + first touch point in the application has id 0, the second has id 1, + and so on. +*/ +int QTouchEvent::TouchPoint::id() const +{ + return d->id; +} + +/*! + Returns the current state of this touch point. +*/ +Qt::TouchPointState QTouchEvent::TouchPoint::state() const +{ + return Qt::TouchPointState(int(d->state) & Qt::TouchPointStateMask); +} + +/*! + Returns true if this touch point is the primary touch point. The primary touch point is the + point for which the windowing system generates mouse events. +*/ +bool QTouchEvent::TouchPoint::isPrimary() const +{ + return (d->state & Qt::TouchPointPrimary) != 0; +} + +/*! + Returns the position of this touch point, relative to the widget + or QGraphicsItem that received the event. + + \sa startPos(), lastPos(), screenPos(), scenePos(), normalizedPos() +*/ +QPointF QTouchEvent::TouchPoint::pos() const +{ + return d->rect.center(); +} + +/*! + Returns the scene position of this touch point. + + The scene position is the position in QGraphicsScene coordinates + if the QTouchEvent is handled by a QGraphicsItem::touchEvent() + reimplementation, and identical to the screen position for + widgets. + + \sa startScenePos(), lastScenePos(), pos() +*/ +QPointF QTouchEvent::TouchPoint::scenePos() const +{ + return d->sceneRect.center(); +} + +/*! + Returns the screen position of this touch point. + + \sa startScreenPos(), lastScreenPos(), pos() +*/ +QPointF QTouchEvent::TouchPoint::screenPos() const +{ + return d->screenRect.center(); +} + +/*! + Returns the normalized position of this touch point. + + The coordinates are normalized to the size of the touch device, + i.e. (0,0) is the top-left corner and (1,1) is the bottom-right corner. + + \sa startNormalizedPos(), lastNormalizedPos(), pos() +*/ +QPointF QTouchEvent::TouchPoint::normalizedPos() const +{ + return d->normalizedPos; +} + +/*! + Returns the starting position of this touch point, relative to the + widget or QGraphicsItem that received the event. + + \sa pos(), lastPos() +*/ +QPointF QTouchEvent::TouchPoint::startPos() const +{ + return d->startPos; +} + +/*! + Returns the starting scene position of this touch point. + + The scene position is the position in QGraphicsScene coordinates + if the QTouchEvent is handled by a QGraphicsItem::touchEvent() + reimplementation, and identical to the screen position for + widgets. + + \sa scenePos(), lastScenePos() +*/ +QPointF QTouchEvent::TouchPoint::startScenePos() const +{ + return d->startScenePos; +} + +/*! + Returns the starting screen position of this touch point. + + \sa screenPos(), lastScreenPos() +*/ +QPointF QTouchEvent::TouchPoint::startScreenPos() const +{ + return d->startScreenPos; +} + +/*! + Returns the normalized starting position of this touch point. + + The coordinates are normalized to the size of the touch device, + i.e. (0,0) is the top-left corner and (1,1) is the bottom-right corner. + + \sa normalizedPos(), lastNormalizedPos() +*/ +QPointF QTouchEvent::TouchPoint::startNormalizedPos() const +{ + return d->startNormalizedPos; +} + +/*! + Returns the position of this touch point from the previous touch + event, relative to the widget or QGraphicsItem that received the event. + + \sa pos(), startPos() +*/ +QPointF QTouchEvent::TouchPoint::lastPos() const +{ + return d->lastPos; +} + +/*! + Returns the scene position of this touch point from the previous + touch event. + + The scene position is the position in QGraphicsScene coordinates + if the QTouchEvent is handled by a QGraphicsItem::touchEvent() + reimplementation, and identical to the screen position for + widgets. + + \sa scenePos(), startScenePos() +*/ +QPointF QTouchEvent::TouchPoint::lastScenePos() const +{ + return d->lastScenePos; +} + +/*! + Returns the screen position of this touch point from the previous + touch event. + + \sa screenPos(), startScreenPos() +*/ +QPointF QTouchEvent::TouchPoint::lastScreenPos() const +{ + return d->lastScreenPos; +} + +/*! + Returns the normalized position of this touch point from the + previous touch event. + + The coordinates are normalized to the size of the touch device, + i.e. (0,0) is the top-left corner and (1,1) is the bottom-right corner. + + \sa normalizedPos(), startNormalizedPos() +*/ +QPointF QTouchEvent::TouchPoint::lastNormalizedPos() const +{ + return d->lastNormalizedPos; +} + +/*! + Returns the rect for this touch point, relative to the widget + or QGraphicsItem that received the event. The rect is centered + around the point returned by pos(). + + \note This function returns an empty rect if the device does not report touch point sizes. +*/ +QRectF QTouchEvent::TouchPoint::rect() const +{ + return d->rect; +} + +/*! + Returns the rect for this touch point in scene coordinates. + + \note This function returns an empty rect if the device does not report touch point sizes. + + \sa scenePos(), rect() +*/ +QRectF QTouchEvent::TouchPoint::sceneRect() const +{ + return d->sceneRect; +} + +/*! + Returns the rect for this touch point in screen coordinates. + + \note This function returns an empty rect if the device does not report touch point sizes. + + \sa screenPos(), rect() +*/ +QRectF QTouchEvent::TouchPoint::screenRect() const +{ + return d->screenRect; +} + +/*! + Returns the pressure of this touch point. The return value is in + the range 0.0 to 1.0. +*/ +qreal QTouchEvent::TouchPoint::pressure() const +{ + return d->pressure; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setId(int id) +{ + if (d->ref != 1) + d = d->detach(); + d->id = id; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setState(Qt::TouchPointStates state) +{ + if (d->ref != 1) + d = d->detach(); + d->state = state; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setPos(const QPointF &pos) +{ + if (d->ref != 1) + d = d->detach(); + d->rect.moveCenter(pos); +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setScenePos(const QPointF &scenePos) +{ + if (d->ref != 1) + d = d->detach(); + d->sceneRect.moveCenter(scenePos); +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos) +{ + if (d->ref != 1) + d = d->detach(); + d->screenRect.moveCenter(screenPos); +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setNormalizedPos(const QPointF &normalizedPos) +{ + if (d->ref != 1) + d = d->detach(); + d->normalizedPos = normalizedPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setStartPos(const QPointF &startPos) +{ + if (d->ref != 1) + d = d->detach(); + d->startPos = startPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setStartScenePos(const QPointF &startScenePos) +{ + if (d->ref != 1) + d = d->detach(); + d->startScenePos = startScenePos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setStartScreenPos(const QPointF &startScreenPos) +{ + if (d->ref != 1) + d = d->detach(); + d->startScreenPos = startScreenPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setStartNormalizedPos(const QPointF &startNormalizedPos) +{ + if (d->ref != 1) + d = d->detach(); + d->startNormalizedPos = startNormalizedPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setLastPos(const QPointF &lastPos) +{ + if (d->ref != 1) + d = d->detach(); + d->lastPos = lastPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setLastScenePos(const QPointF &lastScenePos) +{ + if (d->ref != 1) + d = d->detach(); + d->lastScenePos = lastScenePos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setLastScreenPos(const QPointF &lastScreenPos) +{ + if (d->ref != 1) + d = d->detach(); + d->lastScreenPos = lastScreenPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setLastNormalizedPos(const QPointF &lastNormalizedPos) +{ + if (d->ref != 1) + d = d->detach(); + d->lastNormalizedPos = lastNormalizedPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setRect(const QRectF &rect) +{ + if (d->ref != 1) + d = d->detach(); + d->rect = rect; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setSceneRect(const QRectF &sceneRect) +{ + if (d->ref != 1) + d = d->detach(); + d->sceneRect = sceneRect; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect) +{ + if (d->ref != 1) + d = d->detach(); + d->screenRect = screenRect; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setPressure(qreal pressure) +{ + if (d->ref != 1) + d = d->detach(); + d->pressure = pressure; +} + +/*! \internal */ +QTouchEvent::TouchPoint &QTouchEvent::TouchPoint::operator=(const QTouchEvent::TouchPoint &other) +{ + other.d->ref.ref(); + if (!d->ref.deref()) + delete d; + d = other.d; + return *this; +} + +#ifndef QT_NO_GESTURES +/*! + \class QGestureEvent + \since 4.6 + \ingroup events + \ingroup gestures + + \brief The QGestureEvent class provides the description of triggered gestures. + + The QGestureEvent class contains a list of gestures, which can be obtained using the + gestures() function. + + The gestures are either active or canceled. A list of those that are currently being + executed can be obtained using the activeGestures() function. A list of those which + were previously active and have been canceled can be accessed using the + canceledGestures() function. A gesture might be canceled if the current window loses + focus, for example, or because of a timeout, or for other reasons. + + If the event handler does not accept the event by calling the generic + QEvent::accept() function, all individual QGesture object that were not + accepted and in the Qt::GestureStarted state will be propagated up the + parent widget chain until a widget accepts them individually, by calling + QGestureEvent::accept() for each of them, or an event filter consumes the + event. + + \section1 Further Reading + + For an overview of gesture handling in Qt and information on using gestures + in your applications, see the \l{Gestures Programming} document. + + \sa QGesture, QGestureRecognizer, + QWidget::grabGesture(), QGraphicsObject::grabGesture() +*/ + +/*! + Creates new QGestureEvent containing a list of \a gestures. +*/ +QGestureEvent::QGestureEvent(const QList<QGesture *> &gestures) + : QEvent(QEvent::Gesture) +{ + d = reinterpret_cast<QEventPrivate *>(new QGestureEventPrivate(gestures)); +} + +/*! + Destroys QGestureEvent. +*/ +QGestureEvent::~QGestureEvent() +{ + delete reinterpret_cast<QGestureEventPrivate *>(d); +} + +/*! + Returns all gestures that are delivered in the event. +*/ +QList<QGesture *> QGestureEvent::gestures() const +{ + return d_func()->gestures; +} + +/*! + Returns a gesture object by \a type. +*/ +QGesture *QGestureEvent::gesture(Qt::GestureType type) const +{ + const QGestureEventPrivate *d = d_func(); + for(int i = 0; i < d->gestures.size(); ++i) + if (d->gestures.at(i)->gestureType() == type) + return d->gestures.at(i); + return 0; +} + +/*! + Returns a list of active (not canceled) gestures. +*/ +QList<QGesture *> QGestureEvent::activeGestures() const +{ + QList<QGesture *> gestures; + foreach (QGesture *gesture, d_func()->gestures) { + if (gesture->state() != Qt::GestureCanceled) + gestures.append(gesture); + } + return gestures; +} + +/*! + Returns a list of canceled gestures. +*/ +QList<QGesture *> QGestureEvent::canceledGestures() const +{ + QList<QGesture *> gestures; + foreach (QGesture *gesture, d_func()->gestures) { + if (gesture->state() == Qt::GestureCanceled) + gestures.append(gesture); + } + return gestures; +} + +/*! + Sets the accept flag of the given \a gesture object to the specified \a value. + + Setting the accept flag indicates that the event receiver wants the \a gesture. + Unwanted gestures may be propagated to the parent widget. + + By default, gestures in events of type QEvent::Gesture are accepted, and + gestures in QEvent::GestureOverride events are ignored. + + For convenience, the accept flag can also be set with + \l{QGestureEvent::accept()}{accept(gesture)}, and cleared with + \l{QGestureEvent::ignore()}{ignore(gesture)}. +*/ +void QGestureEvent::setAccepted(QGesture *gesture, bool value) +{ + if (gesture) + setAccepted(gesture->gestureType(), value); +} + +/*! + Sets the accept flag of the given \a gesture object, the equivalent of calling + \l{QGestureEvent::setAccepted()}{setAccepted(gesture, true)}. + + Setting the accept flag indicates that the event receiver wants the + gesture. Unwanted gestures may be propagated to the parent widget. + + \sa QGestureEvent::ignore() +*/ +void QGestureEvent::accept(QGesture *gesture) +{ + if (gesture) + setAccepted(gesture->gestureType(), true); +} + +/*! + Clears the accept flag parameter of the given \a gesture object, the equivalent + of calling \l{QGestureEvent::setAccepted()}{setAccepted(gesture, false)}. + + Clearing the accept flag indicates that the event receiver does not + want the gesture. Unwanted gestures may be propagated to the parent widget. + + \sa QGestureEvent::accept() +*/ +void QGestureEvent::ignore(QGesture *gesture) +{ + if (gesture) + setAccepted(gesture->gestureType(), false); +} + +/*! + Returns true if the \a gesture is accepted; otherwise returns false. +*/ +bool QGestureEvent::isAccepted(QGesture *gesture) const +{ + return gesture ? isAccepted(gesture->gestureType()) : false; +} + +/*! + Sets the accept flag of the given \a gestureType object to the specified + \a value. + + Setting the accept flag indicates that the event receiver wants to receive + gestures of the specified type, \a gestureType. Unwanted gestures may be + propagated to the parent widget. + + By default, gestures in events of type QEvent::Gesture are accepted, and + gestures in QEvent::GestureOverride events are ignored. + + For convenience, the accept flag can also be set with + \l{QGestureEvent::accept()}{accept(gestureType)}, and cleared with + \l{QGestureEvent::ignore()}{ignore(gestureType)}. +*/ +void QGestureEvent::setAccepted(Qt::GestureType gestureType, bool value) +{ + setAccepted(false); + d_func()->accepted[gestureType] = value; +} + +/*! + Sets the accept flag of the given \a gestureType, the equivalent of calling + \l{QGestureEvent::setAccepted()}{setAccepted(gestureType, true)}. + + Setting the accept flag indicates that the event receiver wants the + gesture. Unwanted gestures may be propagated to the parent widget. + + \sa QGestureEvent::ignore() +*/ +void QGestureEvent::accept(Qt::GestureType gestureType) +{ + setAccepted(gestureType, true); +} + +/*! + Clears the accept flag parameter of the given \a gestureType, the equivalent + of calling \l{QGestureEvent::setAccepted()}{setAccepted(gesture, false)}. + + Clearing the accept flag indicates that the event receiver does not + want the gesture. Unwanted gestures may be propgated to the parent widget. + + \sa QGestureEvent::accept() +*/ +void QGestureEvent::ignore(Qt::GestureType gestureType) +{ + setAccepted(gestureType, false); +} + +/*! + Returns true if the gesture of type \a gestureType is accepted; otherwise + returns false. +*/ +bool QGestureEvent::isAccepted(Qt::GestureType gestureType) const +{ + return d_func()->accepted.value(gestureType, true); +} + +/*! + \internal + + Sets the widget for this event to the \a widget specified. +*/ +void QGestureEvent::setWidget(QWidget *widget) +{ + d_func()->widget = widget; +} + +/*! + Returns the widget on which the event occurred. +*/ +QWidget *QGestureEvent::widget() const +{ + return d_func()->widget; +} + +#ifndef QT_NO_GRAPHICSVIEW +/*! + Returns the scene-local coordinates if the \a gesturePoint is inside a + graphics view. + + This functional might be useful when the gesture event is delivered to a + QGraphicsObject to translate a point in screen coordinates to scene-local + coordinates. + + \sa QPointF::isNull(). +*/ +QPointF QGestureEvent::mapToGraphicsScene(const QPointF &gesturePoint) const +{ + QWidget *w = widget(); + if (w) // we get the viewport as widget, not the graphics view + w = w->parentWidget(); + QGraphicsView *view = qobject_cast<QGraphicsView*>(w); + if (view) { + return view->mapToScene(view->mapFromGlobal(gesturePoint.toPoint())); + } + return QPointF(); +} +#endif //QT_NO_GRAPHICSVIEW + +/*! + \internal +*/ +QGestureEventPrivate *QGestureEvent::d_func() +{ + return reinterpret_cast<QGestureEventPrivate *>(d); +} + +/*! + \internal +*/ +const QGestureEventPrivate *QGestureEvent::d_func() const +{ + return reinterpret_cast<const QGestureEventPrivate *>(d); +} + +#ifdef Q_NO_USING_KEYWORD +/*! + \fn void QGestureEvent::setAccepted(bool accepted) + + Sets or clears the event's internal flag that determines whether it should + be delivered to other objects. + + Calling this function with a value of true for \a accepted indicates that the + caller has accepted the event and that it should not be propagated further. + Calling this function with a value of false indicates that the caller has + ignored the event and that it should be delivered to other objects. + + For convenience, the accept flag can also be set with accept(), and cleared + with ignore(). + + \sa QEvent::accepted +*/ +/*! + \fn bool QGestureEvent::isAccepted() const + + Returns true is the event has been accepted; otherwise returns false. + + \sa QEvent::accepted +*/ +/*! + \fn void QGestureEvent::accept() + + Accepts the event, the equivalent of calling setAccepted(true). + + \sa QEvent::accept() +*/ +/*! + \fn void QGestureEvent::ignore() + + Ignores the event, the equivalent of calling setAccepted(false). + + \sa QEvent::ignore() +*/ +#endif + +#endif // QT_NO_GESTURES + +/*! + \class QScrollPrepareEvent + \since 4.8 + \ingroup events + + \brief The QScrollPrepareEvent class is send in preparation of a scrolling. + + The scroll prepare event is send before scrolling (usually by QScroller) is started. + The object receiving this event should set viewportSize, maxContentPos and contentPos. + It also should accept this event to indicate that scrolling should be started. + + It is not guaranteed that a QScrollEvent will be send after an acceepted + QScrollPrepareEvent, e.g. in a case where the maximum content position is (0,0). + + \sa QScrollEvent, QScroller +*/ + +/*! + Creates new QScrollPrepareEvent + The \a startPos is the position of a touch or mouse event that started the scrolling. +*/ +QScrollPrepareEvent::QScrollPrepareEvent(const QPointF &startPos) + : QEvent(QEvent::ScrollPrepare) +{ + d = reinterpret_cast<QEventPrivate *>(new QScrollPrepareEventPrivate()); + d_func()->startPos = startPos; +} + +/*! + Destroys QScrollEvent. +*/ +QScrollPrepareEvent::~QScrollPrepareEvent() +{ + delete reinterpret_cast<QScrollPrepareEventPrivate *>(d); +} + +/*! + Returns the position of the touch or mouse event that started the scrolling. +*/ +QPointF QScrollPrepareEvent::startPos() const +{ + return d_func()->startPos; +} + +/*! + Returns size of the area that is to be scrolled as set by setViewportSize + + \sa setViewportSize() +*/ +QSizeF QScrollPrepareEvent::viewportSize() const +{ + return d_func()->viewportSize; +} + +/*! + Returns the range of coordinates for the content as set by setContentPosRange(). +*/ +QRectF QScrollPrepareEvent::contentPosRange() const +{ + return d_func()->contentPosRange; +} + +/*! + Returns the current position of the content as set by setContentPos. +*/ +QPointF QScrollPrepareEvent::contentPos() const +{ + return d_func()->contentPos; +} + + +/*! + Sets the size of the area that is to be scrolled to \a size. + + \sa viewportSize() +*/ +void QScrollPrepareEvent::setViewportSize(const QSizeF &size) +{ + d_func()->viewportSize = size; +} + +/*! + Sets the range of content coordinates to \a rect. + + \sa contentPosRange() +*/ +void QScrollPrepareEvent::setContentPosRange(const QRectF &rect) +{ + d_func()->contentPosRange = rect; +} + +/*! + Sets the current content position to \a pos. + + \sa contentPos() +*/ +void QScrollPrepareEvent::setContentPos(const QPointF &pos) +{ + d_func()->contentPos = pos; +} + + +/*! + \internal +*/ +QScrollPrepareEventPrivate *QScrollPrepareEvent::d_func() +{ + return reinterpret_cast<QScrollPrepareEventPrivate *>(d); +} + +/*! + \internal +*/ +const QScrollPrepareEventPrivate *QScrollPrepareEvent::d_func() const +{ + return reinterpret_cast<const QScrollPrepareEventPrivate *>(d); +} + +/*! + \class QScrollEvent + \since 4.8 + \ingroup events + + \brief The QScrollEvent class is send when scrolling. + + The scroll event is send to indicate that the receiver should be scrolled. + Usually the receiver should be something visual like QWidget or QGraphicsObject. + + Some care should be taken that no conflicting QScrollEvents are sent from two + sources. Using QScroller::scrollTo is save however. + + \sa QScrollPrepareEvent, QScroller +*/ + +/*! + \enum QScrollEvent::ScrollState + + This enum describes the states a scroll event can have. + + \value ScrollStarted Set for the first scroll event of a scroll activity. + + \value ScrollUpdated Set for all but the first and the last scroll event of a scroll activity. + + \value ScrollFinished Set for the last scroll event of a scroll activity. + + \sa QScrollEvent::scrollState() +*/ + +/*! + Creates a new QScrollEvent + \a contentPos is the new content position, \a overshootDistance is the + new overshoot distance while \a scrollState indicates if this scroll + event is the first one, the last one or some event in between. +*/ +QScrollEvent::QScrollEvent(const QPointF &contentPos, const QPointF &overshootDistance, ScrollState scrollState) + : QEvent(QEvent::Scroll) +{ + d = reinterpret_cast<QEventPrivate *>(new QScrollEventPrivate()); + d_func()->contentPos = contentPos; + d_func()->overshoot= overshootDistance; + d_func()->state = scrollState; +} + +/*! + Destroys QScrollEvent. +*/ +QScrollEvent::~QScrollEvent() +{ + delete reinterpret_cast<QScrollEventPrivate *>(d); +} + +/*! + Returns the new scroll position. +*/ +QPointF QScrollEvent::contentPos() const +{ + return d_func()->contentPos; +} + +/*! + Returns the new overshoot distance. + See QScroller for an explanation of the term overshoot. + + \sa QScroller +*/ +QPointF QScrollEvent::overshootDistance() const +{ + return d_func()->overshoot; +} + +/*! + Returns the current scroll state as a combination of ScrollStateFlag values. + ScrollStarted (or ScrollFinished) will be set, if this scroll event is the first (or last) event in a scrolling activity. + Please note that both values can be set at the same time, if the activity consists of a single QScrollEvent. + All other scroll events in between will have their state set to ScrollUpdated. + + A widget could for example revert selections when scrolling is started and stopped. +*/ +QScrollEvent::ScrollState QScrollEvent::scrollState() const +{ + return d_func()->state; +} + +/*! + \internal +*/ +QScrollEventPrivate *QScrollEvent::d_func() +{ + return reinterpret_cast<QScrollEventPrivate *>(d); +} + +/*! + \internal +*/ +const QScrollEventPrivate *QScrollEvent::d_func() const +{ + return reinterpret_cast<const QScrollEventPrivate *>(d); +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qevent.h b/src/gui/guikernel/qevent.h new file mode 100644 index 0000000000..6128db4a64 --- /dev/null +++ b/src/gui/guikernel/qevent.h @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENT_H +#define QEVENT_H + +#include <QtGui/qwindowdefs.h> +#include <QtCore/qobject.h> +#include <QtGui/qregion.h> +#include <QtCore/qnamespace.h> +#include <QtCore/qstring.h> +#include <QtGui/qkeysequence.h> +#include <QtCore/qcoreevent.h> +#include <QtGui/qmime.h> +#include <QtGui/qdrag.h> +#include <QtCore/qvariant.h> +#include <QtCore/qmap.h> +#include <QtCore/qset.h> +#include <QtCore/qfile.h> + +#ifdef Q_OS_SYMBIAN +class RFile; +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QAction; +#ifndef QT_NO_GESTURES +class QGesture; +#endif + +class Q_GUI_EXPORT QInputEvent : public QEvent +{ +public: + QInputEvent(Type type, Qt::KeyboardModifiers modifiers = Qt::NoModifier); + ~QInputEvent(); + inline Qt::KeyboardModifiers modifiers() const { return modState; } + inline void setModifiers(Qt::KeyboardModifiers amodifiers) { modState = amodifiers; } +protected: + Qt::KeyboardModifiers modState; +}; + +class Q_GUI_EXPORT QMouseEvent : public QInputEvent +{ +public: + QMouseEvent(Type type, const QPoint &pos, Qt::MouseButton button, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers); + QMouseEvent(Type type, const QPoint &pos, const QPoint &globalPos, + Qt::MouseButton button, Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers); + ~QMouseEvent(); + + inline const QPoint &pos() const { return p; } + inline const QPoint &globalPos() const { return g; } + inline int x() const { return p.x(); } + inline int y() const { return p.y(); } + inline int globalX() const { return g.x(); } + inline int globalY() const { return g.y(); } + inline Qt::MouseButton button() const { return b; } + inline Qt::MouseButtons buttons() const { return mouseState; } + + static QMouseEvent *createExtendedMouseEvent(Type type, const QPointF &pos, + const QPoint &globalPos, Qt::MouseButton button, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers); + inline bool hasExtendedInfo() const { return reinterpret_cast<const QMouseEvent *>(d) == this; } + QPointF posF() const; + +protected: + QPoint p, g; + Qt::MouseButton b; + Qt::MouseButtons mouseState; +}; + +class Q_GUI_EXPORT QHoverEvent : public QEvent +{ +public: + QHoverEvent(Type type, const QPoint &pos, const QPoint &oldPos); + ~QHoverEvent(); + + inline const QPoint &pos() const { return p; } + inline const QPoint &oldPos() const { return op; } + +protected: + QPoint p, op; +}; + +#ifndef QT_NO_WHEELEVENT +class Q_GUI_EXPORT QWheelEvent : public QInputEvent +{ +public: + QWheelEvent(const QPoint &pos, int delta, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, + Qt::Orientation orient = Qt::Vertical); + QWheelEvent(const QPoint &pos, const QPoint& globalPos, int delta, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, + Qt::Orientation orient = Qt::Vertical); + ~QWheelEvent(); + + inline int delta() const { return d; } + inline const QPoint &pos() const { return p; } + inline const QPoint &globalPos() const { return g; } + inline int x() const { return p.x(); } + inline int y() const { return p.y(); } + inline int globalX() const { return g.x(); } + inline int globalY() const { return g.y(); } + + inline Qt::MouseButtons buttons() const { return mouseState; } + Qt::Orientation orientation() const { return o; } + +protected: + QPoint p; + QPoint g; + int d; + Qt::MouseButtons mouseState; + Qt::Orientation o; +}; +#endif + +#ifndef QT_NO_TABLETEVENT +class Q_GUI_EXPORT QTabletEvent : public QInputEvent +{ +public: + enum TabletDevice { NoDevice, Puck, Stylus, Airbrush, FourDMouse, + XFreeEraser /*internal*/, RotationStylus }; + enum PointerType { UnknownPointer, Pen, Cursor, Eraser }; + QTabletEvent(Type t, const QPoint &pos, const QPoint &globalPos, const QPointF &hiResGlobalPos, + int device, int pointerType, qreal pressure, int xTilt, int yTilt, + qreal tangentialPressure, qreal rotation, int z, + Qt::KeyboardModifiers keyState, qint64 uniqueID); + ~QTabletEvent(); + + inline const QPoint &pos() const { return mPos; } + inline const QPoint &globalPos() const { return mGPos; } + inline const QPointF &hiResGlobalPos() const { return mHiResGlobalPos; } + inline int x() const { return mPos.x(); } + inline int y() const { return mPos.y(); } + inline int globalX() const { return mGPos.x(); } + inline int globalY() const { return mGPos.y(); } + inline qreal hiResGlobalX() const { return mHiResGlobalPos.x(); } + inline qreal hiResGlobalY() const { return mHiResGlobalPos.y(); } + inline TabletDevice device() const { return TabletDevice(mDev); } + inline PointerType pointerType() const { return PointerType(mPointerType); } + inline qint64 uniqueId() const { return mUnique; } + inline qreal pressure() const { return mPress; } + inline int z() const { return mZ; } + inline qreal tangentialPressure() const { return mTangential; } + inline qreal rotation() const { return mRot; } + inline int xTilt() const { return mXT; } + inline int yTilt() const { return mYT; } + +protected: + QPoint mPos, mGPos; + QPointF mHiResGlobalPos; + int mDev, mPointerType, mXT, mYT, mZ; + qreal mPress, mTangential, mRot; + qint64 mUnique; + + // I don't know what the future holds for tablets but there could be some + // new devices coming along, and there seem to be "holes" in the + // OS-specific events for this. + void *mExtra; +}; +#endif // QT_NO_TABLETEVENT + +class Q_GUI_EXPORT QKeyEvent : public QInputEvent +{ +public: + QKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, const QString& text = QString(), + bool autorep = false, ushort count = 1); + ~QKeyEvent(); + + int key() const { return k; } +#ifndef QT_NO_SHORTCUT + bool matches(QKeySequence::StandardKey key) const; +#endif + Qt::KeyboardModifiers modifiers() const; + inline QString text() const { return txt; } + inline bool isAutoRepeat() const { return autor; } + inline int count() const { return int(c); } + + // Functions for the extended key event information + static QKeyEvent *createExtendedKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text = QString(), bool autorep = false, + ushort count = 1); + inline bool hasExtendedInfo() const { return reinterpret_cast<const QKeyEvent*>(d) == this; } + quint32 nativeScanCode() const; + quint32 nativeVirtualKey() const; + quint32 nativeModifiers() const; + +protected: + QString txt; + int k; + ushort c; + uint autor:1; +}; + + +class Q_GUI_EXPORT QFocusEvent : public QEvent +{ +public: + QFocusEvent(Type type, Qt::FocusReason reason=Qt::OtherFocusReason); + ~QFocusEvent(); + + inline bool gotFocus() const { return type() == FocusIn; } + inline bool lostFocus() const { return type() == FocusOut; } + + Qt::FocusReason reason(); + Qt::FocusReason reason() const; + +private: + Qt::FocusReason m_reason; +}; + + +class Q_GUI_EXPORT QPaintEvent : public QEvent +{ +public: + QPaintEvent(const QRegion& paintRegion); + QPaintEvent(const QRect &paintRect); + ~QPaintEvent(); + + inline const QRect &rect() const { return m_rect; } + inline const QRegion ®ion() const { return m_region; } + +protected: + friend class QApplication; + friend class QCoreApplication; + QRect m_rect; + QRegion m_region; + bool m_erased; +}; + +class QUpdateLaterEvent : public QEvent +{ +public: + QUpdateLaterEvent(const QRegion& paintRegion); + ~QUpdateLaterEvent(); + + inline const QRegion ®ion() const { return m_region; } + +protected: + QRegion m_region; +}; + +class Q_GUI_EXPORT QMoveEvent : public QEvent +{ +public: + QMoveEvent(const QPoint &pos, const QPoint &oldPos); + ~QMoveEvent(); + + inline const QPoint &pos() const { return p; } + inline const QPoint &oldPos() const { return oldp;} +protected: + QPoint p, oldp; + friend class QApplication; + friend class QCoreApplication; +}; + + +class Q_GUI_EXPORT QResizeEvent : public QEvent +{ +public: + QResizeEvent(const QSize &size, const QSize &oldSize); + ~QResizeEvent(); + + inline const QSize &size() const { return s; } + inline const QSize &oldSize()const { return olds;} +protected: + QSize s, olds; + friend class QApplication; + friend class QCoreApplication; +}; + + +class Q_GUI_EXPORT QCloseEvent : public QEvent +{ +public: + QCloseEvent(); + ~QCloseEvent(); +}; + + +class Q_GUI_EXPORT QIconDragEvent : public QEvent +{ +public: + QIconDragEvent(); + ~QIconDragEvent(); +}; + + +class Q_GUI_EXPORT QShowEvent : public QEvent +{ +public: + QShowEvent(); + ~QShowEvent(); +}; + + +class Q_GUI_EXPORT QHideEvent : public QEvent +{ +public: + QHideEvent(); + ~QHideEvent(); +}; + +#ifndef QT_NO_CONTEXTMENU +class Q_GUI_EXPORT QContextMenuEvent : public QInputEvent +{ +public: + enum Reason { Mouse, Keyboard, Other }; + + QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos, + Qt::KeyboardModifiers modifiers); + QContextMenuEvent(Reason reason, const QPoint &pos, const QPoint &globalPos); + QContextMenuEvent(Reason reason, const QPoint &pos); + ~QContextMenuEvent(); + + inline int x() const { return p.x(); } + inline int y() const { return p.y(); } + inline int globalX() const { return gp.x(); } + inline int globalY() const { return gp.y(); } + + inline const QPoint& pos() const { return p; } + inline const QPoint& globalPos() const { return gp; } + + inline Reason reason() const { return Reason(reas); } + +protected: + QPoint p; + QPoint gp; + uint reas : 8; +}; +#endif // QT_NO_CONTEXTMENU + +#ifndef QT_NO_INPUTMETHOD +class Q_GUI_EXPORT QInputMethodEvent : public QEvent +{ +public: + enum AttributeType { + TextFormat, + Cursor, + Language, + Ruby, + Selection + }; + class Attribute { + public: + Attribute(AttributeType t, int s, int l, QVariant val) : type(t), start(s), length(l), value(val) {} + AttributeType type; + + int start; + int length; + QVariant value; + }; + QInputMethodEvent(); + QInputMethodEvent(const QString &preeditText, const QList<Attribute> &attributes); + void setCommitString(const QString &commitString, int replaceFrom = 0, int replaceLength = 0); + + inline const QList<Attribute> &attributes() const { return attrs; } + inline const QString &preeditString() const { return preedit; } + + inline const QString &commitString() const { return commit; } + inline int replacementStart() const { return replace_from; } + inline int replacementLength() const { return replace_length; } + + QInputMethodEvent(const QInputMethodEvent &other); + +private: + QString preedit; + QList<Attribute> attrs; + QString commit; + int replace_from; + int replace_length; +}; +#endif // QT_NO_INPUTMETHOD + +#ifndef QT_NO_DRAGANDDROP + +class QMimeData; + +class Q_GUI_EXPORT QDropEvent : public QEvent +// QT3_SUPPORT + , public QMimeSource +// END QT3_SUPPORT +{ +public: + QDropEvent(const QPoint& pos, Qt::DropActions actions, const QMimeData *data, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Type type = Drop); + ~QDropEvent(); + + inline const QPoint &pos() const { return p; } + inline Qt::MouseButtons mouseButtons() const { return mouseState; } + inline Qt::KeyboardModifiers keyboardModifiers() const { return modState; } + + inline Qt::DropActions possibleActions() const { return act; } + inline Qt::DropAction proposedAction() const { return default_action; } + inline void acceptProposedAction() { drop_action = default_action; accept(); } + + inline Qt::DropAction dropAction() const { return drop_action; } + void setDropAction(Qt::DropAction action); + + QWidget* source() const; + inline const QMimeData *mimeData() const { return mdata; } + +// QT3_SUPPORT + const char* format(int n = 0) const; + QByteArray encodedData(const char*) const; + bool provides(const char*) const; +// END QT3_SUPPORT + + +protected: + friend class QApplication; + QPoint p; + Qt::MouseButtons mouseState; + Qt::KeyboardModifiers modState; + Qt::DropActions act; + Qt::DropAction drop_action; + Qt::DropAction default_action; + const QMimeData *mdata; + mutable QList<QByteArray> fmts; // only used for QT3_SUPPORT +}; + + +class Q_GUI_EXPORT QDragMoveEvent : public QDropEvent +{ +public: + QDragMoveEvent(const QPoint &pos, Qt::DropActions actions, const QMimeData *data, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Type type = DragMove); + ~QDragMoveEvent(); + + inline QRect answerRect() const { return rect; } + + inline void accept() { QDropEvent::accept(); } + inline void ignore() { QDropEvent::ignore(); } + + inline void accept(const QRect & r) { accept(); rect = r; } + inline void ignore(const QRect & r) { ignore(); rect = r; } + +protected: + friend class QApplication; + QRect rect; +}; + + +class Q_GUI_EXPORT QDragEnterEvent : public QDragMoveEvent +{ +public: + QDragEnterEvent(const QPoint &pos, Qt::DropActions actions, const QMimeData *data, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers); + ~QDragEnterEvent(); +}; + + +/* An internal class */ +class Q_GUI_EXPORT QDragResponseEvent : public QEvent +{ +public: + QDragResponseEvent(bool accepted); + ~QDragResponseEvent(); + + inline bool dragAccepted() const { return a; } +protected: + bool a; +}; + + +class Q_GUI_EXPORT QDragLeaveEvent : public QEvent +{ +public: + QDragLeaveEvent(); + ~QDragLeaveEvent(); +}; +#endif // QT_NO_DRAGANDDROP + + +class Q_GUI_EXPORT QHelpEvent : public QEvent +{ +public: + QHelpEvent(Type type, const QPoint &pos, const QPoint &globalPos); + ~QHelpEvent(); + + inline int x() const { return p.x(); } + inline int y() const { return p.y(); } + inline int globalX() const { return gp.x(); } + inline int globalY() const { return gp.y(); } + + inline const QPoint& pos() const { return p; } + inline const QPoint& globalPos() const { return gp; } + +private: + QPoint p; + QPoint gp; +}; + +#ifndef QT_NO_STATUSTIP +class Q_GUI_EXPORT QStatusTipEvent : public QEvent +{ +public: + QStatusTipEvent(const QString &tip); + ~QStatusTipEvent(); + + inline QString tip() const { return s; } +private: + QString s; +}; +#endif + +#ifndef QT_NO_WHATSTHIS +class Q_GUI_EXPORT QWhatsThisClickedEvent : public QEvent +{ +public: + QWhatsThisClickedEvent(const QString &href); + ~QWhatsThisClickedEvent(); + + inline QString href() const { return s; } +private: + QString s; +}; +#endif + +#ifndef QT_NO_ACTION +class Q_GUI_EXPORT QActionEvent : public QEvent +{ + QAction *act, *bef; +public: + QActionEvent(int type, QAction *action, QAction *before = 0); + ~QActionEvent(); + + inline QAction *action() const { return act; } + inline QAction *before() const { return bef; } +}; +#endif + +class Q_GUI_EXPORT QFileOpenEvent : public QEvent +{ +public: + QFileOpenEvent(const QString &file); + QFileOpenEvent(const QUrl &url); +#ifdef Q_OS_SYMBIAN + QFileOpenEvent(const RFile &fileHandle); +#endif + ~QFileOpenEvent(); + + inline QString file() const { return f; } + QUrl url() const; + bool openFile(QFile &file, QIODevice::OpenMode flags) const; +private: + QString f; +}; + +#ifndef QT_NO_TOOLBAR +class Q_GUI_EXPORT QToolBarChangeEvent : public QEvent +{ +public: + QToolBarChangeEvent(bool t); + ~QToolBarChangeEvent(); + + inline bool toggle() const { return tog; } +private: + uint tog : 1; +}; +#endif + +#ifndef QT_NO_SHORTCUT +class Q_GUI_EXPORT QShortcutEvent : public QEvent +{ +public: + QShortcutEvent(const QKeySequence &key, int id, bool ambiguous = false); + ~QShortcutEvent(); + + inline const QKeySequence &key() { return sequence; } + inline const QKeySequence &key() const { return sequence; } + inline int shortcutId() { return sid; } + inline int shortcutId() const { return sid; } + inline bool isAmbiguous() { return ambig; } + inline bool isAmbiguous() const { return ambig; } +protected: + QKeySequence sequence; + bool ambig; + int sid; +}; +#endif + +#ifndef QT_NO_CLIPBOARD +class Q_GUI_EXPORT QClipboardEvent : public QEvent +{ +public: + QClipboardEvent(QEventPrivate *data); + ~QClipboardEvent(); + + QEventPrivate *data() { return d; } +}; +#endif + +class Q_GUI_EXPORT QWindowStateChangeEvent: public QEvent +{ +public: + QWindowStateChangeEvent(Qt::WindowStates aOldState); + QWindowStateChangeEvent(Qt::WindowStates aOldState, bool isOverride); + ~QWindowStateChangeEvent(); + + inline Qt::WindowStates oldState() const { return ostate; } + bool isOverride() const; + +private: + Qt::WindowStates ostate; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QEvent *); +#endif + +#ifndef QT_NO_SHORTCUT +inline bool operator==(QKeyEvent *e, QKeySequence::StandardKey key){return (e ? e->matches(key) : false);} +inline bool operator==(QKeySequence::StandardKey key, QKeyEvent *e){return (e ? e->matches(key) : false);} +#endif // QT_NO_SHORTCUT + +class QTouchEventTouchPointPrivate; +class Q_GUI_EXPORT QTouchEvent : public QInputEvent +{ +public: + class Q_GUI_EXPORT TouchPoint + { + public: + TouchPoint(int id = -1); + TouchPoint(const QTouchEvent::TouchPoint &other); + ~TouchPoint(); + + int id() const; + + Qt::TouchPointState state() const; + bool isPrimary() const; + + QPointF pos() const; + QPointF startPos() const; + QPointF lastPos() const; + + QPointF scenePos() const; + QPointF startScenePos() const; + QPointF lastScenePos() const; + + QPointF screenPos() const; + QPointF startScreenPos() const; + QPointF lastScreenPos() const; + + QPointF normalizedPos() const; + QPointF startNormalizedPos() const; + QPointF lastNormalizedPos() const; + + QRectF rect() const; + QRectF sceneRect() const; + QRectF screenRect() const; + + qreal pressure() const; + + // internal + void setId(int id); + void setState(Qt::TouchPointStates state); + void setPos(const QPointF &pos); + void setScenePos(const QPointF &scenePos); + void setScreenPos(const QPointF &screenPos); + void setNormalizedPos(const QPointF &normalizedPos); + void setStartPos(const QPointF &startPos); + void setStartScenePos(const QPointF &startScenePos); + void setStartScreenPos(const QPointF &startScreenPos); + void setStartNormalizedPos(const QPointF &startNormalizedPos); + void setLastPos(const QPointF &lastPos); + void setLastScenePos(const QPointF &lastScenePos); + void setLastScreenPos(const QPointF &lastScreenPos); + void setLastNormalizedPos(const QPointF &lastNormalizedPos); + void setRect(const QRectF &rect); + void setSceneRect(const QRectF &sceneRect); + void setScreenRect(const QRectF &screenRect); + void setPressure(qreal pressure); + QTouchEvent::TouchPoint &operator=(const QTouchEvent::TouchPoint &other); + + private: + QTouchEventTouchPointPrivate *d; + friend class QApplication; + friend class QApplicationPrivate; + }; + + enum DeviceType { + TouchScreen, + TouchPad + }; + + QTouchEvent(QEvent::Type eventType, + QTouchEvent::DeviceType deviceType = TouchScreen, + Qt::KeyboardModifiers modifiers = Qt::NoModifier, + Qt::TouchPointStates touchPointStates = 0, + const QList<QTouchEvent::TouchPoint> &touchPoints = QList<QTouchEvent::TouchPoint>()); + ~QTouchEvent(); + + inline QWidget *widget() const { return _widget; } + inline QTouchEvent::DeviceType deviceType() const { return _deviceType; } + inline Qt::TouchPointStates touchPointStates() const { return _touchPointStates; } + inline const QList<QTouchEvent::TouchPoint> &touchPoints() const { return _touchPoints; } + + // internal + inline void setWidget(QWidget *awidget) { _widget = awidget; } + inline void setDeviceType(DeviceType adeviceType) { _deviceType = adeviceType; } + inline void setTouchPointStates(Qt::TouchPointStates aTouchPointStates) { _touchPointStates = aTouchPointStates; } + inline void setTouchPoints(const QList<QTouchEvent::TouchPoint> &atouchPoints) { _touchPoints = atouchPoints; } + +protected: + QWidget *_widget; + QTouchEvent::DeviceType _deviceType; + Qt::TouchPointStates _touchPointStates; + QList<QTouchEvent::TouchPoint> _touchPoints; + + friend class QApplication; + friend class QApplicationPrivate; +}; + +#ifndef QT_NO_GESTURES +class QGesture; +class QGestureEventPrivate; +class Q_GUI_EXPORT QGestureEvent : public QEvent +{ +public: + QGestureEvent(const QList<QGesture *> &gestures); + ~QGestureEvent(); + + QList<QGesture *> gestures() const; + QGesture *gesture(Qt::GestureType type) const; + + QList<QGesture *> activeGestures() const; + QList<QGesture *> canceledGestures() const; + +#ifdef Q_NO_USING_KEYWORD + inline void setAccepted(bool accepted) { QEvent::setAccepted(accepted); } + inline bool isAccepted() const { return QEvent::isAccepted(); } + + inline void accept() { QEvent::accept(); } + inline void ignore() { QEvent::ignore(); } +#else + using QEvent::setAccepted; + using QEvent::isAccepted; + using QEvent::accept; + using QEvent::ignore; +#endif + + void setAccepted(QGesture *, bool); + void accept(QGesture *); + void ignore(QGesture *); + bool isAccepted(QGesture *) const; + + void setAccepted(Qt::GestureType, bool); + void accept(Qt::GestureType); + void ignore(Qt::GestureType); + bool isAccepted(Qt::GestureType) const; + + void setWidget(QWidget *widget); + QWidget *widget() const; + +#ifndef QT_NO_GRAPHICSVIEW + QPointF mapToGraphicsScene(const QPointF &gesturePoint) const; +#endif + +private: + QGestureEventPrivate *d_func(); + const QGestureEventPrivate *d_func() const; + + friend class QApplication; + friend class QGestureManager; +}; +#endif // QT_NO_GESTURES + +class QScrollPrepareEventPrivate; +class Q_GUI_EXPORT QScrollPrepareEvent : public QEvent +{ +public: + QScrollPrepareEvent(const QPointF &startPos); + ~QScrollPrepareEvent(); + + QPointF startPos() const; + + QSizeF viewportSize() const; + QRectF contentPosRange() const; + QPointF contentPos() const; + + void setViewportSize(const QSizeF &size); + void setContentPosRange(const QRectF &rect); + void setContentPos(const QPointF &pos); + +private: + QScrollPrepareEventPrivate *d_func(); + const QScrollPrepareEventPrivate *d_func() const; +}; + + +class QScrollEventPrivate; +class Q_GUI_EXPORT QScrollEvent : public QEvent +{ +public: + enum ScrollState + { + ScrollStarted, + ScrollUpdated, + ScrollFinished + }; + + QScrollEvent(const QPointF &contentPos, const QPointF &overshoot, ScrollState scrollState); + ~QScrollEvent(); + + QPointF contentPos() const; + QPointF overshootDistance() const; + ScrollState scrollState() const; + +private: + QScrollEventPrivate *d_func(); + const QScrollEventPrivate *d_func() const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QEVENT_H diff --git a/src/gui/guikernel/qevent_p.h b/src/gui/guikernel/qevent_p.h new file mode 100644 index 0000000000..b79f372d8d --- /dev/null +++ b/src/gui/guikernel/qevent_p.h @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENT_P_H +#define QEVENT_P_H + +#include <QtCore/qglobal.h> +#include <QtCore/qurl.h> +#include <QtGui/qevent.h> + +#ifdef Q_OS_SYMBIAN +#include <f32file.h> +#endif + +QT_BEGIN_NAMESPACE + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// ### Qt 5: remove +class QKeyEventEx : public QKeyEvent +{ +public: + QKeyEventEx(Type type, int key, Qt::KeyboardModifiers modifiers, + const QString &text, bool autorep, ushort count, + quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers); + QKeyEventEx(const QKeyEventEx &other); + + ~QKeyEventEx(); + +protected: + quint32 nScanCode; + quint32 nVirtualKey; + quint32 nModifiers; + friend class QKeyEvent; +}; + +// ### Qt 5: remove +class QMouseEventEx : public QMouseEvent +{ +public: + QMouseEventEx(Type type, const QPointF &pos, const QPoint &globalPos, + Qt::MouseButton button, Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers); + ~QMouseEventEx(); + +protected: + QPointF posF; + friend class QMouseEvent; +}; + +class QTouchEventTouchPointPrivate +{ +public: + inline QTouchEventTouchPointPrivate(int id) + : ref(1), + id(id), + state(Qt::TouchPointReleased), + pressure(qreal(-1.)) + { } + + inline QTouchEventTouchPointPrivate *detach() + { + QTouchEventTouchPointPrivate *d = new QTouchEventTouchPointPrivate(*this); + d->ref = 1; + if (!this->ref.deref()) + delete this; + return d; + } + + QAtomicInt ref; + int id; + Qt::TouchPointStates state; + QRectF rect, sceneRect, screenRect; + QPointF normalizedPos, + startPos, startScenePos, startScreenPos, startNormalizedPos, + lastPos, lastScenePos, lastScreenPos, lastNormalizedPos; + qreal pressure; +}; + +#ifndef QT_NO_GESTURES +class QNativeGestureEvent : public QEvent +{ +public: + enum Type { + None, + GestureBegin, + GestureEnd, + Pan, + Zoom, + Rotate, + Swipe + }; + + QNativeGestureEvent() + : QEvent(QEvent::NativeGesture), gestureType(None), percentage(0) +#ifdef Q_WS_WIN + , sequenceId(0), argument(0) +#endif + { + } + + Type gestureType; + float percentage; + QPoint position; + float angle; +#ifdef Q_WS_WIN + ulong sequenceId; + quint64 argument; +#endif +}; + +class QGestureEventPrivate +{ +public: + inline QGestureEventPrivate(const QList<QGesture *> &list) + : gestures(list), widget(0) + { + } + + QList<QGesture *> gestures; + QWidget *widget; + QMap<Qt::GestureType, bool> accepted; + QMap<Qt::GestureType, QWidget *> targetWidgets; +}; +#endif // QT_NO_GESTURES + +class QFileOpenEventPrivate +{ +public: + inline QFileOpenEventPrivate(const QUrl &url) + : url(url) + { + } + ~QFileOpenEventPrivate(); + + QUrl url; +#ifdef Q_OS_SYMBIAN + RFile file; +#endif +}; + + +class QScrollPrepareEventPrivate +{ +public: + inline QScrollPrepareEventPrivate() + : target(0) + { + } + + QObject* target; + QPointF startPos; + QSizeF viewportSize; + QRectF contentPosRange; + QPointF contentPos; +}; + +class QScrollEventPrivate +{ +public: + inline QScrollEventPrivate() + { + } + + QPointF contentPos; + QPointF overshoot; + QScrollEvent::ScrollState state; +}; + +QT_END_NAMESPACE + +#endif // QEVENT_P_H diff --git a/src/gui/guikernel/qeventdispatcher_glib_qpa.cpp b/src/gui/guikernel/qeventdispatcher_glib_qpa.cpp new file mode 100644 index 0000000000..c63314c889 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_glib_qpa.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeventdispatcher_glib_qpa_p.h" + +#include "qapplication.h" + +#include "qplatformdefs.h" +#include "qapplication.h" + +#include <glib.h> +#include "private/qapplication_p.h" + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +struct GUserEventSource +{ + GSource source; + QPAEventDispatcherGlib *q; +}; + +static gboolean userEventSourcePrepare(GSource *s, gint *timeout) +{ + Q_UNUSED(s) + Q_UNUSED(timeout) + + return QWindowSystemInterfacePrivate::windowSystemEventsQueued() > 0; +} + +static gboolean userEventSourceCheck(GSource *source) +{ + return userEventSourcePrepare(source, 0); +} + +static gboolean userEventSourceDispatch(GSource *s, GSourceFunc, gpointer) +{ + GUserEventSource * source = reinterpret_cast<GUserEventSource *>(s); + + QWindowSystemInterfacePrivate::WindowSystemEvent * event; + while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) { + event = QWindowSystemInterfacePrivate::getWindowSystemEvent(); + if (!event) + break; + + // send through event filter + if (source->q->filterEvent(event)) { + delete event; + continue; + } + QGuiApplicationPrivate::processWindowSystemEvent(event); + delete event; + } + + return true; +} + + +static GSourceFuncs userEventSourceFuncs = { + userEventSourcePrepare, + userEventSourceCheck, + userEventSourceDispatch, + NULL, + NULL, + NULL +}; + +QPAEventDispatcherGlibPrivate::QPAEventDispatcherGlibPrivate(GMainContext *context) + : QEventDispatcherGlibPrivate(context) +{ + userEventSource = reinterpret_cast<GUserEventSource *>(g_source_new(&userEventSourceFuncs, + sizeof(GUserEventSource))); + userEventSource->q = 0; + g_source_set_can_recurse(&userEventSource->source, true); + g_source_attach(&userEventSource->source, mainContext); +} + + +QPAEventDispatcherGlib::QPAEventDispatcherGlib(QObject *parent) + : QEventDispatcherGlib(*new QPAEventDispatcherGlibPrivate, parent) +{ + Q_D(QPAEventDispatcherGlib); + d->userEventSource->q = this; +} + +QPAEventDispatcherGlib::~QPAEventDispatcherGlib() +{ + Q_D(QPAEventDispatcherGlib); + + g_source_destroy(&d->userEventSource->source); + g_source_unref(&d->userEventSource->source); + d->userEventSource = 0; +} + +bool QPAEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + static bool init = false; + if (!init) { + if (QGuiApplicationPrivate::platformIntegration()->createEventLoopIntegration()) { + qWarning("Eventloop integration is not supported by the glib event dispatcher"); + qWarning("Use the UNIX event dispatcher by defining environment variable QT_NO_GLIB=1"); + } + init = true; + } + return QEventDispatcherGlib::processEvents(flags); +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qeventdispatcher_glib_qpa_p.h b/src/gui/guikernel/qeventdispatcher_glib_qpa_p.h new file mode 100644 index 0000000000..701f6735c4 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_glib_qpa_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_GLIB_QPA_P_H +#define QEVENTDISPATCHER_GLIB_QPA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qeventdispatcher_glib_p.h> + +typedef struct _GMainContext GMainContext; + +QT_BEGIN_NAMESPACE +class QPAEventDispatcherGlibPrivate; + +class QPAEventDispatcherGlib : public QEventDispatcherGlib +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPAEventDispatcherGlib) + +public: + explicit QPAEventDispatcherGlib(QObject *parent = 0); + ~QPAEventDispatcherGlib(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); +}; + +struct GUserEventSource; + +class QPAEventDispatcherGlibPrivate : public QEventDispatcherGlibPrivate +{ + Q_DECLARE_PUBLIC(QPAEventDispatcherGlib) +public: + QPAEventDispatcherGlibPrivate(GMainContext *context = 0); + GUserEventSource *userEventSource; +}; + + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_GLIB_QPA_P_H diff --git a/src/gui/guikernel/qeventdispatcher_mac.mm b/src/gui/guikernel/qeventdispatcher_mac.mm new file mode 100644 index 0000000000..677a7368b4 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_mac.mm @@ -0,0 +1,1200 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (c) 2007-2008, Apple, Inc. +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** +** * Redistributions of source code must retain the above copyright notice, +** this list of conditions and the following disclaimer. +** +** * Redistributions in binary form must reproduce the above copyright notice, +** this list of conditions and the following disclaimer in the documentation +** and/or other materials provided with the distribution. +** +** * Neither the name of Apple, Inc. nor the names of its contributors +** may be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "private/qt_mac_p.h" +#include "qeventdispatcher_mac_p.h" +#include "qapplication.h" +#include "qevent.h" +#include "qdialog.h" +#include "qhash.h" +#include "qsocketnotifier.h" +#include "private/qwidget_p.h" +#include "private/qthread_p.h" +#include "private/qapplication_p.h" + +#include <private/qcocoaapplication_mac_p.h> +#include "private/qt_cocoa_helpers_mac_p.h" + +#ifndef QT_NO_THREAD +# include "qmutex.h" +#endif + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +/***************************************************************************** + Externals + *****************************************************************************/ +extern void qt_event_request_timer(MacTimerInfo *); //qapplication_mac.cpp +extern MacTimerInfo *qt_event_get_timer(EventRef); //qapplication_mac.cpp +extern void qt_event_request_select(QEventDispatcherMac *); //qapplication_mac.cpp +extern void qt_event_request_updates(); //qapplication_mac.cpp +extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp +extern bool qt_is_gui_used; //qapplication.cpp +extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp +extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp + +static inline CFRunLoopRef mainRunLoop() +{ +#ifndef QT_MAC_USE_COCOA + return reinterpret_cast<CFRunLoopRef>(const_cast<void *>(GetCFRunLoopFromEventLoop(GetMainEventLoop()))); +#else + return CFRunLoopGetMain(); +#endif +} + +/***************************************************************************** + Timers stuff + *****************************************************************************/ + +/* timer call back */ +void QEventDispatcherMacPrivate::activateTimer(CFRunLoopTimerRef, void *info) +{ + int timerID = +#ifdef Q_OS_MAC64 + qint64(info); +#else + int(info); +#endif + + MacTimerInfo *tmr; + tmr = macTimerHash.value(timerID); + if (tmr == 0 || tmr->pending == true) + return; // Can't send another timer event if it's pending. + + + if (blockSendPostedEvents) { + QCoreApplication::postEvent(tmr->obj, new QTimerEvent(tmr->id)); + } else { + tmr->pending = true; + QTimerEvent e(tmr->id); + qt_sendSpontaneousEvent(tmr->obj, &e); + // Get the value again in case the timer gets unregistered during the sendEvent. + tmr = macTimerHash.value(timerID); + if (tmr != 0) + tmr->pending = false; + } + +} + +void QEventDispatcherMac::registerTimer(int timerId, int interval, QObject *obj) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1 || interval < 0 || !obj) { + qWarning("QEventDispatcherMac::registerTimer: invalid arguments"); + return; + } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::startTimer: timers cannot be started from another thread"); + return; + } +#endif + + MacTimerInfo *t = new MacTimerInfo(); + t->id = timerId; + t->interval = interval; + t->obj = obj; + t->runLoopTimer = 0; + t->pending = false; + + CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent(); + CFTimeInterval cfinterval = qMax(CFTimeInterval(interval) / 1000, 0.0000001); + fireDate += cfinterval; + QEventDispatcherMacPrivate::macTimerHash.insert(timerId, t); + CFRunLoopTimerContext info = { 0, (void *)timerId, 0, 0, 0 }; + t->runLoopTimer = CFRunLoopTimerCreate(0, fireDate, cfinterval, 0, 0, + QEventDispatcherMacPrivate::activateTimer, &info); + if (t->runLoopTimer == 0) { + qFatal("QEventDispatcherMac::registerTimer: Cannot create timer"); + } + CFRunLoopAddTimer(mainRunLoop(), t->runLoopTimer, kCFRunLoopCommonModes); +} + +bool QEventDispatcherMac::unregisterTimer(int identifier) +{ +#ifndef QT_NO_DEBUG + if (identifier < 1) { + qWarning("QEventDispatcherMac::unregisterTimer: invalid argument"); + return false; + } else if (thread() != QThread::currentThread()) { + qWarning("QObject::killTimer: timers cannot be stopped from another thread"); + return false; + } +#endif + if (identifier <= 0) + return false; // not init'd or invalid timer + + MacTimerInfo *timerInfo = QEventDispatcherMacPrivate::macTimerHash.take(identifier); + if (timerInfo == 0) + return false; + + if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent) + QAbstractEventDispatcherPrivate::releaseTimerId(identifier); + CFRunLoopTimerInvalidate(timerInfo->runLoopTimer); + CFRelease(timerInfo->runLoopTimer); + delete timerInfo; + + return true; +} + +bool QEventDispatcherMac::unregisterTimers(QObject *obj) +{ +#ifndef QT_NO_DEBUG + if (!obj) { + qWarning("QEventDispatcherMac::unregisterTimers: invalid argument"); + return false; + } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::killTimers: timers cannot be stopped from another thread"); + return false; + } +#endif + + MacTimerHash::iterator it = QEventDispatcherMacPrivate::macTimerHash.begin(); + while (it != QEventDispatcherMacPrivate::macTimerHash.end()) { + MacTimerInfo *timerInfo = it.value(); + if (timerInfo->obj != obj) { + ++it; + } else { + if (!QObjectPrivate::get(timerInfo->obj)->inThreadChangeEvent) + QAbstractEventDispatcherPrivate::releaseTimerId(timerInfo->id); + CFRunLoopTimerInvalidate(timerInfo->runLoopTimer); + CFRelease(timerInfo->runLoopTimer); + delete timerInfo; + it = QEventDispatcherMacPrivate::macTimerHash.erase(it); + } + } + return true; +} + +QList<QEventDispatcherMac::TimerInfo> +QEventDispatcherMac::registeredTimers(QObject *object) const +{ + if (!object) { + qWarning("QEventDispatcherMac:registeredTimers: invalid argument"); + return QList<TimerInfo>(); + } + + QList<TimerInfo> list; + + MacTimerHash::const_iterator it = QEventDispatcherMacPrivate::macTimerHash.constBegin(); + while (it != QEventDispatcherMacPrivate::macTimerHash.constEnd()) { + MacTimerInfo *t = it.value(); + if (t->obj == object) + list << TimerInfo(t->id, t->interval); + ++it; + } + return list; +} + +/************************************************************************** + Socket Notifiers + *************************************************************************/ +void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef, + const void *, void *info) { + QEventDispatcherMacPrivate *const eventDispatcher + = static_cast<QEventDispatcherMacPrivate *>(info); + int nativeSocket = CFSocketGetNative(s); + MacSocketInfo *socketInfo = eventDispatcher->macSockets.value(nativeSocket); + QEvent notifierEvent(QEvent::SockAct); + + // There is a race condition that happen where we disable the notifier and + // the kernel still has a notification to pass on. We then get this + // notification after we've successfully disabled the CFSocket, but our Qt + // notifier is now gone. The upshot is we have to check the notifier + // everytime. + if (callbackType == kCFSocketReadCallBack) { + if (socketInfo->readNotifier) + QApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent); + } else if (callbackType == kCFSocketWriteCallBack) { + if (socketInfo->writeNotifier) + QApplication::sendEvent(socketInfo->writeNotifier, ¬ifierEvent); + } +} + +/* + Adds a loop source for the given socket to the current run loop. +*/ +CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket) +{ + CFRunLoopSourceRef loopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0); + if (!loopSource) + return 0; + + CFRunLoopAddSource(mainRunLoop(), loopSource, kCFRunLoopCommonModes); + return loopSource; +} + +/* + Removes the loop source for the given socket from the current run loop. +*/ +void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop) +{ + Q_ASSERT(runloop); + CFRunLoopRemoveSource(mainRunLoop(), runloop, kCFRunLoopCommonModes); + CFSocketDisableCallBacks(socket, kCFSocketReadCallBack); + CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack); + CFRunLoopSourceInvalidate(runloop); +} + +/* + Register a QSocketNotifier with the mac event system by creating a CFSocket with + with a read/write callback. + + Qt has separate socket notifiers for reading and writing, but on the mac there is + a limitation of one CFSocket object for each native socket. +*/ +void QEventDispatcherMac::registerSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int nativeSocket = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() + || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherMac); + + if (type == QSocketNotifier::Exception) { + qWarning("QSocketNotifier::Exception is not supported on Mac OS X"); + return; + } + + // Check if we have a CFSocket for the native socket, create one if not. + MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket); + if (!socketInfo) { + socketInfo = new MacSocketInfo(); + + // Create CFSocket, specify that we want both read and write callbacks (the callbacks + // are enabled/disabled later on). + const int callbackTypes = kCFSocketReadCallBack | kCFSocketWriteCallBack; + CFSocketContext context = {0, d, 0, 0, 0}; + socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context); + if (CFSocketIsValid(socketInfo->socket) == false) { + qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket"); + return; + } + + CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket); + flags |= kCFSocketAutomaticallyReenableWriteCallBack; //QSocketNotifier stays enabled after a write + flags &= ~kCFSocketCloseOnInvalidate; //QSocketNotifier doesn't close the socket upon destruction/invalidation + CFSocketSetSocketFlags(socketInfo->socket, flags); + + // Add CFSocket to runloop. + if(!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) { + qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop"); + CFSocketInvalidate(socketInfo->socket); + CFRelease(socketInfo->socket); + return; + } + + // Disable both callback types by default. This must be done after + // we add the CFSocket to the runloop, or else these calls will have + // no effect. + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); + + d->macSockets.insert(nativeSocket, socketInfo); + } + + // Increment read/write counters and select enable callbacks if necessary. + if (type == QSocketNotifier::Read) { + Q_ASSERT(socketInfo->readNotifier == 0); + socketInfo->readNotifier = notifier; + CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack); + } else if (type == QSocketNotifier::Write) { + Q_ASSERT(socketInfo->writeNotifier == 0); + socketInfo->writeNotifier = notifier; + CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); + } +} + +/* + Unregister QSocketNotifer. The CFSocket correspoding to this notifier is + removed from the runloop of this is the last notifier that users + that CFSocket. +*/ +void QEventDispatcherMac::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int nativeSocket = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherMac); + + if (type == QSocketNotifier::Exception) { + qWarning("QSocketNotifier::Exception is not supported on Mac OS X"); + return; + } + MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket); + if (!socketInfo) { + qWarning("QEventDispatcherMac::unregisterSocketNotifier: Tried to unregister a not registered notifier"); + return; + } + + // Decrement read/write counters and disable callbacks if necessary. + if (type == QSocketNotifier::Read) { + Q_ASSERT(notifier == socketInfo->readNotifier); + socketInfo->readNotifier = 0; + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); + } else if (type == QSocketNotifier::Write) { + Q_ASSERT(notifier == socketInfo->writeNotifier); + socketInfo->writeNotifier = 0; + CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); + } + + // Remove CFSocket from runloop if this was the last QSocketNotifier. + if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) { + if (CFSocketIsValid(socketInfo->socket)) + qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop); + CFRunLoopSourceInvalidate(socketInfo->runloop); + CFRelease(socketInfo->runloop); + CFSocketInvalidate(socketInfo->socket); + CFRelease(socketInfo->socket); + delete socketInfo; + d->macSockets.remove(nativeSocket); + } +} + +bool QEventDispatcherMac::hasPendingEvents() +{ + extern uint qGlobalPostedEventsCount(); + return qGlobalPostedEventsCount() || (qt_is_gui_used && GetNumEventsInQueue(GetMainEventQueue())); +} + + +static bool qt_mac_send_event(QEventLoop::ProcessEventsFlags, OSEventRef event, OSWindowRef pt) +{ +#ifndef QT_MAC_USE_COCOA + if(pt && SendEventToWindow(event, pt) != eventNotHandledErr) + return true; + return !SendEventToEventTarget(event, GetEventDispatcherTarget()); +#else // QT_MAC_USE_COCOA + if (pt) + [pt sendEvent:event]; + else + [NSApp sendEvent:event]; + return true; +#endif +} + +#ifdef QT_MAC_USE_COCOA +static bool IsMouseOrKeyEvent( NSEvent* event ) +{ + bool result = false; + + switch( [event type] ) + { + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSRightMouseDown: + case NSRightMouseUp: + case NSMouseMoved: // ?? + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSMouseEntered: + case NSMouseExited: + case NSKeyDown: + case NSKeyUp: + case NSFlagsChanged: // key modifiers changed? + case NSCursorUpdate: // ?? + case NSScrollWheel: + case NSTabletPoint: + case NSTabletProximity: + case NSOtherMouseDown: + case NSOtherMouseUp: + case NSOtherMouseDragged: +#ifndef QT_NO_GESTURES +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + case NSEventTypeGesture: // touch events + case NSEventTypeMagnify: + case NSEventTypeSwipe: + case NSEventTypeRotate: + case NSEventTypeBeginGesture: + case NSEventTypeEndGesture: +#endif +#endif // QT_NO_GESTURES + result = true; + break; + + default: + break; + } + return result; +} +#endif + +static inline void qt_mac_waitForMoreEvents() +{ +#ifndef QT_MAC_USE_COCOA + while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, true) == kCFRunLoopRunTimedOut) ; +#else + // If no event exist in the cocoa event que, wait + // (and free up cpu time) until at least one event occur. + // This implementation is a bit on the edge, but seems to + // work fine: + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) + [NSApp postEvent:event atStart:YES]; +#endif +} + +#ifdef QT_MAC_USE_COCOA +static inline void qt_mac_waitForMoreModalSessionEvents() +{ + // If no event exist in the cocoa event que, wait + // (and free up cpu time) until at least one event occur. + // This implementation is a bit on the edge, but seems to + // work fine: + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSModalPanelRunLoopMode + dequeue:YES]; + if (event) + [NSApp postEvent:event atStart:YES]; +} +#endif + +bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QEventDispatcherMac); + d->interrupt = false; + +#ifdef QT_MAC_USE_COCOA + bool interruptLater = false; + QtMacInterruptDispatcherHelp::cancelInterruptLater(); +#endif + + // In case we end up recursing while we now process events, make sure + // that we send remaining posted Qt events before this call returns: + wakeUp(); + emit awake(); + + bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; + bool retVal = false; + forever { + if (d->interrupt) + break; + +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + NSEvent* event = 0; + + // First, send all previously excluded input events, if any: + if (!excludeUserEvents) { + while (!d->queuedUserInputEvents.isEmpty()) { + event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); + if (!filterEvent(event)) { + qt_mac_send_event(flags, event, 0); + retVal = true; + } + [event release]; + } + } + + // If Qt is used as a plugin, or as an extension in a native cocoa + // application, we should not run or stop NSApplication; This will be + // done from the application itself. And if processEvents is called + // manually (rather than from a QEventLoop), we cannot enter a tight + // loop and block this call, but instead we need to return after one flush. + // Finally, if we are to exclude user input events, we cannot call [NSApp run] + // as we then loose control over which events gets dispatched: + const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning]; + const bool canExec_Qt = !excludeUserEvents && + (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) ; + + if (canExec_Qt && canExec_3rdParty) { + // We can use exec-mode, meaning that we can stay in a tight loop until + // interrupted. This is mostly an optimization, but it allow us to use + // [NSApp run], which is the normal code path for cocoa applications. + if (NSModalSession session = d->currentModalSession()) { + QBoolBlocker execGuard(d->currentExecIsNSAppRun, false); + while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt) + qt_mac_waitForMoreModalSessionEvents(); + + if (!d->interrupt && session == d->currentModalSessionCached) { + // Someone called [NSApp stopModal:] from outside the event + // dispatcher (e.g to stop a native dialog). But that call wrongly stopped + // 'session' as well. As a result, we need to restart all internal sessions: + d->temporarilyStopAllModalSessions(); + } + } else { + d->nsAppRunCalledByQt = true; + QBoolBlocker execGuard(d->currentExecIsNSAppRun, true); + [NSApp run]; + } + retVal = true; + } else { + // We cannot block the thread (and run in a tight loop). + // Instead we will process all current pending events and return. + d->ensureNSAppInitialized(); + if (NSModalSession session = d->currentModalSession()) { + // INVARIANT: a modal window is executing. + if (!excludeUserEvents) { + // Since we can dispatch all kinds of events, we choose + // to use cocoa's native way of running modal sessions: + if (flags & QEventLoop::WaitForMoreEvents) + qt_mac_waitForMoreModalSessionEvents(); + NSInteger status = [NSApp runModalSession:session]; + if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { + // INVARIANT: Someone called [NSApp stopModal:] from outside the event + // dispatcher (e.g to stop a native dialog). But that call wrongly stopped + // 'session' as well. As a result, we need to restart all internal sessions: + d->temporarilyStopAllModalSessions(); + } + retVal = true; + } else do { + // Dispatch all non-user events (but que non-user events up for later). In + // this case, we need more control over which events gets dispatched, and + // cannot use [NSApp runModalSession:session]: + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSModalPanelRunLoopMode + dequeue: YES]; + + if (event) { + if (IsMouseOrKeyEvent(event)) { + [event retain]; + d->queuedUserInputEvents.append(event); + continue; + } + if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) + retVal = true; + } + } while (!d->interrupt && event != nil); + } else do { + // INVARIANT: No modal window is executing. + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSDefaultRunLoopMode + dequeue: YES]; + + if (event) { + if (flags & QEventLoop::ExcludeUserInputEvents) { + if (IsMouseOrKeyEvent(event)) { + [event retain]; + d->queuedUserInputEvents.append(event); + continue; + } + } + if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) + retVal = true; + } + } while (!d->interrupt && event != nil); + + // Be sure to flush the Qt posted events when not using exec mode + // (exec mode will always do this call from the event loop source): + if (!d->interrupt) + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + + // Since the window that holds modality might have changed while processing + // events, we we need to interrupt when we return back the previous process + // event recursion to ensure that we spin the correct modal session. + // We do the interruptLater at the end of the function to ensure that we don't + // disturb the 'wait for more events' below (as deleteLater will post an event): + interruptLater = true; + } +#else + do { + EventRef event; + if (!(flags & QEventLoop::ExcludeUserInputEvents) + && !d->queuedUserInputEvents.isEmpty()) { + // process a pending user input event + event = static_cast<EventRef>(d->queuedUserInputEvents.takeFirst()); + } else { + OSStatus err = ReceiveNextEvent(0,0, kEventDurationNoWait, true, &event); + if(err != noErr) + continue; + // else + if (flags & QEventLoop::ExcludeUserInputEvents) { + UInt32 ekind = GetEventKind(event), + eclass = GetEventClass(event); + switch(eclass) { + case kEventClassQt: + if(ekind != kEventQtRequestContext) + break; + // fall through + case kEventClassMouse: + case kEventClassKeyboard: + d->queuedUserInputEvents.append(event); + continue; + } + } + } + + if (!filterEvent(&event) && qt_mac_send_event(flags, event, 0)) + retVal = true; + ReleaseEvent(event); + } while(!d->interrupt && GetNumEventsInQueue(GetMainEventQueue()) > 0); + +#endif + + bool canWait = (d->threadData->canWait + && !retVal + && !d->interrupt + && (flags & QEventLoop::WaitForMoreEvents)); + if (canWait) { + // INVARIANT: We haven't processed any events yet. And we're told + // to stay inside this function until at least one event is processed. + qt_mac_waitForMoreEvents(); + flags &= ~QEventLoop::WaitForMoreEvents; + } else { + // Done with event processing for now. + // Leave the function: + break; + } + } + + // If we're interrupted, we need to interrupt the _current_ + // recursion as well to check if it is still supposed to be + // executing. This way we wind down the stack until we land + // on a recursion that again calls processEvents (typically + // from QEventLoop), and set interrupt to false: + if (d->interrupt) + interrupt(); + +#ifdef QT_MAC_USE_COCOA + if (interruptLater) + QtMacInterruptDispatcherHelp::interruptLater(); +#endif + + return retVal; +} + +void QEventDispatcherMac::wakeUp() +{ + Q_D(QEventDispatcherMac); + d->serialNumber.ref(); + CFRunLoopSourceSignal(d->postedEventsSource); + CFRunLoopWakeUp(mainRunLoop()); +} + +void QEventDispatcherMac::flush() +{ + if(qApp) { + QWidgetList tlws = QApplication::topLevelWidgets(); + for(int i = 0; i < tlws.size(); i++) { + QWidget *tlw = tlws.at(i); + if(tlw->isVisible()) + macWindowFlush(qt_mac_window_for(tlw)); + } + } +} + +/***************************************************************************** + QEventDispatcherMac Implementation + *****************************************************************************/ +MacTimerHash QEventDispatcherMacPrivate::macTimerHash; +bool QEventDispatcherMacPrivate::blockSendPostedEvents = false; +bool QEventDispatcherMacPrivate::interrupt = false; + +#ifdef QT_MAC_USE_COCOA +QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack; +bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false; +bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false; +bool QEventDispatcherMacPrivate::cleanupModalSessionsNeeded = false; +NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0; + +void QEventDispatcherMacPrivate::ensureNSAppInitialized() +{ + // Some elements in Cocoa require NSApplication to be running before + // they get fully initialized, in particular the menu bar. This + // function is intended for cases where a dialog is told to execute before + // QApplication::exec is called, or the application spins the events loop + // manually rather than calling QApplication:exec. + // The function makes sure that NSApplication starts running, but stops + // it again as soon as the send posted events callback is called. That way + // we let Cocoa finish the initialization it seems to need. We'll only + // apply this trick at most once for any application, and we avoid doing it + // for the common case where main just starts QApplication::exec. + if (nsAppRunCalledByQt || [NSApp isRunning]) + return; + nsAppRunCalledByQt = true; + QBoolBlocker block1(interrupt, true); + QBoolBlocker block2(currentExecIsNSAppRun, true); + [NSApp run]; +} + +void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions() +{ + // Flush, and Stop, all created modal session, and as + // such, make them pending again. The next call to + // currentModalSession will recreate them again. The + // reason to stop all session like this is that otherwise + // a call [NSApp stop] would not stop NSApp, but rather + // the current modal session. So if we need to stop NSApp + // we need to stop all the modal session first. To avoid changing + // the stacking order of the windows while doing so, we put + // up a block that is used in QCocoaWindow and QCocoaPanel: + int stackSize = cocoaModalSessionStack.size(); + for (int i=0; i<stackSize; ++i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.session) { + [NSApp endModalSession:info.session]; + info.session = 0; + } + } + currentModalSessionCached = 0; +} + +NSModalSession QEventDispatcherMacPrivate::currentModalSession() +{ + // If we have one or more modal windows, this function will create + // a session for each of those, and return the one for the top. + if (currentModalSessionCached) + return currentModalSessionCached; + + if (cocoaModalSessionStack.isEmpty()) + return 0; + + int sessionCount = cocoaModalSessionStack.size(); + for (int i=0; i<sessionCount; ++i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (!info.widget) + continue; + if (info.widget->testAttribute(Qt::WA_DontShowOnScreen)) + continue; + if (!info.session) { + QMacCocoaAutoReleasePool pool; + NSWindow *window = qt_mac_window_for(info.widget); + if (!window) + continue; + + ensureNSAppInitialized(); + QBoolBlocker block1(blockSendPostedEvents, true); + info.nswindow = window; + [(NSWindow*) info.nswindow retain]; + int levelBeforeEnterModal = [window level]; + info.session = [NSApp beginModalSessionForWindow:window]; + // Make sure we don't stack the window lower that it was before + // entering modal, in case it e.g. had the stays-on-top flag set: + if (levelBeforeEnterModal > [window level]) + [window setLevel:levelBeforeEnterModal]; + } + currentModalSessionCached = info.session; + cleanupModalSessionsNeeded = false; + } + return currentModalSessionCached; +} + +static void setChildrenWorksWhenModal(QWidget *widget, bool worksWhenModal) +{ + // For NSPanels (but not NSWindows, sadly), we can set the flag + // worksWhenModal, so that they are active even when they are not modal. + QList<QDialog *> dialogs = widget->findChildren<QDialog *>(); + for (int i=0; i<dialogs.size(); ++i){ + NSWindow *window = qt_mac_window_for(dialogs[i]); + if (window && [window isKindOfClass:[NSPanel class]]) { + [static_cast<NSPanel *>(window) setWorksWhenModal:worksWhenModal]; + if (worksWhenModal && [window isVisible]){ + [window orderFront:window]; + } + } + } +} + +void QEventDispatcherMacPrivate::updateChildrenWorksWhenModal() +{ + // Make the dialog children of the widget + // active. And make the dialog children of + // the previous modal dialog unactive again: + QMacCocoaAutoReleasePool pool; + int size = cocoaModalSessionStack.size(); + if (size > 0){ + if (QWidget *prevModal = cocoaModalSessionStack[size-1].widget) + setChildrenWorksWhenModal(prevModal, true); + if (size > 1){ + if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget) + setChildrenWorksWhenModal(prevModal, false); + } + } +} + +void QEventDispatcherMacPrivate::cleanupModalSessions() +{ + // Go through the list of modal sessions, and end those + // that no longer has a widget assosiated; no widget means + // the the session has logically ended. The reason we wait like + // this to actually end the sessions for real (rather than at the + // point they were marked as stopped), is that ending a session + // when no other session runs below it on the stack will make cocoa + // drop some events on the floor. + QMacCocoaAutoReleasePool pool; + int stackSize = cocoaModalSessionStack.size(); + + for (int i=stackSize-1; i>=0; --i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.widget) { + // This session has a widget, and is therefore not marked + // as stopped. So just make it current. There might still be other + // stopped sessions on the stack, but those will be stopped on + // a later "cleanup" call. + currentModalSessionCached = info.session; + break; + } + cocoaModalSessionStack.remove(i); + currentModalSessionCached = 0; + if (info.session) { + [NSApp endModalSession:info.session]; + [(NSWindow *)info.nswindow release]; + } + } + + updateChildrenWorksWhenModal(); + cleanupModalSessionsNeeded = false; +} + +void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget) +{ + // Add a new, empty (null), NSModalSession to the stack. + // It will become active the next time QEventDispatcher::processEvents is called. + // A QCocoaModalSessionInfo is considered pending to become active if the widget pointer + // is non-zero, and the session pointer is zero (it will become active upon a call to + // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if + // the widget pointer is zero, and the session pointer is non-zero (it will be fully + // stopped in cleanupModalSessions()). + QCocoaModalSessionInfo info = {widget, 0, 0}; + cocoaModalSessionStack.push(info); + updateChildrenWorksWhenModal(); + currentModalSessionCached = 0; +} + +void QEventDispatcherMacPrivate::endModalSession(QWidget *widget) +{ + // Mark all sessions attached to widget as pending to be stopped. We do this + // by setting the widget pointer to zero, but leave the session pointer. + // We don't tell cocoa to stop any sessions just yet, because cocoa only understands + // when we stop the _current_ modal session (which is the session on top of + // the stack, and might not belong to 'widget'). + int stackSize = cocoaModalSessionStack.size(); + for (int i=stackSize-1; i>=0; --i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.widget == widget) { + info.widget = 0; + if (i == stackSize-1) { + // The top sessions ended. Interrupt the event dispatcher + // to start spinning the correct session immidiatly: + currentModalSessionCached = 0; + cleanupModalSessionsNeeded = true; + QEventDispatcherMac::instance()->interrupt(); + } + } + } +} + +#endif + +QEventDispatcherMacPrivate::QEventDispatcherMacPrivate() +{ +} + +QEventDispatcherMac::QEventDispatcherMac(QObject *parent) + : QAbstractEventDispatcher(*new QEventDispatcherMacPrivate, parent) +{ + Q_D(QEventDispatcherMac); + CFRunLoopSourceContext context; + bzero(&context, sizeof(CFRunLoopSourceContext)); + context.info = d; + context.equal = QEventDispatcherMacPrivate::postedEventSourceEqualCallback; + context.perform = QEventDispatcherMacPrivate::postedEventsSourcePerformCallback; + d->postedEventsSource = CFRunLoopSourceCreate(0, 0, &context); + Q_ASSERT(d->postedEventsSource); + CFRunLoopAddSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes); + + CFRunLoopObserverContext observerContext; + bzero(&observerContext, sizeof(CFRunLoopObserverContext)); + observerContext.info = this; + d->waitingObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, + kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting, + true, 0, + QEventDispatcherMacPrivate::waitingObserverCallback, + &observerContext); + CFRunLoopAddObserver(mainRunLoop(), d->waitingObserver, kCFRunLoopCommonModes); + + /* The first cycle in the loop adds the source and the events of the source + are not processed. + We use an observer to process the posted events for the first + execution of the loop. */ + CFRunLoopObserverContext firstTimeObserverContext; + bzero(&firstTimeObserverContext, sizeof(CFRunLoopObserverContext)); + firstTimeObserverContext.info = d; + d->firstTimeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, + kCFRunLoopEntry, + /* repeats = */ false, + 0, + QEventDispatcherMacPrivate::firstLoopEntry, + &firstTimeObserverContext); + CFRunLoopAddObserver(mainRunLoop(), d->firstTimeObserver, kCFRunLoopCommonModes); +} + +void QEventDispatcherMacPrivate::waitingObserverCallback(CFRunLoopObserverRef, + CFRunLoopActivity activity, void *info) +{ + if (activity == kCFRunLoopBeforeWaiting) + emit static_cast<QEventDispatcherMac*>(info)->aboutToBlock(); + else + emit static_cast<QEventDispatcherMac*>(info)->awake(); +} + +Boolean QEventDispatcherMacPrivate::postedEventSourceEqualCallback(const void *info1, const void *info2) +{ + return info1 == info2; +} + +inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents) +{ + if (blockSendPostedEvents) { + // We're told to not send posted events (because the event dispatcher + // is currently working on setting up the correct session to run). But + // we still need to make sure that we don't fall asleep until pending events + // are sendt, so we just signal this need, and return: + CFRunLoopSourceSignal(d->postedEventsSource); + return; + } + +#ifdef QT_MAC_USE_COCOA + if (d->cleanupModalSessionsNeeded) + d->cleanupModalSessions(); +#endif + + if (d->interrupt) { +#ifdef QT_MAC_USE_COCOA + if (d->currentExecIsNSAppRun) { + // The event dispatcher has been interrupted. But since + // [NSApplication run] is running the event loop, we + // delayed stopping it until now (to let cocoa process + // pending cocoa events first). + if (d->currentModalSessionCached) + d->temporarilyStopAllModalSessions(); + [NSApp stop:NSApp]; + d->cancelWaitForMoreEvents(); + } +#endif + return; + } + + if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) { + d->lastSerial = d->serialNumber; + QApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + } +} + +void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref, + CFRunLoopActivity activity, + void *info) +{ + Q_UNUSED(ref); + Q_UNUSED(activity); +#ifdef QT_MAC_USE_COCOA + QApplicationPrivate::qt_initAfterNSAppStarted(); +#endif + processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); +} + +void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info) +{ + processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); +} + +#ifdef QT_MAC_USE_COCOA +void QEventDispatcherMacPrivate::cancelWaitForMoreEvents() +{ + // In case the event dispatcher is waiting for more + // events somewhere, we post a dummy event to wake it up: + QMacCocoaAutoReleasePool pool; + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint + modifierFlags:0 timestamp:0. windowNumber:0 context:0 + subtype:QtCocoaEventSubTypeWakeup data1:0 data2:0] atStart:NO]; +} +#endif + +void QEventDispatcherMac::interrupt() +{ + Q_D(QEventDispatcherMac); + d->interrupt = true; + wakeUp(); + +#ifndef QT_MAC_USE_COCOA + CFRunLoopStop(mainRunLoop()); +#else + // We do nothing more here than setting d->interrupt = true, and + // poke the event loop if it is sleeping. Actually stopping + // NSApp, or the current modal session, is done inside the send + // posted events callback. We do this to ensure that all current pending + // cocoa events gets delivered before we stop. Otherwise, if we now stop + // the last event loop recursion, cocoa will just drop pending posted + // events on the floor before we get a chance to reestablish a new session. + d->cancelWaitForMoreEvents(); +#endif +} + +QEventDispatcherMac::~QEventDispatcherMac() +{ + Q_D(QEventDispatcherMac); + //timer cleanup + MacTimerHash::iterator it = QEventDispatcherMacPrivate::macTimerHash.begin(); + while (it != QEventDispatcherMacPrivate::macTimerHash.end()) { + MacTimerInfo *t = it.value(); + if (t->runLoopTimer) { + CFRunLoopTimerInvalidate(t->runLoopTimer); + CFRelease(t->runLoopTimer); + } + delete t; + ++it; + } + QEventDispatcherMacPrivate::macTimerHash.clear(); + + // Remove CFSockets from the runloop. + for (MacSocketHash::ConstIterator it = d->macSockets.constBegin(); it != d->macSockets.constEnd(); ++it) { + MacSocketInfo *socketInfo = (*it); + if (CFSocketIsValid(socketInfo->socket)) { + qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop); + CFRunLoopSourceInvalidate(socketInfo->runloop); + CFRelease(socketInfo->runloop); + CFSocketInvalidate(socketInfo->socket); + CFRelease(socketInfo->socket); + } + } + CFRunLoopRemoveSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes); + CFRelease(d->postedEventsSource); + + CFRunLoopObserverInvalidate(d->waitingObserver); + CFRelease(d->waitingObserver); + + CFRunLoopObserverInvalidate(d->firstTimeObserver); + CFRelease(d->firstTimeObserver); +} + +#ifdef QT_MAC_USE_COCOA + +QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0; + +QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false) +{ + // The whole point of this class is that we enable a way to interrupt + // the event dispatcher when returning back to a lower recursion level + // than where interruptLater was called. This is needed to detect if + // [NSApp run] should still be running at the recursion level it is at. + // Since the interrupt is canceled if processEvents is called before + // this object gets deleted, we also avoid interrupting unnecessary. + deleteLater(); +} + +QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp() +{ + if (cancelled) + return; + instance = 0; + QEventDispatcherMac::instance()->interrupt(); +} + +void QtMacInterruptDispatcherHelp::cancelInterruptLater() +{ + if (!instance) + return; + instance->cancelled = true; + delete instance; + instance = 0; +} + +void QtMacInterruptDispatcherHelp::interruptLater() +{ + cancelInterruptLater(); + instance = new QtMacInterruptDispatcherHelp; +} + +#endif + +QT_END_NAMESPACE + diff --git a/src/gui/guikernel/qeventdispatcher_mac_p.h b/src/gui/guikernel/qeventdispatcher_mac_p.h new file mode 100644 index 0000000000..12fcafbb01 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_mac_p.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (c) 2007-2008, Apple, Inc. +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** +** * Redistributions of source code must retain the above copyright notice, +** this list of conditions and the following disclaimer. +** +** * Redistributions in binary form must reproduce the above copyright notice, +** this list of conditions and the following disclaimer in the documentation +** and/or other materials provided with the distribution. +** +** * Neither the name of Apple, Inc. nor the names of its contributors +** may be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_MAC_P_H +#define QEVENTDISPATCHER_MAC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qwindowdefs.h> +#include <QtCore/qhash.h> +#include <QtCore/qstack.h> +#include "private/qabstracteventdispatcher_p.h" +#include "private/qt_mac_p.h" + +QT_BEGIN_NAMESPACE + +#ifdef QT_MAC_USE_COCOA +typedef struct _NSModalSession *NSModalSession; +typedef struct _QCocoaModalSessionInfo { + QPointer<QWidget> widget; + NSModalSession session; + void *nswindow; +} QCocoaModalSessionInfo; +#endif + +class QEventDispatcherMacPrivate; + +class QEventDispatcherMac : public QAbstractEventDispatcher +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventDispatcherMac) + +public: + explicit QEventDispatcherMac(QObject *parent = 0); + ~QEventDispatcherMac(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void registerSocketNotifier(QSocketNotifier *notifier); + void unregisterSocketNotifier(QSocketNotifier *notifier); + + void registerTimer(int timerId, int interval, QObject *object); + bool unregisterTimer(int timerId); + bool unregisterTimers(QObject *object); + QList<TimerInfo> registeredTimers(QObject *object) const; + + void wakeUp(); + void flush(); + void interrupt(); + +private: + friend void qt_mac_select_timer_callbk(__EventLoopTimer*, void*); + friend class QApplicationPrivate; +}; + +struct MacTimerInfo { + int id; + int interval; + QObject *obj; + bool pending; + CFRunLoopTimerRef runLoopTimer; + bool operator==(const MacTimerInfo &other) + { + return (id == other.id); + } +}; +typedef QHash<int, MacTimerInfo *> MacTimerHash; + +struct MacSocketInfo { + MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0) {} + CFSocketRef socket; + CFRunLoopSourceRef runloop; + QObject *readNotifier; + QObject *writeNotifier; +}; +typedef QHash<int, MacSocketInfo *> MacSocketHash; + +class QEventDispatcherMacPrivate : public QAbstractEventDispatcherPrivate +{ + Q_DECLARE_PUBLIC(QEventDispatcherMac) + +public: + QEventDispatcherMacPrivate(); + + static MacTimerHash macTimerHash; + // Set 'blockSendPostedEvents' to true if you _really_ need + // to make sure that qt events are not posted while calling + // low-level cocoa functions (like beginModalForWindow). And + // use a QBoolBlocker to be safe: + static bool blockSendPostedEvents; +#ifdef QT_MAC_USE_COCOA + // The following variables help organizing modal sessions: + static QStack<QCocoaModalSessionInfo> cocoaModalSessionStack; + static bool currentExecIsNSAppRun; + static bool nsAppRunCalledByQt; + static bool cleanupModalSessionsNeeded; + static NSModalSession currentModalSessionCached; + static NSModalSession currentModalSession(); + static void updateChildrenWorksWhenModal(); + static void temporarilyStopAllModalSessions(); + static void beginModalSession(QWidget *widget); + static void endModalSession(QWidget *widget); + static void cancelWaitForMoreEvents(); + static void cleanupModalSessions(); + static void ensureNSAppInitialized(); +#endif + + MacSocketHash macSockets; + QList<void *> queuedUserInputEvents; // List of EventRef in Carbon, and NSEvent * in Cocoa + CFRunLoopSourceRef postedEventsSource; + CFRunLoopObserverRef waitingObserver; + CFRunLoopObserverRef firstTimeObserver; + QAtomicInt serialNumber; + int lastSerial; + static bool interrupt; +private: + static Boolean postedEventSourceEqualCallback(const void *info1, const void *info2); + static void postedEventsSourcePerformCallback(void *info); + static void activateTimer(CFRunLoopTimerRef, void *info); + static void waitingObserverCallback(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void *info); + static void firstLoopEntry(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info); +}; + +#ifdef QT_MAC_USE_COCOA +class QtMacInterruptDispatcherHelp : public QObject +{ + static QtMacInterruptDispatcherHelp *instance; + bool cancelled; + + QtMacInterruptDispatcherHelp(); + ~QtMacInterruptDispatcherHelp(); + + public: + static void interruptLater(); + static void cancelInterruptLater(); +}; +#endif + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_MAC_P_H diff --git a/src/gui/guikernel/qeventdispatcher_qpa.cpp b/src/gui/guikernel/qeventdispatcher_qpa.cpp new file mode 100644 index 0000000000..6271804740 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_qpa.cpp @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qapplication.h" +#include "qeventdispatcher_qpa_p.h" +#include "private/qeventdispatcher_unix_p.h" +#include "private/qapplication_p.h" +#include "qplatformeventloopintegration_qpa.h" + +#include <QWindowSystemInterface> +#include <QtCore/QElapsedTimer> +#include <QtCore/QAtomicInt> +#include <QtCore/QSemaphore> + +#include <QtCore/QDebug> + +#include <errno.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +class Rendezvous +{ +public: + void checkpoint() + { + if (state.testAndSetOrdered(0,1)) { + semaphore.acquire(); + } else if (state.testAndSetAcquire(1,0)) { + semaphore.release(); + } else { + qWarning("Barrier internal error"); + } + } +private: + QSemaphore semaphore; + QAtomicInt state; +}; + +class SelectWorker : public QThread +{ +public: + SelectWorker(QEventDispatcherQPAPrivate *eventDispatcherPrivate) + : QThread(), + m_edPrivate(eventDispatcherPrivate), + m_retVal(0) + { + } + + void setSelectValues(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds) + { + m_nfds = nfds; + m_readfds = readfds; + m_writefds = writefds; + m_exceptfds = exceptfds; + + + } + + int retVal() const { + return m_retVal; + } + +protected: + void run(); + +private: + QEventDispatcherQPAPrivate *m_edPrivate; + int m_retVal; + + int m_nfds; + fd_set *m_readfds, *m_writefds, *m_exceptfds; +}; + +class QEventDispatcherQPAPrivate : public QEventDispatcherUNIXPrivate +{ + Q_DECLARE_PUBLIC(QEventDispatcherQPA) +public: + QEventDispatcherQPAPrivate() + : eventLoopIntegration(0), + barrierBeforeBlocking(0), + barrierReturnValue(0), + selectReturnMutex(0), + selectWorkerNeedsSync(true), + selectWorkerHasResult(false), + m_integrationInitialised(false), + m_hasIntegration(false), + m_isEventLoopIntegrationRunning(false) + { + + } + + ~QEventDispatcherQPAPrivate() + { + delete selectWorker; + delete eventLoopIntegration; + delete barrierBeforeBlocking; + delete barrierReturnValue; + delete selectReturnMutex; + } + + bool hasIntegration() const + { + if (!m_integrationInitialised) { + QEventDispatcherQPAPrivate *that = const_cast<QEventDispatcherQPAPrivate *>(this); + if (qApp && (qApp->thread() == QThread::currentThread())) { // guiThread + if (QGuiApplicationPrivate::platformIntegration()) { + that->eventLoopIntegration = QGuiApplicationPrivate::platformIntegration()->createEventLoopIntegration(); + if (that->eventLoopIntegration) { + that->selectWorker = new SelectWorker(that); + that->barrierBeforeBlocking = new Rendezvous; + that->barrierReturnValue = new Rendezvous; + that->selectReturnMutex = new QMutex; + that->selectWorker->start(); + that->m_hasIntegration = true; + if (!QElapsedTimer::isMonotonic()) + qWarning("Having eventloop integration without monotonic timers can lead to undefined behaviour"); + } + } + } + that->m_integrationInitialised = true; + } + return m_hasIntegration; + } + + bool isEventLoopIntegrationRunning() const + { + return m_isEventLoopIntegrationRunning; + } + + void runEventLoopIntegration() + { + if (qApp && (qApp->thread() == QThread::currentThread())) { + m_isEventLoopIntegrationRunning = true; + eventLoopIntegration->startEventLoop(); + } + } + + QPlatformEventLoopIntegration *eventLoopIntegration; + Rendezvous *barrierBeforeBlocking; + Rendezvous *barrierReturnValue; + + QMutex *selectReturnMutex; + bool selectWorkerNeedsSync; + bool selectWorkerHasResult; + + SelectWorker *selectWorker; +private: + bool m_integrationInitialised; + bool m_hasIntegration; + bool m_isEventLoopIntegrationRunning; +}; + +QEventDispatcherQPA::QEventDispatcherQPA(QObject *parent) + : QEventDispatcherUNIX(*new QEventDispatcherQPAPrivate, parent) +{ } + +QEventDispatcherQPA::~QEventDispatcherQPA() +{ } + +bool QEventDispatcherQPA::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QEventDispatcherQPA); + + if (d->hasIntegration()) { + if (!d->isEventLoopIntegrationRunning()) { + d->runEventLoopIntegration(); + } + if (d->threadData->quitNow) { + d->eventLoopIntegration->quitEventLoop(); + return false; + } + } + + int nevents = 0; + + // handle gui and posted events + d->interrupt = false; + QApplication::sendPostedEvents(); + + while (!d->interrupt) { // also flushes output buffer ###can be optimized + QWindowSystemInterfacePrivate::WindowSystemEvent *event; + if (!(flags & QEventLoop::ExcludeUserInputEvents) + && QWindowSystemInterfacePrivate::windowSystemEventsQueued() > 0) { + // process a pending user input event + event = QWindowSystemInterfacePrivate::getWindowSystemEvent(); + if (!event) + break; + } else { + break; + } + + if (filterEvent(event)) { + delete event; + continue; + } + nevents++; + + QGuiApplicationPrivate::processWindowSystemEvent(event); + delete event; + } + + if (!d->interrupt) { + if (QEventDispatcherUNIX::processEvents(flags)) { + QEventDispatcherUNIX::processEvents(flags); + return true; + } + } + return (nevents > 0); +} + +bool QEventDispatcherQPA::hasPendingEvents() +{ + extern uint qGlobalPostedEventsCount(); // from qapplication.cpp + return qGlobalPostedEventsCount() || QWindowSystemInterfacePrivate::windowSystemEventsQueued(); +} + +void QEventDispatcherQPA::registerSocketNotifier(QSocketNotifier *notifier) +{ + Q_D(QEventDispatcherQPA); + QEventDispatcherUNIX::registerSocketNotifier(notifier); + if (d->hasIntegration()) + wakeUp(); + +} + +void QEventDispatcherQPA::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + Q_D(QEventDispatcherQPA); + QEventDispatcherUNIX::unregisterSocketNotifier(notifier); + if (d->hasIntegration()) + wakeUp(); +} + +void QEventDispatcherQPA::flush() +{ + if(qApp) + qApp->sendPostedEvents(); +} + +int QEventDispatcherQPA::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout) +{ + Q_D(QEventDispatcherQPA); + int retVal = 0; + if (d->hasIntegration()) { + qint64 timeoutmsec = 0; + if (timeout) + timeoutmsec = timeout->tv_sec * 1000 + (timeout->tv_usec/1000); + d->selectReturnMutex->lock(); + if (d->selectWorkerNeedsSync) { + if (d->selectWorkerHasResult) { + retVal = d->selectWorker->retVal(); + d->selectWorkerHasResult = false; + + d->selectReturnMutex->unlock(); + d->barrierReturnValue->checkpoint(); + d->eventLoopIntegration->setNextTimerEvent(0); + return retVal; + } else { + d->selectWorkerNeedsSync = false; + d->selectWorker->setSelectValues(nfds,readfds, writefds, exceptfds); + d->barrierBeforeBlocking->checkpoint(); + } + } + d->selectReturnMutex->unlock(); + d->eventLoopIntegration->setNextTimerEvent(timeoutmsec); + retVal = 0; //is 0 if select has not returned + } else { + retVal = QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout); + } + return retVal; +} + + +void SelectWorker::run() +{ + + while(true) { + m_retVal = 0; + m_edPrivate->barrierBeforeBlocking->checkpoint(); // wait for mainthread + int tmpRet = qt_safe_select(m_nfds,m_readfds,m_writefds,m_exceptfds,0); + m_edPrivate->selectReturnMutex->lock(); + m_edPrivate->eventLoopIntegration->qtNeedsToProcessEvents(); + + m_edPrivate->selectWorkerNeedsSync = true; + m_edPrivate->selectWorkerHasResult = true; + m_retVal = tmpRet; + + m_edPrivate->selectReturnMutex->unlock(); + m_edPrivate->barrierReturnValue->checkpoint(); + } +} +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qeventdispatcher_qpa_p.h b/src/gui/guikernel/qeventdispatcher_qpa_p.h new file mode 100644 index 0000000000..d4d2be1f38 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_qpa_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_QPA_P_H +#define QEVENTDISPATCHER_QPA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qeventdispatcher_unix_p.h" + +QT_BEGIN_NAMESPACE + +class QEventDispatcherQPAPrivate; + +class QEventDispatcherQPA : public QEventDispatcherUNIX +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventDispatcherQPA) + +public: + explicit QEventDispatcherQPA(QObject *parent = 0); + ~QEventDispatcherQPA(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void registerSocketNotifier(QSocketNotifier *notifier); + void unregisterSocketNotifier(QSocketNotifier *notifier); + + void flush(); + +protected: + int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout); +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_QPA_P_H diff --git a/src/gui/guikernel/qeventdispatcher_s60.cpp b/src/gui/guikernel/qeventdispatcher_s60.cpp new file mode 100644 index 0000000000..2d92c89c07 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_s60.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qwidget.h> + +#include "qeventdispatcher_s60_p.h" + +QT_BEGIN_NAMESPACE + +QtEikonEnv::QtEikonEnv() + : m_lastIterationCount(0) + , m_savedStatusCode(KRequestPending) + , m_hasAlreadyRun(false) +{ +} + +QtEikonEnv::~QtEikonEnv() +{ +} + +void QtEikonEnv::RunL() +{ + QEventDispatcherS60 *dispatcher = qobject_cast<QEventDispatcherS60 *>(QAbstractEventDispatcher::instance()); + if (!dispatcher) { + CEikonEnv::RunL(); + return; + } + + if (m_lastIterationCount != dispatcher->iterationCount()) { + m_hasAlreadyRun = false; + m_lastIterationCount = dispatcher->iterationCount(); + } + + if (m_hasAlreadyRun) { + // Fool the active scheduler into believing we are still waiting for events. + // The window server thinks we are not, however. + m_savedStatusCode = iStatus.Int(); + iStatus = KRequestPending; + SetActive(); + dispatcher->queueDeferredActiveObjectsCompletion(); + } else { + m_hasAlreadyRun = true; + CEikonEnv::RunL(); + } +} + +void QtEikonEnv::DoCancel() +{ + complete(); + + CEikonEnv::DoCancel(); +} + +void QtEikonEnv::complete() +{ + if (m_hasAlreadyRun) { + if (m_savedStatusCode != KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, m_savedStatusCode); + m_savedStatusCode = KRequestPending; + } + m_hasAlreadyRun = false; + } +} + +QEventDispatcherS60::QEventDispatcherS60(QObject *parent) + : QEventDispatcherSymbian(parent), + m_noInputEvents(false) +{ +} + +QEventDispatcherS60::~QEventDispatcherS60() +{ + for (int c = 0; c < m_deferredInputEvents.size(); ++c) { + delete m_deferredInputEvents[c].event; + } +} + +bool QEventDispatcherS60::processEvents ( QEventLoop::ProcessEventsFlags flags ) +{ + bool ret = false; + + QT_TRY { + bool oldNoInputEventsValue = m_noInputEvents; + if (flags & QEventLoop::ExcludeUserInputEvents) { + m_noInputEvents = true; + } else { + m_noInputEvents = false; + ret = sendDeferredInputEvents() || ret; + } + + ret = QEventDispatcherSymbian::processEvents(flags) || ret; + + m_noInputEvents = oldNoInputEventsValue; + } QT_CATCH (const std::exception& ex) { +#ifndef QT_NO_EXCEPTIONS + CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex)); +#endif + } + + return ret; +} + +bool QEventDispatcherS60::hasPendingEvents() +{ + return !m_deferredInputEvents.isEmpty() || QEventDispatcherSymbian::hasPendingEvents(); +} + +void QEventDispatcherS60::saveInputEvent(QSymbianControl *control, QWidget *widget, QInputEvent *event) +{ + DeferredInputEvent inputEvent = {control, widget, event}; + m_deferredInputEvents.append(inputEvent); + connect(widget, SIGNAL(destroyed(QObject*)), SLOT(removeInputEventsForWidget(QObject*))); +} + +bool QEventDispatcherS60::sendDeferredInputEvents() +{ + bool eventsSent = false; + while (!m_deferredInputEvents.isEmpty()) { + DeferredInputEvent inputEvent = m_deferredInputEvents.takeFirst(); +#ifndef QT_NO_EXCEPTIONS + try { +#endif + inputEvent.control->sendInputEvent(inputEvent.widget, inputEvent.event); +#ifndef QT_NO_EXCEPTIONS + } catch (...) { + delete inputEvent.event; + throw; + } +#endif + delete inputEvent.event; + eventsSent = true; + } + + return eventsSent; +} + +void QEventDispatcherS60::removeInputEventsForWidget(QObject *object) +{ + for (int c = 0; c < m_deferredInputEvents.size(); ++c) { + if (m_deferredInputEvents[c].widget == object) { + delete m_deferredInputEvents[c].event; + m_deferredInputEvents.removeAt(c--); + } + } +} + +// reimpl +void QEventDispatcherS60::reactivateDeferredActiveObjects() +{ + if (S60->qtOwnsS60Environment) { + static_cast<QtEikonEnv *>(CCoeEnv::Static())->complete(); + } + + QEventDispatcherSymbian::reactivateDeferredActiveObjects(); +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qeventdispatcher_s60_p.h b/src/gui/guikernel/qeventdispatcher_s60_p.h new file mode 100644 index 0000000000..7c5a8d03d4 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_s60_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_S60_P_H +#define QEVENTDISPATCHER_S60_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qeventdispatcher_symbian_p.h> +#include "qt_s60_p.h" + +#include <eikenv.h> + +QT_BEGIN_NAMESPACE + +class QEventDispatcherS60; + +class QtEikonEnv : public CEikonEnv +{ +public: + QtEikonEnv(); + ~QtEikonEnv(); + + // from CActive. + void RunL(); + void DoCancel(); + + void complete(); + +private: + // Workaround for a BC break from S60 3.2 -> 5.0, where the CEikonEnv override was removed. + // To avoid linking to that when we build against 3.2, define an empty body here. + // Reserved_*() have been verified to be empty in the S60 code. + void Reserved_1() {} + void Reserved_2() {} + +private: + int m_lastIterationCount; + TInt m_savedStatusCode; + bool m_hasAlreadyRun; +}; + +class Q_GUI_EXPORT QEventDispatcherS60 : public QEventDispatcherSymbian +{ + Q_OBJECT + +public: + QEventDispatcherS60(QObject *parent = 0); + ~QEventDispatcherS60(); + + bool processEvents ( QEventLoop::ProcessEventsFlags flags ); + bool hasPendingEvents(); + + bool excludeUserInputEvents() { return m_noInputEvents; } + + void saveInputEvent(QSymbianControl *control, QWidget *widget, QInputEvent *event); + + void reactivateDeferredActiveObjects(); + +private: + bool sendDeferredInputEvents(); + +private Q_SLOTS: + void removeInputEventsForWidget(QObject *object); + +private: + bool m_noInputEvents; + + struct DeferredInputEvent + { + QSymbianControl *control; + QWidget *widget; + QInputEvent *event; + }; + QList<DeferredInputEvent> m_deferredInputEvents; +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_S60_P_H diff --git a/src/gui/guikernel/qeventdispatcher_x11.cpp b/src/gui/guikernel/qeventdispatcher_x11.cpp new file mode 100644 index 0000000000..110786a378 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_x11.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeventdispatcher_x11_p.h" + +#include "qapplication.h" +#include "qx11info_x11.h" + +#include "qt_x11_p.h" +#include <private/qeventdispatcher_unix_p.h> + +QT_BEGIN_NAMESPACE + +class QEventDispatcherX11Private : public QEventDispatcherUNIXPrivate +{ + Q_DECLARE_PUBLIC(QEventDispatcherX11) +public: + inline QEventDispatcherX11Private() + : xfd(-1) + { } + int xfd; + QList<XEvent> queuedUserInputEvents; +}; + +QEventDispatcherX11::QEventDispatcherX11(QObject *parent) + : QEventDispatcherUNIX(*new QEventDispatcherX11Private, parent) +{ } + +QEventDispatcherX11::~QEventDispatcherX11() +{ } + +bool QEventDispatcherX11::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QEventDispatcherX11); + + d->interrupt = false; + QApplication::sendPostedEvents(); + + ulong marker = XNextRequest(X11->display); + int nevents = 0; + do { + while (!d->interrupt) { + XEvent event; + if (!(flags & QEventLoop::ExcludeUserInputEvents) + && !d->queuedUserInputEvents.isEmpty()) { + // process a pending user input event + event = d->queuedUserInputEvents.takeFirst(); + } else if (XEventsQueued(X11->display, QueuedAlready)) { + // process events from the X server + XNextEvent(X11->display, &event); + + if (flags & QEventLoop::ExcludeUserInputEvents) { + // queue user input events + switch (event.type) { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + case XKeyPress: + case XKeyRelease: + case EnterNotify: + case LeaveNotify: + d->queuedUserInputEvents.append(event); + continue; + + case ClientMessage: + // only keep the wm_take_focus and + // _qt_scrolldone protocols, queue all other + // client messages + if (event.xclient.format == 32) { + if (event.xclient.message_type == ATOM(WM_PROTOCOLS) && + (Atom) event.xclient.data.l[0] == ATOM(WM_TAKE_FOCUS)) { + break; + } else if (event.xclient.message_type == ATOM(_QT_SCROLL_DONE)) { + break; + } + } + d->queuedUserInputEvents.append(event); + continue; + + default: + break; + } + } + } else { + // no event to process + break; + } + + // send through event filter + if (filterEvent(&event)) + continue; + + nevents++; + if (qApp->x11ProcessEvent(&event) == 1) + return true; + + if (event.xany.serial >= marker) { + if (XEventsQueued(X11->display, QueuedAfterFlush)) + flags &= ~QEventLoop::WaitForMoreEvents; + goto out; + } + } + } while (!d->interrupt && XEventsQueued(X11->display, QueuedAfterFlush)); + + out: + if (!d->interrupt) { + const uint exclude_all = + QEventLoop::ExcludeSocketNotifiers | QEventLoop::X11ExcludeTimers | QEventLoop::WaitForMoreEvents; + if (nevents > 0 && ((uint)flags & exclude_all) == exclude_all) { + QApplication::sendPostedEvents(); + return nevents > 0; + } + // return true if we handled events, false otherwise + return QEventDispatcherUNIX::processEvents(flags) || (nevents > 0); + } + return nevents > 0; +} + +bool QEventDispatcherX11::hasPendingEvents() +{ + extern uint qGlobalPostedEventsCount(); // from qapplication.cpp + return (qGlobalPostedEventsCount() || XPending(X11->display)); +} + +void QEventDispatcherX11::flush() +{ + XFlush(X11->display); +} + +void QEventDispatcherX11::startingUp() +{ + Q_D(QEventDispatcherX11); + d->xfd = XConnectionNumber(X11->display); +} + +void QEventDispatcherX11::closingDown() +{ + Q_D(QEventDispatcherX11); + d->xfd = -1; +} + +int QEventDispatcherX11::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout) +{ + Q_D(QEventDispatcherX11); + if (d->xfd > 0) { + nfds = qMax(nfds - 1, d->xfd) + 1; + FD_SET(d->xfd, readfds); + } + return QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout); +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qeventdispatcher_x11_p.h b/src/gui/guikernel/qeventdispatcher_x11_p.h new file mode 100644 index 0000000000..cfdd2a5fa6 --- /dev/null +++ b/src/gui/guikernel/qeventdispatcher_x11_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_X11_P_H +#define QEVENTDISPATCHER_X11_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qeventdispatcher_unix_p.h" + +QT_BEGIN_NAMESPACE + +class QEventDispatcherX11Private; + +class QEventDispatcherX11 : public QEventDispatcherUNIX +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventDispatcherX11) + +public: + explicit QEventDispatcherX11(QObject *parent = 0); + ~QEventDispatcherX11(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void flush(); + + void startingUp(); + void closingDown(); + +protected: + int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout); +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_X11_P_H diff --git a/src/gui/guikernel/qgenericplugin_qpa.cpp b/src/gui/guikernel/qgenericplugin_qpa.cpp new file mode 100644 index 0000000000..43d6525bb6 --- /dev/null +++ b/src/gui/guikernel/qgenericplugin_qpa.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgenericplugin_qpa.h" + +#ifndef QT_NO_LIBRARY + +QT_BEGIN_NAMESPACE + +/*! + \class QGenericPlugin + \ingroup plugins + \ingroup qpa + + \brief The QGenericPlugin class is an abstract base class for + window-system related plugins in Qt QPA. + + Note that this class is only available in \l{Qt QPA}. + + A mouse plugin can be created by subclassing + QGenericPlugin and reimplementing the pure virtual keys() and + create() functions. By exporting the derived class using the + Q_EXPORT_PLUGIN2() macro, The default implementation of the + QGenericPluginFactory class will automatically detect the plugin and + load the driver into the server application at run-time. See \l + {How to Create Qt Plugins} for details. + + \sa QGenericPluginFactory +*/ + +/*! + \fn QStringList QGenericPlugin::keys() const + + Implement this function to return the list of valid keys, i.e. the + drivers supported by this plugin. + + \sa create() +*/ + +/*! + Constructs a plugin with the given \a parent. + + Note that this constructor is invoked automatically by the + Q_EXPORT_PLUGIN2() macro, so there is no need for calling it + explicitly. +*/ +QGenericPlugin::QGenericPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys the plugin. + + Note that Qt destroys a plugin automatically when it is no longer + used, so there is no need for calling the destructor explicitly. +*/ +QGenericPlugin::~QGenericPlugin() +{ +} + +/*! + \fn QObject* QGenericPlugin::create(const QString &key, const QString& specification) + + Implement this function to create a driver matching the type + specified by the given \a key and \a specification parameters. Note that + keys are case-insensitive. + + \sa keys() +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_LIBRARY diff --git a/src/gui/guikernel/qgenericplugin_qpa.h b/src/gui/guikernel/qgenericplugin_qpa.h new file mode 100644 index 0000000000..e1792cd417 --- /dev/null +++ b/src/gui/guikernel/qgenericplugin_qpa.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGENERICPLUGIN_QPA_H +#define QGENERICPLUGIN_QPA_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_LIBRARY + +//class QGenericObject; ????? + + struct Q_GUI_EXPORT QGenericPluginFactoryInterface : public QFactoryInterface +{ + virtual QObject* create(const QString &name, const QString &spec) = 0; +}; + +#define QGenericPluginFactoryInterface_iid "com.trolltech.Qt.QGenericPluginFactoryInterface" +Q_DECLARE_INTERFACE(QGenericPluginFactoryInterface, QGenericPluginFactoryInterface_iid) + +class Q_GUI_EXPORT QGenericPlugin : public QObject, public QGenericPluginFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QGenericPluginFactoryInterface:QFactoryInterface) +public: + explicit QGenericPlugin(QObject *parent = 0); + ~QGenericPlugin(); + + virtual QStringList keys() const = 0; + virtual QObject* create(const QString& name, const QString &spec) = 0; +}; + +#endif // QT_NO_LIBRARY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGENERICPLUGIN_QPA_H diff --git a/src/gui/guikernel/qgenericpluginfactory_qpa.cpp b/src/gui/guikernel/qgenericpluginfactory_qpa.cpp new file mode 100644 index 0000000000..abc575ae86 --- /dev/null +++ b/src/gui/guikernel/qgenericpluginfactory_qpa.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgenericpluginfactory_qpa.h" + +#include "qapplication.h" +#include "private/qfactoryloader_p.h" +#include "qgenericplugin_qpa.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QGenericPluginFactoryInterface_iid, + QLatin1String("/generic"), Qt::CaseInsensitive)) + +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + +/*! + \class QGenericPluginFactory + \ingroup qpa + + \brief The QGenericPluginFactory class creates window-system + related plugin drivers in Qt QPA. + + Note that this class is only available in \l{Qt QPA}. + + + \sa QGenericPlugin +*/ + +/*! + Creates the driver specified by \a key, using the given \a specification. + + Note that the keys are case-insensitive. + + \sa keys() +*/ +QObject *QGenericPluginFactory::create(const QString& key, const QString &specification) +{ + QString driver = key.toLower(); + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + if (QGenericPluginFactoryInterface *factory = qobject_cast<QGenericPluginFactoryInterface*>(loader()->instance(driver))) + return factory->create(driver, specification); +#endif +#endif + return 0; +} + +/*! + Returns the list of valid keys, i.e. the available mouse drivers. + + \sa create() +*/ +QStringList QGenericPluginFactory::keys() +{ + QStringList list; + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + QStringList plugins = loader()->keys(); + for (int i = 0; i < plugins.size(); ++i) { + if (!list.contains(plugins.at(i))) + list += plugins.at(i); + } +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + return list; +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qgenericpluginfactory_qpa.h b/src/gui/guikernel/qgenericpluginfactory_qpa.h new file mode 100644 index 0000000000..59eac386ed --- /dev/null +++ b/src/gui/guikernel/qgenericpluginfactory_qpa.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGENERICPLUGINFACTORY_QPA_H +#define QGENERICPLUGINFACTORY_QPA_H + +#include <QtCore/qstringlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QString; +class QObject; + +class Q_GUI_EXPORT QGenericPluginFactory +{ +public: + static QStringList keys(); + static QObject *create(const QString&, const QString &); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGENERICPLUGINFACTORY_QPA_H diff --git a/src/gui/guikernel/qguiapplication.cpp b/src/gui/guikernel/qguiapplication.cpp new file mode 100644 index 0000000000..52303b929f --- /dev/null +++ b/src/gui/guikernel/qguiapplication.cpp @@ -0,0 +1,925 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qguiapplication.h" + +#include "private/qguiapplication_p.h" +#include "private/qplatformintegrationfactory_qpa_p.h" +#include "private/qevent_p.h" + +#if !defined(QT_NO_GLIB) +#include "qeventdispatcher_glib_qpa_p.h" +#endif +#include "qeventdispatcher_qpa_p.h" + +#include <QtCore/QAbstractEventDispatcher> +#include <QtCore/private/qcoreapplication_p.h> +#include <QtCore/private/qabstracteventdispatcher_p.h> +#include <QtCore/qmutex.h> +#include <QtDebug> + +#include <QtGui/QPlatformIntegration> +#include <QtGui/QGenericPluginFactory> + +#include <QWindowSystemInterface> +#include "private/qwindowsysteminterface_qpa_p.h" +#include "private/qwindow_p.h" +#include "private/qkeymapper_p.h" + +#ifndef QT_NO_CLIPBOARD +#include <QtGui/QClipboard> +#endif + +QT_BEGIN_NAMESPACE + +Qt::MouseButtons QGuiApplicationPrivate::mouse_buttons = Qt::NoButton; +Qt::KeyboardModifiers QGuiApplicationPrivate::modifier_buttons = Qt::NoModifier; + +int QGuiApplicationPrivate::keyboard_input_time = 0; +int QGuiApplicationPrivate::mouse_double_click_time = 0; + +QPlatformIntegration *QGuiApplicationPrivate::platform_integration = 0; + +QWidget *qt_button_down = 0; // widget got last button-down + +bool QGuiApplicationPrivate::app_do_modal = false; + +int qt_last_x = 0; +int qt_last_y = 0; +QPointer<QWidget> QGuiApplicationPrivate::qt_last_mouse_receiver = 0; + +QWidgetList QGuiApplicationPrivate::qt_modal_stack; + +Qt::MouseButtons QGuiApplicationPrivate::buttons = Qt::NoButton; +ulong QGuiApplicationPrivate::mousePressTime = 0; +Qt::MouseButton QGuiApplicationPrivate::mousePressButton = Qt::NoButton; +int QGuiApplicationPrivate::mousePressX = 0; +int QGuiApplicationPrivate::mousePressY = 0; +int QGuiApplicationPrivate::mouse_double_click_distance = 5; + +static Qt::LayoutDirection layout_direction = Qt::LeftToRight; +static bool force_reverse = false; + +QGuiApplicationPrivate *QGuiApplicationPrivate::self = 0; + +#ifndef QT_NO_CLIPBOARD +QClipboard *QGuiApplicationPrivate::qt_clipboard = 0; +#endif + +Q_GLOBAL_STATIC(QMutex, applicationFontMutex) +QFont *QGuiApplicationPrivate::app_font = 0; + +static bool qt_detectRTLLanguage() +{ + return force_reverse ^ + (QApplication::tr("QT_LAYOUT_DIRECTION", + "Translate this string to the string 'LTR' in left-to-right" + " languages or to 'RTL' in right-to-left languages (such as Hebrew" + " and Arabic) to get proper widget layout.") == QLatin1String("RTL")); +} + + +QGuiApplication::QGuiApplication(int &argc, char **argv, int flags) + : QCoreApplication(*new QGuiApplicationPrivate(argc, argv, flags)) +{ + d_func()->init(); + + QCoreApplicationPrivate::eventDispatcher->startingUp(); +} + +QGuiApplication::QGuiApplication(QGuiApplicationPrivate &p) + : QCoreApplication(p) +{ + d_func()->init(); +} + +QGuiApplication::~QGuiApplication() +{ + Q_D(QGuiApplication); + // flush clipboard contents + if (QGuiApplicationPrivate::qt_clipboard) { + QEvent event(QEvent::Clipboard); + QGuiApplication::sendEvent(QGuiApplicationPrivate::qt_clipboard, &event); + } + + d->eventDispatcher->closingDown(); + d->eventDispatcher = 0; + + delete QGuiApplicationPrivate::qt_clipboard; + QGuiApplicationPrivate::qt_clipboard = 0; +} + +QGuiApplicationPrivate::QGuiApplicationPrivate(int &argc, char **argv, int flags) + : QCoreApplicationPrivate(argc, argv, flags) +{ + self = this; +} + +static void init_platform(const QString &name, const QString &platformPluginPath) +{ + QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, platformPluginPath); + if (!QGuiApplicationPrivate::platform_integration) { + QStringList keys = QPlatformIntegrationFactory::keys(platformPluginPath); + QString fatalMessage = + QString::fromLatin1("Failed to load platform plugin \"%1\". Available platforms are: \n").arg(name); + foreach(QString key, keys) { + fatalMessage.append(key + QString::fromLatin1("\n")); + } + qFatal("%s", fatalMessage.toLocal8Bit().constData()); + + } + +} + +static void init_plugins(const QList<QByteArray> pluginList) +{ + for (int i = 0; i < pluginList.count(); ++i) { + QByteArray pluginSpec = pluginList.at(i); + qDebug() << "init_plugins" << i << pluginSpec; + int colonPos = pluginSpec.indexOf(':'); + QObject *plugin; + if (colonPos < 0) + plugin = QGenericPluginFactory::create(QLatin1String(pluginSpec), QString()); + else + plugin = QGenericPluginFactory::create(QLatin1String(pluginSpec.mid(0, colonPos)), + QLatin1String(pluginSpec.mid(colonPos+1))); + qDebug() << " created" << plugin; + } +} + +void QGuiApplicationPrivate::createEventDispatcher() +{ + Q_Q(QGuiApplication); +#if !defined(QT_NO_GLIB) + if (qgetenv("QT_NO_GLIB").isEmpty() && QEventDispatcherGlib::versionSupported()) + eventDispatcher = new QPAEventDispatcherGlib(q); + else +#endif + eventDispatcher = new QEventDispatcherQPA(q); +} + +void QGuiApplicationPrivate::init() +{ + QList<QByteArray> pluginList; + QString platformPluginPath = QLatin1String(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH")); + QByteArray platformName; +#ifdef QT_QPA_DEFAULT_PLATFORM_NAME + platformName = QT_QPA_DEFAULT_PLATFORM_NAME; +#endif + QByteArray platformNameEnv = qgetenv("QT_QPA_PLATFORM"); + if (!platformNameEnv.isEmpty()) { + platformName = platformNameEnv; + } + + // Get command line params + + int j = argc ? 1 : 0; + for (int i=1; i<argc; i++) { + if (argv[i] && *argv[i] != '-') { + argv[j++] = argv[i]; + continue; + } + QByteArray arg = argv[i]; + if (arg == "-platformpluginpath") { + if (++i < argc) + platformPluginPath = QLatin1String(argv[i]); + } else if (arg == "-platform") { + if (++i < argc) + platformName = argv[i]; + } else if (arg == "-plugin") { + if (++i < argc) + pluginList << argv[i]; + } else if (arg == "-reverse") { + force_reverse = true; + QGuiApplication::setLayoutDirection(Qt::RightToLeft); + } else { + argv[j++] = argv[i]; + } + } + + argv[j] = 0; + argc = j; + +#if 0 + QByteArray pluginEnv = qgetenv("QT_QPA_PLUGINS"); + if (!pluginEnv.isEmpty()) { + pluginList.append(pluginEnv.split(';')); + } +#endif + + init_platform(QLatin1String(platformName), platformPluginPath); + init_plugins(pluginList); + + QFont::initialize(); + + is_app_running = true; +} + +QGuiApplicationPrivate::~QGuiApplicationPrivate() +{ + delete platform_integration; + platform_integration = 0; + + is_app_closing = true; + is_app_running = false; + + QFont::cleanup(); + + layout_direction = Qt::LeftToRight; +} + +#if 0 +#ifndef QT_NO_CURSOR +QCursor *overrideCursor(); +void setOverrideCursor(const QCursor &); +void changeOverrideCursor(const QCursor &); +void restoreOverrideCursor(); +#endif + +static QFont font(); +static QFont font(const QWidget*); +static QFont font(const char *className); +static void setFont(const QFont &, const char* className = 0); +static QFontMetrics fontMetrics(); + +#ifndef QT_NO_CLIPBOARD +static QClipboard *clipboard(); +#endif +#endif + +Qt::KeyboardModifiers QGuiApplication::keyboardModifiers() +{ + return QGuiApplicationPrivate::modifier_buttons; +} + +Qt::MouseButtons QGuiApplication::mouseButtons() +{ + return QGuiApplicationPrivate::mouse_buttons; +} + +void QGuiApplication::setDoubleClickInterval(int ms) +{ + QGuiApplicationPrivate::mouse_double_click_time = ms; +} + +int QGuiApplication::doubleClickInterval() +{ + return QGuiApplicationPrivate::mouse_double_click_time; +} + +void QGuiApplication::setKeyboardInputInterval(int ms) +{ + QGuiApplicationPrivate::keyboard_input_time = ms; +} + +int QGuiApplication::keyboardInputInterval() +{ + return QGuiApplicationPrivate::keyboard_input_time; +} + +QPlatformNativeInterface *QGuiApplication::platformNativeInterface() +{ + QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration(); + return pi->nativeInterface(); +} + +int QGuiApplication::exec() +{ + return QCoreApplication::exec(); +} + +bool QGuiApplication::notify(QObject *object, QEvent *event) +{ + return QCoreApplication::notify(object, event); +} + +bool QGuiApplication::event(QEvent *e) +{ + if(e->type() == QEvent::LanguageChange) { + setLayoutDirection(qt_detectRTLLanguage()?Qt::RightToLeft:Qt::LeftToRight); + } + return QCoreApplication::event(e); +} + +bool QGuiApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents) +{ + return QCoreApplication::compressEvent(event, receiver, postedEvents); +} + +void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) +{ + switch(e->type) { + case QWindowSystemInterfacePrivate::Mouse: + QGuiApplicationPrivate::processMouseEvent(static_cast<QWindowSystemInterfacePrivate::MouseEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Wheel: + QGuiApplicationPrivate::processWheelEvent(static_cast<QWindowSystemInterfacePrivate::WheelEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Key: + QGuiApplicationPrivate::processKeyEvent(static_cast<QWindowSystemInterfacePrivate::KeyEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Touch: + QGuiApplicationPrivate::processTouchEvent(static_cast<QWindowSystemInterfacePrivate::TouchEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::GeometryChange: + QGuiApplicationPrivate::processGeometryChangeEvent(static_cast<QWindowSystemInterfacePrivate::GeometryChangeEvent*>(e)); + break; + case QWindowSystemInterfacePrivate::Enter: + QGuiApplicationPrivate::processEnterEvent(static_cast<QWindowSystemInterfacePrivate::EnterEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Leave: + QGuiApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::ActivatedWindow: + QGuiApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Close: + QGuiApplicationPrivate::processCloseEvent( + static_cast<QWindowSystemInterfacePrivate::CloseEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::ScreenCountChange: + QGuiApplicationPrivate::reportScreenCount( + static_cast<QWindowSystemInterfacePrivate::ScreenCountEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::ScreenGeometry: + QGuiApplicationPrivate::reportGeometryChange( + static_cast<QWindowSystemInterfacePrivate::ScreenGeometryEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::ScreenAvailableGeometry: + QGuiApplicationPrivate::reportAvailableGeometryChange( + static_cast<QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent *>(e)); + break; + default: + qWarning() << "Unknown user input event type:" << e->type; + break; + } +} + +void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e) +{ + // qDebug() << "handleMouseEvent" << tlw << ev.pos() << ev.globalPos() << hex << ev.buttons(); + static QWeakPointer<QWidget> implicit_mouse_grabber; + + QEvent::Type type; + // move first + Qt::MouseButtons stateChange = e->buttons ^ buttons; + if (e->globalPos != QPoint(qt_last_x, qt_last_y) && (stateChange != Qt::NoButton)) { + QWindowSystemInterfacePrivate::MouseEvent * newMouseEvent = + new QWindowSystemInterfacePrivate::MouseEvent(e->window.data(), e->timestamp, e->localPos, e->globalPos, e->buttons); + QWindowSystemInterfacePrivate::windowSystemEventQueue.prepend(newMouseEvent); // just in case the move triggers a new event loop + stateChange = Qt::NoButton; + } + + QWindow *window = e->window.data(); + + QWidget * tlw = 0;//window ? window->widget() : 0; + + QPoint localPoint = e->localPos; + QPoint globalPoint = e->globalPos; + QWidget *mouseWindow = tlw; + + Qt::MouseButton button = Qt::NoButton; + + if (qt_last_x != globalPoint.x() || qt_last_y != globalPoint.y()) { + type = QEvent::MouseMove; + qt_last_x = globalPoint.x(); + qt_last_y = globalPoint.y(); + if (qAbs(globalPoint.x() - mousePressX) > mouse_double_click_distance|| + qAbs(globalPoint.y() - mousePressY) > mouse_double_click_distance) + mousePressButton = Qt::NoButton; + } + else { // check to see if a new button has been pressed/released + for (int check = Qt::LeftButton; + check <= Qt::XButton2; + check = check << 1) { + if (check & stateChange) { + button = Qt::MouseButton(check); + break; + } + } + if (button == Qt::NoButton) { + // Ignore mouse events that don't change the current state + return; + } + buttons = e->buttons; + if (button & e->buttons) { + if ((e->timestamp - mousePressTime) < static_cast<ulong>(QGuiApplication::doubleClickInterval()) && button == mousePressButton) { + type = QEvent::MouseButtonDblClick; + mousePressButton = Qt::NoButton; + } + else { + type = QEvent::MouseButtonPress; + mousePressTime = e->timestamp; + mousePressButton = button; + mousePressX = qt_last_x; + mousePressY = qt_last_y; + } + } + else + type = QEvent::MouseButtonRelease; + } + + + if (window && !tlw) { + QMouseEvent ev(type, localPoint, globalPoint, button, buttons, QGuiApplication::keyboardModifiers()); + QGuiApplication::sendSpontaneousEvent(window, &ev); + return; + } + +#if 0 + if (self->inPopupMode()) { + //popup mouse handling is magical... + mouseWindow = qApp->activePopupWidget(); + + implicit_mouse_grabber.clear(); + //### how should popup mode and implicit mouse grab interact? + + } else if (tlw && app_do_modal && !qt_try_modal(tlw, QEvent::MouseButtonRelease) ) { + //even if we're blocked by modality, we should deliver the mouse release event.. + //### this code is not completely correct: multiple buttons can be pressed simultaneously + if (!(implicit_mouse_grabber && buttons == Qt::NoButton)) { + //qDebug() << "modal blocked mouse event to" << tlw; + return; + } + } +#endif + +#if 0 + // find the tlw if we didn't get it from the plugin + if (!mouseWindow) { + mouseWindow = QGuiApplication::topLevelAt(globalPoint); + } + + if (!mouseWindow && !implicit_mouse_grabber) + mouseWindow = QGuiApplication::desktop(); + + if (mouseWindow && mouseWindow != tlw) { + //we did not get a sensible localPoint from the window system, so let's calculate it + localPoint = mouseWindow->mapFromGlobal(globalPoint); + } +#endif + + // which child should have it? + QWidget *mouseWidget = mouseWindow; + if (mouseWindow) { + QWidget *w = mouseWindow->childAt(localPoint); + if (w) { + mouseWidget = w; + } + } + + //handle implicit mouse grab + if (type == QEvent::MouseButtonPress && !implicit_mouse_grabber) { + implicit_mouse_grabber = mouseWidget; + + Q_ASSERT(mouseWindow); + mouseWindow->activateWindow(); //focus + } else if (implicit_mouse_grabber) { + mouseWidget = implicit_mouse_grabber.data(); + mouseWindow = mouseWidget->window(); +#if 0 + if (mouseWindow != tlw) + localPoint = mouseWindow->mapFromGlobal(globalPoint); +#endif + } + + if (!mouseWidget) + return; + + Q_ASSERT(mouseWidget); + + //localPoint is local to mouseWindow, but it needs to be local to mouseWidget + localPoint = mouseWidget->mapFrom(mouseWindow, localPoint); + + if (buttons == Qt::NoButton) { + //qDebug() << "resetting mouse grabber"; + implicit_mouse_grabber.clear(); + } + +#if 0 + if (mouseWidget != qt_last_mouse_receiver) { +// dispatchEnterLeave(mouseWidget, qt_last_mouse_receiver); + qt_last_mouse_receiver = mouseWidget; + } +#endif + + // Remember, we might enter a modal event loop when sending the event, + // so think carefully before adding code below this point. + + // qDebug() << "sending mouse ev." << ev.type() << localPoint << globalPoint << ev.button() << ev.buttons() << mouseWidget << "mouse grabber" << implicit_mouse_grabber; + + QMouseEvent ev(type, localPoint, globalPoint, button, buttons, QGuiApplication::keyboardModifiers()); + +#if 0 + QList<QWeakPointer<QPlatformCursor> > cursors = QPlatformCursorPrivate::getInstances(); + foreach (QWeakPointer<QPlatformCursor> cursor, cursors) { + if (cursor) + cursor.data()->pointerEvent(ev); + } +#endif + +// int oldOpenPopupCount = openPopupCount; + QGuiApplication::sendSpontaneousEvent(mouseWidget, &ev); + +#if 0 +#ifndef QT_NO_CONTEXTMENU + if (type == QEvent::MouseButtonPress && button == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) { + QContextMenuEvent e(QContextMenuEvent::Mouse, localPoint, globalPoint, QGuiApplication::keyboardModifiers()); + QGuiApplication::sendSpontaneousEvent(mouseWidget, &e); + } +#endif // QT_NO_CONTEXTMENU +#endif +} + + +//### there's a lot of duplicated logic here -- refactoring required! + +void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e) +{ +// QPoint localPoint = ev.pos(); + QPoint globalPoint = e->globalPos; +// bool trustLocalPoint = !!tlw; //is there something the local point can be local to? + + qt_last_x = globalPoint.x(); + qt_last_y = globalPoint.y(); + + QWindow *window = e->window.data(); + if (!window) + return; + + QWidget *mouseWidget = 0;//window ? window->widget() : 0; + + // find the tlw if we didn't get it from the plugin +#if 0 + if (!mouseWindow) { + mouseWindow = QGuiApplication::topLevelAt(globalPoint); + } +#endif + + if (!mouseWidget) { + QWheelEvent ev(e->localPos, e->globalPos, e->delta, buttons, QGuiApplication::keyboardModifiers(), + e->orient); + QGuiApplication::sendSpontaneousEvent(window, &ev); + return; + } + +#if 0 + if (app_do_modal && !qt_try_modal(mouseWindow, QEvent::Wheel) ) { + qDebug() << "modal blocked wheel event" << mouseWindow; + return; + } + QPoint p = mouseWindow->mapFromGlobal(globalPoint); + QWidget *w = mouseWindow->childAt(p); + if (w) { + mouseWidget = w; + p = mouseWidget->mapFromGlobal(globalPoint); + } +#endif + + QWheelEvent ev(e->localPos, e->globalPos, e->delta, buttons, QGuiApplication::keyboardModifiers(), + e->orient); + QGuiApplication::sendSpontaneousEvent(mouseWidget, &ev); +} + + + +// Remember, Qt convention is: keyboard state is state *before* + +void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyEvent *e) +{ + QWindow *window = e->window.data(); + if (!window) + return; + + QObject *target = window;//window->widget() ? static_cast<QObject *>(window->widget()) : static_cast<QObject *>(window); + +#if 0 + QWidget *focusW = 0; + if (self->inPopupMode()) { + QWidget *popupW = qApp->activePopupWidget(); + focusW = popupW->focusWidget() ? popupW->focusWidget() : popupW; + } + if (!focusW) + focusW = QGuiApplication::focusWidget(); + if (!focusW) + focusW = window->widget(); + if (!focusW) + focusW = QGuiApplication::activeWindow(); +#endif + + //qDebug() << "handleKeyEvent" << hex << e->key() << e->modifiers() << e->text() << "widget" << focusW; + +#if 0 + if (!focusW) + return; + if (app_do_modal && !qt_try_modal(focusW, e->keyType)) + return; +#endif + + if (e->nativeScanCode || e->nativeVirtualKey || e->nativeModifiers) { + QKeyEventEx ev(e->keyType, e->key, e->modifiers, e->unicode, e->repeat, e->repeatCount, + e->nativeScanCode, e->nativeVirtualKey, e->nativeModifiers); + QGuiApplication::sendSpontaneousEvent(target, &ev); + } else { + QKeyEvent ev(e->keyType, e->key, e->modifiers, e->unicode, e->repeat, e->repeatCount); + QGuiApplication::sendSpontaneousEvent(target, &ev); + } +} + +void QGuiApplicationPrivate::processEnterEvent(QWindowSystemInterfacePrivate::EnterEvent *) +{ +// QGuiApplicationPrivate::dispatchEnterLeave(e->enter.data(),0); +// qt_last_mouse_receiver = e->enter.data(); +} + +void QGuiApplicationPrivate::processLeaveEvent(QWindowSystemInterfacePrivate::LeaveEvent *) +{ +// QGuiApplicationPrivate::dispatchEnterLeave(0,qt_last_mouse_receiver); + +#if 0 + if (e->leave.data() && !e->leave.data()->isAncestorOf(qt_last_mouse_receiver)) //(???) this should not happen + QGuiApplicationPrivate::dispatchEnterLeave(0, e->leave.data()); +#endif + qt_last_mouse_receiver = 0; + +} + +void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *) +{ +// QGuiApplication::setActiveWindow(e->activated.data()); +} + +void QGuiApplicationPrivate::processGeometryChangeEvent(QWindowSystemInterfacePrivate::GeometryChangeEvent *e) +{ + if (e->tlw.isNull()) + return; + + QWindow *window = e->tlw.data(); + if (!window) + return; + + QWidget *tlw = 0;//window->widget(); + QObject *target = tlw ? static_cast<QObject *>(tlw) : static_cast<QObject *>(window); + + QRect newRect = e->newGeometry; + QRect cr = tlw ? tlw->geometry() : window->geometry(); + + bool isResize = cr.size() != newRect.size(); + bool isMove = cr.topLeft() != newRect.topLeft(); + + if (tlw && !tlw->isWindow()) + return; //geo of native child widgets is controlled by lighthouse + //so we already have sent the events; besides this new rect + //is not mapped to parent + + + if (tlw) + tlw->data->crect = newRect; + else + window->d_func()->geometry = newRect; + + if (isResize) { + QResizeEvent e(newRect.size(), cr.size()); + QGuiApplication::sendSpontaneousEvent(target, &e); + if (tlw) + tlw->update(); + } + + if (isMove) { + //### frame geometry + QMoveEvent e(newRect.topLeft(), cr.topLeft()); + QGuiApplication::sendSpontaneousEvent(target, &e); + } +} + +void QGuiApplicationPrivate::processCloseEvent(QWindowSystemInterfacePrivate::CloseEvent *e) +{ + if (e->window.isNull()) + return; + + QCloseEvent event; + QGuiApplication::sendSpontaneousEvent(e->window.data(), &event); +} + +void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::TouchEvent *) +{ +// translateRawTouchEvent(e->widget.data(), e->devType, e->points); +} + +void QGuiApplicationPrivate::reportScreenCount(QWindowSystemInterfacePrivate::ScreenCountEvent *) +{ + // This operation only makes sense after the QGuiApplication constructor runs + if (QCoreApplication::startingUp()) + return; + + //QGuiApplication::desktop()->d_func()->updateScreenList(); + // signal anything listening for creation or deletion of screens + //QDesktopWidget *desktop = QGuiApplication::desktop(); + //emit desktop->screenCountChanged(e->count); +} + +void QGuiApplicationPrivate::reportGeometryChange(QWindowSystemInterfacePrivate::ScreenGeometryEvent *) +{ + // This operation only makes sense after the QGuiApplication constructor runs + if (QCoreApplication::startingUp()) + return; + +#if 0 + QGuiApplication::desktop()->d_func()->updateScreenList(); + + // signal anything listening for screen geometry changes + QDesktopWidget *desktop = QGuiApplication::desktop(); + emit desktop->resized(e->index); + + // make sure maximized and fullscreen windows are updated + QWidgetList list = QGuiApplication::topLevelWidgets(); + for (int i = list.size() - 1; i >= 0; --i) { + QWidget *w = list.at(i); + if (w->isFullScreen()) + w->d_func()->setFullScreenSize_helper(); + else if (w->isMaximized()) + w->d_func()->setMaxWindowState_helper(); + } +#endif +} + +void QGuiApplicationPrivate::reportAvailableGeometryChange( + QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent *) +{ + // This operation only makes sense after the QGuiApplication constructor runs + if (QCoreApplication::startingUp()) + return; + +#if 0 + QGuiApplication::desktop()->d_func()->updateScreenList(); + + // signal anything listening for screen geometry changes + QDesktopWidget *desktop = QGuiApplication::desktop(); + emit desktop->workAreaResized(e->index); + + // make sure maximized and fullscreen windows are updated + QWidgetList list = QGuiApplication::topLevelWidgets(); + for (int i = list.size() - 1; i >= 0; --i) { + QWidget *w = list.at(i); + if (w->isFullScreen()) + w->d_func()->setFullScreenSize_helper(); + else if (w->isMaximized()) + w->d_func()->setMaxWindowState_helper(); + } +#endif +} + +#ifndef QT_NO_CLIPBOARD +QClipboard * QGuiApplication::clipboard() +{ + if (QGuiApplicationPrivate::qt_clipboard == 0) { + if (!qApp) { + qWarning("QApplication: Must construct a QApplication before accessing a QClipboard"); + return 0; + } + QGuiApplicationPrivate::qt_clipboard = new QClipboard(0); + } + return QGuiApplicationPrivate::qt_clipboard; +} +#endif + +QFont QGuiApplication::font() +{ + QMutexLocker locker(applicationFontMutex()); + if (!QGuiApplicationPrivate::app_font) + QGuiApplicationPrivate::app_font = new QFont(QLatin1String("Helvetica")); + return *QGuiApplicationPrivate::app_font; +} + +void QGuiApplication::setFont(const QFont &font) +{ + QMutexLocker locker(applicationFontMutex()); + if (!QGuiApplicationPrivate::app_font) + QGuiApplicationPrivate::app_font = new QFont(font); + else + *QGuiApplicationPrivate::app_font = font; +} + +/*! + \fn bool QGuiApplication::isRightToLeft() + + Returns true if the application's layout direction is + Qt::RightToLeft; otherwise returns false. + + \sa layoutDirection(), isLeftToRight() +*/ + +/*! + \fn bool QGuiApplication::isLeftToRight() + + Returns true if the application's layout direction is + Qt::LeftToRight; otherwise returns false. + + \sa layoutDirection(), isRightToLeft() +*/ + +void QGuiApplicationPrivate::notifyLayoutDirectionChange() +{ +} + +/*! + \property QGuiApplication::layoutDirection + \brief the default layout direction for this application + + On system start-up, the default layout direction depends on the + application's language. + + \sa QWidget::layoutDirection, isLeftToRight(), isRightToLeft() + */ + +void QGuiApplication::setLayoutDirection(Qt::LayoutDirection direction) +{ + if (layout_direction == direction || direction == Qt::LayoutDirectionAuto) + return; + + layout_direction = direction; + + QGuiApplicationPrivate::self->notifyLayoutDirectionChange(); +} + +Qt::LayoutDirection QGuiApplication::layoutDirection() +{ + return layout_direction; +} + +/*! + \since 4.2 + + Returns the current keyboard input locale. +*/ +QLocale QGuiApplication::keyboardInputLocale() +{ + if (!QGuiApplicationPrivate::checkInstance("keyboardInputLocale")) + return QLocale::c(); + return qt_keymapper_private()->keyboardInputLocale; +} + +/*! + \since 4.2 + + Returns the current keyboard input direction. +*/ +Qt::LayoutDirection QGuiApplication::keyboardInputDirection() +{ + if (!QGuiApplicationPrivate::checkInstance("keyboardInputDirection")) + return Qt::LeftToRight; + return qt_keymapper_private()->keyboardInputDirection; +} + +/*! + \since 4.5 + \fn void QGuiApplication::fontDatabaseChanged() + + This signal is emitted when application fonts are loaded or removed. + + \sa QFontDatabase::addApplicationFont(), + QFontDatabase::addApplicationFontFromData(), + QFontDatabase::removeAllApplicationFonts(), + QFontDatabase::removeApplicationFont() +*/ + + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qguiapplication.h b/src/gui/guikernel/qguiapplication.h new file mode 100644 index 0000000000..16f56d0a51 --- /dev/null +++ b/src/gui/guikernel/qguiapplication.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGUIAPPLICATION_QPA_H +#define QGUIAPPLICATION_QPA_H + +#include <QtCore/qcoreapplication.h> +#include <QtGui/qwindowdefs.h> +#include <QtCore/qpoint.h> +#include <QtCore/qsize.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGuiApplicationPrivate; +class QPlatformNativeInterface; + +class Q_GUI_EXPORT QGuiApplication : public QCoreApplication +{ + Q_OBJECT + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection) + Q_PROPERTY(int doubleClickInterval READ doubleClickInterval WRITE setDoubleClickInterval) + Q_PROPERTY(int keyboardInputInterval READ keyboardInputInterval WRITE setKeyboardInputInterval) + +public: + QGuiApplication(int &argc, char **argv, int = ApplicationFlags); + virtual ~QGuiApplication(); + +#if 0 +#ifndef QT_NO_CURSOR + static QCursor *overrideCursor(); + static void setOverrideCursor(const QCursor &); + static void changeOverrideCursor(const QCursor &); + static void restoreOverrideCursor(); +#endif +#endif + + static QFont font(); + static void setFont(const QFont &); + +#ifndef QT_NO_CLIPBOARD + static QClipboard *clipboard(); +#endif + + static Qt::KeyboardModifiers keyboardModifiers(); + static Qt::MouseButtons mouseButtons(); + + static void setDoubleClickInterval(int); + static int doubleClickInterval(); + + static void setKeyboardInputInterval(int); + static int keyboardInputInterval(); + + static void setLayoutDirection(Qt::LayoutDirection direction); + static Qt::LayoutDirection layoutDirection(); + + static inline bool isRightToLeft() { return layoutDirection() == Qt::RightToLeft; } + static inline bool isLeftToRight() { return layoutDirection() == Qt::LeftToRight; } + + static QLocale keyboardInputLocale(); + static Qt::LayoutDirection keyboardInputDirection(); + + static QPlatformNativeInterface *platformNativeInterface(); + + static int exec(); + bool notify(QObject *, QEvent *); + +Q_SIGNALS: + void fontDatabaseChanged(); + +protected: + bool event(QEvent *); + bool compressEvent(QEvent *, QObject *receiver, QPostEventList *); + + QGuiApplication(QGuiApplicationPrivate &p); + +private: + Q_DISABLE_COPY(QGuiApplication) + Q_DECLARE_PRIVATE(QGuiApplication) + +#ifndef QT_NO_GESTURES + friend class QGestureManager; +#endif + friend class QFontDatabasePrivate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGUIAPPLICATION_QPA_H diff --git a/src/gui/guikernel/qguiapplication_p.h b/src/gui/guikernel/qguiapplication_p.h new file mode 100644 index 0000000000..2cc2e54f6c --- /dev/null +++ b/src/gui/guikernel/qguiapplication_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGUIAPPLICATION_QPA_P_H +#define QGUIAPPLICATION_QPA_P_H + +#include <QtGui/qguiapplication.h> + +#include <QtCore/private/qcoreapplication_p.h> + +#include <QtCore/private/qthread_p.h> + +#include <QWindowSystemInterface> +#include "private/qwindowsysteminterface_qpa_p.h" +#include "QtGui/qplatformintegration_qpa.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformIntegration; + +class Q_GUI_EXPORT QGuiApplicationPrivate : public QCoreApplicationPrivate +{ + Q_DECLARE_PUBLIC(QGuiApplication) +public: + QGuiApplicationPrivate(int &argc, char **argv, int flags); + ~QGuiApplicationPrivate(); + + void createEventDispatcher(); + + virtual void notifyLayoutDirectionChange(); + + static int keyboard_input_time; + static int mouse_double_click_time; + + static Qt::KeyboardModifiers modifier_buttons; + static Qt::MouseButtons mouse_buttons; + + static QPlatformIntegration *platform_integration; + + static QPlatformIntegration *platformIntegration() + { return platform_integration; } + + static QAbstractEventDispatcher *qt_qpa_core_dispatcher() + { return QCoreApplication::instance()->d_func()->threadData->eventDispatcher; } + + static void processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e); + static void processKeyEvent(QWindowSystemInterfacePrivate::KeyEvent *e); + static void processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e); + static void processTouchEvent(QWindowSystemInterfacePrivate::TouchEvent *e); + + static void processCloseEvent(QWindowSystemInterfacePrivate::CloseEvent *e); + + static void processGeometryChangeEvent(QWindowSystemInterfacePrivate::GeometryChangeEvent *e); + + static void processEnterEvent(QWindowSystemInterfacePrivate::EnterEvent *e); + static void processLeaveEvent(QWindowSystemInterfacePrivate::LeaveEvent *e); + + static void processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e); + + static void processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e); + + static void reportScreenCount(QWindowSystemInterfacePrivate::ScreenCountEvent *e); + static void reportGeometryChange(QWindowSystemInterfacePrivate::ScreenGeometryEvent *e); + static void reportAvailableGeometryChange(QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent *e); + + static inline Qt::Alignment visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment) + { + if (!(alignment & Qt::AlignHorizontal_Mask)) + alignment |= Qt::AlignLeft; + if ((alignment & Qt::AlignAbsolute) == 0 && (alignment & (Qt::AlignLeft | Qt::AlignRight))) { + if (direction == Qt::RightToLeft) + alignment ^= (Qt::AlignLeft | Qt::AlignRight); + alignment |= Qt::AlignAbsolute; + } + return alignment; + } + + + static bool app_do_modal; + + static QPointer<QWidget> qt_last_mouse_receiver; + + static QWidgetList qt_modal_stack; + + static Qt::MouseButtons buttons; + static ulong mousePressTime; + static Qt::MouseButton mousePressButton; + static int mousePressX; + static int mousePressY; + static int mouse_double_click_distance; + +#ifndef QT_NO_CLIPBOARD + static QClipboard *qt_clipboard; +#endif + + static QFont *app_font; +private: + void init(); + + static QGuiApplicationPrivate *self; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGUIAPPLICATION_QPA_P_H diff --git a/src/gui/guikernel/qguivariant.cpp b/src/gui/guikernel/qguivariant.cpp new file mode 100644 index 0000000000..ef0cfc987c --- /dev/null +++ b/src/gui/guikernel/qguivariant.cpp @@ -0,0 +1,800 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvariant.h" + +#include "qbitmap.h" +#include "qbrush.h" +#include "qcolor.h" +#include "qcursor.h" +#include "qdatastream.h" +#include "qdebug.h" +#include "qfont.h" +#include "qicon.h" +#include "qimage.h" +#include "qkeysequence.h" +#include "qtransform.h" +#include "qmatrix.h" +#include "qpalette.h" +#include "qpen.h" +#include "qpixmap.h" +#include "qpolygon.h" +#include "qregion.h" +#include "qsizepolicy.h" +#include "qtextformat.h" +#include "qmatrix4x4.h" +#include "qvector2d.h" +#include "qvector3d.h" +#include "qvector4d.h" +#include "qquaternion.h" + +#include "private/qvariant_p.h" + +QT_BEGIN_NAMESPACE + + +Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler(); + +static void construct(QVariant::Private *x, const void *copy) +{ + switch (x->type) { + case QVariant::Bitmap: + v_construct<QBitmap>(x, copy); + break; + case QVariant::Region: + v_construct<QRegion>(x, copy); + break; + case QVariant::Polygon: + v_construct<QPolygon>(x, copy); + break; + case QVariant::Font: + v_construct<QFont>(x, copy); + break; + case QVariant::Pixmap: + v_construct<QPixmap>(x, copy); + break; + case QVariant::Image: + v_construct<QImage>(x, copy); + break; + case QVariant::Brush: + v_construct<QBrush>(x, copy); + break; + case QVariant::Color: + v_construct<QColor>(x, copy); + break; + case QVariant::Palette: + v_construct<QPalette>(x, copy); + break; +#ifndef QT_NO_ICON + case QVariant::Icon: + v_construct<QIcon>(x, copy); + break; +#endif + case QVariant::Matrix: + v_construct<QMatrix>(x, copy); + break; + case QVariant::Transform: + v_construct<QTransform>(x, copy); + break; + case QVariant::TextFormat: + v_construct<QTextFormat>(x, copy); + break; + case QVariant::TextLength: + v_construct<QTextLength>(x, copy); + break; +#ifndef QT_NO_SHORTCUT + case QVariant::KeySequence: + v_construct<QKeySequence>(x, copy); + break; +#endif + case QVariant::Pen: + v_construct<QPen>(x, copy); + break; + case QVariant::SizePolicy: + v_construct<QSizePolicy>(x, copy); + break; +#ifndef QT_NO_CURSOR + case QVariant::Cursor: + v_construct<QCursor>(x, copy); + break; +#endif + case 62: { + // small 'trick' to let a QVariant(Qt::blue) create a variant + // of type QColor + x->type = QVariant::Color; + QColor color(*reinterpret_cast<const Qt::GlobalColor *>(copy)); + v_construct<QColor>(x, &color); + break; + } +#ifndef QT_NO_MATRIX4X4 + case QVariant::Matrix4x4: + v_construct<QMatrix4x4>(x, copy); + break; +#endif +#ifndef QT_NO_VECTOR2D + case QVariant::Vector2D: + v_construct<QVector2D>(x, copy); + break; +#endif +#ifndef QT_NO_VECTOR3D + case QVariant::Vector3D: + v_construct<QVector3D>(x, copy); + break; +#endif +#ifndef QT_NO_VECTOR4D + case QVariant::Vector4D: + v_construct<QVector4D>(x, copy); + break; +#endif +#ifndef QT_NO_QUATERNION + case QVariant::Quaternion: + v_construct<QQuaternion>(x, copy); + break; +#endif + default: + qcoreVariantHandler()->construct(x, copy); + return; + } + x->is_null = !copy; +} + +static void clear(QVariant::Private *d) +{ + switch (d->type) { + case QVariant::Bitmap: + v_clear<QBitmap>(d); + break; + case QVariant::Cursor: + v_clear<QCursor>(d); + break; + case QVariant::Region: + v_clear<QRegion>(d); + break; + case QVariant::Polygon: + v_clear<QPolygon>(d); + break; + case QVariant::Font: + v_clear<QFont>(d); + break; + case QVariant::Pixmap: + v_clear<QPixmap>(d); + break; + case QVariant::Image: + v_clear<QImage>(d); + break; + case QVariant::Brush: + v_clear<QBrush>(d); + break; + case QVariant::Color: + v_clear<QColor>(d); + break; + case QVariant::Palette: + v_clear<QPalette>(d); + break; +#ifndef QT_NO_ICON + case QVariant::Icon: + v_clear<QIcon>(d); + break; +#endif + case QVariant::Matrix: + v_clear<QMatrix>(d); + break; + case QVariant::Transform: + v_clear<QTransform>(d); + break; + case QVariant::TextFormat: + v_clear<QTextFormat>(d); + break; + case QVariant::TextLength: + v_clear<QTextLength>(d); + break; + case QVariant::SizePolicy: + v_clear<QSizePolicy>(d); + break; +#ifndef QT_NO_SHORTCUT + case QVariant::KeySequence: + v_clear<QKeySequence>(d); + break; +#endif + case QVariant::Pen: + v_clear<QPen>(d); + break; +#ifndef QT_NO_MATRIX4X4 + case QVariant::Matrix4x4: + v_clear<QMatrix4x4>(d); + break; +#endif +#ifndef QT_NO_VECTOR2D + case QVariant::Vector2D: + v_clear<QVector2D>(d); + break; +#endif +#ifndef QT_NO_VECTOR3D + case QVariant::Vector3D: + v_clear<QVector3D>(d); + break; +#endif +#ifndef QT_NO_VECTOR4D + case QVariant::Vector4D: + v_clear<QVector4D>(d); + break; +#endif +#ifndef QT_NO_QUATERNION + case QVariant::Quaternion: + v_clear<QVector4D>(d); + break; +#endif + default: + qcoreVariantHandler()->clear(d); + return; + } + + d->type = QVariant::Invalid; + d->is_null = true; + d->is_shared = false; +} + + +static bool isNull(const QVariant::Private *d) +{ + switch(d->type) { + case QVariant::Bitmap: + return v_cast<QBitmap>(d)->isNull(); + case QVariant::Region: + return v_cast<QRegion>(d)->isEmpty(); + case QVariant::Polygon: + return v_cast<QPolygon>(d)->isEmpty(); + case QVariant::Pixmap: + return v_cast<QPixmap>(d)->isNull(); + case QVariant::Image: + return v_cast<QImage>(d)->isNull(); +#ifndef QT_NO_ICON + case QVariant::Icon: + return v_cast<QIcon>(d)->isNull(); +#endif + case QVariant::Matrix: + case QVariant::TextFormat: + case QVariant::TextLength: + case QVariant::Cursor: + case QVariant::StringList: + case QVariant::Font: + case QVariant::Brush: + case QVariant::Color: + case QVariant::Palette: + case QVariant::SizePolicy: +#ifndef QT_NO_SHORTCUT + case QVariant::KeySequence: +#endif + case QVariant::Pen: +#ifndef QT_NO_MATRIX4X4 + case QVariant::Matrix4x4: +#endif + break; +#ifndef QT_NO_VECTOR2D + case QVariant::Vector2D: + return v_cast<QVector2D>(d)->isNull(); +#endif +#ifndef QT_NO_VECTOR3D + case QVariant::Vector3D: + return v_cast<QVector3D>(d)->isNull(); +#endif +#ifndef QT_NO_VECTOR4D + case QVariant::Vector4D: + return v_cast<QVector4D>(d)->isNull(); +#endif +#ifndef QT_NO_QUATERNION + case QVariant::Quaternion: + return v_cast<QQuaternion>(d)->isNull(); +#endif + default: + return qcoreVariantHandler()->isNull(d); + } + return d->is_null; +} + +static bool compare(const QVariant::Private *a, const QVariant::Private *b) +{ + Q_ASSERT(a->type == b->type); + switch(a->type) { + case QVariant::Cursor: +#ifndef QT_NO_CURSOR + return v_cast<QCursor>(a)->shape() == v_cast<QCursor>(b)->shape(); +#endif + case QVariant::Bitmap: + return v_cast<QBitmap>(a)->cacheKey() + == v_cast<QBitmap>(b)->cacheKey(); + case QVariant::Polygon: + return *v_cast<QPolygon>(a) == *v_cast<QPolygon>(b); + case QVariant::Region: + return *v_cast<QRegion>(a) == *v_cast<QRegion>(b); + case QVariant::Font: + return *v_cast<QFont>(a) == *v_cast<QFont>(b); + case QVariant::Pixmap: + return v_cast<QPixmap>(a)->cacheKey() == v_cast<QPixmap>(b)->cacheKey(); + case QVariant::Image: + return *v_cast<QImage>(a) == *v_cast<QImage>(b); + case QVariant::Brush: + return *v_cast<QBrush>(a) == *v_cast<QBrush>(b); + case QVariant::Color: + return *v_cast<QColor>(a) == *v_cast<QColor>(b); + case QVariant::Palette: + return *v_cast<QPalette>(a) == *v_cast<QPalette>(b); +#ifndef QT_NO_ICON + case QVariant::Icon: + /* QIcon::operator==() cannot be reasonably implemented for QIcon, + * so we always return false. */ + return false; +#endif + case QVariant::Matrix: + return *v_cast<QMatrix>(a) == *v_cast<QMatrix>(b); + case QVariant::Transform: + return *v_cast<QTransform>(a) == *v_cast<QTransform>(b); + case QVariant::TextFormat: + return *v_cast<QTextFormat>(a) == *v_cast<QTextFormat>(b); + case QVariant::TextLength: + return *v_cast<QTextLength>(a) == *v_cast<QTextLength>(b); + case QVariant::SizePolicy: + return *v_cast<QSizePolicy>(a) == *v_cast<QSizePolicy>(b); +#ifndef QT_NO_SHORTCUT + case QVariant::KeySequence: + return *v_cast<QKeySequence>(a) == *v_cast<QKeySequence>(b); +#endif + case QVariant::Pen: + return *v_cast<QPen>(a) == *v_cast<QPen>(b); +#ifndef QT_NO_MATRIX4X4 + case QVariant::Matrix4x4: + return *v_cast<QMatrix4x4>(a) == *v_cast<QMatrix4x4>(b); +#endif +#ifndef QT_NO_VECTOR2D + case QVariant::Vector2D: + return *v_cast<QVector2D>(a) == *v_cast<QVector2D>(b); +#endif +#ifndef QT_NO_VECTOR3D + case QVariant::Vector3D: + return *v_cast<QVector3D>(a) == *v_cast<QVector3D>(b); +#endif +#ifndef QT_NO_VECTOR4D + case QVariant::Vector4D: + return *v_cast<QVector4D>(a) == *v_cast<QVector4D>(b); +#endif +#ifndef QT_NO_QUATERNION + case QVariant::Quaternion: + return *v_cast<QQuaternion>(a) == *v_cast<QQuaternion>(b); +#endif + default: + break; + } + return qcoreVariantHandler()->compare(a, b); +} + + + +static bool convert(const QVariant::Private *d, QVariant::Type t, + void *result, bool *ok) +{ + switch (t) { + case QVariant::ByteArray: + if (d->type == QVariant::Color) { + *static_cast<QByteArray *>(result) = v_cast<QColor>(d)->name().toLatin1(); + return true; + } + break; + case QVariant::String: { + QString *str = static_cast<QString *>(result); + switch (d->type) { +#ifndef QT_NO_SHORTCUT + case QVariant::KeySequence: + *str = QString(*v_cast<QKeySequence>(d)); + return true; +#endif + case QVariant::Font: + *str = v_cast<QFont>(d)->toString(); + return true; + case QVariant::Color: + *str = v_cast<QColor>(d)->name(); + return true; + default: + break; + } + break; + } + case QVariant::Pixmap: + if (d->type == QVariant::Image) { + *static_cast<QPixmap *>(result) = QPixmap::fromImage(*v_cast<QImage>(d)); + return true; + } else if (d->type == QVariant::Bitmap) { + *static_cast<QPixmap *>(result) = *v_cast<QBitmap>(d); + return true; + } else if (d->type == QVariant::Brush) { + if (v_cast<QBrush>(d)->style() == Qt::TexturePattern) { + *static_cast<QPixmap *>(result) = v_cast<QBrush>(d)->texture(); + return true; + } + } + break; + case QVariant::Image: + if (d->type == QVariant::Pixmap) { + *static_cast<QImage *>(result) = v_cast<QPixmap>(d)->toImage(); + return true; + } else if (d->type == QVariant::Bitmap) { + *static_cast<QImage *>(result) = v_cast<QBitmap>(d)->toImage(); + return true; + } + break; + case QVariant::Bitmap: + if (d->type == QVariant::Pixmap) { + *static_cast<QBitmap *>(result) = *v_cast<QPixmap>(d); + return true; + } else if (d->type == QVariant::Image) { + *static_cast<QBitmap *>(result) = QBitmap::fromImage(*v_cast<QImage>(d)); + return true; + } + break; +#ifndef QT_NO_SHORTCUT + case QVariant::Int: + if (d->type == QVariant::KeySequence) { + *static_cast<int *>(result) = (int)(*(v_cast<QKeySequence>(d))); + return true; + } + break; +#endif + case QVariant::Font: + if (d->type == QVariant::String) { + QFont *f = static_cast<QFont *>(result); + f->fromString(*v_cast<QString>(d)); + return true; + } + break; + case QVariant::Color: + if (d->type == QVariant::String) { + static_cast<QColor *>(result)->setNamedColor(*v_cast<QString>(d)); + return static_cast<QColor *>(result)->isValid(); + } else if (d->type == QVariant::ByteArray) { + static_cast<QColor *>(result)->setNamedColor(QString::fromLatin1( + *v_cast<QByteArray>(d))); + return true; + } else if (d->type == QVariant::Brush) { + if (v_cast<QBrush>(d)->style() == Qt::SolidPattern) { + *static_cast<QColor *>(result) = v_cast<QBrush>(d)->color(); + return true; + } + } + break; + case QVariant::Brush: + if (d->type == QVariant::Color) { + *static_cast<QBrush *>(result) = QBrush(*v_cast<QColor>(d)); + return true; + } else if (d->type == QVariant::Pixmap) { + *static_cast<QBrush *>(result) = QBrush(*v_cast<QPixmap>(d)); + return true; + } + break; +#ifndef QT_NO_SHORTCUT + case QVariant::KeySequence: { + QKeySequence *seq = static_cast<QKeySequence *>(result); + switch (d->type) { + case QVariant::String: + *seq = QKeySequence(*v_cast<QString>(d)); + return true; + case QVariant::Int: + *seq = QKeySequence(d->data.i); + return true; + default: + break; + } + } +#endif + default: + break; + } + return qcoreVariantHandler()->convert(d, t, result, ok); +} + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM) +static void streamDebug(QDebug dbg, const QVariant &v) +{ + switch(v.type()) { + case QVariant::Cursor: +#ifndef QT_NO_CURSOR +// dbg.nospace() << qvariant_cast<QCursor>(v); //FIXME +#endif + break; + case QVariant::Bitmap: +// dbg.nospace() << qvariant_cast<QBitmap>(v); //FIXME + break; + case QVariant::Polygon: + dbg.nospace() << qvariant_cast<QPolygon>(v); + break; + case QVariant::Region: + dbg.nospace() << qvariant_cast<QRegion>(v); + break; + case QVariant::Font: +// dbg.nospace() << qvariant_cast<QFont>(v); //FIXME + break; + case QVariant::Matrix: + dbg.nospace() << qvariant_cast<QMatrix>(v); + break; + case QVariant::Transform: + dbg.nospace() << qvariant_cast<QTransform>(v); + break; + case QVariant::Pixmap: +// dbg.nospace() << qvariant_cast<QPixmap>(v); //FIXME + break; + case QVariant::Image: +// dbg.nospace() << qvariant_cast<QImage>(v); //FIXME + break; + case QVariant::Brush: + dbg.nospace() << qvariant_cast<QBrush>(v); + break; + case QVariant::Color: + dbg.nospace() << qvariant_cast<QColor>(v); + break; + case QVariant::Palette: +// dbg.nospace() << qvariant_cast<QPalette>(v); //FIXME + break; +#ifndef QT_NO_ICON + case QVariant::Icon: +// dbg.nospace() << qvariant_cast<QIcon>(v); // FIXME + break; +#endif + case QVariant::SizePolicy: +// dbg.nospace() << qvariant_cast<QSizePolicy>(v); //FIXME + break; +#ifndef QT_NO_SHORTCUT + case QVariant::KeySequence: + dbg.nospace() << qvariant_cast<QKeySequence>(v); + break; +#endif + case QVariant::Pen: + dbg.nospace() << qvariant_cast<QPen>(v); + break; +#ifndef QT_NO_MATRIX4X4 + case QVariant::Matrix4x4: + dbg.nospace() << qvariant_cast<QMatrix4x4>(v); + break; +#endif +#ifndef QT_NO_VECTOR2D + case QVariant::Vector2D: + dbg.nospace() << qvariant_cast<QVector2D>(v); + break; +#endif +#ifndef QT_NO_VECTOR3D + case QVariant::Vector3D: + dbg.nospace() << qvariant_cast<QVector3D>(v); + break; +#endif +#ifndef QT_NO_VECTOR4D + case QVariant::Vector4D: + dbg.nospace() << qvariant_cast<QVector4D>(v); + break; +#endif +#ifndef QT_NO_QUATERNION + case QVariant::Quaternion: + dbg.nospace() << qvariant_cast<QQuaternion>(v); + break; +#endif + default: + qcoreVariantHandler()->debugStream(dbg, v); + break; + } +} +#endif + +const QVariant::Handler qt_gui_variant_handler = { + construct, + clear, + isNull, +#ifndef QT_NO_DATASTREAM + 0, + 0, +#endif + compare, + convert, + 0, +#if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM) + streamDebug +#else + 0 +#endif +}; + +struct QMetaTypeGuiHelper +{ + QMetaType::Constructor constr; + QMetaType::Destructor destr; +#ifndef QT_NO_DATASTREAM + QMetaType::SaveOperator saveOp; + QMetaType::LoadOperator loadOp; +#endif +}; + +extern Q_CORE_EXPORT const QMetaTypeGuiHelper *qMetaTypeGuiHelper; + + +#ifdef QT_NO_DATASTREAM +# define Q_DECL_METATYPE_HELPER(TYPE) \ + typedef void *(*QConstruct##TYPE)(const TYPE *); \ + static const QConstruct##TYPE qConstruct##TYPE = qMetaTypeConstructHelper<TYPE>; \ + typedef void (*QDestruct##TYPE)(TYPE *); \ + static const QDestruct##TYPE qDestruct##TYPE = qMetaTypeDeleteHelper<TYPE>; +#else +# define Q_DECL_METATYPE_HELPER(TYPE) \ + typedef void *(*QConstruct##TYPE)(const TYPE *); \ + static const QConstruct##TYPE qConstruct##TYPE = qMetaTypeConstructHelper<TYPE>; \ + typedef void (*QDestruct##TYPE)(TYPE *); \ + static const QDestruct##TYPE qDestruct##TYPE = qMetaTypeDeleteHelper<TYPE>; \ + typedef void (*QSave##TYPE)(QDataStream &, const TYPE *); \ + static const QSave##TYPE qSave##TYPE = qMetaTypeSaveHelper<TYPE>; \ + typedef void (*QLoad##TYPE)(QDataStream &, TYPE *); \ + static const QLoad##TYPE qLoad##TYPE = qMetaTypeLoadHelper<TYPE>; +#endif + +Q_DECL_METATYPE_HELPER(QFont) +Q_DECL_METATYPE_HELPER(QPixmap) +Q_DECL_METATYPE_HELPER(QBrush) +Q_DECL_METATYPE_HELPER(QColor) +Q_DECL_METATYPE_HELPER(QPalette) +#ifndef QT_NO_ICON +Q_DECL_METATYPE_HELPER(QIcon) +#endif +Q_DECL_METATYPE_HELPER(QImage) +Q_DECL_METATYPE_HELPER(QPolygon) +Q_DECL_METATYPE_HELPER(QRegion) +Q_DECL_METATYPE_HELPER(QBitmap) +#ifndef QT_NO_CURSOR +Q_DECL_METATYPE_HELPER(QCursor) +#endif +Q_DECL_METATYPE_HELPER(QSizePolicy) +#ifndef QT_NO_SHORTCUT +Q_DECL_METATYPE_HELPER(QKeySequence) +#endif +Q_DECL_METATYPE_HELPER(QPen) +Q_DECL_METATYPE_HELPER(QTextLength) +Q_DECL_METATYPE_HELPER(QTextFormat) +Q_DECL_METATYPE_HELPER(QMatrix) +Q_DECL_METATYPE_HELPER(QTransform) +#ifndef QT_NO_MATRIX4X4 +Q_DECL_METATYPE_HELPER(QMatrix4x4) +#endif +#ifndef QT_NO_VECTOR2D +Q_DECL_METATYPE_HELPER(QVector2D) +#endif +#ifndef QT_NO_VECTOR3D +Q_DECL_METATYPE_HELPER(QVector3D) +#endif +#ifndef QT_NO_VECTOR4D +Q_DECL_METATYPE_HELPER(QVector4D) +#endif +#ifndef QT_NO_QUATERNION +Q_DECL_METATYPE_HELPER(QQuaternion) +#endif + +#ifdef QT_NO_DATASTREAM +# define Q_IMPL_METATYPE_HELPER(TYPE) \ + { reinterpret_cast<QMetaType::Constructor>(qConstruct##TYPE), \ + reinterpret_cast<QMetaType::Destructor>(qDestruct##TYPE) } +#else +# define Q_IMPL_METATYPE_HELPER(TYPE) \ + { reinterpret_cast<QMetaType::Constructor>(qConstruct##TYPE), \ + reinterpret_cast<QMetaType::Destructor>(qDestruct##TYPE), \ + reinterpret_cast<QMetaType::SaveOperator>(qSave##TYPE), \ + reinterpret_cast<QMetaType::LoadOperator>(qLoad##TYPE) \ + } +#endif + +static const QMetaTypeGuiHelper qVariantGuiHelper[] = { + {0, 0, 0, 0}, + Q_IMPL_METATYPE_HELPER(QFont), + Q_IMPL_METATYPE_HELPER(QPixmap), + Q_IMPL_METATYPE_HELPER(QBrush), + Q_IMPL_METATYPE_HELPER(QColor), + Q_IMPL_METATYPE_HELPER(QPalette), +#ifdef QT_NO_ICON + {0, 0, 0, 0}, +#else + Q_IMPL_METATYPE_HELPER(QIcon), +#endif + Q_IMPL_METATYPE_HELPER(QImage), + Q_IMPL_METATYPE_HELPER(QPolygon), + Q_IMPL_METATYPE_HELPER(QRegion), + Q_IMPL_METATYPE_HELPER(QBitmap), +#ifdef QT_NO_CURSOR + {0, 0, 0, 0}, +#else + Q_IMPL_METATYPE_HELPER(QCursor), +#endif + Q_IMPL_METATYPE_HELPER(QSizePolicy), +#ifdef QT_NO_SHORTCUT + {0, 0, 0, 0}, +#else + Q_IMPL_METATYPE_HELPER(QKeySequence), +#endif + Q_IMPL_METATYPE_HELPER(QPen), + Q_IMPL_METATYPE_HELPER(QTextLength), + Q_IMPL_METATYPE_HELPER(QTextFormat), + Q_IMPL_METATYPE_HELPER(QMatrix), + Q_IMPL_METATYPE_HELPER(QTransform), +#ifndef QT_NO_MATRIX4X4 + Q_IMPL_METATYPE_HELPER(QMatrix4x4), +#else + {0, 0, 0, 0}, +#endif +#ifndef QT_NO_VECTOR2D + Q_IMPL_METATYPE_HELPER(QVector2D), +#else + {0, 0, 0, 0}, +#endif +#ifndef QT_NO_VECTOR3D + Q_IMPL_METATYPE_HELPER(QVector3D), +#else + {0, 0, 0, 0}, +#endif +#ifndef QT_NO_VECTOR4D + Q_IMPL_METATYPE_HELPER(QVector4D), +#else + {0, 0, 0, 0}, +#endif +#ifndef QT_NO_QUATERNION + Q_IMPL_METATYPE_HELPER(QQuaternion) +#else + {0, 0, 0, 0} +#endif +}; + +static const QVariant::Handler *qt_guivariant_last_handler = 0; +int qRegisterGuiVariant() +{ + qt_guivariant_last_handler = QVariant::handler; + QVariant::handler = &qt_gui_variant_handler; + qMetaTypeGuiHelper = qVariantGuiHelper; + return 1; +} +Q_CONSTRUCTOR_FUNCTION(qRegisterGuiVariant) + +int qUnregisterGuiVariant() +{ + QVariant::handler = qt_guivariant_last_handler; + qMetaTypeGuiHelper = 0; + return 1; +} +Q_DESTRUCTOR_FUNCTION(qUnregisterGuiVariant) + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qhexstring_p.h b/src/gui/guikernel/qhexstring_p.h new file mode 100644 index 0000000000..3c8d562756 --- /dev/null +++ b/src/gui/guikernel/qhexstring_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qglobal.h> +#include <QtCore/qpoint.h> +#include <QtCore/qstring.h> +#include <QtGui/qpolygon.h> +#include <QtCore/qstringbuilder.h> + +#ifndef QHEXSTRING_P_H +#define QHEXSTRING_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +// internal helper. Converts an integer value to an unique string token +template <typename T> + struct HexString +{ + inline HexString(const T t) + : val(t) + {} + + inline void write(QChar *&dest) const + { + const ushort hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + const char *c = reinterpret_cast<const char *>(&val); + for (uint i = 0; i < sizeof(T); ++i) { + *dest++ = hexChars[*c & 0xf]; + *dest++ = hexChars[(*c & 0xf0) >> 4]; + ++c; + } + } + const T val; +}; + +// specialization to enable fast concatenating of our string tokens to a string +template <typename T> + struct QConcatenable<HexString<T> > +{ + typedef HexString<T> type; + enum { ExactSize = true }; + static int size(const HexString<T> &) { return sizeof(T) * 2; } + static inline void appendTo(const HexString<T> &str, QChar *&out) { str.write(out); } + typedef QString ConvertTo; +}; + +QT_END_NAMESPACE + +#endif // QHEXSTRING_P_H diff --git a/src/gui/guikernel/qkeymapper.cpp b/src/gui/guikernel/qkeymapper.cpp new file mode 100644 index 0000000000..e6ba3ce142 --- /dev/null +++ b/src/gui/guikernel/qkeymapper.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qapplication.h" + +#include <private/qobject_p.h> +#include "qkeymapper_p.h" +#include <qwidget.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QKeyMapper + \since 4.2 + \internal + + \sa QObject +*/ + +/*! + Constructs a new key mapper. +*/ +QKeyMapper::QKeyMapper() + : QObject(*new QKeyMapperPrivate, 0) +{ +} + +/*! + Destroys the key mapper. +*/ +QKeyMapper::~QKeyMapper() +{ +} + +QList<int> QKeyMapper::possibleKeys(QKeyEvent *e) +{ + QList<int> result; + + if (!e->nativeScanCode()) { + if (e->key() && (e->key() != Qt::Key_unknown)) + result << int(e->key() + e->modifiers()); + else if (!e->text().isEmpty()) + result << int(e->text().at(0).unicode() + e->modifiers()); + return result; + } + + return instance()->d_func()->possibleKeys(e); +} + +extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // in qapplication_*.cpp +void QKeyMapper::changeKeyboard() +{ + instance()->d_func()->clearMappings(); + + // inform all toplevel widgets of the change + QEvent e(QEvent::KeyboardLayoutChange); + QWidgetList list = QApplication::topLevelWidgets(); + for (int i = 0; i < list.size(); ++i) { + QWidget *w = list.at(i); + qt_sendSpontaneousEvent(w, &e); + } +} + +Q_GLOBAL_STATIC(QKeyMapper, keymapper) +/*! + Returns the pointer to the single instance of QKeyMapper in the application. + If none yet exists, the function ensures that one is created. +*/ +QKeyMapper *QKeyMapper::instance() +{ + return keymapper(); +} + +QKeyMapperPrivate *qt_keymapper_private() +{ + return QKeyMapper::instance()->d_func(); +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qkeymapper_mac.cpp b/src/gui/guikernel/qkeymapper_mac.cpp new file mode 100644 index 0000000000..d3bbf89711 --- /dev/null +++ b/src/gui/guikernel/qkeymapper_mac.cpp @@ -0,0 +1,1023 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qt_mac_p.h> +#include <qdebug.h> +#include <qevent.h> +#include <private/qevent_p.h> +#include <qtextcodec.h> +#include <qapplication.h> +#include <qinputcontext.h> +#include <private/qkeymapper_p.h> +#include <private/qapplication_p.h> +#include <private/qmacinputcontext_p.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +/***************************************************************************** + QKeyMapper debug facilities + *****************************************************************************/ +//#define DEBUG_KEY_BINDINGS +//#define DEBUG_KEY_BINDINGS_MODIFIERS +//#define DEBUG_KEY_MAPS + +/***************************************************************************** + Internal variables and functions + *****************************************************************************/ +bool qt_mac_eat_unicode_key = false; +extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); //qapplication_mac.cpp + +Q_GUI_EXPORT void qt_mac_secure_keyboard(bool b) +{ + static bool secure = false; + if (b != secure){ + b ? EnableSecureEventInput() : DisableSecureEventInput(); + secure = b; + } +} + +/* + \internal + A Mac KeyboardLayoutItem has 8 possible states: + 1. Unmodified + 2. Shift + 3. Control + 4. Control + Shift + 5. Alt + 6. Alt + Shift + 7. Alt + Control + 8. Alt + Control + Shift + 9. Meta + 10. Meta + Shift + 11. Meta + Control + 12. Meta + Control + Shift + 13. Meta + Alt + 14. Meta + Alt + Shift + 15. Meta + Alt + Control + 16. Meta + Alt + Control + Shift +*/ +struct KeyboardLayoutItem { + bool dirty; + quint32 qtKey[16]; // Can by any Qt::Key_<foo>, or unicode character +}; + +// Possible modifier states. +// NOTE: The order of these states match the order in QKeyMapperPrivate::updatePossibleKeyCodes()! +static const Qt::KeyboardModifiers ModsTbl[] = { + Qt::NoModifier, // 0 + Qt::ShiftModifier, // 1 + Qt::ControlModifier, // 2 + Qt::ControlModifier | Qt::ShiftModifier, // 3 + Qt::AltModifier, // 4 + Qt::AltModifier | Qt::ShiftModifier, // 5 + Qt::AltModifier | Qt::ControlModifier, // 6 + Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 + Qt::MetaModifier, // 8 + Qt::MetaModifier | Qt::ShiftModifier, // 9 + Qt::MetaModifier | Qt::ControlModifier, // 10 + Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier,// 11 + Qt::MetaModifier | Qt::AltModifier, // 12 + Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier, // 13 + Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier, // 14 + Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 15 +}; + +/* key maps */ +struct qt_mac_enum_mapper +{ + int mac_code; + int qt_code; +#if defined(DEBUG_KEY_BINDINGS) +# define QT_MAC_MAP_ENUM(x) x, #x + const char *desc; +#else +# define QT_MAC_MAP_ENUM(x) x +#endif +}; + +//modifiers +static qt_mac_enum_mapper qt_mac_modifier_symbols[] = { + { shiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) }, + { rightShiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) }, + { controlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) }, + { rightControlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) }, + { cmdKey, QT_MAC_MAP_ENUM(Qt::ControlModifier) }, + { optionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) }, + { rightOptionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) }, + { kEventKeyModifierNumLockMask, QT_MAC_MAP_ENUM(Qt::KeypadModifier) }, + { 0, QT_MAC_MAP_ENUM(0) } +}; +Qt::KeyboardModifiers qt_mac_get_modifiers(int keys) +{ +#ifdef DEBUG_KEY_BINDINGS_MODIFIERS + qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", keys, keys); +#endif + Qt::KeyboardModifiers ret = Qt::NoModifier; + for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) { + if (keys & qt_mac_modifier_symbols[i].mac_code) { +#ifdef DEBUG_KEY_BINDINGS_MODIFIERS + qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc); +#endif + ret |= Qt::KeyboardModifier(qt_mac_modifier_symbols[i].qt_code); + } + } + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + Qt::KeyboardModifiers oldModifiers = ret; + ret &= ~(Qt::MetaModifier | Qt::ControlModifier); + if (oldModifiers & Qt::ControlModifier) + ret |= Qt::MetaModifier; + if (oldModifiers & Qt::MetaModifier) + ret |= Qt::ControlModifier; + } + return ret; +} +static int qt_mac_get_mac_modifiers(Qt::KeyboardModifiers keys) +{ +#ifdef DEBUG_KEY_BINDINGS_MODIFIERS + qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", (int)keys, (int)keys); +#endif + int ret = 0; + for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) { + if (keys & qt_mac_modifier_symbols[i].qt_code) { +#ifdef DEBUG_KEY_BINDINGS_MODIFIERS + qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc); +#endif + ret |= qt_mac_modifier_symbols[i].mac_code; + } + } + + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + int oldModifiers = ret; + ret &= ~(controlKeyBit | cmdKeyBit); + if (oldModifiers & controlKeyBit) + ret |= cmdKeyBit; + if (oldModifiers & cmdKeyBit) + ret |= controlKeyBit; + } + return ret; +} +void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object) +{ + static quint32 cachedModifiers = 0; + quint32 lastModifiers = cachedModifiers, + changedModifiers = lastModifiers ^ modifiers; + cachedModifiers = modifiers; + + //check the bits + static qt_mac_enum_mapper modifier_key_symbols[] = { + { shiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) }, + { rightShiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) }, //??? + { controlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) }, + { rightControlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) }, //??? + { cmdKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Control) }, + { optionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) }, + { rightOptionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) }, //??? + { alphaLockBit, QT_MAC_MAP_ENUM(Qt::Key_CapsLock) }, + { kEventKeyModifierNumLockBit, QT_MAC_MAP_ENUM(Qt::Key_NumLock) }, + { 0, QT_MAC_MAP_ENUM(0) } }; + for (int i = 0; i <= 32; i++) { //just check each bit + if (!(changedModifiers & (1 << i))) + continue; + QEvent::Type etype = QEvent::KeyPress; + if (lastModifiers & (1 << i)) + etype = QEvent::KeyRelease; + int key = 0; + for (uint x = 0; modifier_key_symbols[x].mac_code; x++) { + if (modifier_key_symbols[x].mac_code == i) { +#ifdef DEBUG_KEY_BINDINGS_MODIFIERS + qDebug("got modifier changed: %s", modifier_key_symbols[x].desc); +#endif + key = modifier_key_symbols[x].qt_code; + break; + } + } + if (!key) { +#ifdef DEBUG_KEY_BINDINGS_MODIFIERS + qDebug("could not get modifier changed: %d", i); +#endif + continue; + } +#ifdef DEBUG_KEY_BINDINGS_MODIFIERS + qDebug("KeyEvent (modif): Sending %s to %s::%s: %d - 0x%08x", + etype == QEvent::KeyRelease ? "KeyRelease" : "KeyPress", + object ? object->metaObject()->className() : "none", + object ? object->objectName().toLatin1().constData() : "", + key, (int)modifiers); +#endif + QKeyEvent ke(etype, key, qt_mac_get_modifiers(modifiers ^ (1 << i)), QLatin1String("")); + qt_sendSpontaneousEvent(object, &ke); + } +} + +//keyboard keys (non-modifiers) +static qt_mac_enum_mapper qt_mac_keyboard_symbols[] = { + { kHomeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Home) }, + { kEnterCharCode, QT_MAC_MAP_ENUM(Qt::Key_Enter) }, + { kEndCharCode, QT_MAC_MAP_ENUM(Qt::Key_End) }, + { kBackspaceCharCode, QT_MAC_MAP_ENUM(Qt::Key_Backspace) }, + { kTabCharCode, QT_MAC_MAP_ENUM(Qt::Key_Tab) }, + { kPageUpCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageUp) }, + { kPageDownCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageDown) }, + { kReturnCharCode, QT_MAC_MAP_ENUM(Qt::Key_Return) }, + { kEscapeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Escape) }, + { kLeftArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Left) }, + { kRightArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Right) }, + { kUpArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Up) }, + { kDownArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Down) }, + { kHelpCharCode, QT_MAC_MAP_ENUM(Qt::Key_Help) }, + { kDeleteCharCode, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, +//ascii maps, for debug + { ':', QT_MAC_MAP_ENUM(Qt::Key_Colon) }, + { ';', QT_MAC_MAP_ENUM(Qt::Key_Semicolon) }, + { '<', QT_MAC_MAP_ENUM(Qt::Key_Less) }, + { '=', QT_MAC_MAP_ENUM(Qt::Key_Equal) }, + { '>', QT_MAC_MAP_ENUM(Qt::Key_Greater) }, + { '?', QT_MAC_MAP_ENUM(Qt::Key_Question) }, + { '@', QT_MAC_MAP_ENUM(Qt::Key_At) }, + { ' ', QT_MAC_MAP_ENUM(Qt::Key_Space) }, + { '!', QT_MAC_MAP_ENUM(Qt::Key_Exclam) }, + { '"', QT_MAC_MAP_ENUM(Qt::Key_QuoteDbl) }, + { '#', QT_MAC_MAP_ENUM(Qt::Key_NumberSign) }, + { '$', QT_MAC_MAP_ENUM(Qt::Key_Dollar) }, + { '%', QT_MAC_MAP_ENUM(Qt::Key_Percent) }, + { '&', QT_MAC_MAP_ENUM(Qt::Key_Ampersand) }, + { '\'', QT_MAC_MAP_ENUM(Qt::Key_Apostrophe) }, + { '(', QT_MAC_MAP_ENUM(Qt::Key_ParenLeft) }, + { ')', QT_MAC_MAP_ENUM(Qt::Key_ParenRight) }, + { '*', QT_MAC_MAP_ENUM(Qt::Key_Asterisk) }, + { '+', QT_MAC_MAP_ENUM(Qt::Key_Plus) }, + { ',', QT_MAC_MAP_ENUM(Qt::Key_Comma) }, + { '-', QT_MAC_MAP_ENUM(Qt::Key_Minus) }, + { '.', QT_MAC_MAP_ENUM(Qt::Key_Period) }, + { '/', QT_MAC_MAP_ENUM(Qt::Key_Slash) }, + { '[', QT_MAC_MAP_ENUM(Qt::Key_BracketLeft) }, + { ']', QT_MAC_MAP_ENUM(Qt::Key_BracketRight) }, + { '\\', QT_MAC_MAP_ENUM(Qt::Key_Backslash) }, + { '_', QT_MAC_MAP_ENUM(Qt::Key_Underscore) }, + { '`', QT_MAC_MAP_ENUM(Qt::Key_QuoteLeft) }, + { '{', QT_MAC_MAP_ENUM(Qt::Key_BraceLeft) }, + { '}', QT_MAC_MAP_ENUM(Qt::Key_BraceRight) }, + { '|', QT_MAC_MAP_ENUM(Qt::Key_Bar) }, + { '~', QT_MAC_MAP_ENUM(Qt::Key_AsciiTilde) }, + { '^', QT_MAC_MAP_ENUM(Qt::Key_AsciiCircum) }, + { 0, QT_MAC_MAP_ENUM(0) } +}; + +static qt_mac_enum_mapper qt_mac_keyvkey_symbols[] = { //real scan codes + { 122, QT_MAC_MAP_ENUM(Qt::Key_F1) }, + { 120, QT_MAC_MAP_ENUM(Qt::Key_F2) }, + { 99, QT_MAC_MAP_ENUM(Qt::Key_F3) }, + { 118, QT_MAC_MAP_ENUM(Qt::Key_F4) }, + { 96, QT_MAC_MAP_ENUM(Qt::Key_F5) }, + { 97, QT_MAC_MAP_ENUM(Qt::Key_F6) }, + { 98, QT_MAC_MAP_ENUM(Qt::Key_F7) }, + { 100, QT_MAC_MAP_ENUM(Qt::Key_F8) }, + { 101, QT_MAC_MAP_ENUM(Qt::Key_F9) }, + { 109, QT_MAC_MAP_ENUM(Qt::Key_F10) }, + { 103, QT_MAC_MAP_ENUM(Qt::Key_F11) }, + { 111, QT_MAC_MAP_ENUM(Qt::Key_F12) }, + { 105, QT_MAC_MAP_ENUM(Qt::Key_F13) }, + { 107, QT_MAC_MAP_ENUM(Qt::Key_F14) }, + { 113, QT_MAC_MAP_ENUM(Qt::Key_F15) }, + { 106, QT_MAC_MAP_ENUM(Qt::Key_F16) }, + { 0, QT_MAC_MAP_ENUM(0) } +}; + +static qt_mac_enum_mapper qt_mac_private_unicode[] = { + { 0xF700, QT_MAC_MAP_ENUM(Qt::Key_Up) }, //NSUpArrowFunctionKey + { 0xF701, QT_MAC_MAP_ENUM(Qt::Key_Down) }, //NSDownArrowFunctionKey + { 0xF702, QT_MAC_MAP_ENUM(Qt::Key_Left) }, //NSLeftArrowFunctionKey + { 0xF703, QT_MAC_MAP_ENUM(Qt::Key_Right) }, //NSRightArrowFunctionKey + { 0xF727, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertFunctionKey + { 0xF728, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteFunctionKey + { 0xF729, QT_MAC_MAP_ENUM(Qt::Key_Home) }, //NSHomeFunctionKey + { 0xF72B, QT_MAC_MAP_ENUM(Qt::Key_End) }, //NSEndFunctionKey + { 0xF72C, QT_MAC_MAP_ENUM(Qt::Key_PageUp) }, //NSPageUpFunctionKey + { 0xF72D, QT_MAC_MAP_ENUM(Qt::Key_PageDown) }, //NSPageDownFunctionKey + { 0xF72F, QT_MAC_MAP_ENUM(Qt::Key_ScrollLock) }, //NSScrollLockFunctionKey + { 0xF730, QT_MAC_MAP_ENUM(Qt::Key_Pause) }, //NSPauseFunctionKey + { 0xF731, QT_MAC_MAP_ENUM(Qt::Key_SysReq) }, //NSSysReqFunctionKey + { 0xF735, QT_MAC_MAP_ENUM(Qt::Key_Menu) }, //NSMenuFunctionKey + { 0xF738, QT_MAC_MAP_ENUM(Qt::Key_Print) }, //NSPrintFunctionKey + { 0xF73A, QT_MAC_MAP_ENUM(Qt::Key_Clear) }, //NSClearDisplayFunctionKey + { 0xF73D, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertCharFunctionKey + { 0xF73E, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteCharFunctionKey + { 0xF741, QT_MAC_MAP_ENUM(Qt::Key_Select) }, //NSSelectFunctionKey + { 0xF742, QT_MAC_MAP_ENUM(Qt::Key_Execute) }, //NSExecuteFunctionKey + { 0xF746, QT_MAC_MAP_ENUM(Qt::Key_Help) }, //NSHelpFunctionKey + { 0xF747, QT_MAC_MAP_ENUM(Qt::Key_Mode_switch) }, //NSModeSwitchFunctionKey + { 0, QT_MAC_MAP_ENUM(0) } +}; + +static int qt_mac_get_key(int modif, const QChar &key, int virtualKey) +{ +#ifdef DEBUG_KEY_BINDINGS + qDebug("**Mapping key: %d (0x%04x) - %d (0x%04x)", key.unicode(), key.unicode(), virtualKey, virtualKey); +#endif + + if (key == kClearCharCode && virtualKey == 0x47) + return Qt::Key_Clear; + + if (key.isDigit()) { +#ifdef DEBUG_KEY_BINDINGS + qDebug("%d: got key: %d", __LINE__, key.digitValue()); +#endif + return key.digitValue() + Qt::Key_0; + } + + if (key.isLetter()) { +#ifdef DEBUG_KEY_BINDINGS + qDebug("%d: got key: %d", __LINE__, (key.toUpper().unicode() - 'A')); +#endif + return (key.toUpper().unicode() - 'A') + Qt::Key_A; + } + if (key.isSymbol()) { +#ifdef DEBUG_KEY_BINDINGS + qDebug("%d: got key: %d", __LINE__, (key.unicode())); +#endif + return key.unicode(); + } + + for (int i = 0; qt_mac_keyboard_symbols[i].qt_code; i++) { + if (qt_mac_keyboard_symbols[i].mac_code == key) { + /* To work like Qt for X11 we issue Backtab when Shift + Tab are pressed */ + if (qt_mac_keyboard_symbols[i].qt_code == Qt::Key_Tab && (modif & Qt::ShiftModifier)) { +#ifdef DEBUG_KEY_BINDINGS + qDebug("%d: got key: Qt::Key_Backtab", __LINE__); +#endif + return Qt::Key_Backtab; + } + +#ifdef DEBUG_KEY_BINDINGS + qDebug("%d: got key: %s", __LINE__, qt_mac_keyboard_symbols[i].desc); +#endif + return qt_mac_keyboard_symbols[i].qt_code; + } + } + + //last ditch try to match the scan code + for (int i = 0; qt_mac_keyvkey_symbols[i].qt_code; i++) { + if (qt_mac_keyvkey_symbols[i].mac_code == virtualKey) { +#ifdef DEBUG_KEY_BINDINGS + qDebug("%d: got key: %s", __LINE__, qt_mac_keyvkey_symbols[i].desc); +#endif + return qt_mac_keyvkey_symbols[i].qt_code; + } + } + + // check if they belong to key codes in private unicode range + if (key >= 0xf700 && key <= 0xf747) { + if (key >= 0xf704 && key <= 0xf726) { + return Qt::Key_F1 + (key.unicode() - 0xf704) ; + } + for (int i = 0; qt_mac_private_unicode[i].qt_code; i++) { + if (qt_mac_private_unicode[i].mac_code == key) { + return qt_mac_private_unicode[i].qt_code; + } + } + + } + + //oh well +#ifdef DEBUG_KEY_BINDINGS + qDebug("Unknown case.. %s:%d %d[%d] %d", __FILE__, __LINE__, key.unicode(), key.toLatin1(), virtualKey); +#endif + return Qt::Key_unknown; +} + +static Boolean qt_KeyEventComparatorProc(EventRef inEvent, void *data) +{ + UInt32 ekind = GetEventKind(inEvent), + eclass = GetEventClass(inEvent); + return (eclass == kEventClassKeyboard && (void *)ekind == data); +} + +static bool translateKeyEventInternal(EventHandlerCallRef er, EventRef keyEvent, int *qtKey, + QChar *outChar, Qt::KeyboardModifiers *outModifiers, bool *outHandled) +{ +#if !defined(QT_MAC_USE_COCOA) || defined(Q_OS_MAC64) + Q_UNUSED(er); + Q_UNUSED(outHandled); +#endif + const UInt32 ekind = GetEventKind(keyEvent); + { + UInt32 mac_modifiers = 0; + GetEventParameter(keyEvent, kEventParamKeyModifiers, typeUInt32, 0, + sizeof(mac_modifiers), 0, &mac_modifiers); +#ifdef DEBUG_KEY_BINDINGS_MODIFIERS + qDebug("************ Mapping modifiers and key ***********"); +#endif + *outModifiers = qt_mac_get_modifiers(mac_modifiers); +#ifdef DEBUG_KEY_BINDINGS_MODIFIERS + qDebug("------------ Mapping modifiers and key -----------"); +#endif + } + + //get keycode + UInt32 keyCode = 0; + GetEventParameter(keyEvent, kEventParamKeyCode, typeUInt32, 0, sizeof(keyCode), 0, &keyCode); + + //get mac mapping + static UInt32 tmp_unused_state = 0L; + const UCKeyboardLayout *uchrData = 0; +#if defined(Q_OS_MAC32) + KeyboardLayoutRef keyLayoutRef = 0; + KLGetCurrentKeyboardLayout(&keyLayoutRef); + OSStatus err; + if (keyLayoutRef != 0) { + err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData, + (reinterpret_cast<const void **>(&uchrData))); + if (err != noErr) { + qWarning("Qt::internal::unable to get keyboardlayout %ld %s:%d", + long(err), __FILE__, __LINE__); + } + } +#else + QCFType<TISInputSourceRef> inputSource = TISCopyCurrentKeyboardInputSource(); + Q_ASSERT(inputSource != 0); + CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(inputSource, + kTISPropertyUnicodeKeyLayoutData)); + uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0; +#endif + *qtKey = Qt::Key_unknown; + if (uchrData) { + // The easy stuff; use the unicode stuff! + UniChar string[4]; + UniCharCount actualLength; + UInt32 currentModifiers = GetCurrentEventKeyModifiers(); + UInt32 currentModifiersWOAltOrControl = currentModifiers & ~(controlKey | optionKey); + int keyAction; + switch (ekind) { + default: + case kEventRawKeyDown: + keyAction = kUCKeyActionDown; + break; + case kEventRawKeyUp: + keyAction = kUCKeyActionUp; + break; + case kEventRawKeyRepeat: + keyAction = kUCKeyActionAutoKey; + break; + } + OSStatus err = UCKeyTranslate(uchrData, keyCode, keyAction, + ((currentModifiersWOAltOrControl >> 8) & 0xff), LMGetKbdType(), + kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength, + string); + if (err == noErr) { + *outChar = QChar(string[0]); + *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode); + if (currentModifiersWOAltOrControl != currentModifiers) { + // Now get the real char. + err = UCKeyTranslate(uchrData, keyCode, keyAction, + ((currentModifiers >> 8) & 0xff), LMGetKbdType(), + kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength, + string); + if (err == noErr) + *outChar = QChar(string[0]); + } + } else { + qWarning("Qt::internal::UCKeyTranslate is returnining %ld %s:%d", + long(err), __FILE__, __LINE__); + } + } +#ifdef Q_OS_MAC32 + else { + // The road less travelled; use KeyTranslate + const void *keyboard_layout; + KeyboardLayoutRef keyLayoutRef = 0; + KLGetCurrentKeyboardLayout(&keyLayoutRef); + err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData, + reinterpret_cast<const void **>(&keyboard_layout)); + + int translatedChar = KeyTranslate(keyboard_layout, (GetCurrentEventKeyModifiers() & + (kEventKeyModifierNumLockMask|shiftKey|cmdKey| + rightShiftKey|alphaLock)) | keyCode, + &tmp_unused_state); + if (!translatedChar) { +#ifdef QT_MAC_USE_COCOA + if (outHandled) { + qt_mac_eat_unicode_key = false; + if (er) + CallNextEventHandler(er, keyEvent); + *outHandled = qt_mac_eat_unicode_key; + } +#endif + return false; + } + + //map it into qt keys + *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode); + if (*outModifiers & (Qt::AltModifier | Qt::ControlModifier)) { + if (translatedChar & (1 << 7)) //high ascii + translatedChar = 0; + } else { //now get the real ascii value + UInt32 tmp_mod = 0L; + static UInt32 tmp_state = 0L; + if (*outModifiers & Qt::ShiftModifier) + tmp_mod |= shiftKey; + if (*outModifiers & Qt::MetaModifier) + tmp_mod |= controlKey; + if (*outModifiers & Qt::ControlModifier) + tmp_mod |= cmdKey; + if (GetCurrentEventKeyModifiers() & alphaLock) //no Qt mapper + tmp_mod |= alphaLock; + if (*outModifiers & Qt::AltModifier) + tmp_mod |= optionKey; + if (*outModifiers & Qt::KeypadModifier) + tmp_mod |= kEventKeyModifierNumLockMask; + translatedChar = KeyTranslate(keyboard_layout, tmp_mod | keyCode, &tmp_state); + } + { + ByteCount unilen = 0; + if (GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0) + == noErr && unilen == 2) { + GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, outChar); + } else if (translatedChar) { + static QTextCodec *c = 0; + if (!c) + c = QTextCodec::codecForName("Apple Roman"); + char tmpChar = (char)translatedChar; // **sigh** + *outChar = c->toUnicode(&tmpChar, 1).at(0); + } else { + *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode); + } + } + } +#endif + if (*qtKey == Qt::Key_unknown) + *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode); + return true; +} + +QKeyMapperPrivate::QKeyMapperPrivate() +{ + memset(keyLayout, 0, sizeof(keyLayout)); + keyboard_layout_format.unicode = 0; +#ifdef Q_OS_MAC32 + keyboard_mode = NullMode; +#else + currentInputSource = 0; +#endif +} + +QKeyMapperPrivate::~QKeyMapperPrivate() +{ + deleteLayouts(); +} + +bool +QKeyMapperPrivate::updateKeyboard() +{ + const UCKeyboardLayout *uchrData = 0; +#ifdef Q_OS_MAC32 + KeyboardLayoutRef keyLayoutRef = 0; + KLGetCurrentKeyboardLayout(&keyLayoutRef); + + if (keyboard_mode != NullMode && currentKeyboardLayout == keyLayoutRef) + return false; + + OSStatus err; + if (keyLayoutRef != 0) { + err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData, + const_cast<const void **>(reinterpret_cast<const void **>(&uchrData))); + if (err != noErr) { + qWarning("Qt::internal::unable to get unicode keyboardlayout %ld %s:%d", + long(err), __FILE__, __LINE__); + } + } +#else + QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource(); + if (keyboard_mode != NullMode && source == currentInputSource) { + return false; + } + Q_ASSERT(source != 0); + CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(source, + kTISPropertyUnicodeKeyLayoutData)); + uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0; +#endif + + keyboard_kind = LMGetKbdType(); + if (uchrData) { + keyboard_layout_format.unicode = uchrData; + keyboard_mode = UnicodeMode; + } +#ifdef Q_OS_MAC32 + else { + void *happy; + err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData, + const_cast<const void **>(reinterpret_cast<void **>(&happy))); + if (err != noErr) { + qFatal("Qt::internal::unable to get non-unicode layout, cannot procede %ld %s:%d", + long(err), __FILE__, __LINE__); + } + keyboard_layout_format.other = happy; + keyboard_mode = OtherMode; + } + + currentKeyboardLayout = keyLayoutRef; +#else + currentInputSource = source; +#endif + keyboard_dead = 0; + CFStringRef iso639Code; +#ifdef Q_OS_MAC32 +# ifndef kKLLanguageCode +# define kKLLanguageCode 9 +# endif + KLGetKeyboardLayoutProperty(currentKeyboardLayout, kKLLanguageCode, + reinterpret_cast<const void **>(&iso639Code)); +#else + CFArrayRef array = static_cast<CFArrayRef>(TISGetInputSourceProperty(currentInputSource, kTISPropertyInputSourceLanguages)); + iso639Code = static_cast<CFStringRef>(CFArrayGetValueAtIndex(array, 0)); // Actually a RFC3066bis, but it's close enough +#endif + if (iso639Code) { + keyboardInputLocale = QLocale(QCFString::toQString(iso639Code)); + keyboardInputDirection = keyboardInputLocale.textDirection(); + } else { + keyboardInputLocale = QLocale::c(); + keyboardInputDirection = Qt::LeftToRight; + } + return true; +} + +void +QKeyMapperPrivate::deleteLayouts() +{ + keyboard_mode = NullMode; + for (int i = 0; i < 255; ++i) { + if (keyLayout[i]) { + delete keyLayout[i]; + keyLayout[i] = 0; + } + } +} + +void +QKeyMapperPrivate::clearMappings() +{ + deleteLayouts(); + updateKeyboard(); +} + +QList<int> +QKeyMapperPrivate::possibleKeys(QKeyEvent *e) +{ + QList<int> ret; + + KeyboardLayoutItem *kbItem = keyLayout[e->nativeVirtualKey()]; + if (!kbItem) // Key is not in any keyboard layout (e.g. eisu-key on Japanese keyboard) + return ret; + + int baseKey = kbItem->qtKey[0]; + Qt::KeyboardModifiers keyMods = e->modifiers(); + ret << int(baseKey + keyMods); // The base key is _always_ valid, of course + + for (int i = 1; i < 8; ++i) { + Qt::KeyboardModifiers neededMods = ModsTbl[i]; + int key = kbItem->qtKey[i]; + if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) + ret << int(key + (keyMods & ~neededMods)); + } + + return ret; +} + +bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, EventHandlerCallRef er, EventRef event, + void *info, bool grab) +{ + Q_ASSERT(GetEventClass(event) == kEventClassKeyboard); + bool handled_event=true; + UInt32 ekind = GetEventKind(event); + + // unfortunately modifiers changed event looks quite different, so I have a separate + // code path + if (ekind == kEventRawKeyModifiersChanged) { + //figure out changed modifiers, wish Apple would just send a delta + UInt32 modifiers = 0; + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0, + sizeof(modifiers), 0, &modifiers); + qt_mac_send_modifiers_changed(modifiers, widget); + return true; + } + + QInputContext *currentContext = qApp->inputContext(); + if (currentContext && currentContext->isComposing()) { + if (ekind == kEventRawKeyDown) { + QMacInputContext *context = qobject_cast<QMacInputContext*>(currentContext); + if (context) + context->setLastKeydownEvent(event); + } + return false; + } + // Once we process the key down , we don't need to send the saved event again from + // kEventTextInputUnicodeForKeyEvent, so clear it. + if (currentContext && ekind == kEventRawKeyDown) { + QMacInputContext *context = qobject_cast<QMacInputContext*>(currentContext); + if (context) + context->setLastKeydownEvent(0); + } + + //get modifiers + Qt::KeyboardModifiers modifiers; + int qtKey; + QChar ourChar; + if (translateKeyEventInternal(er, event, &qtKey, &ourChar, &modifiers, + &handled_event) == false) + return handled_event; + QString text(ourChar); + /* This is actually wrong - but unfortunately it is the best that can be + done for now because of the Control/Meta mapping problems */ + if (modifiers & (Qt::ControlModifier | Qt::MetaModifier) + && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + text = QString(); + } + + + if (widget) { +#ifndef QT_MAC_USE_COCOA + Q_UNUSED(info); + // Try not to call "other" event handlers if we have a popup, + // However, if the key has text + // then we should pass it along because otherwise then people + // can use input method stuff. + if (!qApp->activePopupWidget() + || (qApp->activePopupWidget() && !text.isEmpty())) { + //Find out if someone else wants the event, namely + //is it of use to text services? If so we won't bother + //with a QKeyEvent. + qt_mac_eat_unicode_key = false; + if (er) + CallNextEventHandler(er, event); + extern bool qt_mac_menubar_is_open(); + if (qt_mac_eat_unicode_key || qt_mac_menubar_is_open()) { + return true; + } + } +#endif + // Try to compress key events. + if (!text.isEmpty() && widget->testAttribute(Qt::WA_KeyCompression)) { + EventTime lastTime = GetEventTime(event); + for (;;) { + EventRef releaseEvent = FindSpecificEventInQueue(GetMainEventQueue(), + qt_KeyEventComparatorProc, + (void*)kEventRawKeyUp); + if (!releaseEvent) + break; + const EventTime releaseTime = GetEventTime(releaseEvent); + if (releaseTime < lastTime) + break; + lastTime = releaseTime; + + EventRef pressEvent = FindSpecificEventInQueue(GetMainEventQueue(), + qt_KeyEventComparatorProc, + (void*)kEventRawKeyDown); + if (!pressEvent) + break; + const EventTime pressTime = GetEventTime(pressEvent); + if (pressTime < lastTime) + break; + lastTime = pressTime; + + Qt::KeyboardModifiers compressMod; + int compressQtKey = 0; + QChar compressChar; + if (translateKeyEventInternal(er, pressEvent, + &compressQtKey, &compressChar, &compressMod, 0) + == false) { + break; + } + // Copied from qapplication_x11.cpp (change both). + + bool stopCompression = + // 1) misc keys + (compressQtKey >= Qt::Key_Escape && compressQtKey <= Qt::Key_SysReq) + // 2) cursor movement + || (compressQtKey >= Qt::Key_Home && compressQtKey <= Qt::Key_PageDown) + // 3) extra keys + || (compressQtKey >= Qt::Key_Super_L && compressQtKey <= Qt::Key_Direction_R) + // 4) something that a) doesn't translate to text or b) translates + // to newline text + || (compressQtKey == 0) + || (compressChar == QLatin1Char('\n')) + || (compressQtKey == Qt::Key_unknown); + + if (compressMod == modifiers && !compressChar.isNull() && !stopCompression) { +#ifdef DEBUG_KEY_BINDINGS + qDebug("compressing away %c", compressChar.toLatin1()); +#endif + text += compressChar; + // Clean up + RemoveEventFromQueue(GetMainEventQueue(), releaseEvent); + RemoveEventFromQueue(GetMainEventQueue(), pressEvent); + } else { +#ifdef DEBUG_KEY_BINDINGS + qDebug("stoping compression.."); +#endif + break; + } + } + } + + // There is no way to get the scan code from carbon. But we cannot use the value 0, since + // it indicates that the event originates from somewhere else than the keyboard + UInt32 macScanCode = 1; + UInt32 macVirtualKey = 0; + GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey); + UInt32 macModifiers = 0; + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0, + sizeof(macModifiers), 0, &macModifiers); +#ifdef QT_MAC_USE_COCOA + // The unicode characters in the range 0xF700-0xF747 are reserved + // by Mac OS X for transient use as keyboard function keys. We + // wont send 'text' for such key events. This is done to match + // behavior on other platforms. + unsigned int *unicodeKey = (unsigned int*)info; + if (*unicodeKey >= 0xf700 && *unicodeKey <= 0xf747) + text = QString(); + bool isAccepted; +#endif + handled_event = QKeyMapper::sendKeyEvent(widget, grab, + (ekind == kEventRawKeyUp) ? QEvent::KeyRelease : QEvent::KeyPress, + qtKey, modifiers, text, ekind == kEventRawKeyRepeat, 0, + macScanCode, macVirtualKey, macModifiers +#ifdef QT_MAC_USE_COCOA + ,&isAccepted +#endif + ); +#ifdef QT_MAC_USE_COCOA + *unicodeKey = (unsigned int)isAccepted; +#endif + } + return handled_event; +} + +void +QKeyMapperPrivate::updateKeyMap(EventHandlerCallRef, EventRef event, void * +#if defined(QT_MAC_USE_COCOA) + unicodeKey // unicode character from NSEvent (modifiers applied) +#endif + ) +{ + UInt32 macVirtualKey = 0; + GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey); + if (updateKeyboard()) + QKeyMapper::changeKeyboard(); + else if (keyLayout[macVirtualKey]) + return; + + UniCharCount buffer_size = 10; + UniChar buffer[buffer_size]; + keyLayout[macVirtualKey] = new KeyboardLayoutItem; + for (int i = 0; i < 16; ++i) { + UniCharCount out_buffer_size = 0; + keyLayout[macVirtualKey]->qtKey[i] = 0; +#ifdef Q_WS_MAC32 + if (keyboard_mode == UnicodeMode) { +#endif + const UInt32 keyModifier = ((qt_mac_get_mac_modifiers(ModsTbl[i]) >> 8) & 0xFF); + OSStatus err = UCKeyTranslate(keyboard_layout_format.unicode, macVirtualKey, kUCKeyActionDown, keyModifier, + keyboard_kind, 0, &keyboard_dead, buffer_size, &out_buffer_size, buffer); + if (err == noErr && out_buffer_size) { + const QChar unicode(buffer[0]); + int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey); + if (qtkey == Qt::Key_unknown) + qtkey = unicode.unicode(); + keyLayout[macVirtualKey]->qtKey[i] = qtkey; + } +#ifndef Q_WS_MAC32 + else { + const QChar unicode(*((UniChar *)unicodeKey)); + int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey); + if (qtkey == Qt::Key_unknown) + qtkey = unicode.unicode(); + keyLayout[macVirtualKey]->qtKey[i] = qtkey; + } +#endif +#ifdef Q_WS_MAC32 + } else { + const UInt32 keyModifier = (qt_mac_get_mac_modifiers(ModsTbl[i])); + + uchar translatedChar = KeyTranslate(keyboard_layout_format.other, keyModifier | macVirtualKey, &keyboard_dead); + if (translatedChar) { + static QTextCodec *c = 0; + if (!c) + c = QTextCodec::codecForName("Apple Roman"); + const QChar unicode(c->toUnicode((const char *)&translatedChar, 1).at(0)); + int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey); + if (qtkey == Qt::Key_unknown) + qtkey = unicode.unicode(); + keyLayout[macVirtualKey]->qtKey[i] = qtkey; + } + } +#endif + } +#ifdef DEBUG_KEY_MAPS + qDebug("updateKeyMap for virtual key = 0x%02x!", (uint)macVirtualKey); + for (int i = 0; i < 16; ++i) { + qDebug(" [%d] (%d,0x%02x,'%c')", i, + keyLayout[macVirtualKey]->qtKey[i], + keyLayout[macVirtualKey]->qtKey[i], + keyLayout[macVirtualKey]->qtKey[i]); + } +#endif +} + +bool +QKeyMapper::sendKeyEvent(QWidget *widget, bool grab, + QEvent::Type type, int code, Qt::KeyboardModifiers modifiers, + const QString &text, bool autorepeat, int count, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, bool *isAccepted) +{ + Q_UNUSED(count); + if (widget && widget->isEnabled()) { + bool key_event = true; +#if defined(QT3_SUPPORT) && !defined(QT_NO_SHORTCUT) + if (type == QEvent::KeyPress && !grab + && QApplicationPrivate::instance()->use_compat()) { + QKeyEventEx accel_ev(type, code, modifiers, + text, autorepeat, qMax(1, int(text.length())), + nativeScanCode, nativeVirtualKey, nativeModifiers); + if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &accel_ev)) { +#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS) + qDebug("KeyEvent: %s::%s consumed Accel: %s", + widget ? widget->metaObject()->className() : "none", + widget ? widget->objectName().toLatin1().constData() : "", + text.toLatin1().constData()); +#endif + key_event = false; + } else { + if (accel_ev.isAccepted()) { +#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS) + qDebug("KeyEvent: %s::%s overrode Accel: %s", + widget ? widget->metaObject()->className() : "none", + widget ? widget->objectName().toLatin1().constData() : "", + text.toLatin1().constData()); +#endif + } + } + } +#else +Q_UNUSED(grab); +#endif // QT3_SUPPORT && !QT_NO_SHORTCUT + if (key_event) { +#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS) + qDebug("KeyEvent: Sending %s to %s::%s: %s 0x%08x%s", + type == QEvent::KeyRelease ? "KeyRelease" : "KeyPress", + widget ? widget->metaObject()->className() : "none", + widget ? widget->objectName().toLatin1().constData() : "", + text.toLatin1().constData(), int(modifiers), + autorepeat ? " Repeat" : ""); +#endif + QKeyEventEx ke(type, code, modifiers, text, autorepeat, qMax(1, text.length()), + nativeScanCode, nativeVirtualKey, nativeModifiers); + bool retMe = qt_sendSpontaneousEvent(widget,&ke); + if (isAccepted) + *isAccepted = ke.isAccepted(); + return retMe; + } + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qkeymapper_p.h b/src/gui/guikernel/qkeymapper_p.h new file mode 100644 index 0000000000..ec2d8492fe --- /dev/null +++ b/src/gui/guikernel/qkeymapper_p.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QKEYMAPPER_P_H +#define QKEYMAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qobject.h> +#include <private/qobject_p.h> +#include <qkeysequence.h> +#include <qlist.h> +#include <qlocale.h> +#include <qevent.h> +#include <qhash.h> + +#if defined (Q_WS_MAC64) +# include <private/qt_mac_p.h> +#endif + +QT_BEGIN_NAMESPACE + +class QKeyMapperPrivate; +class QKeyMapper : public QObject +{ + Q_OBJECT +public: + explicit QKeyMapper(); + ~QKeyMapper(); + + static QKeyMapper *instance(); + static void changeKeyboard(); + static bool sendKeyEvent(QWidget *widget, bool grab, + QEvent::Type type, int code, Qt::KeyboardModifiers modifiers, + const QString &text, bool autorepeat, int count, + quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, + bool *unusedExceptForCocoa = 0); + static QList<int> possibleKeys(QKeyEvent *e); + +private: + friend QKeyMapperPrivate *qt_keymapper_private(); + Q_DECLARE_PRIVATE(QKeyMapper) + Q_DISABLE_COPY(QKeyMapper) +}; + + + +#if defined(Q_OS_WIN) +enum WindowsNativeModifiers { + ShiftLeft = 0x00000001, + ControlLeft = 0x00000002, + AltLeft = 0x00000004, + MetaLeft = 0x00000008, + ShiftRight = 0x00000010, + ControlRight = 0x00000020, + AltRight = 0x00000040, + MetaRight = 0x00000080, + CapsLock = 0x00000100, + NumLock = 0x00000200, + ScrollLock = 0x00000400, + ExtendedKey = 0x01000000, + + // Convenience mappings + ShiftAny = 0x00000011, + ControlAny = 0x00000022, + AltAny = 0x00000044, + MetaAny = 0x00000088, + LockAny = 0x00000700 +}; +# if !defined(tagMSG) + typedef struct tagMSG MSG; +# endif +#elif defined(Q_WS_MAC) +QT_BEGIN_INCLUDE_NAMESPACE +# include <private/qt_mac_p.h> +QT_END_INCLUDE_NAMESPACE +#elif defined(Q_WS_X11) + +QT_BEGIN_INCLUDE_NAMESPACE +typedef ulong XID; +typedef XID KeySym; +QT_END_INCLUDE_NAMESPACE + +struct QXCoreDesc { + int min_keycode; + int max_keycode; + int keysyms_per_keycode; + KeySym *keysyms; + uchar mode_switch; + uchar num_lock; + KeySym lock_meaning; +}; + +#endif + +struct KeyboardLayoutItem; +typedef struct __TISInputSource * TISInputSourceRef; +class QKeyEvent; +class QKeyMapperPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QKeyMapper) +public: + QKeyMapperPrivate(); + ~QKeyMapperPrivate(); + + void clearMappings(); + QList<int> possibleKeys(QKeyEvent *e); + + QLocale keyboardInputLocale; + Qt::LayoutDirection keyboardInputDirection; + +#if defined(Q_OS_WIN) + void clearRecordedKeys(); + void updateKeyMap(const MSG &msg); + bool translateKeyEvent(QWidget *receiver, const MSG &msg, bool grab); + void updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode, quint32 vk_key); + bool isADeadKey(unsigned int vk_key, unsigned int modifiers); + void deleteLayouts(); + + KeyboardLayoutItem *keyLayout[256]; + +#elif defined(Q_WS_X11) + + QList<int> possibleKeysXKB(QKeyEvent *event); + QList<int> possibleKeysCore(QKeyEvent *event); + + bool translateKeyEventInternal(QWidget *keywidget, + const XEvent *, + KeySym &keysym, + int& count, + QString& text, + Qt::KeyboardModifiers& modifiers, + int &code, + QEvent::Type &type, + bool statefulTranslation = true); + bool translateKeyEvent(QWidget *keywidget, + const XEvent *, + bool grab); + + int xkb_currentGroup; + QXCoreDesc coreDesc; + +#elif defined(Q_WS_MAC) + bool updateKeyboard(); + void updateKeyMap(EventHandlerCallRef, EventRef, void *); + bool translateKeyEvent(QWidget *, EventHandlerCallRef, EventRef, void *, bool); + void deleteLayouts(); + + enum { NullMode, UnicodeMode, OtherMode } keyboard_mode; + union { + const UCKeyboardLayout *unicode; + void *other; + } keyboard_layout_format; +#ifdef Q_WS_MAC64 + QCFType<TISInputSourceRef> currentInputSource; +#else + KeyboardLayoutRef currentKeyboardLayout; +#endif + KeyboardLayoutKind keyboard_kind; + UInt32 keyboard_dead; + KeyboardLayoutItem *keyLayout[256]; +#elif defined(Q_WS_QWS) +#elif defined(Q_OS_SYMBIAN) +public: + QString translateKeyEvent(int keySym, Qt::KeyboardModifiers modifiers); + int mapS60KeyToQt(TUint s60key); + int mapS60ScanCodesToQt(TUint s60key); + int mapQtToS60Key(int qtKey); + int mapQtToS60ScanCodes(int qtKey); + void updateInputLanguage(); +#endif +}; + +QKeyMapperPrivate *qt_keymapper_private(); // from qkeymapper.cpp + +QT_END_NAMESPACE + +#endif // QKEYMAPPER_P_H diff --git a/src/gui/guikernel/qkeymapper_qpa.cpp b/src/gui/guikernel/qkeymapper_qpa.cpp new file mode 100644 index 0000000000..7e4114057f --- /dev/null +++ b/src/gui/guikernel/qkeymapper_qpa.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qkeymapper_p.h" +#include <qdebug.h> +#include <private/qevent_p.h> +#include <private/qlocale_p.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + + +QKeyMapperPrivate::QKeyMapperPrivate() +{ + keyboardInputLocale = QLocale::system(); + keyboardInputDirection = keyboardInputLocale.textDirection(); +} + +QKeyMapperPrivate::~QKeyMapperPrivate() +{ + // clearMappings(); +} + +void QKeyMapperPrivate::clearMappings() +{ +} + +QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *e) +{ + QList<int> result; + if (e->key() && (e->key() != Qt::Key_unknown)) + result << int(e->key() + e->modifiers()); + else if (!e->text().isEmpty()) + result << int(e->text().at(0).unicode() + e->modifiers()); + return result; +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qkeymapper_s60.cpp b/src/gui/guikernel/qkeymapper_s60.cpp new file mode 100644 index 0000000000..08cfae0d2d --- /dev/null +++ b/src/gui/guikernel/qkeymapper_s60.cpp @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qkeymapper_p.h" +#include <private/qcore_symbian_p.h> +#include <e32keys.h> +#include <e32cmn.h> +#include <centralrepository.h> +#include <biditext.h> + +QT_BEGIN_NAMESPACE + +QKeyMapperPrivate::QKeyMapperPrivate() +{ +} + +QKeyMapperPrivate::~QKeyMapperPrivate() +{ +} + +QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent * /* e */) +{ + QList<int> result; + return result; +} + +void QKeyMapperPrivate::clearMappings() +{ + // stub +} + +QString QKeyMapperPrivate::translateKeyEvent(int keySym, Qt::KeyboardModifiers /* modifiers */) +{ + if (keySym >= Qt::Key_Escape) { + switch (keySym) { + case Qt::Key_Tab: + return QString(QChar('\t')); + case Qt::Key_Return: // fall through + case Qt::Key_Enter: + return QString(QChar('\r')); + default: + return QString(); + } + } + + // Symbian doesn't actually use modifiers, but gives us the character code directly. + + return QString(QChar(keySym)); +} + +#include <e32keys.h> +struct KeyMapping{ + TKeyCode s60KeyCode; + TStdScanCode s60ScanCode; + Qt::Key qtKey; +}; + +using namespace Qt; + +static const KeyMapping keyMapping[] = { + {EKeyBackspace, EStdKeyBackspace, Key_Backspace}, + {EKeyTab, EStdKeyTab, Key_Tab}, + {EKeyEnter, EStdKeyEnter, Key_Enter}, + {EKeyEscape, EStdKeyEscape, Key_Escape}, + {EKeySpace, EStdKeySpace, Key_Space}, + {EKeyDelete, EStdKeyDelete, Key_Delete}, + {EKeyPrintScreen, EStdKeyPrintScreen, Key_SysReq}, + {EKeyPause, EStdKeyPause, Key_Pause}, + {EKeyHome, EStdKeyHome, Key_Home}, + {EKeyEnd, EStdKeyEnd, Key_End}, + {EKeyPageUp, EStdKeyPageUp, Key_PageUp}, + {EKeyPageDown, EStdKeyPageDown, Key_PageDown}, + {EKeyInsert, EStdKeyInsert, Key_Insert}, + {EKeyLeftArrow, EStdKeyLeftArrow, Key_Left}, + {EKeyRightArrow, EStdKeyRightArrow, Key_Right}, + {EKeyUpArrow, EStdKeyUpArrow, Key_Up}, + {EKeyDownArrow, EStdKeyDownArrow, Key_Down}, + {EKeyLeftShift, EStdKeyLeftShift, Key_Shift}, + {EKeyRightShift, EStdKeyRightShift, Key_Shift}, + {EKeyLeftAlt, EStdKeyLeftAlt, Key_Alt}, + {EKeyRightAlt, EStdKeyRightAlt, Key_AltGr}, + {EKeyLeftCtrl, EStdKeyLeftCtrl, Key_Control}, + {EKeyRightCtrl, EStdKeyRightCtrl, Key_Control}, + {EKeyLeftFunc, EStdKeyLeftFunc, Key_Super_L}, + {EKeyRightFunc, EStdKeyRightFunc, Key_Super_R}, + {EKeyCapsLock, EStdKeyCapsLock, Key_CapsLock}, + {EKeyNumLock, EStdKeyNumLock, Key_NumLock}, + {EKeyScrollLock, EStdKeyScrollLock, Key_ScrollLock}, + {EKeyF1, EStdKeyF1, Key_F1}, + {EKeyF2, EStdKeyF2, Key_F2}, + {EKeyF3, EStdKeyF3, Key_F3}, + {EKeyF4, EStdKeyF4, Key_F4}, + {EKeyF5, EStdKeyF5, Key_F5}, + {EKeyF6, EStdKeyF6, Key_F6}, + {EKeyF7, EStdKeyF7, Key_F7}, + {EKeyF8, EStdKeyF8, Key_F8}, + {EKeyF9, EStdKeyF9, Key_F9}, + {EKeyF10, EStdKeyF10, Key_F10}, + {EKeyF11, EStdKeyF11, Key_F11}, + {EKeyF12, EStdKeyF12, Key_F12}, + {EKeyF13, EStdKeyF13, Key_F13}, + {EKeyF14, EStdKeyF14, Key_F14}, + {EKeyF15, EStdKeyF15, Key_F15}, + {EKeyF16, EStdKeyF16, Key_F16}, + {EKeyF17, EStdKeyF17, Key_F17}, + {EKeyF18, EStdKeyF18, Key_F18}, + {EKeyF19, EStdKeyF19, Key_F19}, + {EKeyF20, EStdKeyF20, Key_F20}, + {EKeyF21, EStdKeyF21, Key_F21}, + {EKeyF22, EStdKeyF22, Key_F22}, + {EKeyF23, EStdKeyF23, Key_F23}, + {EKeyF24, EStdKeyF24, Key_F24}, + {EKeyOff, EStdKeyOff, Key_PowerOff}, +// {EKeyMenu, EStdKeyMenu, Key_Menu}, // Menu is EKeyApplication0 + {EKeyHelp, EStdKeyHelp, Key_Help}, + {EKeyDial, EStdKeyDial, Key_Call}, + {EKeyIncVolume, EStdKeyIncVolume, Key_VolumeUp}, + {EKeyDecVolume, EStdKeyDecVolume, Key_VolumeDown}, + {EKeyDevice0, EStdKeyDevice0, Key_Context1}, // Found by manual testing. + {EKeyDevice1, EStdKeyDevice1, Key_Context2}, // Found by manual testing. + {EKeyDevice3, EStdKeyDevice3, Key_Select}, + {EKeyDevice7, EStdKeyDevice7, Key_Camera}, + {EKeyApplication0, EStdKeyApplication0, Key_Menu}, // Found by manual testing. + {EKeyApplication1, EStdKeyApplication1, Key_Launch1}, // Found by manual testing. + {EKeyApplication2, EStdKeyApplication2, Key_MediaPlay}, // Found by manual testing. + {EKeyApplication3, EStdKeyApplication3, Key_MediaStop}, // Found by manual testing. + {EKeyApplication4, EStdKeyApplication4, Key_MediaNext}, // Found by manual testing. + {EKeyApplication5, EStdKeyApplication5, Key_MediaPrevious}, // Found by manual testing. + {EKeyApplication6, EStdKeyApplication6, Key_Launch6}, + {EKeyApplication7, EStdKeyApplication7, Key_Launch7}, + {EKeyApplication8, EStdKeyApplication8, Key_Launch8}, + {EKeyApplication9, EStdKeyApplication9, Key_Launch9}, + {EKeyApplicationA, EStdKeyApplicationA, Key_LaunchA}, + {EKeyApplicationB, EStdKeyApplicationB, Key_LaunchB}, + {EKeyApplicationC, EStdKeyApplicationC, Key_LaunchC}, + {EKeyApplicationD, EStdKeyApplicationD, Key_LaunchD}, + {EKeyApplicationE, EStdKeyApplicationE, Key_LaunchE}, + {EKeyApplicationF, EStdKeyApplicationF, Key_LaunchF}, + {EKeyApplication19, EStdKeyApplication19, Key_CameraFocus}, + {EKeyYes, EStdKeyYes, Key_Yes}, + {EKeyNo, EStdKeyNo, Key_No}, + {TKeyCode(0), TStdScanCode(0), Qt::Key(0)} +}; + +int QKeyMapperPrivate::mapS60KeyToQt(TUint s60key) +{ + int res = Qt::Key_unknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].s60KeyCode == s60key) { + res = keyMapping[i].qtKey; + break; + } + } + return res; +} + +int QKeyMapperPrivate::mapS60ScanCodesToQt(TUint s60scanCode) +{ + int res = Qt::Key_unknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].s60ScanCode == s60scanCode) { + res = keyMapping[i].qtKey; + break; + } + } + return res; +} + +int QKeyMapperPrivate::mapQtToS60Key(int qtKey) +{ + int res = KErrUnknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].qtKey == qtKey) { + res = keyMapping[i].s60KeyCode; + break; + } + } + return res; +} + +int QKeyMapperPrivate::mapQtToS60ScanCodes(int qtKey) +{ + int res = KErrUnknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].qtKey == qtKey) { + res = keyMapping[i].s60ScanCode; + break; + } + } + return res; +} + +void QKeyMapperPrivate::updateInputLanguage() +{ +#ifdef Q_WS_S60 + TInt err; + CRepository *repo; + const TUid KCRUidAknFep = TUid::Uid(0x101F876D); + const TUint32 KAknFepInputTxtLang = 0x00000005; + TRAP(err, repo = CRepository::NewL(KCRUidAknFep)); + if (err != KErrNone) + return; + + TInt symbianLang; + err = repo->Get(KAknFepInputTxtLang, symbianLang); + delete repo; + if (err != KErrNone) + return; + + QString qtLang = QString::fromAscii(qt_symbianLocaleName(symbianLang)); + keyboardInputLocale = QLocale(qtLang); + keyboardInputDirection = (TBidiText::ScriptDirectionality(TLanguage(symbianLang)) == TBidiText::ERightToLeft) + ? Qt::RightToLeft : Qt::LeftToRight; +#else + keyboardInputLocale = QLocale(); + keyboardInputDirection = Qt::LeftToRight; +#endif +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qkeymapper_win.cpp b/src/gui/guikernel/qkeymapper_win.cpp new file mode 100644 index 0000000000..92fa582617 --- /dev/null +++ b/src/gui/guikernel/qkeymapper_win.cpp @@ -0,0 +1,1207 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qkeymapper_p.h" + +#include <qt_windows.h> +#include <qdebug.h> +#include <private/qevent_p.h> +#include <private/qlocale_p.h> +#include <private/qapplication_p.h> +#include <qwidget.h> +#include <qapplication.h> +#include <ctype.h> + +QT_BEGIN_NAMESPACE + +// Uncommend, to show debugging information for the keymapper +//#define DEBUG_KEYMAPPER + +// Implemented elsewhere +extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM); + +extern Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id); +#ifndef LANG_PASHTO +#define LANG_PASHTO 0x63 +#endif +#ifndef LANG_SYRIAC +#define LANG_SYRIAC 0x5a +#endif +#ifndef LANG_DIVEHI +#define LANG_DIVEHI 0x65 +#endif +#ifndef VK_OEM_PLUS +#define VK_OEM_PLUS 0xBB +#endif +#ifndef VK_OEM_3 +#define VK_OEM_3 0xC0 +#endif + +#if defined(Q_OS_WINCE) +bool GetKeyboardState(unsigned char* kbuffer) +{ + for (int i=0; i< 256; ++i) + kbuffer[i] = GetAsyncKeyState(i); + return true; +} +#endif +// Key recorder ------------------------------------------------------------------------[ start ] -- +struct KeyRecord { + KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {} + KeyRecord() {} + + int code; + int ascii; + int state; + QString text; +}; + +static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers... +struct KeyRecorder +{ + KeyRecorder() : nrecs(0) {} + + inline KeyRecord *findKey(int code, bool remove); + inline void storeKey(int code, int ascii, int state, const QString& text); + inline void clearKeys(); + + int nrecs; + KeyRecord deleted_record; // A copy of last entry removed from records[] + KeyRecord records[QT_MAX_KEY_RECORDINGS]; +}; +static KeyRecorder key_recorder; + +KeyRecord *KeyRecorder::findKey(int code, bool remove) +{ + KeyRecord *result = 0; + for (int i = 0; i < nrecs; ++i) { + if (records[i].code == code) { + if (remove) { + deleted_record = records[i]; + // Move rest down, and decrease count + while (i + 1 < nrecs) { + records[i] = records[i + 1]; + ++i; + } + --nrecs; + result = &deleted_record; + } else { + result = &records[i]; + } + break; + } + } + return result; +} + +void KeyRecorder::storeKey(int code, int ascii, int state, const QString& text) +{ + Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS, + "Internal KeyRecorder", + "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS"); + + if (nrecs == QT_MAX_KEY_RECORDINGS) { + qWarning("Qt: Internal keyboard buffer overflow"); + return; + } + records[nrecs++] = KeyRecord(code,ascii,state,text); +} + +void KeyRecorder::clearKeys() +{ + nrecs = 0; +} +// Key recorder --------------------------------------------------------------------------[ end ] -- + + +// Key translation ---------------------------------------------------------------------[ start ] -- +// Meaning of values: +// 0 = Character output key, needs keyboard driver mapping +// Key_unknown = Unknown Virtual Key, no translation possible, ignore +static const uint KeyTbl[] = { // Keyboard mapping table + // Dec | Hex | Windows Virtual key + Qt::Key_unknown, // 0 0x00 + Qt::Key_unknown, // 1 0x01 VK_LBUTTON | Left mouse button + Qt::Key_unknown, // 2 0x02 VK_RBUTTON | Right mouse button + Qt::Key_Cancel, // 3 0x03 VK_CANCEL | Control-Break processing + Qt::Key_unknown, // 4 0x04 VK_MBUTTON | Middle mouse button + Qt::Key_unknown, // 5 0x05 VK_XBUTTON1 | X1 mouse button + Qt::Key_unknown, // 6 0x06 VK_XBUTTON2 | X2 mouse button + Qt::Key_unknown, // 7 0x07 -- unassigned -- + Qt::Key_Backspace, // 8 0x08 VK_BACK | BackSpace key + Qt::Key_Tab, // 9 0x09 VK_TAB | Tab key + Qt::Key_unknown, // 10 0x0A -- reserved -- + Qt::Key_unknown, // 11 0x0B -- reserved -- + Qt::Key_Clear, // 12 0x0C VK_CLEAR | Clear key + Qt::Key_Return, // 13 0x0D VK_RETURN | Enter key + Qt::Key_unknown, // 14 0x0E -- unassigned -- + Qt::Key_unknown, // 15 0x0F -- unassigned -- + Qt::Key_Shift, // 16 0x10 VK_SHIFT | Shift key + Qt::Key_Control, // 17 0x11 VK_CONTROL | Ctrl key + Qt::Key_Alt, // 18 0x12 VK_MENU | Alt key + Qt::Key_Pause, // 19 0x13 VK_PAUSE | Pause key + Qt::Key_CapsLock, // 20 0x14 VK_CAPITAL | Caps-Lock + Qt::Key_unknown, // 21 0x15 VK_KANA / VK_HANGUL | IME Kana or Hangul mode + Qt::Key_unknown, // 22 0x16 -- unassigned -- + Qt::Key_unknown, // 23 0x17 VK_JUNJA | IME Junja mode + Qt::Key_unknown, // 24 0x18 VK_FINAL | IME final mode + Qt::Key_unknown, // 25 0x19 VK_HANJA / VK_KANJI | IME Hanja or Kanji mode + Qt::Key_unknown, // 26 0x1A -- unassigned -- + Qt::Key_Escape, // 27 0x1B VK_ESCAPE | Esc key + Qt::Key_unknown, // 28 0x1C VK_CONVERT | IME convert + Qt::Key_unknown, // 29 0x1D VK_NONCONVERT | IME non-convert + Qt::Key_unknown, // 30 0x1E VK_ACCEPT | IME accept + Qt::Key_Mode_switch,// 31 0x1F VK_MODECHANGE | IME mode change request + Qt::Key_Space, // 32 0x20 VK_SPACE | Spacebar + Qt::Key_PageUp, // 33 0x21 VK_PRIOR | Page Up key + Qt::Key_PageDown, // 34 0x22 VK_NEXT | Page Down key + Qt::Key_End, // 35 0x23 VK_END | End key + Qt::Key_Home, // 36 0x24 VK_HOME | Home key + Qt::Key_Left, // 37 0x25 VK_LEFT | Left arrow key + Qt::Key_Up, // 38 0x26 VK_UP | Up arrow key + Qt::Key_Right, // 39 0x27 VK_RIGHT | Right arrow key + Qt::Key_Down, // 40 0x28 VK_DOWN | Down arrow key + Qt::Key_Select, // 41 0x29 VK_SELECT | Select key + Qt::Key_Printer, // 42 0x2A VK_PRINT | Print key + Qt::Key_Execute, // 43 0x2B VK_EXECUTE | Execute key + Qt::Key_Print, // 44 0x2C VK_SNAPSHOT | Print Screen key + Qt::Key_Insert, // 45 0x2D VK_INSERT | Ins key + Qt::Key_Delete, // 46 0x2E VK_DELETE | Del key + Qt::Key_Help, // 47 0x2F VK_HELP | Help key + 0, // 48 0x30 (VK_0) | 0 key + 0, // 49 0x31 (VK_1) | 1 key + 0, // 50 0x32 (VK_2) | 2 key + 0, // 51 0x33 (VK_3) | 3 key + 0, // 52 0x34 (VK_4) | 4 key + 0, // 53 0x35 (VK_5) | 5 key + 0, // 54 0x36 (VK_6) | 6 key + 0, // 55 0x37 (VK_7) | 7 key + 0, // 56 0x38 (VK_8) | 8 key + 0, // 57 0x39 (VK_9) | 9 key + Qt::Key_unknown, // 58 0x3A -- unassigned -- + Qt::Key_unknown, // 59 0x3B -- unassigned -- + Qt::Key_unknown, // 60 0x3C -- unassigned -- + Qt::Key_unknown, // 61 0x3D -- unassigned -- + Qt::Key_unknown, // 62 0x3E -- unassigned -- + Qt::Key_unknown, // 63 0x3F -- unassigned -- + Qt::Key_unknown, // 64 0x40 -- unassigned -- + 0, // 65 0x41 (VK_A) | A key + 0, // 66 0x42 (VK_B) | B key + 0, // 67 0x43 (VK_C) | C key + 0, // 68 0x44 (VK_D) | D key + 0, // 69 0x45 (VK_E) | E key + 0, // 70 0x46 (VK_F) | F key + 0, // 71 0x47 (VK_G) | G key + 0, // 72 0x48 (VK_H) | H key + 0, // 73 0x49 (VK_I) | I key + 0, // 74 0x4A (VK_J) | J key + 0, // 75 0x4B (VK_K) | K key + 0, // 76 0x4C (VK_L) | L key + 0, // 77 0x4D (VK_M) | M key + 0, // 78 0x4E (VK_N) | N key + 0, // 79 0x4F (VK_O) | O key + 0, // 80 0x50 (VK_P) | P key + 0, // 81 0x51 (VK_Q) | Q key + 0, // 82 0x52 (VK_R) | R key + 0, // 83 0x53 (VK_S) | S key + 0, // 84 0x54 (VK_T) | T key + 0, // 85 0x55 (VK_U) | U key + 0, // 86 0x56 (VK_V) | V key + 0, // 87 0x57 (VK_W) | W key + 0, // 88 0x58 (VK_X) | X key + 0, // 89 0x59 (VK_Y) | Y key + 0, // 90 0x5A (VK_Z) | Z key + Qt::Key_Meta, // 91 0x5B VK_LWIN | Left Windows - MS Natural kbd + Qt::Key_Meta, // 92 0x5C VK_RWIN | Right Windows - MS Natural kbd + Qt::Key_Menu, // 93 0x5D VK_APPS | Application key-MS Natural kbd + Qt::Key_unknown, // 94 0x5E -- reserved -- + Qt::Key_Sleep, // 95 0x5F VK_SLEEP + Qt::Key_0, // 96 0x60 VK_NUMPAD0 | Numeric keypad 0 key + Qt::Key_1, // 97 0x61 VK_NUMPAD1 | Numeric keypad 1 key + Qt::Key_2, // 98 0x62 VK_NUMPAD2 | Numeric keypad 2 key + Qt::Key_3, // 99 0x63 VK_NUMPAD3 | Numeric keypad 3 key + Qt::Key_4, // 100 0x64 VK_NUMPAD4 | Numeric keypad 4 key + Qt::Key_5, // 101 0x65 VK_NUMPAD5 | Numeric keypad 5 key + Qt::Key_6, // 102 0x66 VK_NUMPAD6 | Numeric keypad 6 key + Qt::Key_7, // 103 0x67 VK_NUMPAD7 | Numeric keypad 7 key + Qt::Key_8, // 104 0x68 VK_NUMPAD8 | Numeric keypad 8 key + Qt::Key_9, // 105 0x69 VK_NUMPAD9 | Numeric keypad 9 key + Qt::Key_Asterisk, // 106 0x6A VK_MULTIPLY | Multiply key + Qt::Key_Plus, // 107 0x6B VK_ADD | Add key + Qt::Key_Comma, // 108 0x6C VK_SEPARATOR | Separator key + Qt::Key_Minus, // 109 0x6D VK_SUBTRACT | Subtract key + Qt::Key_Period, // 110 0x6E VK_DECIMAL | Decimal key + Qt::Key_Slash, // 111 0x6F VK_DIVIDE | Divide key + Qt::Key_F1, // 112 0x70 VK_F1 | F1 key + Qt::Key_F2, // 113 0x71 VK_F2 | F2 key + Qt::Key_F3, // 114 0x72 VK_F3 | F3 key + Qt::Key_F4, // 115 0x73 VK_F4 | F4 key + Qt::Key_F5, // 116 0x74 VK_F5 | F5 key + Qt::Key_F6, // 117 0x75 VK_F6 | F6 key + Qt::Key_F7, // 118 0x76 VK_F7 | F7 key + Qt::Key_F8, // 119 0x77 VK_F8 | F8 key + Qt::Key_F9, // 120 0x78 VK_F9 | F9 key + Qt::Key_F10, // 121 0x79 VK_F10 | F10 key + Qt::Key_F11, // 122 0x7A VK_F11 | F11 key + Qt::Key_F12, // 123 0x7B VK_F12 | F12 key + Qt::Key_F13, // 124 0x7C VK_F13 | F13 key + Qt::Key_F14, // 125 0x7D VK_F14 | F14 key + Qt::Key_F15, // 126 0x7E VK_F15 | F15 key + Qt::Key_F16, // 127 0x7F VK_F16 | F16 key + Qt::Key_F17, // 128 0x80 VK_F17 | F17 key + Qt::Key_F18, // 129 0x81 VK_F18 | F18 key + Qt::Key_F19, // 130 0x82 VK_F19 | F19 key + Qt::Key_F20, // 131 0x83 VK_F20 | F20 key + Qt::Key_F21, // 132 0x84 VK_F21 | F21 key + Qt::Key_F22, // 133 0x85 VK_F22 | F22 key + Qt::Key_F23, // 134 0x86 VK_F23 | F23 key + Qt::Key_F24, // 135 0x87 VK_F24 | F24 key + Qt::Key_unknown, // 136 0x88 -- unassigned -- + Qt::Key_unknown, // 137 0x89 -- unassigned -- + Qt::Key_unknown, // 138 0x8A -- unassigned -- + Qt::Key_unknown, // 139 0x8B -- unassigned -- + Qt::Key_unknown, // 140 0x8C -- unassigned -- + Qt::Key_unknown, // 141 0x8D -- unassigned -- + Qt::Key_unknown, // 142 0x8E -- unassigned -- + Qt::Key_unknown, // 143 0x8F -- unassigned -- + Qt::Key_NumLock, // 144 0x90 VK_NUMLOCK | Num Lock key + Qt::Key_ScrollLock, // 145 0x91 VK_SCROLL | Scroll Lock key + // Fujitsu/OASYS kbd -------------------- + 0, //Qt::Key_Jisho, // 146 0x92 VK_OEM_FJ_JISHO | 'Dictionary' key / + // VK_OEM_NEC_EQUAL = key on numpad on NEC PC-9800 kbd + Qt::Key_Massyo, // 147 0x93 VK_OEM_FJ_MASSHOU | 'Unregister word' key + Qt::Key_Touroku, // 148 0x94 VK_OEM_FJ_TOUROKU | 'Register word' key + 0, //Qt::Key_Oyayubi_Left,//149 0x95 VK_OEM_FJ_LOYA | 'Left OYAYUBI' key + 0, //Qt::Key_Oyayubi_Right,//150 0x96 VK_OEM_FJ_ROYA | 'Right OYAYUBI' key + Qt::Key_unknown, // 151 0x97 -- unassigned -- + Qt::Key_unknown, // 152 0x98 -- unassigned -- + Qt::Key_unknown, // 153 0x99 -- unassigned -- + Qt::Key_unknown, // 154 0x9A -- unassigned -- + Qt::Key_unknown, // 155 0x9B -- unassigned -- + Qt::Key_unknown, // 156 0x9C -- unassigned -- + Qt::Key_unknown, // 157 0x9D -- unassigned -- + Qt::Key_unknown, // 158 0x9E -- unassigned -- + Qt::Key_unknown, // 159 0x9F -- unassigned -- + Qt::Key_Shift, // 160 0xA0 VK_LSHIFT | Left Shift key + Qt::Key_Shift, // 161 0xA1 VK_RSHIFT | Right Shift key + Qt::Key_Control, // 162 0xA2 VK_LCONTROL | Left Ctrl key + Qt::Key_Control, // 163 0xA3 VK_RCONTROL | Right Ctrl key + Qt::Key_Alt, // 164 0xA4 VK_LMENU | Left Menu key + Qt::Key_Alt, // 165 0xA5 VK_RMENU | Right Menu key + Qt::Key_Back, // 166 0xA6 VK_BROWSER_BACK | Browser Back key + Qt::Key_Forward, // 167 0xA7 VK_BROWSER_FORWARD | Browser Forward key + Qt::Key_Refresh, // 168 0xA8 VK_BROWSER_REFRESH | Browser Refresh key + Qt::Key_Stop, // 169 0xA9 VK_BROWSER_STOP | Browser Stop key + Qt::Key_Search, // 170 0xAA VK_BROWSER_SEARCH | Browser Search key + Qt::Key_Favorites, // 171 0xAB VK_BROWSER_FAVORITES| Browser Favorites key + Qt::Key_HomePage, // 172 0xAC VK_BROWSER_HOME | Browser Start and Home key + Qt::Key_VolumeMute, // 173 0xAD VK_VOLUME_MUTE | Volume Mute key + Qt::Key_VolumeDown, // 174 0xAE VK_VOLUME_DOWN | Volume Down key + Qt::Key_VolumeUp, // 175 0xAF VK_VOLUME_UP | Volume Up key + Qt::Key_MediaNext, // 176 0xB0 VK_MEDIA_NEXT_TRACK | Next Track key + Qt::Key_MediaPrevious, //177 0xB1 VK_MEDIA_PREV_TRACK | Previous Track key + Qt::Key_MediaStop, // 178 0xB2 VK_MEDIA_STOP | Stop Media key + Qt::Key_MediaPlay, // 179 0xB3 VK_MEDIA_PLAY_PAUSE | Play/Pause Media key + Qt::Key_LaunchMail, // 180 0xB4 VK_LAUNCH_MAIL | Start Mail key + Qt::Key_LaunchMedia,// 181 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key + Qt::Key_Launch0, // 182 0xB6 VK_LAUNCH_APP1 | Start Application 1 key + Qt::Key_Launch1, // 183 0xB7 VK_LAUNCH_APP2 | Start Application 2 key + Qt::Key_unknown, // 184 0xB8 -- reserved -- + Qt::Key_unknown, // 185 0xB9 -- reserved -- + 0, // 186 0xBA VK_OEM_1 | ';:' for US + 0, // 187 0xBB VK_OEM_PLUS | '+' any country + 0, // 188 0xBC VK_OEM_COMMA | ',' any country + 0, // 189 0xBD VK_OEM_MINUS | '-' any country + 0, // 190 0xBE VK_OEM_PERIOD | '.' any country + 0, // 191 0xBF VK_OEM_2 | '/?' for US + 0, // 192 0xC0 VK_OEM_3 | '`~' for US + Qt::Key_unknown, // 193 0xC1 -- reserved -- + Qt::Key_unknown, // 194 0xC2 -- reserved -- + Qt::Key_unknown, // 195 0xC3 -- reserved -- + Qt::Key_unknown, // 196 0xC4 -- reserved -- + Qt::Key_unknown, // 197 0xC5 -- reserved -- + Qt::Key_unknown, // 198 0xC6 -- reserved -- + Qt::Key_unknown, // 199 0xC7 -- reserved -- + Qt::Key_unknown, // 200 0xC8 -- reserved -- + Qt::Key_unknown, // 201 0xC9 -- reserved -- + Qt::Key_unknown, // 202 0xCA -- reserved -- + Qt::Key_unknown, // 203 0xCB -- reserved -- + Qt::Key_unknown, // 204 0xCC -- reserved -- + Qt::Key_unknown, // 205 0xCD -- reserved -- + Qt::Key_unknown, // 206 0xCE -- reserved -- + Qt::Key_unknown, // 207 0xCF -- reserved -- + Qt::Key_unknown, // 208 0xD0 -- reserved -- + Qt::Key_unknown, // 209 0xD1 -- reserved -- + Qt::Key_unknown, // 210 0xD2 -- reserved -- + Qt::Key_unknown, // 211 0xD3 -- reserved -- + Qt::Key_unknown, // 212 0xD4 -- reserved -- + Qt::Key_unknown, // 213 0xD5 -- reserved -- + Qt::Key_unknown, // 214 0xD6 -- reserved -- + Qt::Key_unknown, // 215 0xD7 -- reserved -- + Qt::Key_unknown, // 216 0xD8 -- unassigned -- + Qt::Key_unknown, // 217 0xD9 -- unassigned -- + Qt::Key_unknown, // 218 0xDA -- unassigned -- + 0, // 219 0xDB VK_OEM_4 | '[{' for US + 0, // 220 0xDC VK_OEM_5 | '\|' for US + 0, // 221 0xDD VK_OEM_6 | ']}' for US + 0, // 222 0xDE VK_OEM_7 | ''"' for US + 0, // 223 0xDF VK_OEM_8 + Qt::Key_unknown, // 224 0xE0 -- reserved -- + Qt::Key_unknown, // 225 0xE1 VK_OEM_AX | 'AX' key on Japanese AX kbd + Qt::Key_unknown, // 226 0xE2 VK_OEM_102 | "<>" or "\|" on RT 102-key kbd + Qt::Key_unknown, // 227 0xE3 VK_ICO_HELP | Help key on ICO + Qt::Key_unknown, // 228 0xE4 VK_ICO_00 | 00 key on ICO + Qt::Key_unknown, // 229 0xE5 VK_PROCESSKEY | IME Process key + Qt::Key_unknown, // 230 0xE6 VK_ICO_CLEAR | + Qt::Key_unknown, // 231 0xE7 VK_PACKET | Unicode char as keystrokes + Qt::Key_unknown, // 232 0xE8 -- unassigned -- + // Nokia/Ericsson definitions --------------- + Qt::Key_unknown, // 233 0xE9 VK_OEM_RESET + Qt::Key_unknown, // 234 0xEA VK_OEM_JUMP + Qt::Key_unknown, // 235 0xEB VK_OEM_PA1 + Qt::Key_unknown, // 236 0xEC VK_OEM_PA2 + Qt::Key_unknown, // 237 0xED VK_OEM_PA3 + Qt::Key_unknown, // 238 0xEE VK_OEM_WSCTRL + Qt::Key_unknown, // 239 0xEF VK_OEM_CUSEL + Qt::Key_unknown, // 240 0xF0 VK_OEM_ATTN + Qt::Key_unknown, // 241 0xF1 VK_OEM_FINISH + Qt::Key_unknown, // 242 0xF2 VK_OEM_COPY + Qt::Key_unknown, // 243 0xF3 VK_OEM_AUTO + Qt::Key_unknown, // 244 0xF4 VK_OEM_ENLW + Qt::Key_unknown, // 245 0xF5 VK_OEM_BACKTAB + Qt::Key_unknown, // 246 0xF6 VK_ATTN | Attn key + Qt::Key_unknown, // 247 0xF7 VK_CRSEL | CrSel key + Qt::Key_unknown, // 248 0xF8 VK_EXSEL | ExSel key + Qt::Key_unknown, // 249 0xF9 VK_EREOF | Erase EOF key + Qt::Key_Play, // 250 0xFA VK_PLAY | Play key + Qt::Key_Zoom, // 251 0xFB VK_ZOOM | Zoom key + Qt::Key_unknown, // 252 0xFC VK_NONAME | Reserved + Qt::Key_unknown, // 253 0xFD VK_PA1 | PA1 key + Qt::Key_Clear, // 254 0xFE VK_OEM_CLEAR | Clear key + 0 +}; + +// Possible modifier states. +// NOTE: The order of these states match the order in QKeyMapperPrivate::updatePossibleKeyCodes()! +static const Qt::KeyboardModifiers ModsTbl[] = { + Qt::NoModifier, // 0 + Qt::ShiftModifier, // 1 + Qt::ControlModifier, // 2 + Qt::ControlModifier | Qt::ShiftModifier, // 3 + Qt::AltModifier, // 4 + Qt::AltModifier | Qt::ShiftModifier, // 5 + Qt::AltModifier | Qt::ControlModifier, // 6 + Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 + Qt::NoModifier, // Fall-back to raw Key_* +}; + +/** + Remap return or action key to select key for windows mobile. +*/ +inline int winceKeyBend(int keyCode) +{ +#if defined(Q_OS_WINCE_WM) && defined(QT_KEYPAD_NAVIGATION) + // remap return or action key to select key for windows mobile. + // will be changed to a table remapping function in the next version (4.6/7). + if (keyCode == VK_RETURN && QApplication::keypadNavigationEnabled()) + return Qt::Key_Select; + else + return KeyTbl[keyCode]; +#else + return KeyTbl[keyCode]; +#endif +} + +#if defined(Q_OS_WINCE) + // Use the KeyTbl to resolve a Qt::Key out of the virtual keys. + // In case it is not resolvable, continue using the virtual key itself. + +QT_BEGIN_INCLUDE_NAMESPACE + +int ToUnicode(UINT vk, int /*scancode*/, unsigned char* /*kbdBuffer*/, LPWSTR unicodeBuffer, int, int) +{ + QT_USE_NAMESPACE + QChar* buf = reinterpret_cast< QChar*>(unicodeBuffer); + if (KeyTbl[vk] == 0) { + buf[0] = vk; + return 1; + } + return 0; +} + +int ToAscii(UINT vk, int scancode, unsigned char *kbdBuffer, LPWORD unicodeBuffer, int flag) +{ + return ToUnicode(vk, scancode, kbdBuffer, (LPWSTR) unicodeBuffer, 0, flag); + +} +QT_END_INCLUDE_NAMESPACE + +#endif + +// Translate a VK into a Qt key code, or unicode character +static inline int toKeyOrUnicode(int vk, int scancode, unsigned char *kbdBuffer, bool *isDeadkey = 0) +{ + Q_ASSERT(vk > 0 && vk < 256); + int code = 0; + QChar unicodeBuffer[5]; + int res = ToUnicode(vk, scancode, kbdBuffer, reinterpret_cast<LPWSTR>(unicodeBuffer), 5, 0); + if (res) + code = unicodeBuffer[0].toUpper().unicode(); + + // Qt::Key_*'s are not encoded below 0x20, so try again, and DEL keys (0x7f) is encoded with a + // proper Qt::Key_ code + if (code < 0x20 || code == 0x7f) // Handles res==0 too + code = winceKeyBend(vk); + + if (isDeadkey) + *isDeadkey = (res == -1); + + return code == Qt::Key_unknown ? 0 : code; +} + +Q_GUI_EXPORT int qt_translateKeyCode(int vk) +{ + int code = winceKeyBend((vk < 0 || vk > 255) ? 0 : vk); + return code == Qt::Key_unknown ? 0 : code; +} + +static inline int asciiToKeycode(char a, int state) +{ + if (a >= 'a' && a <= 'z') + a = toupper(a); + if ((state & Qt::ControlModifier) != 0) { + if (a >= 0 && a <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_ + a += '@'; // to @..A..Z.._ + } + return a & 0xff; +} + +static inline bool isModifierKey(int code) +{ + return (code >= Qt::Key_Shift) && (code <= Qt::Key_ScrollLock); +} +// Key translation -----------------------------------------------------------------------[ end ]--- + + +static void qt_show_system_menu(QWidget* tlw) +{ + Q_ASSERT(tlw->testAttribute(Qt::WA_WState_Created)); + HMENU menu = GetSystemMenu(tlw->internalWinId(), FALSE); + if (!menu) + return; // no menu for this window + +#define enabled (MF_BYCOMMAND | MF_ENABLED) +#define disabled (MF_BYCOMMAND | MF_GRAYED) + +#ifndef Q_OS_WINCE + EnableMenuItem(menu, SC_MINIMIZE, (tlw->windowFlags() & Qt::WindowMinimizeButtonHint)?enabled:disabled); + bool maximized = IsZoomed(tlw->internalWinId()); + + EnableMenuItem(menu, SC_MAXIMIZE, ! (tlw->windowFlags() & Qt::WindowMaximizeButtonHint) || maximized?disabled:enabled); + EnableMenuItem(menu, SC_RESTORE, maximized?enabled:disabled); + + // We should _not_ check with the setFixedSize(x,y) case here, since Windows is not able to check + // this and our menu here would be out-of-sync with the menu produced by mouse-click on the + // System Menu, or right-click on the title bar. + EnableMenuItem(menu, SC_SIZE, (tlw->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) || maximized?disabled:enabled); + EnableMenuItem(menu, SC_MOVE, maximized?disabled:enabled); + EnableMenuItem(menu, SC_CLOSE, enabled); + // Set bold on close menu item + MENUITEMINFO closeItem; + closeItem.cbSize = sizeof(MENUITEMINFO); + closeItem.fMask = MIIM_STATE; + closeItem.fState = MFS_DEFAULT; + SetMenuItemInfo(menu, SC_CLOSE, FALSE, &closeItem); +#endif + +#undef enabled +#undef disabled + int ret = TrackPopupMenuEx(menu, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD, + tlw->geometry().x(), tlw->geometry().y(), + tlw->internalWinId(), + 0); + if (ret) + QtWndProc(tlw->internalWinId(), WM_SYSCOMMAND, ret, 0); +} + + +// QETWidget class is only for accessing the sendSpontaneousEvent function in QApplication +class QETWidget : public QWidget { +public: + static bool sendSpontaneousEvent(QObject *r, QEvent *e) + { return QApplication::sendSpontaneousEvent(r, e); } +}; + + +// Keyboard map private ----------------------------------------------------------------[ start ]--- + +/* + \internal + A Windows KeyboardLayoutItem has 8 possible states: + 1. Unmodified + 2. Shift + 3. Control + 4. Control + Shift + 5. Alt + 6. Alt + Shift + 7. Alt + Control + 8. Alt + Control + Shift +*/ +struct KeyboardLayoutItem { + bool dirty; + quint8 deadkeys; + quint32 qtKey[9]; // Can by any Qt::Key_<foo>, or unicode character +}; + +QKeyMapperPrivate::QKeyMapperPrivate() +{ + memset(keyLayout, 0, sizeof(keyLayout)); +} + +QKeyMapperPrivate::~QKeyMapperPrivate() +{ + deleteLayouts(); +} + +void QKeyMapperPrivate::deleteLayouts() +{ + for (int i = 0; i < 255; ++i) { + if (keyLayout[i]) { + delete keyLayout[i]; + keyLayout[i] = 0; + } + } +} + +void QKeyMapperPrivate::clearMappings() +{ + deleteLayouts(); + + /* MAKELCID()'s first argument is a WORD, and GetKeyboardLayout() + * returns a DWORD. */ + + LCID newLCID = MAKELCID((quintptr)GetKeyboardLayout(0), SORT_DEFAULT); +// keyboardInputLocale = qt_localeFromLCID(newLCID); + + bool bidi = false; + wchar_t LCIDFontSig[16]; + if (GetLocaleInfo(newLCID, LOCALE_FONTSIGNATURE, LCIDFontSig, sizeof(LCIDFontSig) / sizeof(wchar_t)) + && (LCIDFontSig[7] & (wchar_t)0x0800)) + bidi = true; + + keyboardInputDirection = bidi ? Qt::RightToLeft : Qt::LeftToRight; +} + +void QKeyMapperPrivate::clearRecordedKeys() +{ + key_recorder.clearKeys(); +} + + +inline void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt) +{ + kbd[VK_LSHIFT ] = (shift ? 0x80 : 0); + kbd[VK_SHIFT ] = (shift ? 0x80 : 0); + kbd[VK_LCONTROL] = (ctrl ? 0x80 : 0); + kbd[VK_CONTROL ] = (ctrl ? 0x80 : 0); + kbd[VK_RMENU ] = (alt ? 0x80 : 0); + kbd[VK_MENU ] = (alt ? 0x80 : 0); +} + +void QKeyMapperPrivate::updateKeyMap(const MSG &msg) +{ + unsigned char kbdBuffer[256]; // Will hold the complete keyboard state + GetKeyboardState(kbdBuffer); + quint32 scancode = (msg.lParam >> 16) & 0xfff; + updatePossibleKeyCodes(kbdBuffer, scancode, msg.wParam); +} + +void QKeyMapperPrivate::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode, + quint32 vk_key) +{ + if (!vk_key || (keyLayout[vk_key] && !keyLayout[vk_key]->dirty)) + return; + + if (!keyLayout[vk_key]) + keyLayout[vk_key] = new KeyboardLayoutItem; + + // Copy keyboard state, so we can modify and query output for each possible permutation + unsigned char buffer[256]; + memcpy(buffer, kbdBuffer, sizeof(buffer)); + // Always 0, as Windows doesn't treat these as modifiers; + buffer[VK_LWIN ] = 0; + buffer[VK_RWIN ] = 0; + buffer[VK_CAPITAL ] = 0; + buffer[VK_NUMLOCK ] = 0; + buffer[VK_SCROLL ] = 0; + // Always 0, since we'll only change the other versions + buffer[VK_RSHIFT ] = 0; + buffer[VK_RCONTROL] = 0; + buffer[VK_LMENU ] = 0; // Use right Alt, since left Ctrl + right Alt is considered AltGraph + + bool isDeadKey = false; + keyLayout[vk_key]->deadkeys = 0; + keyLayout[vk_key]->dirty = false; + setKbdState(buffer, false, false, false); + keyLayout[vk_key]->qtKey[0] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x01 : 0; + setKbdState(buffer, true, false, false); + keyLayout[vk_key]->qtKey[1] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x02 : 0; + setKbdState(buffer, false, true, false); + keyLayout[vk_key]->qtKey[2] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x04 : 0; + setKbdState(buffer, true, true, false); + keyLayout[vk_key]->qtKey[3] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x08 : 0; + setKbdState(buffer, false, false, true); + keyLayout[vk_key]->qtKey[4] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x10 : 0; + setKbdState(buffer, true, false, true); + keyLayout[vk_key]->qtKey[5] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x20 : 0; + setKbdState(buffer, false, true, true); + keyLayout[vk_key]->qtKey[6] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x40 : 0; + setKbdState(buffer, true, true, true); + keyLayout[vk_key]->qtKey[7] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x80 : 0; + // Add a fall back key for layouts which don't do composition and show non-latin1 characters + int fallbackKey = winceKeyBend(vk_key); + if (!fallbackKey || fallbackKey == Qt::Key_unknown) { + fallbackKey = 0; + if (vk_key != keyLayout[vk_key]->qtKey[0] && vk_key < 0x5B && vk_key > 0x2F) + fallbackKey = vk_key; + } + keyLayout[vk_key]->qtKey[8] = fallbackKey; + + // If this vk_key a Dead Key + if (MapVirtualKey(vk_key, 2) & 0x80000000) { + // Push a Space, then the original key through the low-level ToAscii functions. + // We do this because these functions (ToAscii / ToUnicode) will alter the internal state of + // the keyboard driver By doing the following, we set the keyboard driver state back to what + // it was before we wrecked it with the code above. + // We need to push the space with an empty keystate map, since the driver checks the map for + // transitions in modifiers, so this helps us capture all possible deadkeys. + unsigned char emptyBuffer[256]; + memset(emptyBuffer, 0, sizeof(emptyBuffer)); + ::ToAscii(VK_SPACE, 0, emptyBuffer, reinterpret_cast<LPWORD>(&buffer), 0); + ::ToAscii(vk_key, scancode, kbdBuffer, reinterpret_cast<LPWORD>(&buffer), 0); + } + +#ifdef DEBUG_KEYMAPPER + qDebug("updatePossibleKeyCodes for virtual key = 0x%02x!", vk_key); + for (int i = 0; i < 9; ++i) { + qDebug(" [%d] (%d,0x%02x,'%c') %s", i, + keyLayout[vk_key]->qtKey[i], + keyLayout[vk_key]->qtKey[i], + keyLayout[vk_key]->qtKey[i] ? keyLayout[vk_key]->qtKey[i] : 0x03, + keyLayout[vk_key]->deadkeys & (1<<i) ? "deadkey" : ""); + } +#endif // DEBUG_KEYMAPPER +} + +bool QKeyMapperPrivate::isADeadKey(unsigned int vk_key, unsigned int modifiers) +{ + if (keyLayout && (vk_key < 256) && keyLayout[vk_key]) { + for(register int i = 0; i < 9; ++i) { + if (uint(ModsTbl[i]) == modifiers) + return bool(keyLayout[vk_key]->deadkeys & 1<<i); + } + } + return false; +} + +extern bool qt_use_rtl_extensions; + +QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *e) +{ + QList<int> result; + + KeyboardLayoutItem *kbItem = keyLayout[e->nativeVirtualKey()]; + if(!kbItem) + return result; + + quint32 baseKey = kbItem->qtKey[0]; + Qt::KeyboardModifiers keyMods = e->modifiers(); + if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) { + result << int(Qt::Key_Enter + keyMods); + return result; + } + result << int(baseKey + keyMods); // The base key is _always_ valid, of course + + for(int i = 1; i < 9; ++i) { + Qt::KeyboardModifiers neededMods = ModsTbl[i]; + quint32 key = kbItem->qtKey[i]; + if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) + result << int(key + (keyMods & ~neededMods)); + } + + return result; +} + +bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, const MSG &msg, bool grab) +{ + Q_Q(QKeyMapper); + Q_UNUSED(q); // Strange, but the compiler complains on q not being referenced, even if it is.. + bool k0 = false; + bool k1 = false; + int msgType = msg.message; + + quint32 scancode = (msg.lParam >> 16) & 0xfff; + quint32 vk_key = MapVirtualKey(scancode, 1); + bool isNumpad = (msg.wParam >= VK_NUMPAD0 && msg.wParam <= VK_NUMPAD9); + quint32 nModifiers = 0; + +#if defined(Q_OS_WINCE) + nModifiers |= (GetKeyState(VK_SHIFT ) < 0 ? ShiftAny : 0); + nModifiers |= (GetKeyState(VK_CONTROL) < 0 ? ControlAny : 0); + nModifiers |= (GetKeyState(VK_MENU ) < 0 ? AltAny : 0); + nModifiers |= (GetKeyState(VK_LWIN ) < 0 ? MetaLeft : 0); + nModifiers |= (GetKeyState(VK_RWIN ) < 0 ? MetaRight : 0); +#else + // Map native modifiers to some bit representation + nModifiers |= (GetKeyState(VK_LSHIFT ) & 0x80 ? ShiftLeft : 0); + nModifiers |= (GetKeyState(VK_RSHIFT ) & 0x80 ? ShiftRight : 0); + nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0); + nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0); + nModifiers |= (GetKeyState(VK_LMENU ) & 0x80 ? AltLeft : 0); + nModifiers |= (GetKeyState(VK_RMENU ) & 0x80 ? AltRight : 0); + nModifiers |= (GetKeyState(VK_LWIN ) & 0x80 ? MetaLeft : 0); + nModifiers |= (GetKeyState(VK_RWIN ) & 0x80 ? MetaRight : 0); + // Add Lock keys to the same bits + nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0); + nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0); + nModifiers |= (GetKeyState(VK_SCROLL ) & 0x01 ? ScrollLock : 0); +#endif // Q_OS_WINCE + + if (msg.lParam & ExtendedKey) + nModifiers |= msg.lParam & ExtendedKey; + + // Get the modifier states (may be altered later, depending on key code) + int state = 0; + state |= (nModifiers & ShiftAny ? Qt::ShiftModifier : 0); + state |= (nModifiers & ControlAny ? Qt::ControlModifier : 0); + state |= (nModifiers & AltAny ? Qt::AltModifier : 0); + state |= (nModifiers & MetaAny ? Qt::MetaModifier : 0); + + // Now we know enough to either have MapVirtualKey or our own keymap tell us if it's a deadkey + bool isDeadKey = isADeadKey(msg.wParam, state) + || MapVirtualKey(msg.wParam, 2) & 0x80000000; + + // A multi-character key not found by our look-ahead + if (msgType == WM_CHAR) { + QString s; + QChar ch = QChar((ushort)msg.wParam); + if (!ch.isNull()) + s += ch; + + k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers); + k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers); + } + + // Input method characters not found by our look-ahead + else if (msgType == WM_IME_CHAR) { + QString s; + QChar ch = QChar((ushort)msg.wParam); + if (!ch.isNull()) + s += ch; + + k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers); + k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, 0, Qt::KeyboardModifier(state), s, false, 0, scancode, vk_key, nModifiers); + } + + else { + // handle Directionality changes (BiDi) with RTL extensions + if (qt_use_rtl_extensions) { + static int dirStatus = 0; + if (!dirStatus && state == Qt::ControlModifier + && msg.wParam == VK_CONTROL + && msgType == WM_KEYDOWN) { + if (GetKeyState(VK_LCONTROL) < 0) + dirStatus = VK_LCONTROL; + else if (GetKeyState(VK_RCONTROL) < 0) + dirStatus = VK_RCONTROL; + } else if (dirStatus) { + if (msgType == WM_KEYDOWN) { + if (msg.wParam == VK_SHIFT) { + if (dirStatus == VK_LCONTROL && GetKeyState(VK_LSHIFT) < 0) + dirStatus = VK_LSHIFT; + else if (dirStatus == VK_RCONTROL && GetKeyState(VK_RSHIFT) < 0) + dirStatus = VK_RSHIFT; + } else { + dirStatus = 0; + } + } else if (msgType == WM_KEYUP) { + if (dirStatus == VK_LSHIFT + && ((msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL)) + || (msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT)))) { + k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, Qt::Key_Direction_L, 0, + QString(), false, 0, + scancode, msg.wParam, nModifiers); + k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, Qt::Key_Direction_L, 0, + QString(), false, 0, + scancode, msg.wParam, nModifiers); + dirStatus = 0; + } else if (dirStatus == VK_RSHIFT + && ( (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL)) + || (msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT)))) { + k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, Qt::Key_Direction_R, + 0, QString(), false, 0, + scancode, msg.wParam, nModifiers); + k1 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, Qt::Key_Direction_R, + 0, QString(), false, 0, + scancode, msg.wParam, nModifiers); + dirStatus = 0; + } else { + dirStatus = 0; + } + } else { + dirStatus = 0; + } + } + } + + // IME will process these keys, so simply return + if(msg.wParam == VK_PROCESSKEY) + return true; + + // Ignore invalid virtual keycodes (see bugs 127424, QTBUG-3630) + if (msg.wParam == 0 || msg.wParam == 0xFF) + return true; + + // Translate VK_* (native) -> Key_* (Qt) keys + // If it's a dead key, we cannot use the toKeyOrUnicode() function, since that will change + // the internal state of the keyboard driver, resulting in that dead keys no longer works. + // ..also if we're typing numbers on the keypad, while holding down the Alt modifier. + int code = 0; + if (isNumpad && (nModifiers & AltAny)) { + code = winceKeyBend(msg.wParam); + } else if (!isDeadKey) { + unsigned char kbdBuffer[256]; // Will hold the complete keyboard state + GetKeyboardState(kbdBuffer); + code = toKeyOrUnicode(msg.wParam, scancode, kbdBuffer); + } + + // Invert state logic: + // If the key actually pressed is a modifier key, then we remove its modifier key from the + // state, since a modifier-key can't have itself as a modifier + if (code == Qt::Key_Control) + state = state ^ Qt::ControlModifier; + else if (code == Qt::Key_Shift) + state = state ^ Qt::ShiftModifier; + else if (code == Qt::Key_Alt) + state = state ^ Qt::AltModifier; + + // If the bit 24 of lParm is set you received a enter, + // otherwise a Return. (This is the extended key bit) + if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000)) + code = Qt::Key_Enter; + + // All cursor keys without extended bit + if (!(msg.lParam & 0x1000000)) { + switch (code) { + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_Insert: + case Qt::Key_Delete: + case Qt::Key_Asterisk: + case Qt::Key_Plus: + case Qt::Key_Minus: + case Qt::Key_Period: + case Qt::Key_0: + case Qt::Key_1: + case Qt::Key_2: + case Qt::Key_3: + case Qt::Key_4: + case Qt::Key_5: + case Qt::Key_6: + case Qt::Key_7: + case Qt::Key_8: + case Qt::Key_9: + state |= ((msg.wParam >= '0' && msg.wParam <= '9') + || (msg.wParam >= VK_OEM_PLUS && msg.wParam <= VK_OEM_3)) + ? 0 : Qt::KeypadModifier; + default: + if ((uint)msg.lParam == 0x004c0001 || (uint)msg.lParam == 0xc04c0001) + state |= Qt::KeypadModifier; + break; + } + } + // Other keys with with extended bit + else { + switch (code) { + case Qt::Key_Enter: + case Qt::Key_Slash: + case Qt::Key_NumLock: + state |= Qt::KeypadModifier; + default: + break; + } + } + + // KEYDOWN --------------------------------------------------------------------------------- + if (msgType == WM_KEYDOWN || msgType == WM_IME_KEYDOWN || msgType == WM_SYSKEYDOWN) { + // Get the last record of this key press, so we can validate the current state + // The record is not removed from the list + KeyRecord *rec = key_recorder.findKey(msg.wParam, false); + + // If rec's state doesn't match the current state, something has changed behind our back + // (Consumed by modal widget is one possibility) So, remove the record from the list + // This will stop the auto-repeat of the key, should a modifier change, for example + if (rec && rec->state != state) { + key_recorder.findKey(msg.wParam, true); + rec = 0; + } + + // Find unicode character from Windows Message Queue + MSG wm_char; + UINT charType = (msgType == WM_KEYDOWN + ? WM_CHAR + : msgType == WM_IME_KEYDOWN ? WM_IME_CHAR : WM_SYSCHAR); + + QChar uch; + if (PeekMessage(&wm_char, 0, charType, charType, PM_REMOVE)) { + // Found a ?_CHAR + uch = QChar((ushort)wm_char.wParam); + if (msgType == WM_SYSKEYDOWN && uch.isLetter() && (msg.lParam & KF_ALTDOWN)) + uch = uch.toLower(); // (See doc of WM_SYSCHAR) Alt-letter + if (!code && !uch.row()) + code = asciiToKeycode(uch.cell(), state); + } + + // Special handling for the WM_IME_KEYDOWN message. Microsoft IME (Korean) will not + // generate a WM_IME_CHAR message corresponding to this message. We might get wrong + // results, if we map this virtual key-code directly (for eg '?' US layouts). So try + // to find the correct key using the current message parameters & keyboard state. + if (uch.isNull() && msgType == WM_IME_KEYDOWN) { + BYTE keyState[256]; + wchar_t newKey[3] = {0}; + GetKeyboardState(keyState); + int val = ToUnicode(vk_key, scancode, keyState, newKey, 2, 0); + if (val == 1) { + uch = QChar(newKey[0]); + } else { + // If we are still not able to find a unicode key, pass the WM_IME_KEYDOWN + // message to DefWindowProc() for generating a proper WM_KEYDOWN. + return false; + } + } + + // If no ?_CHAR was found in the queue; deduct character from the ?_KEYDOWN parameters + if (uch.isNull()) { + if (msg.wParam == VK_DELETE) { + uch = QChar(QLatin1Char(0x7f)); // Windows doesn't know this one. + } else { + if (msgType != WM_SYSKEYDOWN || !code) { + UINT map = MapVirtualKey(msg.wParam, 2); + // If the high bit of the return value is set, it's a deadkey + if (!(map & 0x80000000)) + uch = QChar((ushort)map); + } + } + if (!code && !uch.row()) + code = asciiToKeycode(uch.cell(), state); + } + + // Special handling of global Windows hotkeys + if (state == Qt::AltModifier) { + switch (code) { + case Qt::Key_Escape: + case Qt::Key_Tab: + case Qt::Key_Enter: + case Qt::Key_F4: + return false; // Send the event on to Windows + case Qt::Key_Space: + // do not pass this key to windows, we will process it ourselves + qt_show_system_menu(widget->window()); + return true; + default: + break; + } + } + + // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation + if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier) + code = Qt::Key_Backtab; + + // If we have a record, it means that the key is already pressed, the state is the same + // so, we have an auto-repeating key + if (rec) { + if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) { + k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, code, + Qt::KeyboardModifier(state), rec->text, true, 0, + scancode, msg.wParam, nModifiers); + k1 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code, + Qt::KeyboardModifier(state), rec->text, true, 0, + scancode, msg.wParam, nModifiers); + } + } + // No record of the key being previous pressed, so we now send a QEvent::KeyPress event, + // and store the key data into our records. + else { + QString text; + if (!uch.isNull()) + text += uch; + char a = uch.row() ? 0 : uch.cell(); + key_recorder.storeKey(msg.wParam, a, state, text); + k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code, Qt::KeyboardModifier(state), + text, false, 0, scancode, msg.wParam, nModifiers); + + bool store = true; + // Alt+<alphanumerical> go to the Win32 menu system if unhandled by Qt +#if !defined(Q_OS_WINCE) + if (msgType == WM_SYSKEYDOWN && !k0 && a) { + HWND parent = GetParent(widget->internalWinId()); + while (parent) { + if (GetMenu(parent)) { + SendMessage(parent, WM_SYSCOMMAND, SC_KEYMENU, a); + store = false; + k0 = true; + break; + } + parent = GetParent(parent); + } + } +#endif + if (!store) + key_recorder.findKey(msg.wParam, true); + } + } + + // KEYUP ----------------------------------------------------------------------------------- + else { + // Try to locate the key in our records, and remove it if it exists. + // The key may not be in our records if, for example, the down event was handled by + // win32 natively, or our window gets focus while a key is already press, but now gets + // the key release event. + KeyRecord* rec = key_recorder.findKey(msg.wParam, true); + if (!rec && !(code == Qt::Key_Shift + || code == Qt::Key_Control + || code == Qt::Key_Meta + || code == Qt::Key_Alt)) { + // Someone ate the key down event + } else { + if (!code) + code = asciiToKeycode(rec->ascii ? rec->ascii : msg.wParam, state); + + // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation + if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier) + code = Qt::Key_Backtab; + + k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, code, Qt::KeyboardModifier(state), + (rec ? rec->text : QString()), false, 0, scancode, msg.wParam, nModifiers); + + // don't pass Alt to Windows unless we are embedded in a non-Qt window +#if !defined(Q_OS_WINCE) + if (code == Qt::Key_Alt) { + k0 = true; + HWND parent = GetParent(widget->internalWinId()); + while (parent) { + if (!QWidget::find(parent) && GetMenu(parent)) { + k0 = false; + break; + } + parent = GetParent(parent); + } + } +#endif + } + } + } + + // Return true, if a QKeyEvent was sent to a widget + return k0 || k1; +} + + +// QKeyMapper (Windows) implementation -------------------------------------------------[ start ]--- + +bool QKeyMapper::sendKeyEvent(QWidget *widget, bool grab, + QEvent::Type type, int code, Qt::KeyboardModifiers modifiers, + const QString &text, bool autorepeat, int count, + quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, + bool *) +{ +#if defined(Q_OS_WINCE) + Q_UNUSED(grab); +#endif + Q_UNUSED(count); +#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT) + if (type == QEvent::KeyPress + && !grab + && QApplicationPrivate::instance()->use_compat()) { + // send accel events if the keyboard is not grabbed + QKeyEventEx a(type, code, modifiers, + text, autorepeat, qMax(1, int(text.length())), + nativeScanCode, nativeVirtualKey, nativeModifiers); + if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &a)) + return true; + } +#else + Q_UNUSED(grab); +#endif + if (!widget->isEnabled()) + return false; + + QKeyEventEx e(type, code, modifiers, + text, autorepeat, qMax(1, int(text.length())), + nativeScanCode, nativeVirtualKey, nativeModifiers); + QETWidget::sendSpontaneousEvent(widget, &e); + + if (!isModifierKey(code) + && modifiers == Qt::AltModifier + && ((code >= Qt::Key_A && code <= Qt::Key_Z) || (code >= Qt::Key_0 && code <= Qt::Key_9)) + && type == QEvent::KeyPress + && !e.isAccepted()) + QApplication::beep(); // Emulate windows behavior + + return e.isAccepted(); +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qkeymapper_x11.cpp b/src/gui/guikernel/qkeymapper_x11.cpp new file mode 100644 index 0000000000..5383bfd456 --- /dev/null +++ b/src/gui/guikernel/qkeymapper_x11.cpp @@ -0,0 +1,1869 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qkeymapper_p.h" + +#include "qdebug.h" +#include "qtextcodec.h" +#include "qwidget.h" + +#include "qapplication_p.h" +#include "qevent_p.h" +#include "qt_x11_p.h" + +#ifndef QT_NO_XKB +# include <X11/XKBlib.h> +#endif + +#define XK_MISCELLANY +#define XK_LATIN1 +#define XK_KOREAN +#define XK_XKB_KEYS +#include <X11/keysymdef.h> + +#include <ctype.h> + +#ifdef QT_LINUXBASE +// LSB's IsKeypadKey define is wrong - see +// http://bugs.linuxbase.org/show_bug.cgi?id=2521 +#undef IsKeypadKey +#define IsKeypadKey(keysym) \ + (((KeySym)(keysym) >= XK_KP_Space) && ((KeySym)(keysym) <= XK_KP_Equal)) + +#undef IsPrivateKeypadKey +#define IsPrivateKeypadKey(keysym) \ + (((KeySym)(keysym) >= 0x11000000) && ((KeySym)(keysym) <= 0x1100FFFF)) +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_XKB + +// bring in the auto-generated xkbLayoutData +#include "qkeymapper_x11_p.cpp" + +QLocale q_getKeyboardLocale(const QByteArray &layoutName, const QByteArray &variantName) +{ + int i = 0; + while (xkbLayoutData[i].layout != 0) { + if (layoutName == xkbLayoutData[i].layout && variantName == xkbLayoutData[i].variant) + return QLocale(xkbLayoutData[i].language, xkbLayoutData[i].country); + ++i; + } + return QLocale::c(); +} +#endif // QT_NO_XKB + +// from qapplication_x11.cpp +extern uchar qt_alt_mask; +extern uchar qt_meta_mask; +extern uchar qt_super_mask; +extern uchar qt_hyper_mask; +extern uchar qt_mode_switch_mask; +uchar qt_num_lock_mask = 0; +extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); + +// ### we should really resolve conflicts with other masks by +// ### decomposing the Qt::KeyboardModifers in possibleKeys() +#define SETMASK(sym, mask) \ + do { \ + if (qt_alt_mask == 0 \ + && qt_meta_mask != mask \ + && qt_super_mask != mask \ + && qt_hyper_mask != mask \ + && (sym == XK_Alt_L || sym == XK_Alt_R)) { \ + qt_alt_mask = mask; \ + } \ + if (qt_meta_mask == 0 \ + && qt_alt_mask != mask \ + && qt_super_mask != mask \ + && qt_hyper_mask != mask \ + && (sym == XK_Meta_L || sym == XK_Meta_R)) { \ + qt_meta_mask = mask; \ + } \ + if (qt_super_mask == 0 \ + && qt_alt_mask != mask \ + && qt_meta_mask != mask \ + && qt_hyper_mask != mask \ + && (sym == XK_Super_L || sym == XK_Super_R)) { \ + qt_super_mask = mask; \ + } \ + if (qt_hyper_mask == 0 \ + && qt_alt_mask != mask \ + && qt_meta_mask != mask \ + && qt_super_mask != mask \ + && (sym == XK_Hyper_L || sym == XK_Hyper_R)) { \ + qt_hyper_mask = mask; \ + } \ + if (qt_mode_switch_mask == 0 \ + && qt_alt_mask != mask \ + && qt_meta_mask != mask \ + && qt_super_mask != mask \ + && qt_hyper_mask != mask \ + && sym == XK_Mode_switch) { \ + qt_mode_switch_mask = mask; \ + } \ + if (qt_num_lock_mask == 0 \ + && sym == XK_Num_Lock) { \ + qt_num_lock_mask = mask; \ + } \ + } while(false) + +// qt_XTranslateKey() is based on _XTranslateKey() taken from: + +/* $Xorg: KeyBind.c,v 1.4 2001/02/09 02:03:34 xorgcvs Exp $ */ + +/* + +Copyright 1985, 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ +static int +qt_XTranslateKey(register QXCoreDesc *dpy, + KeyCode keycode, + register unsigned int modifiers, + unsigned int *modifiers_return, + KeySym *keysym_return) +{ + int per; + register KeySym *syms; + KeySym sym, lsym, usym; + + if (! dpy->keysyms) + return 0; + *modifiers_return = ((ShiftMask|LockMask) + | dpy->mode_switch | dpy->num_lock); + if (((int)keycode < dpy->min_keycode) || ((int)keycode > dpy->max_keycode)) + { + *keysym_return = NoSymbol; + return 1; + } + per = dpy->keysyms_per_keycode; + syms = &dpy->keysyms[(keycode - dpy->min_keycode) * per]; + while ((per > 2) && (syms[per - 1] == NoSymbol)) + per--; + if ((per > 2) && (modifiers & dpy->mode_switch)) { + syms += 2; + per -= 2; + } + if ((modifiers & dpy->num_lock) && + (per > 1 && (IsKeypadKey(syms[1]) || IsPrivateKeypadKey(syms[1])))) { + if ((modifiers & ShiftMask) || + ((modifiers & LockMask) && (dpy->lock_meaning == XK_Shift_Lock))) + *keysym_return = syms[0]; + else + *keysym_return = syms[1]; + } else if (!(modifiers & ShiftMask) && + (!(modifiers & LockMask) || (dpy->lock_meaning == NoSymbol))) { + if ((per == 1) || (syms[1] == NoSymbol)) + XConvertCase(syms[0], keysym_return, &usym); + else + *keysym_return = syms[0]; + } else if (!(modifiers & LockMask) || + (dpy->lock_meaning != XK_Caps_Lock)) { + if ((per == 1) || ((usym = syms[1]) == NoSymbol)) + XConvertCase(syms[0], &lsym, &usym); + *keysym_return = usym; + } else { + if ((per == 1) || ((sym = syms[1]) == NoSymbol)) + sym = syms[0]; + XConvertCase(sym, &lsym, &usym); + if (!(modifiers & ShiftMask) && (sym != syms[0]) && + ((sym != usym) || (lsym == usym))) + XConvertCase(syms[0], &lsym, &usym); + *keysym_return = usym; + } + if (*keysym_return == XK_VoidSymbol) + *keysym_return = NoSymbol; + return 1; +} + + + + +QKeyMapperPrivate::QKeyMapperPrivate() + : keyboardInputDirection(Qt::LeftToRight), xkb_currentGroup(0) +{ + memset(&coreDesc, 0, sizeof(coreDesc)); + +#ifndef QT_NO_XKB + if (X11->use_xkb) { + // get the current group + XkbStateRec xkbState; + if (XkbGetState(X11->display, XkbUseCoreKbd, &xkbState) == Success) + xkb_currentGroup = xkbState.group; + } +#endif +} + +QKeyMapperPrivate::~QKeyMapperPrivate() +{ + if (coreDesc.keysyms) + XFree(coreDesc.keysyms); +} + +QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *event) +{ +#ifndef QT_NO_XKB + if (X11->use_xkb) + return possibleKeysXKB(event); +#endif + return possibleKeysCore(event); +} + +enum { MaxBits = sizeof(uint) * 8 }; +static QString translateKeySym(KeySym keysym, uint xmodifiers, + int &code, Qt::KeyboardModifiers &modifiers, + QByteArray &chars, int &count); + +QList<int> QKeyMapperPrivate::possibleKeysXKB(QKeyEvent *event) +{ +#ifndef QT_NO_XKB + const int xkeycode = event->nativeScanCode(); + const uint xmodifiers = event->nativeModifiers(); + + // first, translate key only using lock modifiers (there are no Qt equivalents for these, so we must + // always use them when determining the baseKeySym) + KeySym baseKeySym; + uint consumedModifiers; + if (!XkbLookupKeySym(X11->display, xkeycode, (xmodifiers & (LockMask | qt_num_lock_mask)), + &consumedModifiers, &baseKeySym)) + return QList<int>(); + + QList<int> result; + + // translate sym -> code + Qt::KeyboardModifiers baseModifiers = 0; + int baseCode = -1; + QByteArray chars; + int count = 0; + QString text = translateKeySym(baseKeySym, xmodifiers, baseCode, baseModifiers, chars, count); + if (baseCode == -1) { + if (text.isEmpty()) + return QList<int>(); + baseCode = text.at(0).unicode(); + } + + if (baseCode && baseCode < 0xfffe) + baseCode = QChar(baseCode).toUpper().unicode(); + result += (baseCode | baseModifiers); + + int pos1Bits[MaxBits]; + int num1Bits = 0; + + for (int i = 0; i < MaxBits; ++i) { + if (consumedModifiers & (1 << i)) + pos1Bits[num1Bits++] = i; + } + + const int numPerms = (1 << num1Bits); + + // translate the key again using each permutation of consumedModifiers + for (int i = 1; i < numPerms; ++i) { + uint val = 0; + for (int j = 0; j < num1Bits; ++j) { + if (i & (1 << j)) + val |= (1 << pos1Bits[j]); + } + + if ((xmodifiers & val) != val) + continue; + + KeySym sym; + uint mods; + if (!XkbLookupKeySym(X11->display, xkeycode, val, &mods, &sym)) + continue; + + // translate sym -> code + Qt::KeyboardModifiers modifiers = 0; + int code = -1; + chars.clear(); + count = 0; + // mask out the modifiers needed to translate keycode + text = translateKeySym(sym, xmodifiers & ~val, code, modifiers, chars, count); + if (code == -1) { + if (text.isEmpty()) + continue; + code = text.at(0).unicode(); + } + + if (code && code < 0xfffe) + code = QChar(code).toUpper().unicode(); + + if (code == Qt::Key_Tab && (baseModifiers & Qt::ShiftModifier)) { + // map shift+tab to shift+backtab + code = Qt::Key_Backtab; + text = QString(); + } + + if (code == baseCode) + continue; + + result += (code | modifiers); + } + +#if 0 + qDebug() << "possibleKeysXKB()" << hex << result; +#endif + return result; +#else + Q_UNUSED(event); + return QList<int>(); +#endif // QT_NO_XKB +} + +QList<int> QKeyMapperPrivate::possibleKeysCore(QKeyEvent *event) +{ + const int xkeycode = event->nativeScanCode(); + const uint xmodifiers = event->nativeModifiers(); + + // first, translate key only using lock modifiers (there are no Qt equivalents for these, so we must + // always use them when determining the baseKeySym) + KeySym baseKeySym; + uint consumedModifiers; + if (!qt_XTranslateKey(&coreDesc, xkeycode, (xmodifiers & (LockMask | qt_num_lock_mask)), + &consumedModifiers, &baseKeySym)) + return QList<int>(); + + QList<int> result; + + // translate sym -> code + Qt::KeyboardModifiers baseModifiers = 0; + int baseCode = -1; + QByteArray chars; + int count = 0; + QString text = translateKeySym(baseKeySym, xmodifiers, baseCode, baseModifiers, chars, count); + if (baseCode == -1) { + if (text.isEmpty()) + return QList<int>(); + baseCode = text.at(0).unicode(); + } + + if (baseCode && baseCode < 0xfffe) + baseCode = QChar(baseCode).toUpper().unicode(); + result += (baseCode | baseModifiers); + + int pos1Bits[MaxBits]; + int num1Bits = 0; + + for (int i = 0; i < MaxBits; ++i) { + if (consumedModifiers & (1 << i)) + pos1Bits[num1Bits++] = i; + } + + const int numPerms = (1 << num1Bits); + + // translate the key again using each permutation of consumedModifiers + for (int i = 1; i < numPerms; ++i) { + uint val = 0; + for (int j = 0; j < num1Bits; ++j) { + if (i & (1 << j)) + val |= (1 << pos1Bits[j]); + } + + if ((xmodifiers & val) != val) + continue; + + KeySym sym; + uint mods; + if (!qt_XTranslateKey(&coreDesc, xkeycode, val, &mods, &sym)) + continue; + + // translate sym -> code + Qt::KeyboardModifiers modifiers = 0; + int code = -1; + chars.clear(); + count = 0; + // mask out the modifiers needed to translate keycode + text = translateKeySym(sym, xmodifiers & ~val, code, modifiers, chars, count); + if (code == -1) { + if (text.isEmpty()) + continue; + code = text.at(0).unicode(); + } + + if (code && code < 0xfffe) + code = QChar(code).toUpper().unicode(); + + if (code == Qt::Key_Tab && (baseModifiers & Qt::ShiftModifier)) { + // map shift+tab to shift+backtab + code = Qt::Key_Backtab; + text = QString(); + } + + if (code == baseCode) + continue; + + result += (code | modifiers); + } + +#if 0 + qDebug() << "possibleKeysCore()" << hex << result; +#endif + return result; +} + +// for parsing the _XKB_RULES_NAMES property +enum { + RulesFileIndex = 0, + ModelIndex = 1, + LayoutIndex = 2, + VariantIndex = 3, + OptionsIndex = 4 +}; + +void QKeyMapperPrivate::clearMappings() +{ +#ifndef QT_NO_XKB + if (X11->use_xkb) { + // try to determine the layout name and input direction by reading the _XKB_RULES_NAMES property off + // the root window + QByteArray layoutName; + QByteArray variantName; + + Atom type = XNone; + int format = 0; + ulong nitems = 0; + ulong bytesAfter = 0; + uchar *data = 0; + if (XGetWindowProperty(X11->display, RootWindow(X11->display, 0), ATOM(_XKB_RULES_NAMES), 0, 1024, + false, XA_STRING, &type, &format, &nitems, &bytesAfter, &data) == Success + && type == XA_STRING && format == 8 && nitems > 2) { + /* + index 0 == rules file name + index 1 == model name + index 2 == layout name + index 3 == variant name + index 4 == options + */ + char *names[5] = { 0, 0, 0, 0, 0 }; + char *p = reinterpret_cast<char *>(data), *end = p + nitems; + int i = 0; + do { + names[i++] = p; + p += qstrlen(p) + 1; + } while (p < end); + + // the layout names and variants are saved in the _XKB_RULES_NAMES property as a comma separated list + QList<QByteArray> layoutNames = QByteArray::fromRawData(names[2], qstrlen(names[2])).split(','); + if (uint(xkb_currentGroup) < uint(layoutNames.count())) + layoutName = layoutNames.at(xkb_currentGroup); + QList<QByteArray> variantNames = QByteArray::fromRawData(names[3], qstrlen(names[3])).split(','); + if (uint(xkb_currentGroup) < uint(variantNames.count())) + variantName = variantNames.at(xkb_currentGroup); + } + + // ### ??? + // if (keyboardLayoutName.isEmpty()) + // qWarning("Qt: unable to determine keyboard layout, please talk to qt-bugs@trolltech.com"); ? + + keyboardInputLocale = q_getKeyboardLocale(layoutName, variantName); + keyboardInputDirection = keyboardInputLocale.textDirection(); + +#if 0 + qDebug() << "keyboard input locale =" + << keyboardInputLocale.name() + << "direction =" + << keyboardInputDirection; +#endif + if (data) + XFree(data); + } else +#endif // QT_NO_XKB + { + if (coreDesc.keysyms) + XFree(coreDesc.keysyms); + + coreDesc.min_keycode = 8; + coreDesc.max_keycode = 255; + XDisplayKeycodes(X11->display, &coreDesc.min_keycode, &coreDesc.max_keycode); + + coreDesc.keysyms_per_keycode = 0; + coreDesc.keysyms = XGetKeyboardMapping(X11->display, + coreDesc.min_keycode, + coreDesc.max_keycode - coreDesc.min_keycode + 1, + &coreDesc.keysyms_per_keycode); + +#if 0 + qDebug() << "min_keycode =" << coreDesc.min_keycode; + qDebug() << "max_keycode =" << coreDesc.max_keycode; + qDebug() << "keysyms_per_keycode =" << coreDesc.keysyms_per_keycode; + qDebug() << "keysyms =" << coreDesc.keysyms; +#endif + + // ### cannot get/guess the locale with the core protocol + keyboardInputLocale = QLocale::c(); + // ### could examine group 0 for RTL keys + keyboardInputDirection = Qt::LeftToRight; + } + + qt_alt_mask = 0; + qt_meta_mask = 0; + qt_super_mask = 0; + qt_hyper_mask = 0; + qt_mode_switch_mask = 0; + + // look at the modifier mapping, and get the correct masks for alt, meta, super, hyper, and mode_switch +#ifndef QT_NO_XKB + if (X11->use_xkb) { + XkbDescPtr xkbDesc = XkbGetMap(X11->display, XkbAllClientInfoMask, XkbUseCoreKbd); + for (int i = xkbDesc->min_key_code; i < xkbDesc->max_key_code; ++i) { + const uint mask = xkbDesc->map->modmap ? xkbDesc->map->modmap[i] : 0; + if (mask == 0) { + // key is not bound to a modifier + continue; + } + + for (int j = 0; j < XkbKeyGroupsWidth(xkbDesc, i); ++j) { + KeySym keySym = XkbKeySym(xkbDesc, i, j); + if (keySym == NoSymbol) + continue; + SETMASK(keySym, mask); + } + } + XkbFreeKeyboard(xkbDesc, XkbAllComponentsMask, true); + } else +#endif // QT_NO_XKB + { + coreDesc.lock_meaning = NoSymbol; + + XModifierKeymap *map = XGetModifierMapping(X11->display); + + if (map) { + int i, maskIndex = 0, mapIndex = 0; + for (maskIndex = 0; maskIndex < 8; maskIndex++) { + for (i = 0; i < map->max_keypermod; i++) { + if (map->modifiermap[mapIndex]) { + KeySym sym; + int x = 0; + do { + sym = XKeycodeToKeysym(X11->display, map->modifiermap[mapIndex], x++); + } while (sym == NoSymbol && x < coreDesc.keysyms_per_keycode); + const uchar mask = 1 << maskIndex; + SETMASK(sym, mask); + } + mapIndex++; + } + } + + // determine the meaning of the Lock modifier + for (i = 0; i < map->max_keypermod; ++i) { + for (int x = 0; x < coreDesc.keysyms_per_keycode; ++x) { + KeySym sym = XKeycodeToKeysym(X11->display, map->modifiermap[LockMapIndex], x); + if (sym == XK_Caps_Lock || sym == XK_ISO_Lock) { + coreDesc.lock_meaning = XK_Caps_Lock; + break; + } else if (sym == XK_Shift_Lock) { + coreDesc.lock_meaning = XK_Shift_Lock; + } + } + } + + XFreeModifiermap(map); + } + + // for qt_XTranslateKey() + coreDesc.num_lock = qt_num_lock_mask; + coreDesc.mode_switch = qt_mode_switch_mask; + +#if 0 + qDebug() << "lock_meaning =" << coreDesc.lock_meaning; + qDebug() << "num_lock =" << coreDesc.num_lock; + qDebug() << "mode_switch =" << coreDesc.mode_switch; +#endif + } + + // set default modifier masks if needed + if( qt_alt_mask == 0 ) + qt_alt_mask = Mod1Mask; + if( qt_meta_mask == 0 ) + qt_meta_mask = Mod4Mask; + + // if we don't have a meta key (or it's hidden behind alt), use super or hyper to generate + // Qt::Key_Meta and Qt::MetaModifier, since most newer XFree86/Xorg installations map the Windows + // key to Super + if (qt_meta_mask == 0 || qt_meta_mask == qt_alt_mask) { + // no meta keys... s,meta,super, + qt_meta_mask = qt_super_mask; + if (qt_meta_mask == 0 || qt_meta_mask == qt_alt_mask) { + // no super keys either? guess we'll use hyper then + qt_meta_mask = qt_hyper_mask; + } + } + +#if 0 + qDebug() << "qt_alt_mask =" << hex << qt_alt_mask; + qDebug() << "qt_meta_mask =" << hex << qt_meta_mask; + qDebug() << "qt_super_mask =" << hex << qt_super_mask; + qDebug() << "qt_hyper_mask =" << hex << qt_hyper_mask; + qDebug() << "qt_mode_switch_mask =" << hex << qt_mode_switch_mask; + qDebug() << "qt_num_lock_mask =" << hex << qt_num_lock_mask; +#endif +} + +extern bool qt_sm_blockUserInput; + +// +// Keyboard event translation +// + +#ifndef XK_ISO_Left_Tab +#define XK_ISO_Left_Tab 0xFE20 +#endif + +#ifndef XK_dead_hook +#define XK_dead_hook 0xFE61 +#endif + +#ifndef XK_dead_horn +#define XK_dead_horn 0xFE62 +#endif + +#ifndef XK_Codeinput +#define XK_Codeinput 0xFF37 +#endif + +#ifndef XK_Kanji_Bangou +#define XK_Kanji_Bangou 0xFF37 /* same as codeinput */ +#endif + +// Fix old X libraries +#ifndef XK_KP_Home +#define XK_KP_Home 0xFF95 +#endif +#ifndef XK_KP_Left +#define XK_KP_Left 0xFF96 +#endif +#ifndef XK_KP_Up +#define XK_KP_Up 0xFF97 +#endif +#ifndef XK_KP_Right +#define XK_KP_Right 0xFF98 +#endif +#ifndef XK_KP_Down +#define XK_KP_Down 0xFF99 +#endif +#ifndef XK_KP_Prior +#define XK_KP_Prior 0xFF9A +#endif +#ifndef XK_KP_Next +#define XK_KP_Next 0xFF9B +#endif +#ifndef XK_KP_End +#define XK_KP_End 0xFF9C +#endif +#ifndef XK_KP_Insert +#define XK_KP_Insert 0xFF9E +#endif +#ifndef XK_KP_Delete +#define XK_KP_Delete 0xFF9F +#endif + +// the next lines are taken on 10/2009 from X.org (X11/XF86keysym.h), defining some special +// multimedia keys. They are included here as not every system has them. +#define XF86XK_MonBrightnessUp 0x1008FF02 +#define XF86XK_MonBrightnessDown 0x1008FF03 +#define XF86XK_KbdLightOnOff 0x1008FF04 +#define XF86XK_KbdBrightnessUp 0x1008FF05 +#define XF86XK_KbdBrightnessDown 0x1008FF06 +#define XF86XK_Standby 0x1008FF10 +#define XF86XK_AudioLowerVolume 0x1008FF11 +#define XF86XK_AudioMute 0x1008FF12 +#define XF86XK_AudioRaiseVolume 0x1008FF13 +#define XF86XK_AudioPlay 0x1008FF14 +#define XF86XK_AudioStop 0x1008FF15 +#define XF86XK_AudioPrev 0x1008FF16 +#define XF86XK_AudioNext 0x1008FF17 +#define XF86XK_HomePage 0x1008FF18 +#define XF86XK_Mail 0x1008FF19 +#define XF86XK_Start 0x1008FF1A +#define XF86XK_Search 0x1008FF1B +#define XF86XK_AudioRecord 0x1008FF1C +#define XF86XK_Calculator 0x1008FF1D +#define XF86XK_Memo 0x1008FF1E +#define XF86XK_ToDoList 0x1008FF1F +#define XF86XK_Calendar 0x1008FF20 +#define XF86XK_PowerDown 0x1008FF21 +#define XF86XK_ContrastAdjust 0x1008FF22 +#define XF86XK_Back 0x1008FF26 +#define XF86XK_Forward 0x1008FF27 +#define XF86XK_Stop 0x1008FF28 +#define XF86XK_Refresh 0x1008FF29 +#define XF86XK_PowerOff 0x1008FF2A +#define XF86XK_WakeUp 0x1008FF2B +#define XF86XK_Eject 0x1008FF2C +#define XF86XK_ScreenSaver 0x1008FF2D +#define XF86XK_WWW 0x1008FF2E +#define XF86XK_Sleep 0x1008FF2F +#define XF86XK_Favorites 0x1008FF30 +#define XF86XK_AudioPause 0x1008FF31 +#define XF86XK_AudioMedia 0x1008FF32 +#define XF86XK_MyComputer 0x1008FF33 +#define XF86XK_LightBulb 0x1008FF35 +#define XF86XK_Shop 0x1008FF36 +#define XF86XK_History 0x1008FF37 +#define XF86XK_OpenURL 0x1008FF38 +#define XF86XK_AddFavorite 0x1008FF39 +#define XF86XK_HotLinks 0x1008FF3A +#define XF86XK_BrightnessAdjust 0x1008FF3B +#define XF86XK_Finance 0x1008FF3C +#define XF86XK_Community 0x1008FF3D +#define XF86XK_AudioRewind 0x1008FF3E +#define XF86XK_BackForward 0x1008FF3F +#define XF86XK_Launch0 0x1008FF40 +#define XF86XK_Launch1 0x1008FF41 +#define XF86XK_Launch2 0x1008FF42 +#define XF86XK_Launch3 0x1008FF43 +#define XF86XK_Launch4 0x1008FF44 +#define XF86XK_Launch5 0x1008FF45 +#define XF86XK_Launch6 0x1008FF46 +#define XF86XK_Launch7 0x1008FF47 +#define XF86XK_Launch8 0x1008FF48 +#define XF86XK_Launch9 0x1008FF49 +#define XF86XK_LaunchA 0x1008FF4A +#define XF86XK_LaunchB 0x1008FF4B +#define XF86XK_LaunchC 0x1008FF4C +#define XF86XK_LaunchD 0x1008FF4D +#define XF86XK_LaunchE 0x1008FF4E +#define XF86XK_LaunchF 0x1008FF4F +#define XF86XK_ApplicationLeft 0x1008FF50 +#define XF86XK_ApplicationRight 0x1008FF51 +#define XF86XK_Book 0x1008FF52 +#define XF86XK_CD 0x1008FF53 +#define XF86XK_Calculater 0x1008FF54 +#define XF86XK_Clear 0x1008FF55 +#define XF86XK_ClearGrab 0x1008FE21 +#define XF86XK_Close 0x1008FF56 +#define XF86XK_Copy 0x1008FF57 +#define XF86XK_Cut 0x1008FF58 +#define XF86XK_Display 0x1008FF59 +#define XF86XK_DOS 0x1008FF5A +#define XF86XK_Documents 0x1008FF5B +#define XF86XK_Excel 0x1008FF5C +#define XF86XK_Explorer 0x1008FF5D +#define XF86XK_Game 0x1008FF5E +#define XF86XK_Go 0x1008FF5F +#define XF86XK_iTouch 0x1008FF60 +#define XF86XK_LogOff 0x1008FF61 +#define XF86XK_Market 0x1008FF62 +#define XF86XK_Meeting 0x1008FF63 +#define XF86XK_MenuKB 0x1008FF65 +#define XF86XK_MenuPB 0x1008FF66 +#define XF86XK_MySites 0x1008FF67 +#define XF86XK_News 0x1008FF69 +#define XF86XK_OfficeHome 0x1008FF6A +#define XF86XK_Option 0x1008FF6C +#define XF86XK_Paste 0x1008FF6D +#define XF86XK_Phone 0x1008FF6E +#define XF86XK_Reply 0x1008FF72 +#define XF86XK_Reload 0x1008FF73 +#define XF86XK_RotateWindows 0x1008FF74 +#define XF86XK_RotationPB 0x1008FF75 +#define XF86XK_RotationKB 0x1008FF76 +#define XF86XK_Save 0x1008FF77 +#define XF86XK_Send 0x1008FF7B +#define XF86XK_Spell 0x1008FF7C +#define XF86XK_SplitScreen 0x1008FF7D +#define XF86XK_Support 0x1008FF7E +#define XF86XK_TaskPane 0x1008FF7F +#define XF86XK_Terminal 0x1008FF80 +#define XF86XK_Tools 0x1008FF81 +#define XF86XK_Travel 0x1008FF82 +#define XF86XK_Video 0x1008FF87 +#define XF86XK_Word 0x1008FF89 +#define XF86XK_Xfer 0x1008FF8A +#define XF86XK_ZoomIn 0x1008FF8B +#define XF86XK_ZoomOut 0x1008FF8C +#define XF86XK_Away 0x1008FF8D +#define XF86XK_Messenger 0x1008FF8E +#define XF86XK_WebCam 0x1008FF8F +#define XF86XK_MailForward 0x1008FF90 +#define XF86XK_Pictures 0x1008FF91 +#define XF86XK_Music 0x1008FF92 +#define XF86XK_Battery 0x1008FF93 +#define XF86XK_Bluetooth 0x1008FF94 +#define XF86XK_WLAN 0x1008FF95 +#define XF86XK_UWB 0x1008FF96 +#define XF86XK_AudioForward 0x1008FF97 +#define XF86XK_AudioRepeat 0x1008FF98 +#define XF86XK_AudioRandomPlay 0x1008FF99 +#define XF86XK_Subtitle 0x1008FF9A +#define XF86XK_AudioCycleTrack 0x1008FF9B +#define XF86XK_Time 0x1008FF9F +#define XF86XK_Select 0x1008FFA0 +#define XF86XK_View 0x1008FFA1 +#define XF86XK_TopMenu 0x1008FFA2 +#define XF86XK_Suspend 0x1008FFA7 +#define XF86XK_Hibernate 0x1008FFA8 + + +// end of XF86keysyms.h + +// Special keys used by Qtopia, mapped into the X11 private keypad range. +#define QTOPIAXK_Select 0x11000601 +#define QTOPIAXK_Yes 0x11000602 +#define QTOPIAXK_No 0x11000603 +#define QTOPIAXK_Cancel 0x11000604 +#define QTOPIAXK_Printer 0x11000605 +#define QTOPIAXK_Execute 0x11000606 +#define QTOPIAXK_Sleep 0x11000607 +#define QTOPIAXK_Play 0x11000608 +#define QTOPIAXK_Zoom 0x11000609 +#define QTOPIAXK_Context1 0x1100060A +#define QTOPIAXK_Context2 0x1100060B +#define QTOPIAXK_Context3 0x1100060C +#define QTOPIAXK_Context4 0x1100060D +#define QTOPIAXK_Call 0x1100060E +#define QTOPIAXK_Hangup 0x1100060F +#define QTOPIAXK_Flip 0x11000610 + +// keyboard mapping table +static const unsigned int KeyTbl[] = { + + // misc keys + + XK_Escape, Qt::Key_Escape, + XK_Tab, Qt::Key_Tab, + XK_ISO_Left_Tab, Qt::Key_Backtab, + XK_BackSpace, Qt::Key_Backspace, + XK_Return, Qt::Key_Return, + XK_Insert, Qt::Key_Insert, + XK_Delete, Qt::Key_Delete, + XK_Clear, Qt::Key_Delete, + XK_Pause, Qt::Key_Pause, + XK_Print, Qt::Key_Print, + 0x1005FF60, Qt::Key_SysReq, // hardcoded Sun SysReq + 0x1007ff00, Qt::Key_SysReq, // hardcoded X386 SysReq + + // cursor movement + + XK_Home, Qt::Key_Home, + XK_End, Qt::Key_End, + XK_Left, Qt::Key_Left, + XK_Up, Qt::Key_Up, + XK_Right, Qt::Key_Right, + XK_Down, Qt::Key_Down, + XK_Prior, Qt::Key_PageUp, + XK_Next, Qt::Key_PageDown, + + // modifiers + + XK_Shift_L, Qt::Key_Shift, + XK_Shift_R, Qt::Key_Shift, + XK_Shift_Lock, Qt::Key_Shift, + XK_Control_L, Qt::Key_Control, + XK_Control_R, Qt::Key_Control, + XK_Meta_L, Qt::Key_Meta, + XK_Meta_R, Qt::Key_Meta, + XK_Alt_L, Qt::Key_Alt, + XK_Alt_R, Qt::Key_Alt, + XK_Caps_Lock, Qt::Key_CapsLock, + XK_Num_Lock, Qt::Key_NumLock, + XK_Scroll_Lock, Qt::Key_ScrollLock, + XK_Super_L, Qt::Key_Super_L, + XK_Super_R, Qt::Key_Super_R, + XK_Menu, Qt::Key_Menu, + XK_Hyper_L, Qt::Key_Hyper_L, + XK_Hyper_R, Qt::Key_Hyper_R, + XK_Help, Qt::Key_Help, + 0x1000FF74, Qt::Key_Backtab, // hardcoded HP backtab + 0x1005FF10, Qt::Key_F11, // hardcoded Sun F36 (labeled F11) + 0x1005FF11, Qt::Key_F12, // hardcoded Sun F37 (labeled F12) + + // numeric and function keypad keys + + XK_KP_Space, Qt::Key_Space, + XK_KP_Tab, Qt::Key_Tab, + XK_KP_Enter, Qt::Key_Enter, + //XK_KP_F1, Qt::Key_F1, + //XK_KP_F2, Qt::Key_F2, + //XK_KP_F3, Qt::Key_F3, + //XK_KP_F4, Qt::Key_F4, + XK_KP_Home, Qt::Key_Home, + XK_KP_Left, Qt::Key_Left, + XK_KP_Up, Qt::Key_Up, + XK_KP_Right, Qt::Key_Right, + XK_KP_Down, Qt::Key_Down, + XK_KP_Prior, Qt::Key_PageUp, + XK_KP_Next, Qt::Key_PageDown, + XK_KP_End, Qt::Key_End, + XK_KP_Begin, Qt::Key_Clear, + XK_KP_Insert, Qt::Key_Insert, + XK_KP_Delete, Qt::Key_Delete, + XK_KP_Equal, Qt::Key_Equal, + XK_KP_Multiply, Qt::Key_Asterisk, + XK_KP_Add, Qt::Key_Plus, + XK_KP_Separator, Qt::Key_Comma, + XK_KP_Subtract, Qt::Key_Minus, + XK_KP_Decimal, Qt::Key_Period, + XK_KP_Divide, Qt::Key_Slash, + + // International input method support keys + + // International & multi-key character composition + XK_ISO_Level3_Shift, Qt::Key_AltGr, + XK_Multi_key, Qt::Key_Multi_key, + XK_Codeinput, Qt::Key_Codeinput, + XK_SingleCandidate, Qt::Key_SingleCandidate, + XK_MultipleCandidate, Qt::Key_MultipleCandidate, + XK_PreviousCandidate, Qt::Key_PreviousCandidate, + + // Misc Functions + XK_Mode_switch, Qt::Key_Mode_switch, + XK_script_switch, Qt::Key_Mode_switch, + + // Japanese keyboard support + XK_Kanji, Qt::Key_Kanji, + XK_Muhenkan, Qt::Key_Muhenkan, + //XK_Henkan_Mode, Qt::Key_Henkan_Mode, + XK_Henkan_Mode, Qt::Key_Henkan, + XK_Henkan, Qt::Key_Henkan, + XK_Romaji, Qt::Key_Romaji, + XK_Hiragana, Qt::Key_Hiragana, + XK_Katakana, Qt::Key_Katakana, + XK_Hiragana_Katakana, Qt::Key_Hiragana_Katakana, + XK_Zenkaku, Qt::Key_Zenkaku, + XK_Hankaku, Qt::Key_Hankaku, + XK_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku, + XK_Touroku, Qt::Key_Touroku, + XK_Massyo, Qt::Key_Massyo, + XK_Kana_Lock, Qt::Key_Kana_Lock, + XK_Kana_Shift, Qt::Key_Kana_Shift, + XK_Eisu_Shift, Qt::Key_Eisu_Shift, + XK_Eisu_toggle, Qt::Key_Eisu_toggle, + //XK_Kanji_Bangou, Qt::Key_Kanji_Bangou, + //XK_Zen_Koho, Qt::Key_Zen_Koho, + //XK_Mae_Koho, Qt::Key_Mae_Koho, + XK_Kanji_Bangou, Qt::Key_Codeinput, + XK_Zen_Koho, Qt::Key_MultipleCandidate, + XK_Mae_Koho, Qt::Key_PreviousCandidate, + +#ifdef XK_KOREAN + // Korean keyboard support + XK_Hangul, Qt::Key_Hangul, + XK_Hangul_Start, Qt::Key_Hangul_Start, + XK_Hangul_End, Qt::Key_Hangul_End, + XK_Hangul_Hanja, Qt::Key_Hangul_Hanja, + XK_Hangul_Jamo, Qt::Key_Hangul_Jamo, + XK_Hangul_Romaja, Qt::Key_Hangul_Romaja, + //XK_Hangul_Codeinput, Qt::Key_Hangul_Codeinput, + XK_Hangul_Codeinput, Qt::Key_Codeinput, + XK_Hangul_Jeonja, Qt::Key_Hangul_Jeonja, + XK_Hangul_Banja, Qt::Key_Hangul_Banja, + XK_Hangul_PreHanja, Qt::Key_Hangul_PreHanja, + XK_Hangul_PostHanja, Qt::Key_Hangul_PostHanja, + //XK_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate, + //XK_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate, + //XK_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate, + XK_Hangul_SingleCandidate, Qt::Key_SingleCandidate, + XK_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate, + XK_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate, + XK_Hangul_Special, Qt::Key_Hangul_Special, + //XK_Hangul_switch, Qt::Key_Hangul_switch, + XK_Hangul_switch, Qt::Key_Mode_switch, +#endif // XK_KOREAN + + // dead keys + XK_dead_grave, Qt::Key_Dead_Grave, + XK_dead_acute, Qt::Key_Dead_Acute, + XK_dead_circumflex, Qt::Key_Dead_Circumflex, + XK_dead_tilde, Qt::Key_Dead_Tilde, + XK_dead_macron, Qt::Key_Dead_Macron, + XK_dead_breve, Qt::Key_Dead_Breve, + XK_dead_abovedot, Qt::Key_Dead_Abovedot, + XK_dead_diaeresis, Qt::Key_Dead_Diaeresis, + XK_dead_abovering, Qt::Key_Dead_Abovering, + XK_dead_doubleacute, Qt::Key_Dead_Doubleacute, + XK_dead_caron, Qt::Key_Dead_Caron, + XK_dead_cedilla, Qt::Key_Dead_Cedilla, + XK_dead_ogonek, Qt::Key_Dead_Ogonek, + XK_dead_iota, Qt::Key_Dead_Iota, + XK_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound, + XK_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound, + XK_dead_belowdot, Qt::Key_Dead_Belowdot, + XK_dead_hook, Qt::Key_Dead_Hook, + XK_dead_horn, Qt::Key_Dead_Horn, + + // Special keys from X.org - This include multimedia keys, + // wireless/bluetooth/uwb keys, special launcher keys, etc. + XF86XK_Back, Qt::Key_Back, + XF86XK_Forward, Qt::Key_Forward, + XF86XK_Stop, Qt::Key_Stop, + XF86XK_Refresh, Qt::Key_Refresh, + XF86XK_Favorites, Qt::Key_Favorites, + XF86XK_AudioMedia, Qt::Key_LaunchMedia, + XF86XK_OpenURL, Qt::Key_OpenUrl, + XF86XK_HomePage, Qt::Key_HomePage, + XF86XK_Search, Qt::Key_Search, + XF86XK_AudioLowerVolume, Qt::Key_VolumeDown, + XF86XK_AudioMute, Qt::Key_VolumeMute, + XF86XK_AudioRaiseVolume, Qt::Key_VolumeUp, + XF86XK_AudioPlay, Qt::Key_MediaPlay, + XF86XK_AudioStop, Qt::Key_MediaStop, + XF86XK_AudioPrev, Qt::Key_MediaPrevious, + XF86XK_AudioNext, Qt::Key_MediaNext, + XF86XK_AudioRecord, Qt::Key_MediaRecord, + XF86XK_Mail, Qt::Key_LaunchMail, + XF86XK_MyComputer, Qt::Key_Launch0, // ### Qt 5: remap properly + XF86XK_Calculator, Qt::Key_Launch1, + XF86XK_Memo, Qt::Key_Memo, + XF86XK_ToDoList, Qt::Key_ToDoList, + XF86XK_Calendar, Qt::Key_Calendar, + XF86XK_PowerDown, Qt::Key_PowerDown, + XF86XK_ContrastAdjust, Qt::Key_ContrastAdjust, + XF86XK_Standby, Qt::Key_Standby, + XF86XK_MonBrightnessUp, Qt::Key_MonBrightnessUp, + XF86XK_MonBrightnessDown, Qt::Key_MonBrightnessDown, + XF86XK_KbdLightOnOff, Qt::Key_KeyboardLightOnOff, + XF86XK_KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp, + XF86XK_KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown, + XF86XK_PowerOff, Qt::Key_PowerOff, + XF86XK_WakeUp, Qt::Key_WakeUp, + XF86XK_Eject, Qt::Key_Eject, + XF86XK_ScreenSaver, Qt::Key_ScreenSaver, + XF86XK_WWW, Qt::Key_WWW, + XF86XK_Sleep, Qt::Key_Sleep, + XF86XK_LightBulb, Qt::Key_LightBulb, + XF86XK_Shop, Qt::Key_Shop, + XF86XK_History, Qt::Key_History, + XF86XK_AddFavorite, Qt::Key_AddFavorite, + XF86XK_HotLinks, Qt::Key_HotLinks, + XF86XK_BrightnessAdjust, Qt::Key_BrightnessAdjust, + XF86XK_Finance, Qt::Key_Finance, + XF86XK_Community, Qt::Key_Community, + XF86XK_AudioRewind, Qt::Key_AudioRewind, + XF86XK_BackForward, Qt::Key_BackForward, + XF86XK_ApplicationLeft, Qt::Key_ApplicationLeft, + XF86XK_ApplicationRight, Qt::Key_ApplicationRight, + XF86XK_Book, Qt::Key_Book, + XF86XK_CD, Qt::Key_CD, + XF86XK_Calculater, Qt::Key_Calculator, + XF86XK_Clear, Qt::Key_Clear, + XF86XK_ClearGrab, Qt::Key_ClearGrab, + XF86XK_Close, Qt::Key_Close, + XF86XK_Copy, Qt::Key_Copy, + XF86XK_Cut, Qt::Key_Cut, + XF86XK_Display, Qt::Key_Display, + XF86XK_DOS, Qt::Key_DOS, + XF86XK_Documents, Qt::Key_Documents, + XF86XK_Excel, Qt::Key_Excel, + XF86XK_Explorer, Qt::Key_Explorer, + XF86XK_Game, Qt::Key_Game, + XF86XK_Go, Qt::Key_Go, + XF86XK_iTouch, Qt::Key_iTouch, + XF86XK_LogOff, Qt::Key_LogOff, + XF86XK_Market, Qt::Key_Market, + XF86XK_Meeting, Qt::Key_Meeting, + XF86XK_MenuKB, Qt::Key_MenuKB, + XF86XK_MenuPB, Qt::Key_MenuPB, + XF86XK_MySites, Qt::Key_MySites, + XF86XK_News, Qt::Key_News, + XF86XK_OfficeHome, Qt::Key_OfficeHome, + XF86XK_Option, Qt::Key_Option, + XF86XK_Paste, Qt::Key_Paste, + XF86XK_Phone, Qt::Key_Phone, + XF86XK_Reply, Qt::Key_Reply, + XF86XK_Reload, Qt::Key_Reload, + XF86XK_RotateWindows, Qt::Key_RotateWindows, + XF86XK_RotationPB, Qt::Key_RotationPB, + XF86XK_RotationKB, Qt::Key_RotationKB, + XF86XK_Save, Qt::Key_Save, + XF86XK_Send, Qt::Key_Send, + XF86XK_Spell, Qt::Key_Spell, + XF86XK_SplitScreen, Qt::Key_SplitScreen, + XF86XK_Support, Qt::Key_Support, + XF86XK_TaskPane, Qt::Key_TaskPane, + XF86XK_Terminal, Qt::Key_Terminal, + XF86XK_Tools, Qt::Key_Tools, + XF86XK_Travel, Qt::Key_Travel, + XF86XK_Video, Qt::Key_Video, + XF86XK_Word, Qt::Key_Word, + XF86XK_Xfer, Qt::Key_Xfer, + XF86XK_ZoomIn, Qt::Key_ZoomIn, + XF86XK_ZoomOut, Qt::Key_ZoomOut, + XF86XK_Away, Qt::Key_Away, + XF86XK_Messenger, Qt::Key_Messenger, + XF86XK_WebCam, Qt::Key_WebCam, + XF86XK_MailForward, Qt::Key_MailForward, + XF86XK_Pictures, Qt::Key_Pictures, + XF86XK_Music, Qt::Key_Music, + XF86XK_Battery, Qt::Key_Battery, + XF86XK_Bluetooth, Qt::Key_Bluetooth, + XF86XK_WLAN, Qt::Key_WLAN, + XF86XK_UWB, Qt::Key_UWB, + XF86XK_AudioForward, Qt::Key_AudioForward, + XF86XK_AudioRepeat, Qt::Key_AudioRepeat, + XF86XK_AudioRandomPlay, Qt::Key_AudioRandomPlay, + XF86XK_Subtitle, Qt::Key_Subtitle, + XF86XK_AudioCycleTrack, Qt::Key_AudioCycleTrack, + XF86XK_Time, Qt::Key_Time, + XF86XK_Select, Qt::Key_Select, + XF86XK_View, Qt::Key_View, + XF86XK_TopMenu, Qt::Key_TopMenu, + XF86XK_Bluetooth, Qt::Key_Bluetooth, + XF86XK_Suspend, Qt::Key_Suspend, + XF86XK_Hibernate, Qt::Key_Hibernate, + XF86XK_Launch0, Qt::Key_Launch2, // ### Qt 5: remap properly + XF86XK_Launch1, Qt::Key_Launch3, + XF86XK_Launch2, Qt::Key_Launch4, + XF86XK_Launch3, Qt::Key_Launch5, + XF86XK_Launch4, Qt::Key_Launch6, + XF86XK_Launch5, Qt::Key_Launch7, + XF86XK_Launch6, Qt::Key_Launch8, + XF86XK_Launch7, Qt::Key_Launch9, + XF86XK_Launch8, Qt::Key_LaunchA, + XF86XK_Launch9, Qt::Key_LaunchB, + XF86XK_LaunchA, Qt::Key_LaunchC, + XF86XK_LaunchB, Qt::Key_LaunchD, + XF86XK_LaunchC, Qt::Key_LaunchE, + XF86XK_LaunchD, Qt::Key_LaunchF, + XF86XK_LaunchE, Qt::Key_LaunchG, + XF86XK_LaunchF, Qt::Key_LaunchH, + + // Qtopia keys + QTOPIAXK_Select, Qt::Key_Select, + QTOPIAXK_Yes, Qt::Key_Yes, + QTOPIAXK_No, Qt::Key_No, + QTOPIAXK_Cancel, Qt::Key_Cancel, + QTOPIAXK_Printer, Qt::Key_Printer, + QTOPIAXK_Execute, Qt::Key_Execute, + QTOPIAXK_Sleep, Qt::Key_Sleep, + QTOPIAXK_Play, Qt::Key_Play, + QTOPIAXK_Zoom, Qt::Key_Zoom, + QTOPIAXK_Context1, Qt::Key_Context1, + QTOPIAXK_Context2, Qt::Key_Context2, + QTOPIAXK_Context3, Qt::Key_Context3, + QTOPIAXK_Context4, Qt::Key_Context4, + QTOPIAXK_Call, Qt::Key_Call, + QTOPIAXK_Hangup, Qt::Key_Hangup, + QTOPIAXK_Flip, Qt::Key_Flip, + + 0, 0 +}; + +static int translateKeySym(uint key) +{ + int code = -1; + int i = 0; // any other keys + while (KeyTbl[i]) { + if (key == KeyTbl[i]) { + code = (int)KeyTbl[i+1]; + break; + } + i += 2; + } + if (qt_meta_mask) { + // translate Super/Hyper keys to Meta if we're using them as the MetaModifier + if (qt_meta_mask == qt_super_mask && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) { + code = Qt::Key_Meta; + } else if (qt_meta_mask == qt_hyper_mask && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) { + code = Qt::Key_Meta; + } + } + return code; +} + +#if !defined(QT_NO_XIM) +static const unsigned short katakanaKeysymsToUnicode[] = { + 0x0000, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1, + 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3, + 0x30FC, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, + 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, + 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, + 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, + 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, + 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0x309B, 0x309C +}; + +static const unsigned short cyrillicKeysymsToUnicode[] = { + 0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, + 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f, + 0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, + 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f, + 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, + 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, + 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, + 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, + 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, + 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, + 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, + 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a +}; + +static const unsigned short greekKeysymsToUnicode[] = { + 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c, + 0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015, + 0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, + 0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, + 0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7, + 0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, + 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, + 0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, + 0x03c8, 0x03c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +static const unsigned short technicalKeysymsToUnicode[] = { + 0x0000, 0x23B7, 0x250C, 0x2500, 0x2320, 0x2321, 0x2502, 0x23A1, + 0x23A3, 0x23A4, 0x23A6, 0x239B, 0x239D, 0x239E, 0x23A0, 0x23A8, + 0x23AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222B, + 0x2234, 0x221D, 0x221E, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000, + 0x223C, 0x2243, 0x0000, 0x0000, 0x0000, 0x21D4, 0x21D2, 0x2261, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221A, 0x0000, + 0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222A, 0x2227, 0x2228, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000, + 0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193, 0x0000 +}; + +static const unsigned short specialKeysymsToUnicode[] = { + 0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x0000, 0x0000, + 0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA, + 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C, + 0x2502, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +static const unsigned short publishingKeysymsToUnicode[] = { + 0x0000, 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009, + 0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025, + 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, + 0x2105, 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000, + 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000, + 0x0000, 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af, + 0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033, + 0x0000, 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae, + 0x25e6, 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa, + 0x25b2, 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000, + 0x2720, 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642, + 0x2640, 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e, 0x0000 +}; + +static const unsigned short aplKeysymsToUnicode[] = { + 0x0000, 0x0000, 0x0000, 0x003c, 0x0000, 0x0000, 0x003e, 0x0000, + 0x2228, 0x2227, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00af, 0x0000, 0x22a5, 0x2229, 0x230a, 0x0000, 0x005f, 0x0000, + 0x0000, 0x0000, 0x2218, 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb, + 0x0000, 0x0000, 0x0000, 0x2308, 0x0000, 0x0000, 0x222a, 0x0000, + 0x2283, 0x0000, 0x2282, 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x22a3, 0x0000, 0x0000, 0x0000 +}; + +static const unsigned short koreanKeysymsToUnicode[] = { + 0x0000, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, + 0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f, + 0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147, + 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f, + 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, + 0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, + 0x3160, 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab, + 0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3, + 0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb, + 0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d, + 0x3171, 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e, + 0x11eb, 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9 +}; + +static QChar keysymToUnicode(unsigned char byte3, unsigned char byte4) +{ + switch (byte3) { + case 0x04: + // katakana + if (byte4 > 0xa0 && byte4 < 0xe0) + return QChar(katakanaKeysymsToUnicode[byte4 - 0xa0]); + else if (byte4 == 0x7e) + return QChar(0x203e); // Overline + break; + case 0x06: + // russian, use lookup table + if (byte4 > 0xa0) + return QChar(cyrillicKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x07: + // greek + if (byte4 > 0xa0) + return QChar(greekKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x08: + // technical + if (byte4 > 0xa0) + return QChar(technicalKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x09: + // special + if (byte4 >= 0xe0) + return QChar(specialKeysymsToUnicode[byte4 - 0xe0]); + break; + case 0x0a: + // publishing + if (byte4 > 0xa0) + return QChar(publishingKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x0b: + // APL + if (byte4 > 0xa0) + return QChar(aplKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x0e: + // Korean + if (byte4 > 0xa0) + return QChar(koreanKeysymsToUnicode[byte4 - 0xa0]); + break; + default: + break; + } + return QChar(0x0); +} +#endif + +static QString translateKeySym(KeySym keysym, uint xmodifiers, + int &code, Qt::KeyboardModifiers &modifiers, + QByteArray &chars, int &count) +{ + // all keysyms smaller than 0xff00 are actally keys that can be mapped to unicode chars + + extern QTextCodec *qt_input_mapper; // from qapplication_x11.cpp + QTextCodec *mapper = qt_input_mapper; + QChar converted; + + if (count == 0 && keysym < 0xff00) { + unsigned char byte3 = (unsigned char)(keysym >> 8); + int mib = -1; + switch(byte3) { + case 0: // Latin 1 + case 1: // Latin 2 + case 2: //latin 3 + case 3: // latin4 + mib = byte3 + 4; break; + case 5: // arabic + mib = 82; break; + case 12: // Hebrew + mib = 85; break; + case 13: // Thai + mib = 2259; break; + case 4: // kana + case 6: // cyrillic + case 7: // greek + case 8: // technical, no mapping here at the moment + case 9: // Special + case 10: // Publishing + case 11: // APL + case 14: // Korean, no mapping + mib = -1; // manual conversion + mapper = 0; +#if !defined(QT_NO_XIM) + converted = keysymToUnicode(byte3, keysym & 0xff); +#endif + case 0x20: + // currency symbols + if (keysym >= 0x20a0 && keysym <= 0x20ac) { + mib = -1; // manual conversion + mapper = 0; + converted = (uint)keysym; + } + break; + default: + break; + } + if (mib != -1) { + mapper = QTextCodec::codecForMib(mib); + if (chars.isEmpty()) + chars.resize(1); + chars[0] = (unsigned char) (keysym & 0xff); // get only the fourth bit for conversion later + count++; + } + } else if (keysym >= 0x1000000 && keysym <= 0x100ffff) { + converted = (ushort) (keysym - 0x1000000); + mapper = 0; + } + if (count < (int)chars.size()-1) + chars[count] = '\0'; + + QString text; + if (!mapper && converted.unicode() != 0x0) { + text = converted; + } else if (!chars.isEmpty()) { + // convert chars (8bit) to text (unicode). + if (mapper) + text = mapper->toUnicode(chars.data(), count, 0); + if (text.isEmpty()) { + // no mapper, or codec couldn't convert to unicode (this + // can happen when running in the C locale or with no LANG + // set). try converting from latin-1 + text = QString::fromLatin1(chars); + } + } + + modifiers = X11->translateModifiers(xmodifiers); + + // Commentary in X11/keysymdef says that X codes match ASCII, so it + // is safe to use the locale functions to process X codes in ISO8859-1. + // + // This is mainly for compatibility - applications should not use the + // Qt keycodes between 128 and 255, but should rather use the + // QKeyEvent::text(). + // + extern QTextCodec *qt_input_mapper; // from qapplication_x11.cpp + if (keysym < 128 || (keysym < 256 && (!qt_input_mapper || qt_input_mapper->mibEnum()==4))) { + // upper-case key, if known + code = isprint((int)keysym) ? toupper((int)keysym) : 0; + } else if (keysym >= XK_F1 && keysym <= XK_F35) { + // function keys + code = Qt::Key_F1 + ((int)keysym - XK_F1); + } else if (keysym >= XK_KP_Space && keysym <= XK_KP_9) { + if (keysym >= XK_KP_0) { + // numeric keypad keys + code = Qt::Key_0 + ((int)keysym - XK_KP_0); + } else { + code = translateKeySym(keysym); + } + modifiers |= Qt::KeypadModifier; + } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f && text.unicode()->unicode() != 0x7f && !(keysym >= XK_dead_grave && keysym <= XK_dead_horn)) { + code = text.unicode()->toUpper().unicode(); + } else { + // any other keys + code = translateKeySym(keysym); + + if (code == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) { + // map shift+tab to shift+backtab, QShortcutMap knows about it + // and will handle it. + code = Qt::Key_Backtab; + text = QString(); + } + } + + return text; +} + +extern bool qt_use_rtl_extensions; // from qapplication_x11.cpp + +bool QKeyMapperPrivate::translateKeyEventInternal(QWidget *keyWidget, + const XEvent *event, + KeySym &keysym, + int& count, + QString& text, + Qt::KeyboardModifiers &modifiers, + int& code, + QEvent::Type &type, + bool statefulTranslation) +{ + XKeyEvent xkeyevent = event->xkey; + int keycode = event->xkey.keycode; + // save the modifier state, we will use the keystate uint later by passing + // it to translateButtonState + uint keystate = event->xkey.state; + + type = (event->type == XKeyPress) ? QEvent::KeyPress : QEvent::KeyRelease; + + static int directionKeyEvent = 0; + static unsigned int lastWinId = 0; + + // translate pending direction change + if (statefulTranslation && qt_use_rtl_extensions && type == QEvent::KeyRelease) { + if (directionKeyEvent == Qt::Key_Direction_R || directionKeyEvent == Qt::Key_Direction_L) { + type = QEvent::KeyPress; + code = directionKeyEvent; + text = QString(); + directionKeyEvent = 0; + lastWinId = 0; + return true; + } else { + directionKeyEvent = 0; + lastWinId = 0; + } + } + + // some XmbLookupString implementations don't return buffer overflow correctly, + // so we increase the input buffer to allow for long strings... + // 256 chars * 2 bytes + 1 null-term == 513 bytes + QByteArray chars; + chars.resize(513); + + count = XLookupString(&xkeyevent, chars.data(), chars.size(), &keysym, 0); + if (count && !keycode) { + extern int qt_ximComposingKeycode; // from qapplication_x11.cpp + keycode = qt_ximComposingKeycode; + qt_ximComposingKeycode = 0; + } + + // translate the keysym + xmodifiers to Qt::Key_* + Qt::KeyboardModifiers + text = translateKeySym(keysym, keystate, code, modifiers, chars, count); + + // Watch for keypresses and if its a key belonging to the Ctrl-Shift + // direction-changing accel, remember it. + // We keep track of those keys instead of using the event's state + // (to figure out whether the Ctrl modifier is held while Shift is pressed, + // or Shift is held while Ctrl is pressed) since the 'state' doesn't tell + // us whether the modifier held is Left or Right. + if (statefulTranslation && qt_use_rtl_extensions && type == QEvent::KeyPress) { + if (keysym == XK_Control_L || keysym == XK_Control_R + || keysym == XK_Shift_L || keysym == XK_Shift_R) { + if (!directionKeyEvent) { + directionKeyEvent = keysym; + // This code exists in order to check that + // the event is occurred in the same widget. + lastWinId = keyWidget->internalWinId(); + } + } else { + // this can no longer be a direction-changing accel. + // if any other key was pressed. + directionKeyEvent = Qt::Key_Space; + } + + if (directionKeyEvent && lastWinId == keyWidget->internalWinId()) { + if ((keysym == XK_Shift_L && directionKeyEvent == XK_Control_L) + || (keysym == XK_Control_L && directionKeyEvent == XK_Shift_L)) { + directionKeyEvent = Qt::Key_Direction_L; + } else if ((keysym == XK_Shift_R && directionKeyEvent == XK_Control_R) + || (keysym == XK_Control_R && directionKeyEvent == XK_Shift_R)) { + directionKeyEvent = Qt::Key_Direction_R; + } + } else if (directionKeyEvent == Qt::Key_Direction_L + || directionKeyEvent == Qt::Key_Direction_R) { + directionKeyEvent = Qt::Key_Space; // invalid + } + } + + return true; +} + + +struct qt_auto_repeat_data +{ + // match the window and keycode with timestamp delta of 10 ms + Window window; + KeyCode keycode; + Time timestamp; + + // queue scanner state + bool release; + bool error; +}; + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +static Bool qt_keypress_scanner(Display *, XEvent *event, XPointer arg) +{ + if (event->type != XKeyPress && event->type != XKeyRelease) + return false; + + qt_auto_repeat_data *data = (qt_auto_repeat_data *) arg; + if (data->error) + return false; + + if (event->xkey.window != data->window || + event->xkey.keycode != data->keycode) { + // deal breakers: key events in a different window or an event + // with a different key code + data->error = true; + return false; + } + + if (event->type == XKeyPress) { + data->error = (! data->release || event->xkey.time - data->timestamp > 10); + return (! data->error); + } + + // must be XKeyRelease event + if (data->release) { + // found a second release + data->error = true; + return false; + } + + // found a single release + data->release = true; + data->timestamp = event->xkey.time; + + return false; +} + +static Bool qt_keyrelease_scanner(Display *, XEvent *event, XPointer arg) +{ + const qt_auto_repeat_data *data = (const qt_auto_repeat_data *) arg; + return (event->type == XKeyRelease && + event->xkey.window == data->window && + event->xkey.keycode == data->keycode); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + +bool QKeyMapperPrivate::translateKeyEvent(QWidget *keyWidget, const XEvent *event, bool grab) +{ + int code = -1; + int count = 0; + Qt::KeyboardModifiers modifiers; + + if (qt_sm_blockUserInput) // block user interaction during session management + return true; + + Display *dpy = X11->display; + + if (!keyWidget->isEnabled()) + return true; + + QEvent::Type type; + bool autor = false; + QString text; + + KeySym keysym = 0; + translateKeyEventInternal(keyWidget, event, keysym, count, text, modifiers, code, type); + + // was this the last auto-repeater? + qt_auto_repeat_data auto_repeat_data; + auto_repeat_data.window = event->xkey.window; + auto_repeat_data.keycode = event->xkey.keycode; + auto_repeat_data.timestamp = event->xkey.time; + + static uint curr_autorep = 0; + if (event->type == XKeyPress) { + if (curr_autorep == event->xkey.keycode) { + autor = true; + curr_autorep = 0; + } + } else { + // look ahead for auto-repeat + XEvent nextpress; + + auto_repeat_data.release = true; + auto_repeat_data.error = false; + if (XCheckIfEvent(dpy, &nextpress, &qt_keypress_scanner, + (XPointer) &auto_repeat_data)) { + autor = true; + + // Put it back... we COULD send the event now and not need + // the static curr_autorep variable. + XPutBackEvent(dpy,&nextpress); + } + curr_autorep = autor ? event->xkey.keycode : 0; + } + +#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT) + // process accelerators before doing key compression + if (type == QEvent::KeyPress && !grab + && QApplicationPrivate::instance()->use_compat()) { + // send accel events if the keyboard is not grabbed + QKeyEventEx a(type, code, modifiers, text, autor, qMax(qMax(count,1), int(text.length())), + event->xkey.keycode, keysym, event->xkey.state); + if (QApplicationPrivate::instance()->qt_tryAccelEvent(keyWidget, &a)) + return true; + } +#endif + +#ifndef QT_NO_IM + QInputContext *qic = keyWidget->inputContext(); +#endif + + // compress keys + if (!text.isEmpty() && keyWidget->testAttribute(Qt::WA_KeyCompression) && +#ifndef QT_NO_IM + // Ordinary input methods require discrete key events to work + // properly, so key compression has to be disabled when input + // context exists. + // + // And further consideration, some complex input method + // require all key press/release events discretely even if + // the input method awares of key compression and compressed + // keys are ordinary alphabets. For example, the uim project + // is planning to implement "combinational shift" feature for + // a Japanese input method, uim-skk. It will work as follows. + // + // 1. press "r" + // 2. press "u" + // 3. release both "r" and "u" in arbitrary order + // 4. above key sequence generates "Ru" + // + // Of course further consideration about other participants + // such as key repeat mechanism is required to implement such + // feature. + !qic && +#endif // QT_NO_IM + // do not compress keys if the key event we just got above matches + // one of the key ranges used to compute stopCompression + !((code >= Qt::Key_Escape && code <= Qt::Key_SysReq) + || (code >= Qt::Key_Home && code <= Qt::Key_PageDown) + || (code >= Qt::Key_Super_L && code <= Qt::Key_Direction_R) + || (code == 0) + || (text.length() == 1 && text.unicode()->unicode() == '\n'))) { + // the widget wants key compression so it gets it + + // sync the event queue, this makes key compress work better + XSync(dpy, false); + + for (;;) { + XEvent evRelease; + XEvent evPress; + if (!XCheckTypedWindowEvent(dpy,event->xkey.window, + XKeyRelease,&evRelease)) + break; + if (!XCheckTypedWindowEvent(dpy,event->xkey.window, + XKeyPress,&evPress)) { + XPutBackEvent(dpy, &evRelease); + break; + } + QString textIntern; + int codeIntern = -1; + int countIntern = 0; + Qt::KeyboardModifiers modifiersIntern; + QEvent::Type t; + KeySym keySymIntern; + translateKeyEventInternal(keyWidget, &evPress, keySymIntern, countIntern, textIntern, + modifiersIntern, codeIntern, t); + // use stopCompression to stop key compression for the following + // key event ranges: + bool stopCompression = + // 1) misc keys + (codeIntern >= Qt::Key_Escape && codeIntern <= Qt::Key_SysReq) + // 2) cursor movement + || (codeIntern >= Qt::Key_Home && codeIntern <= Qt::Key_PageDown) + // 3) extra keys + || (codeIntern >= Qt::Key_Super_L && codeIntern <= Qt::Key_Direction_R) + // 4) something that a) doesn't translate to text or b) translates + // to newline text + || (codeIntern == 0) + || (textIntern.length() == 1 && textIntern.unicode()->unicode() == '\n') + || (codeIntern == Qt::Key_unknown); + + if (modifiersIntern == modifiers && !textIntern.isEmpty() && !stopCompression) { + text += textIntern; + count += countIntern; + } else { + XPutBackEvent(dpy, &evPress); + XPutBackEvent(dpy, &evRelease); + break; + } + } + } + + // autorepeat compression makes sense for all widgets (Windows + // does it automatically ....) + if (event->type == XKeyPress && text.length() <= 1 +#ifndef QT_NO_IM + // input methods need discrete key events + && !qic +#endif// QT_NO_IM + ) { + XEvent dummy; + + for (;;) { + auto_repeat_data.release = false; + auto_repeat_data.error = false; + if (! XCheckIfEvent(dpy, &dummy, &qt_keypress_scanner, + (XPointer) &auto_repeat_data)) + break; + if (! XCheckIfEvent(dpy, &dummy, &qt_keyrelease_scanner, + (XPointer) &auto_repeat_data)) + break; + + count++; + if (!text.isEmpty()) + text += text[0]; + } + } + + return QKeyMapper::sendKeyEvent(keyWidget, grab, type, code, modifiers, text, autor, + qMax(qMax(count,1), int(text.length())), + event->xkey.keycode, keysym, event->xkey.state); +} + +bool QKeyMapper::sendKeyEvent(QWidget *keyWidget, bool grab, + QEvent::Type type, int code, Qt::KeyboardModifiers modifiers, + const QString &text, bool autorepeat, int count, + quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, + bool *) +{ + // try the menukey first + if (type == QEvent::KeyPress && code == Qt::Key_Menu) { + QVariant v = keyWidget->inputMethodQuery(Qt::ImMicroFocus); + QPoint globalPos; + QPoint pos; + if (v.isNull()) { + globalPos = QCursor::pos(); + pos = keyWidget->mapFromGlobal(globalPos); + } else { + pos = v.toRect().center(); + globalPos = keyWidget->mapToGlobal(pos); + } + QContextMenuEvent e(QContextMenuEvent::Keyboard, pos, globalPos); + qt_sendSpontaneousEvent(keyWidget, &e); + if(e.isAccepted()) + return true; + } + + Q_UNUSED(grab); + QKeyEventEx e(type, code, modifiers, text, autorepeat, qMax(qMax(count,1), int(text.length())), + nativeScanCode, nativeVirtualKey, nativeModifiers); + return qt_sendSpontaneousEvent(keyWidget, &e); +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qkeymapper_x11_p.cpp b/src/gui/guikernel/qkeymapper_x11_p.cpp new file mode 100644 index 0000000000..2dbe1e77a4 --- /dev/null +++ b/src/gui/guikernel/qkeymapper_x11_p.cpp @@ -0,0 +1,489 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is auto-generated, do not edit! +// (Generated using util/xkbdatagen) + +static struct { + const char *layout; + const char *variant; // 0 means any variant + Qt::LayoutDirection direction; + QLocale::Language language; + QLocale::Country country; +} xkbLayoutData[] = { + // name = us, description = U.S. English + { "us", "", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates }, + // name = us:intl, description = U.S. English + { "us", "intl", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates }, + // name = us:alt-intl, description = U.S. English + { "us", "alt-intl", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates }, + // name = us:dvorak, description = U.S. English + { "us", "dvorak", Qt::LeftToRight, QLocale::English, QLocale::UnitedStates }, + // name = us:rus, description = U.S. English + { "us", "rus", Qt::LeftToRight, QLocale::Russian, QLocale::UnitedStates }, + // name = ara, description = Arabic + { "ara", "", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates }, + // name = ara:azerty, description = Arabic + { "ara", "azerty", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates }, + // name = ara:azerty_digits, description = Arabic + { "ara", "azerty_digits", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates }, + // name = ara:digits, description = Arabic + { "ara", "digits", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates }, + // name = ara:qwerty, description = Arabic + { "ara", "qwerty", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates }, + // name = ara:qwerty_digits, description = Arabic + { "ara", "qwerty_digits", Qt::RightToLeft, QLocale::Arabic, QLocale::UnitedArabEmirates }, + // name = al, description = Albania + { "al", "", Qt::LeftToRight, QLocale::Albanian, QLocale::Albania }, + // name = am, description = Armenia + { "am", "", Qt::LeftToRight, QLocale::Armenian, QLocale::Armenia }, + // name = am:phonetic, description = Armenia + { "am", "phonetic", Qt::LeftToRight, QLocale::Armenian, QLocale::Armenia }, + // name = az, description = Azerbaijan + { "az", "", Qt::LeftToRight, QLocale::Azerbaijani, QLocale::Azerbaijan }, + // name = az:cyrillic, description = Azerbaijan + { "az", "cyrillic", Qt::LeftToRight, QLocale::Azerbaijani, QLocale::Azerbaijan }, + // name = by, description = Belarus + { "by", "", Qt::LeftToRight, QLocale::Byelorussian, QLocale::Belarus }, + // name = by:winkeys, description = Belarus + { "by", "winkeys", Qt::LeftToRight, QLocale::Byelorussian, QLocale::Belarus }, + // name = be, description = Belgium + { "be", "", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium }, + // name = be:iso-alternate, description = Belgium + { "be", "iso-alternate", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium }, + // name = be:nodeadkeys, description = Belgium + { "be", "nodeadkeys", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium }, + // name = be:sundeadkeys, description = Belgium + { "be", "sundeadkeys", Qt::LeftToRight, QLocale::Dutch, QLocale::Belgium }, + // name = bd, description = Bangladesh + { "bd", "", Qt::LeftToRight, QLocale::Bengali, QLocale::Bangladesh }, + // name = bd:probhat, description = Bangladesh + { "bd", "probhat", Qt::LeftToRight, QLocale::Bengali, QLocale::Bangladesh }, + // name = in, description = India + { "in", "", Qt::LeftToRight, QLocale::Hindi, QLocale::India }, + // name = in:ben, description = India + { "in", "ben", Qt::LeftToRight, QLocale::Bengali, QLocale::India }, + // name = in:ben_probhat, description = India + { "in", "ben_probhat", Qt::LeftToRight, QLocale::Bengali, QLocale::India }, + // name = in:guj, description = India + { "in", "guj", Qt::LeftToRight, QLocale::Gujarati, QLocale::India }, + // name = in:guru, description = India + { "in", "guru", Qt::LeftToRight, QLocale::Punjabi, QLocale::India }, + // name = in:kan, description = India + { "in", "kan", Qt::LeftToRight, QLocale::Kannada, QLocale::India }, + // name = in:mal, description = India + { "in", "mal", Qt::LeftToRight, QLocale::Malayalam, QLocale::India }, + // name = in:ori, description = India + { "in", "ori", Qt::LeftToRight, QLocale::Oriya, QLocale::India }, + // name = in:tam_unicode, description = India + { "in", "tam_unicode", Qt::LeftToRight, QLocale::Tamil, QLocale::India }, + // name = in:tam_TAB, description = India + { "in", "tam_TAB", Qt::LeftToRight, QLocale::Tamil, QLocale::India }, + // name = in:tam_TSCII, description = India + { "in", "tam_TSCII", Qt::LeftToRight, QLocale::Tamil, QLocale::India }, + // name = in:tam, description = India + { "in", "tam", Qt::LeftToRight, QLocale::Tamil, QLocale::India }, + // name = in:tel, description = India + { "in", "tel", Qt::LeftToRight, QLocale::Telugu, QLocale::India }, + // name = in:urd, description = India + { "in", "urd", Qt::RightToLeft, QLocale::Urdu, QLocale::India }, + // name = ba, description = Bosnia and Herzegovina + { "ba", "", Qt::LeftToRight, QLocale::Bosnian, QLocale::BosniaAndHerzegowina }, + // name = br, description = Brazil + { "br", "", Qt::LeftToRight, QLocale::Portuguese, QLocale::Brazil }, + // name = br:nodeadkeys, description = Brazil + { "br", "nodeadkeys", Qt::LeftToRight, QLocale::Portuguese, QLocale::Brazil }, + // name = bg, description = Bulgaria + { "bg", "", Qt::LeftToRight, QLocale::Bulgarian, QLocale::Bulgaria }, + // name = bg:phonetic, description = Bulgaria + { "bg", "phonetic", Qt::LeftToRight, QLocale::Bulgarian, QLocale::Bulgaria }, + // name = mm, description = Myanmar + { "mm", "", Qt::LeftToRight, QLocale::Burmese, QLocale::Myanmar }, + // name = ca, description = Canada + { "ca", "", Qt::LeftToRight, QLocale::English, QLocale::Canada }, + // name = ca:fr-dvorak, description = Canada + { "ca", "fr-dvorak", Qt::LeftToRight, QLocale::French, QLocale::Canada }, + // name = ca:fr-legacy, description = Canada + { "ca", "fr-legacy", Qt::LeftToRight, QLocale::French, QLocale::Canada }, + // name = ca:multi, description = Canada + { "ca", "multi", Qt::LeftToRight, QLocale::English, QLocale::Canada }, + // name = ca:multi-2gr, description = Canada + { "ca", "multi-2gr", Qt::LeftToRight, QLocale::English, QLocale::Canada }, + // name = ca:ike, description = Canada + { "ca", "ike", Qt::LeftToRight, QLocale::Inuktitut, QLocale::Canada }, + // name = hr, description = Croatia + { "hr", "", Qt::LeftToRight, QLocale::Croatian, QLocale::Croatia }, + // name = hr:us, description = Croatia + { "hr", "us", Qt::LeftToRight, QLocale::Croatian, QLocale::Croatia }, + // name = cz, description = Czechia + { "cz", "", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic }, + // name = cz:bksl, description = Czechia + { "cz", "bksl", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic }, + // name = cz:qwerty, description = Czechia + { "cz", "qwerty", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic }, + // name = cz:qwerty_bksl, description = Czechia + { "cz", "qwerty_bksl", Qt::LeftToRight, QLocale::Czech, QLocale::CzechRepublic }, + // name = dk, description = Denmark + { "dk", "", Qt::LeftToRight, QLocale::Danish, QLocale::Denmark }, + // name = dk:nodeadkeys, description = Denmark + { "dk", "nodeadkeys", Qt::LeftToRight, QLocale::Danish, QLocale::Denmark }, + // name = nl, description = Netherlands + { "nl", "", Qt::LeftToRight, QLocale::Dutch, QLocale::Netherlands }, + // name = bt, description = Bhutan + { "bt", "", Qt::LeftToRight, QLocale::Bhutani, QLocale::Bhutan }, + // name = ee, description = Estonia + { "ee", "", Qt::LeftToRight, QLocale::Estonian, QLocale::Estonia }, + // name = ee:nodeadkeys, description = Estonia + { "ee", "nodeadkeys", Qt::LeftToRight, QLocale::Estonian, QLocale::Estonia }, + // name = ir, description = Iran + { "ir", "", Qt::RightToLeft, QLocale::Persian, QLocale::Iran }, + // name = fo, description = Faroe Islands + { "fo", "", Qt::LeftToRight, QLocale::Faroese, QLocale::FaroeIslands }, + // name = fo:nodeadkeys, description = Faroe Islands + { "fo", "nodeadkeys", Qt::LeftToRight, QLocale::Faroese, QLocale::FaroeIslands }, + // name = fi, description = Finland + { "fi", "", Qt::LeftToRight, QLocale::Finnish, QLocale::Finland }, + // name = fi:nodeadkeys, description = Finland + { "fi", "nodeadkeys", Qt::LeftToRight, QLocale::Finnish, QLocale::Finland }, + // name = fi:smi, description = Finland + { "fi", "smi", Qt::LeftToRight, QLocale::Finnish, QLocale::Finland }, + // name = fr, description = France + { "fr", "", Qt::LeftToRight, QLocale::French, QLocale::France }, + // name = fr:nodeadkeys, description = France + { "fr", "nodeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France }, + // name = fr:sundeadkeys, description = France + { "fr", "sundeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France }, + // name = fr:latin9, description = France + { "fr", "latin9", Qt::LeftToRight, QLocale::French, QLocale::France }, + // name = fr:latin9_nodeadkeys, description = France + { "fr", "latin9_nodeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France }, + // name = fr:latin9_sundeadkeys, description = France + { "fr", "latin9_sundeadkeys", Qt::LeftToRight, QLocale::French, QLocale::France }, + // name = fr:dvorak, description = France + { "fr", "dvorak", Qt::LeftToRight, QLocale::French, QLocale::France }, + // name = ge, description = Georgia + { "ge", "", Qt::LeftToRight, QLocale::Georgian, QLocale::Georgia }, + // name = ge:ru, description = Georgia + { "ge", "ru", Qt::LeftToRight, QLocale::Russian, QLocale::Georgia }, + // name = de, description = Germany + { "de", "", Qt::LeftToRight, QLocale::German, QLocale::Germany }, + // name = de:deadacute, description = Germany + { "de", "deadacute", Qt::LeftToRight, QLocale::German, QLocale::Germany }, + // name = de:deadgraveacute, description = Germany + { "de", "deadgraveacute", Qt::LeftToRight, QLocale::German, QLocale::Germany }, + // name = de:nodeadkeys, description = Germany + { "de", "nodeadkeys", Qt::LeftToRight, QLocale::German, QLocale::Germany }, + // name = de:ro, description = Germany + { "de", "ro", Qt::LeftToRight, QLocale::Romanian, QLocale::Germany }, + // name = de:ro_nodeadkeys, description = Germany + { "de", "ro_nodeadkeys", Qt::LeftToRight, QLocale::Romanian, QLocale::Germany }, + // name = de:dvorak, description = Germany + { "de", "dvorak", Qt::LeftToRight, QLocale::German, QLocale::Germany }, + // name = gr, description = Greece + { "gr", "", Qt::LeftToRight, QLocale::Greek, QLocale::Greece }, + // name = gr:extended, description = Greece + { "gr", "extended", Qt::LeftToRight, QLocale::Greek, QLocale::Greece }, + // name = gr:nodeadkeys, description = Greece + { "gr", "nodeadkeys", Qt::LeftToRight, QLocale::Greek, QLocale::Greece }, + // name = gr:polytonic, description = Greece + { "gr", "polytonic", Qt::LeftToRight, QLocale::Greek, QLocale::Greece }, + // name = hu, description = Hungary + { "hu", "", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:standard, description = Hungary + { "hu", "standard", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:nodeadkeys, description = Hungary + { "hu", "nodeadkeys", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:qwerty, description = Hungary + { "hu", "qwerty", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:101_qwertz_comma_dead, description = Hungary + { "hu", "101_qwertz_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:101_qwertz_comma_nodead, description = Hungary + { "hu", "101_qwertz_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:101_qwertz_dot_dead, description = Hungary + { "hu", "101_qwertz_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:101_qwertz_dot_nodead, description = Hungary + { "hu", "101_qwertz_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:101_qwerty_comma_dead, description = Hungary + { "hu", "101_qwerty_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:101_qwerty_comma_nodead, description = Hungary + { "hu", "101_qwerty_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:101_qwerty_dot_dead, description = Hungary + { "hu", "101_qwerty_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:101_qwerty_dot_nodead, description = Hungary + { "hu", "101_qwerty_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:102_qwertz_comma_dead, description = Hungary + { "hu", "102_qwertz_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:102_qwertz_comma_nodead, description = Hungary + { "hu", "102_qwertz_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:102_qwertz_dot_dead, description = Hungary + { "hu", "102_qwertz_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:102_qwertz_dot_nodead, description = Hungary + { "hu", "102_qwertz_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:102_qwerty_comma_dead, description = Hungary + { "hu", "102_qwerty_comma_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:102_qwerty_comma_nodead, description = Hungary + { "hu", "102_qwerty_comma_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:102_qwerty_dot_dead, description = Hungary + { "hu", "102_qwerty_dot_dead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = hu:102_qwerty_dot_nodead, description = Hungary + { "hu", "102_qwerty_dot_nodead", Qt::LeftToRight, QLocale::Hungarian, QLocale::Hungary }, + // name = is, description = Iceland + { "is", "", Qt::LeftToRight, QLocale::Icelandic, QLocale::Iceland }, + // name = is:Sundeadkeys, description = Iceland + { "is", "Sundeadkeys", Qt::LeftToRight, QLocale::Icelandic, QLocale::Iceland }, + // name = is:nodeadkeys, description = Iceland + { "is", "nodeadkeys", Qt::LeftToRight, QLocale::Icelandic, QLocale::Iceland }, + // name = il, description = Israel + { "il", "", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel }, + // name = il:lyx, description = Israel + { "il", "lyx", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel }, + // name = il:si1452, description = Israel + { "il", "si1452", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel }, + // name = il:phonetic, description = Israel + { "il", "phonetic", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel }, + // name = it, description = Italy + { "it", "", Qt::LeftToRight, QLocale::Italian, QLocale::Italy }, + // name = it:nodeadkeys, description = Italy + { "it", "nodeadkeys", Qt::LeftToRight, QLocale::Italian, QLocale::Italy }, + // name = jp, description = Japan + { "jp", "", Qt::LeftToRight, QLocale::Japanese, QLocale::Japan }, + // name = kg, description = Kyrgyzstan + { "kg", "", Qt::LeftToRight, QLocale::Kirghiz, QLocale::Kyrgyzstan }, + // name = la, description = Laos + { "la", "", Qt::LeftToRight, QLocale::Laothian, QLocale::Lao }, + // name = latam, description = Latin American + { "latam", "", Qt::LeftToRight, QLocale::Spanish, QLocale::Mexico }, + // name = latam:nodeadkeys, description = Latin American + { "latam", "nodeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Mexico }, + // name = latam:sundeadkeys, description = Latin American + { "latam", "sundeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Mexico }, + // name = lt, description = Lithuania + { "lt", "", Qt::LeftToRight, QLocale::Lithuanian, QLocale::Lithuania }, + // name = lt:std, description = Lithuania + { "lt", "std", Qt::LeftToRight, QLocale::Lithuanian, QLocale::Lithuania }, + // name = lt:us, description = Lithuania + { "lt", "us", Qt::LeftToRight, QLocale::Lithuanian, QLocale::Lithuania }, + // name = lv, description = Latvia + { "lv", "", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia }, + // name = lv:apostrophe, description = Latvia + { "lv", "apostrophe", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia }, + // name = lv:tilde, description = Latvia + { "lv", "tilde", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia }, + // name = lv:fkey, description = Latvia + { "lv", "fkey", Qt::LeftToRight, QLocale::Latvian, QLocale::Latvia }, + // name = mao, description = Maori + { "mao", "", Qt::LeftToRight, QLocale::Maori, QLocale::NewZealand }, + // name = mkd, description = Macedonian + { "mkd", "", Qt::LeftToRight, QLocale::Macedonian, QLocale::Macedonia }, + // name = mkd:nodeadkeys, description = Macedonian + { "mkd", "nodeadkeys", Qt::LeftToRight, QLocale::Macedonian, QLocale::Macedonia }, + // name = mt, description = Malta + { "mt", "", Qt::LeftToRight, QLocale::Maltese, QLocale::Malta }, + // name = mt:us, description = Malta + { "mt", "us", Qt::LeftToRight, QLocale::Maltese, QLocale::Malta }, + // name = mn, description = Mongolia + { "mn", "", Qt::LeftToRight, QLocale::Mongolian, QLocale::Mongolia }, + // name = no, description = Norway + { "no", "", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway }, + // name = no:nodeadkeys, description = Norway + { "no", "nodeadkeys", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway }, + // name = no:dvorak, description = Norway + { "no", "dvorak", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway }, + // name = no:smi, description = Norway + { "no", "smi", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway }, + // name = no:smi_nodeadkeys, description = Norway + { "no", "smi_nodeadkeys", Qt::LeftToRight, QLocale::Norwegian, QLocale::Norway }, + // name = pl, description = Poland + { "pl", "", Qt::LeftToRight, QLocale::Polish, QLocale::Poland }, + // name = pl:qwertz, description = Poland + { "pl", "qwertz", Qt::LeftToRight, QLocale::Polish, QLocale::Poland }, + // name = pl:dvorak, description = Poland + { "pl", "dvorak", Qt::LeftToRight, QLocale::Polish, QLocale::Poland }, + // name = pl:dvorak_quotes, description = Poland + { "pl", "dvorak_quotes", Qt::LeftToRight, QLocale::Polish, QLocale::Poland }, + // name = pl:dvorak_altquotes, description = Poland + { "pl", "dvorak_altquotes", Qt::LeftToRight, QLocale::Polish, QLocale::Poland }, + // name = pt, description = Portugal + { "pt", "", Qt::LeftToRight, QLocale::Portuguese, QLocale::Portugal }, + // name = pt:nodeadkeys, description = Portugal + { "pt", "nodeadkeys", Qt::LeftToRight, QLocale::Portuguese, QLocale::Portugal }, + // name = pt:sundeadkeys, description = Portugal + { "pt", "sundeadkeys", Qt::LeftToRight, QLocale::Portuguese, QLocale::Portugal }, + // name = ro, description = Romania + { "ro", "", Qt::LeftToRight, QLocale::Romanian, QLocale::Romania }, + // name = ro:us, description = Romania + { "ro", "us", Qt::LeftToRight, QLocale::English, QLocale::Romania }, + // name = ro:de, description = Romania + { "ro", "de", Qt::LeftToRight, QLocale::German, QLocale::Romania }, + // name = ru, description = Russia + { "ru", "", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation }, + // name = ru:phonetic, description = Russia + { "ru", "phonetic", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation }, + // name = ru:typewriter, description = Russia + { "ru", "typewriter", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation }, + // name = ru:winkeys, description = Russia + { "ru", "winkeys", Qt::LeftToRight, QLocale::Russian, QLocale::RussianFederation }, + // name = srp, description = Serbian + { "srp", "", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro }, + // name = srp:yz, description = Serbian + { "srp", "yz", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro }, + // name = srp:latin, description = Serbian + { "srp", "latin", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro }, + // name = srp:latinunicode, description = Serbian + { "srp", "latinunicode", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro }, + // name = srp:latinyz, description = Serbian + { "srp", "latinyz", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro }, + // name = srp:latinunicodeyz, description = Serbian + { "srp", "latinunicodeyz", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro }, + // name = srp:alternatequotes, description = Serbian + { "srp", "alternatequotes", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro }, + // name = srp:latinalternatequotes, description = Serbian + { "srp", "latinalternatequotes", Qt::LeftToRight, QLocale::Serbian, QLocale::SerbiaAndMontenegro }, + // name = si, description = Slovenia + { "si", "", Qt::LeftToRight, QLocale::Slovenian, QLocale::Slovenia }, + // name = sk, description = Slovakia + { "sk", "", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia }, + // name = sk:bksl, description = Slovakia + { "sk", "bksl", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia }, + // name = sk:qwerty, description = Slovakia + { "sk", "qwerty", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia }, + // name = sk:qwerty_bksl, description = Slovakia + { "sk", "qwerty_bksl", Qt::LeftToRight, QLocale::Slovak, QLocale::Slovakia }, + // name = es, description = Spain + { "es", "", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain }, + // name = es:nodeadkeys, description = Spain + { "es", "nodeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain }, + // name = es:sundeadkeys, description = Spain + { "es", "sundeadkeys", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain }, + // name = es:dvorak, description = Spain + { "es", "dvorak", Qt::LeftToRight, QLocale::Spanish, QLocale::Spain }, + // name = se, description = Sweden + { "se", "", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden }, + // name = se:nodeadkeys, description = Sweden + { "se", "nodeadkeys", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden }, + // name = se:dvorak, description = Sweden + { "se", "dvorak", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden }, + // name = se:rus, description = Sweden + { "se", "rus", Qt::LeftToRight, QLocale::Russian, QLocale::Sweden }, + // name = se:rus_nodeadkeys, description = Sweden + { "se", "rus_nodeadkeys", Qt::LeftToRight, QLocale::Russian, QLocale::Sweden }, + // name = se:smi, description = Sweden + { "se", "smi", Qt::LeftToRight, QLocale::Swedish, QLocale::Sweden }, + // name = ch, description = Switzerland + { "ch", "", Qt::LeftToRight, QLocale::German, QLocale::Switzerland }, + // name = ch:de_nodeadkeys, description = Switzerland + { "ch", "de_nodeadkeys", Qt::LeftToRight, QLocale::German, QLocale::Switzerland }, + // name = ch:de_sundeadkeys, description = Switzerland + { "ch", "de_sundeadkeys", Qt::LeftToRight, QLocale::German, QLocale::Switzerland }, + // name = ch:fr, description = Switzerland + { "ch", "fr", Qt::LeftToRight, QLocale::French, QLocale::Switzerland }, + // name = ch:fr_nodeadkeys, description = Switzerland + { "ch", "fr_nodeadkeys", Qt::LeftToRight, QLocale::French, QLocale::Switzerland }, + // name = ch:fr_sundeadkeys, description = Switzerland + { "ch", "fr_sundeadkeys", Qt::LeftToRight, QLocale::French, QLocale::Switzerland }, + // name = sy, description = Syria + { "sy", "", Qt::RightToLeft, QLocale::Syriac, QLocale::SyrianArabRepublic }, + // name = sy:syc, description = Syria + { "sy", "syc", Qt::RightToLeft, QLocale::Syriac, QLocale::SyrianArabRepublic }, + // name = sy:syc_phonetic, description = Syria + { "sy", "syc_phonetic", Qt::RightToLeft, QLocale::Syriac, QLocale::SyrianArabRepublic }, + // name = tj, description = Tajikistan + { "tj", "", Qt::LeftToRight, QLocale::Tajik, QLocale::Tajikistan }, + // name = lk, description = Sri Lanka + { "lk", "", Qt::LeftToRight, QLocale::Singhalese, QLocale::SriLanka }, + // name = lk:tam_unicode, description = Sri Lanka + { "lk", "tam_unicode", Qt::LeftToRight, QLocale::Tamil, QLocale::SriLanka }, + // name = lk:tam_TAB, description = Sri Lanka + { "lk", "tam_TAB", Qt::LeftToRight, QLocale::Tamil, QLocale::SriLanka }, + // name = lk:tam_TSCII, description = Sri Lanka + { "lk", "tam_TSCII", Qt::LeftToRight, QLocale::Tamil, QLocale::SriLanka }, + // name = lk:sin_phonetic, description = Sri Lanka + { "lk", "sin_phonetic", Qt::LeftToRight, QLocale::Singhalese, QLocale::SriLanka }, + // name = th, description = Thailand + { "th", "", Qt::LeftToRight, QLocale::Thai, QLocale::Thailand }, + // name = th:tis, description = Thailand + { "th", "tis", Qt::LeftToRight, QLocale::Thai, QLocale::Thailand }, + // name = th:pat, description = Thailand + { "th", "pat", Qt::LeftToRight, QLocale::Thai, QLocale::Thailand }, + // name = tr, description = Turkish + { "tr", "", Qt::LeftToRight, QLocale::Turkish, QLocale::Turkey }, + // name = tr:f, description = Turkish + { "tr", "f", Qt::LeftToRight, QLocale::Turkish, QLocale::Turkey }, + // name = tr:alt, description = Turkish + { "tr", "alt", Qt::LeftToRight, QLocale::Turkish, QLocale::Turkey }, + // name = ua, description = Ukraine + { "ua", "", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine }, + // name = ua:phonetic, description = Ukraine + { "ua", "phonetic", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine }, + // name = ua:typewriter, description = Ukraine + { "ua", "typewriter", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine }, + // name = ua:winkeys, description = Ukraine + { "ua", "winkeys", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine }, + // name = ua:rstu, description = Ukraine + { "ua", "rstu", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine }, + // name = ua:rstu_ru, description = Ukraine + { "ua", "rstu_ru", Qt::LeftToRight, QLocale::Ukrainian, QLocale::Ukraine }, + // name = gb, description = United Kingdom + { "gb", "", Qt::LeftToRight, QLocale::English, QLocale::UnitedKingdom }, + // name = gb:intl, description = United Kingdom + { "gb", "intl", Qt::LeftToRight, QLocale::English, QLocale::UnitedKingdom }, + // name = gb:dvorak, description = United Kingdom + { "gb", "dvorak", Qt::LeftToRight, QLocale::English, QLocale::UnitedKingdom }, + // name = uz, description = Uzbekistan + { "uz", "", Qt::LeftToRight, QLocale::Uzbek, QLocale::Uzbekistan }, + // name = vn, description = Vietnam + { "vn", "", Qt::LeftToRight, QLocale::Vietnamese, QLocale::VietNam }, + // name = nec_vndr/jp, description = PC-98xx Series + { "nec_vndr/jp", "", Qt::LeftToRight, QLocale::Japanese, QLocale::Japan }, + // name = ie, description = Ireland + { "ie", "", Qt::LeftToRight, QLocale::Irish, QLocale::Ireland }, + // name = ie:CloGaelach, description = Ireland + { "ie", "CloGaelach", Qt::LeftToRight, QLocale::Gaelic, QLocale::Ireland }, + // name = ie:UnicodeExpert, description = Ireland + { "ie", "UnicodeExpert", Qt::LeftToRight, QLocale::Irish, QLocale::Ireland }, + // name = ie:ogam, description = Ireland + { "ie", "ogam", Qt::LeftToRight, QLocale::Gaelic, QLocale::Ireland }, + // name = ie:ogam_is434, description = Ireland + { "ie", "ogam_is434", Qt::LeftToRight, QLocale::Gaelic, QLocale::Ireland }, + // name = pk, description = Pakistan + { "pk", "", Qt::RightToLeft, QLocale::Urdu, QLocale::Pakistan }, + { 0, 0, Qt::LeftToRight, QLocale::C, QLocale::AnyCountry } +}; diff --git a/src/gui/guikernel/qkeysequence.cpp b/src/gui/guikernel/qkeysequence.cpp new file mode 100644 index 0000000000..3cf5dc5275 --- /dev/null +++ b/src/gui/guikernel/qkeysequence.cpp @@ -0,0 +1,1726 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qkeysequence.h" +#include "qkeysequence_p.h" +#include "private/qapplication_p.h" + +#ifndef QT_NO_SHORTCUT + +#include "qshortcut.h" +#include "qdebug.h" +#ifndef QT_NO_REGEXP +# include "qregexp.h" +#endif +#ifndef QT_NO_DATASTREAM +# include "qdatastream.h" +#endif +#include "qvariant.h" + +#ifdef Q_WS_MAC +# include <private/qt_mac_p.h> + +#endif + +QT_BEGIN_NAMESPACE + +#ifdef Q_WS_MAC +static bool qt_sequence_no_mnemonics = true; +struct MacSpecialKey { + int key; + ushort macSymbol; +}; + +static const int NumEntries = 21; +static const MacSpecialKey entries[NumEntries] = { + { Qt::Key_Escape, 0x238B }, + { Qt::Key_Tab, 0x21E5 }, + { Qt::Key_Backtab, 0x21E4 }, + { Qt::Key_Backspace, 0x232B }, + { Qt::Key_Return, 0x21B5 }, + { Qt::Key_Enter, 0x2324 }, + { Qt::Key_Delete, 0x2326 }, + { Qt::Key_Home, 0x2196 }, + { Qt::Key_End, 0x2198 }, + { Qt::Key_Left, 0x2190 }, + { Qt::Key_Up, 0x2191 }, + { Qt::Key_Right, 0x2192 }, + { Qt::Key_Down, 0x2193 }, + { Qt::Key_PageUp, 0x21DE }, + { Qt::Key_PageDown, 0x21DF }, + { Qt::Key_Shift, kShiftUnicode }, + { Qt::Key_Control, kCommandUnicode }, + { Qt::Key_Meta, kControlUnicode }, + { Qt::Key_Alt, kOptionUnicode }, + { Qt::Key_CapsLock, 0x21EA }, +}; + +static bool operator<(const MacSpecialKey &entry, int key) +{ + return entry.key < key; +} + +static bool operator<(int key, const MacSpecialKey &entry) +{ + return key < entry.key; +} + +static const MacSpecialKey * const MacSpecialKeyEntriesEnd = entries + NumEntries; + +QChar qt_macSymbolForQtKey(int key) +{ + const MacSpecialKey *i = qBinaryFind(entries, MacSpecialKeyEntriesEnd, key); + if (i == MacSpecialKeyEntriesEnd) + return QChar(); + ushort macSymbol = i->macSymbol; + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) + && (macSymbol == kControlUnicode || macSymbol == kCommandUnicode)) { + if (macSymbol == kControlUnicode) + macSymbol = kCommandUnicode; + else + macSymbol = kControlUnicode; + } + + return QChar(macSymbol); +} + +static int qtkeyForMacSymbol(const QChar ch) +{ + const ushort unicode = ch.unicode(); + for (int i = 0; i < NumEntries; ++i) { + const MacSpecialKey &entry = entries[i]; + if (entry.macSymbol == unicode) { + int key = entry.key; + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) + && (unicode == kControlUnicode || unicode == kCommandUnicode)) { + if (unicode == kControlUnicode) + key = Qt::Key_Control; + else + key = Qt::Key_Meta; + } + return key; + } + } + return -1; +} + +#else +static bool qt_sequence_no_mnemonics = false; +#endif +void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemonics = !b; } + +/*! + \class QKeySequence + \brief The QKeySequence class encapsulates a key sequence as used + by shortcuts. + + \ingroup shared + + + In its most common form, a key sequence describes a combination of + keys that must be used together to perform some action. Key sequences + are used with QAction objects to specify which keyboard shortcuts can + be used to trigger actions. + + Key sequences can be constructed for use as keyboard shortcuts in + three different ways: + + \list + \o For standard shortcuts, a \l{QKeySequence::StandardKey}{standard key} + can be used to request the platform-specific key sequence associated + with each shortcut. + \o For custom shortcuts, human-readable strings such as "Ctrl+X" can + be used, and these can be translated into the appropriate shortcuts + for users of different languages. Translations are made in the + "QShortcut" context. + \o For hard-coded shortcuts, integer key codes can be specified with + a combination of values defined by the Qt::Key and Qt::Modifier enum + values. Each key code consists of a single Qt::Key value and zero or + more modifiers, such as Qt::SHIFT, Qt::CTRL, Qt::ALT and Qt::META. + \endlist + + For example, \gui{Ctrl P} might be a sequence used as a shortcut for + printing a document, and can be specified in any of the following + ways: + + \snippet doc/src/snippets/code/src_gui_kernel_qkeysequence.cpp 0 + + Note that, for letters, the case used in the specification string + does not matter. In the above examples, the user does not need to + hold down the \key{Shift} key to activate a shortcut specified + with "Ctrl+P". However, for other keys, the use of \key{Shift} as + an unspecified extra modifier key can lead to confusion for users + of an application whose keyboards have different layouts to those + used by the developers. See the \l{Keyboard Layout Issues} section + below for more details. + + It is preferable to use standard shortcuts where possible. + When creating key sequences for non-standard shortcuts, you should use + human-readable strings in preference to hard-coded integer values. + + QKeySequence objects can be cast to a QString to obtain a human-readable + translated version of the sequence. Similarly, the toString() function + produces human-readable strings for use in menus. On Mac OS X, the + appropriate symbols are used to describe keyboard shortcuts using special + keys on the Macintosh keyboard. + + An alternative way to specify hard-coded key codes is to use the Unicode + code point of the character; for example, 'A' gives the same key sequence + as Qt::Key_A. + + \bold{Note:} On Mac OS X, references to "Ctrl", Qt::CTRL, Qt::Control + and Qt::ControlModifier correspond to the \key Command keys on the + Macintosh keyboard, and references to "Meta", Qt::META, Qt::Meta and + Qt::MetaModifier correspond to the \key Control keys. Developers on + Mac OS X can use the same shortcut descriptions across all platforms, + and their applications will automatically work as expected on Mac OS X. + + \section1 Standard Shortcuts + + QKeySequence defines many \l{QKeySequence::StandardKey} {standard + keyboard shortcuts} to reduce the amount of effort required when + setting up actions in a typical application. The table below shows + some common key sequences that are often used for these standard + shortcuts by applications on four widely-used platforms. Note + that on Mac OS X, the \key Ctrl value corresponds to the \key + Command keys on the Macintosh keyboard, and the \key Meta value + corresponds to the \key Control keys. + + \table + \header \i StandardKey \i Windows \i Mac OS X \i KDE \i GNOME \i S60 + \row \i HelpContents \i F1 \i Ctrl+? \i F1 \i F1 \i F2 + \row \i WhatsThis \i Shift+F1 \i Shift+F1 \i Shift+F1 \i Shift+F1 \i Shift+F1 + \row \i Open \i Ctrl+O \i Ctrl+O \i Ctrl+O \i Ctrl+O \i (none) + \row \i Close \i Ctrl+F4, Ctrl+W \i Ctrl+W, Ctrl+F4 \i Ctrl+W \i Ctrl+W \i (none) + \row \i Save \i Ctrl+S \i Ctrl+S \i Ctrl+S \i Ctrl+S \i (none) + \row \i Quit \i \i Ctrl+Q \i Qtrl+Q \i Qtrl+Q \i (none) + \row \i SaveAs \i \i Ctrl+Shift+S \i \i Ctrl+Shift+S \i (none) + \row \i New \i Ctrl+N \i Ctrl+N \i Ctrl+N \i Ctrl+N \i (none) + \row \i Delete \i Del \i Del, Meta+D \i Del, Ctrl+D \i Del, Ctrl+D \i Del + \row \i Cut \i Ctrl+X, Shift+Del \i Ctrl+X \i Ctrl+X, F20, Shift+Del \i Ctrl+X, F20, Shift+Del \i Ctrl+X + \row \i Copy \i Ctrl+C, Ctrl+Ins \i Ctrl+C \i Ctrl+C, F16, Ctrl+Ins \i Ctrl+C, F16, Ctrl+Ins \i Ctrl+C + \row \i Paste \i Ctrl+V, Shift+Ins \i Ctrl+V \i Ctrl+V, F18, Shift+Ins \i Ctrl+V, F18, Shift+Ins \i Ctrl+V + \row \i Preferences \i \i Ctrl+, \i \i \i (none) + \row \i Undo \i Ctrl+Z, Alt+Backspace \i Ctrl+Z \i Ctrl+Z, F14 \i Ctrl+Z, F14 \i Ctrl+Z + \row \i Redo \i Ctrl+Y, Shift+Ctrl+Z, Alt+Shift+Backspace \i Ctrl+Shift+Z \i Ctrl+Shift+Z \i Ctrl+Shift+Z \i (none) + \row \i Back \i Alt+Left, Backspace \i Ctrl+[ \i Alt+Left \i Alt+Left \i (none) + \row \i Forward \i Alt+Right, Shift+Backspace \i Ctrl+] \i Alt+Right \i Alt+Right \i (none) + \row \i Refresh \i F5 \i F5 \i F5 \i Ctrl+R, F5 \i (none) + \row \i ZoomIn \i Ctrl+Plus \i Ctrl+Plus \i Ctrl+Plus \i Ctrl+Plus \i (none) + \row \i ZoomOut \i Ctrl+Minus \i Ctrl+Minus \i Ctrl+Minus \i Ctrl+Minus \i (none) + \row \i Print \i Ctrl+P \i Ctrl+P \i Ctrl+P \i Ctrl+P \i (none) + \row \i AddTab \i Ctrl+T \i Ctrl+T \i Ctrl+Shift+N, Ctrl+T \i Ctrl+T \i (none) + \row \i NextChild \i Ctrl+Tab, Forward, Ctrl+F6 \i Ctrl+}, Forward, Ctrl+Tab \i Ctrl+Tab, Forward, Ctrl+Comma \i Ctrl+Tab, Forward \i (none) + \row \i PreviousChild \i Ctrl+Shift+Tab, Back, Ctrl+Shift+F6 \i Ctrl+{, Back, Ctrl+Shift+Tab \i Ctrl+Shift+Tab, Back, Ctrl+Period \i Ctrl+Shift+Tab, Back \i (none) + \row \i Find \i Ctrl+F \i Ctrl+F \i Ctrl+F \i Ctrl+F \i (none) + \row \i FindNext \i F3, Ctrl+G \i Ctrl+G \i F3 \i Ctrl+G, F3 \i (none) + \row \i FindPrevious \i Shift+F3, Ctrl+Shift+G \i Ctrl+Shift+G \i Shift+F3 \i Ctrl+Shift+G, Shift+F3 \i (none) + \row \i Replace \i Ctrl+H \i (none) \i Ctrl+R \i Ctrl+H \i (none) + \row \i SelectAll \i Ctrl+A \i Ctrl+A \i Ctrl+A \i Ctrl+A \i (none) + \row \i Bold \i Ctrl+B \i Ctrl+B \i Ctrl+B \i Ctrl+B \i (none) + \row \i Italic \i Ctrl+I \i Ctrl+I \i Ctrl+I \i Ctrl+I \i (none) + \row \i Underline \i Ctrl+U \i Ctrl+U \i Ctrl+U \i Ctrl+U \i (none) + \row \i MoveToNextChar \i Right \i Right \i Right \i Right \i Right + \row \i MoveToPreviousChar \i Left \i Left \i Left \i Left \i Left + \row \i MoveToNextWord \i Ctrl+Right \i Alt+Right \i Ctrl+Right \i Ctrl+Right \i Ctrl+Right + \row \i MoveToPreviousWord \i Ctrl+Left \i Alt+Left \i Ctrl+Left \i Ctrl+Left \i Ctrl+Left + \row \i MoveToNextLine \i Down \i Down \i Down \i Down \i Down + \row \i MoveToPreviousLine \i Up \i Up \i Up \i Up \i Up + \row \i MoveToNextPage \i PgDown \i PgDown, Alt+PgDown, Meta+Down, Meta+PgDown\i PgDown \i PgDown \i PgDown + \row \i MoveToPreviousPage \i PgUp \i PgUp, Alt+PgUp, Meta+Up, Meta+PgUp \i PgUp \i PgUp \i PgUp + \row \i MoveToStartOfLine \i Home \i Ctrl+Left, Meta+Left \i Home \i Home \i Home + \row \i MoveToEndOfLine \i End \i Ctrl+Right, Meta+Right \i End \i End \i End + \row \i MoveToStartOfBlock \i (none) \i Alt+Up, Meta+A \i (none) \i (none) \i (none) + \row \i MoveToEndOfBlock \i (none) \i Alt+Down, Meta+E \i (none) \i (none) \i (none) + \row \i MoveToStartOfDocument\i Ctrl+Home \i Ctrl+Up, Home \i Ctrl+Home \i Ctrl+Home \i Ctrl+Home + \row \i MoveToEndOfDocument \i Ctrl+End \i Ctrl+Down, End \i Ctrl+End \i Ctrl+End \i Ctrl+End + \row \i SelectNextChar \i Shift+Right \i Shift+Right \i Shift+Right \i Shift+Right \i Shift+Right + \row \i SelectPreviousChar \i Shift+Left \i Shift+Left \i Shift+Left \i Shift+Left \i Shift+Left + \row \i SelectNextWord \i Ctrl+Shift+Right \i Alt+Shift+Right \i Ctrl+Shift+Right \i Ctrl+Shift+Right \i Ctrl+Shift+Right + \row \i SelectPreviousWord \i Ctrl+Shift+Left \i Alt+Shift+Left \i Ctrl+Shift+Left \i Ctrl+Shift+Left \i Ctrl+Shift+Left + \row \i SelectNextLine \i Shift+Down \i Shift+Down \i Shift+Down \i Shift+Down \i Shift+Down + \row \i SelectPreviousLine \i Shift+Up \i Shift+Up \i Shift+Up \i Shift+Up \i Shift+Up + \row \i SelectNextPage \i Shift+PgDown \i Shift+PgDown \i Shift+PgDown \i Shift+PgDown \i Shift+PgDown + \row \i SelectPreviousPage \i Shift+PgUp \i Shift+PgUp \i Shift+PgUp \i Shift+PgUp \i Shift+PgUp + \row \i SelectStartOfLine \i Shift+Home \i Ctrl+Shift+Left \i Shift+Home \i Shift+Home \i Shift+Home + \row \i SelectEndOfLine \i Shift+End \i Ctrl+Shift+Right \i Shift+End \i Shift+End \i Shift+End + \row \i SelectStartOfBlock \i (none) \i Alt+Shift+Up, Meta+Shift+A \i (none) \i (none) \i (none) + \row \i SelectEndOfBlock \i (none) \i Alt+Shift+Down, Meta+Shift+E \i (none) \i (none) \i (none) + \row \i SelectStartOfDocument\i Ctrl+Shift+Home \i Ctrl+Shift+Up, Shift+Home \i Ctrl+Shift+Home\i Ctrl+Shift+Home \i Ctrl+Shift+Home + \row \i SelectEndOfDocument \i Ctrl+Shift+End \i Ctrl+Shift+Down, Shift+End \i Ctrl+Shift+End \i Ctrl+Shift+End \i Ctrl+Shift+End + \row \i DeleteStartOfWord \i Ctrl+Backspace \i Alt+Backspace \i Ctrl+Backspace \i Ctrl+Backspace \i (none) + \row \i DeleteEndOfWord \i Ctrl+Del \i (none) \i Ctrl+Del \i Ctrl+Del \i (none) + \row \i DeleteEndOfLine \i (none) \i (none) \i Ctrl+K \i Ctrl+K \i (none) + \row \i InsertParagraphSeparator \i Enter \i Enter \i Enter \i Enter \i (none) + \row \i InsertLineSeparator \i Shift+Enter \i Meta+Enter \i Shift+Enter \i Shift+Enter \i (none) + \endtable + + Note that, since the key sequences used for the standard shortcuts differ + between platforms, you still need to test your shortcuts on each platform + to ensure that you do not unintentionally assign the same key sequence to + many actions. + + \section1 Keyboard Layout Issues + + Many key sequence specifications are chosen by developers based on the + layout of certain types of keyboard, rather than choosing keys that + represent the first letter of an action's name, such as \key{Ctrl S} + ("Ctrl+S") or \key{Ctrl C} ("Ctrl+C"). + Additionally, because certain symbols can only be entered with the + help of modifier keys on certain keyboard layouts, key sequences intended + for use with one keyboard layout may map to a different key, map to no + keys at all, or require an additional modifier key to be used on + different keyboard layouts. + + For example, the shortcuts, \key{Ctrl plus} and \key{Ctrl minus}, are often + used as shortcuts for zoom operations in graphics applications, and these + may be specified as "Ctrl++" and "Ctrl+-" respectively. However, the way + these shortcuts are specified and interpreted depends on the keyboard layout. + Users of Norwegian keyboards will note that the \key{+} and \key{-} keys + are not adjacent on the keyboard, but will still be able to activate both + shortcuts without needing to press the \key{Shift} key. However, users + with British keyboards will need to hold down the \key{Shift} key + to enter the \key{+} symbol, making the shortcut effectively the same as + "Ctrl+Shift+=". + + Although some developers might resort to fully specifying all the modifiers + they use on their keyboards to activate a shortcut, this will also result + in unexpected behavior for users of different keyboard layouts. + + For example, a developer using a British keyboard may decide to specify + "Ctrl+Shift+=" as the key sequence in order to create a shortcut that + coincidentally behaves in the same way as \key{Ctrl plus}. However, the + \key{=} key needs to be accessed using the \key{Shift} key on Norwegian + keyboard, making the required shortcut effectively \key{Ctrl Shift Shift =} + (an impossible key combination). + + As a result, both human-readable strings and hard-coded key codes + can both be problematic to use when specifying a key sequence that + can be used on a variety of different keyboard layouts. Only the + use of \l{QKeySequence::StandardKey} {standard shortcuts} + guarantees that the user will be able to use the shortcuts that + the developer intended. + + Despite this, we can address this issue by ensuring that human-readable + strings are used, making it possible for translations of key sequences to + be made for users of different languages. This approach will be successful + for users whose keyboards have the most typical layout for the language + they are using. + + \section1 GNU Emacs Style Key Sequences + + Key sequences similar to those used in \l{GNU Emacs}, allowing up to four + key codes, can be created by using the multiple argument constructor, + or by passing a human-readable string of comma-separated key sequences. + + For example, the key sequence, \key{Ctrl X} followed by \key{Ctrl C}, can + be specified using either of the following ways: + + \snippet doc/src/snippets/code/src_gui_kernel_qkeysequence.cpp 1 + + \warning A QApplication instance must have been constructed before a + QKeySequence is created; otherwise, your application may crash. + + \sa QShortcut +*/ + +/*! + \enum QKeySequence::SequenceMatch + + \value NoMatch The key sequences are different; not even partially + matching. + \value PartialMatch The key sequences match partially, but are not + the same. + \value ExactMatch The key sequences are the same. + \omitvalue Identical +*/ + +/*! + \enum QKeySequence::SequenceFormat + + \value NativeText The key sequence as a platform specific string. + This means that it will be shown translated and on the Mac it will + resemble a key sequence from the menu bar. This enum is best used when you + want to display the string to the user. + + \value PortableText The key sequence is given in a "portable" format, + suitable for reading and writing to a file. In many cases, it will look + similar to the native text on Windows and X11. +*/ + +static const struct { + int key; + const char* name; +} keyname[] = { + //: This and all following "incomprehensible" strings in QShortcut context + //: are key names. Please use the localized names appearing on actual + //: keyboards or whatever is commonly used. + { Qt::Key_Space, QT_TRANSLATE_NOOP("QShortcut", "Space") }, + { Qt::Key_Escape, QT_TRANSLATE_NOOP("QShortcut", "Esc") }, + { Qt::Key_Tab, QT_TRANSLATE_NOOP("QShortcut", "Tab") }, + { Qt::Key_Backtab, QT_TRANSLATE_NOOP("QShortcut", "Backtab") }, + { Qt::Key_Backspace, QT_TRANSLATE_NOOP("QShortcut", "Backspace") }, + { Qt::Key_Return, QT_TRANSLATE_NOOP("QShortcut", "Return") }, + { Qt::Key_Enter, QT_TRANSLATE_NOOP("QShortcut", "Enter") }, + { Qt::Key_Insert, QT_TRANSLATE_NOOP("QShortcut", "Ins") }, + { Qt::Key_Delete, QT_TRANSLATE_NOOP("QShortcut", "Del") }, + { Qt::Key_Pause, QT_TRANSLATE_NOOP("QShortcut", "Pause") }, + { Qt::Key_Print, QT_TRANSLATE_NOOP("QShortcut", "Print") }, + { Qt::Key_SysReq, QT_TRANSLATE_NOOP("QShortcut", "SysReq") }, + { Qt::Key_Home, QT_TRANSLATE_NOOP("QShortcut", "Home") }, + { Qt::Key_End, QT_TRANSLATE_NOOP("QShortcut", "End") }, + { Qt::Key_Left, QT_TRANSLATE_NOOP("QShortcut", "Left") }, + { Qt::Key_Up, QT_TRANSLATE_NOOP("QShortcut", "Up") }, + { Qt::Key_Right, QT_TRANSLATE_NOOP("QShortcut", "Right") }, + { Qt::Key_Down, QT_TRANSLATE_NOOP("QShortcut", "Down") }, + { Qt::Key_PageUp, QT_TRANSLATE_NOOP("QShortcut", "PgUp") }, + { Qt::Key_PageDown, QT_TRANSLATE_NOOP("QShortcut", "PgDown") }, + { Qt::Key_CapsLock, QT_TRANSLATE_NOOP("QShortcut", "CapsLock") }, + { Qt::Key_NumLock, QT_TRANSLATE_NOOP("QShortcut", "NumLock") }, + { Qt::Key_ScrollLock, QT_TRANSLATE_NOOP("QShortcut", "ScrollLock") }, + { Qt::Key_Menu, QT_TRANSLATE_NOOP("QShortcut", "Menu") }, + { Qt::Key_Help, QT_TRANSLATE_NOOP("QShortcut", "Help") }, + + // Special keys + // Includes multimedia, launcher, lan keys ( bluetooth, wireless ) + // window navigation + { Qt::Key_Back, QT_TRANSLATE_NOOP("QShortcut", "Back") }, + { Qt::Key_Forward, QT_TRANSLATE_NOOP("QShortcut", "Forward") }, + { Qt::Key_Stop, QT_TRANSLATE_NOOP("QShortcut", "Stop") }, + { Qt::Key_Refresh, QT_TRANSLATE_NOOP("QShortcut", "Refresh") }, + { Qt::Key_VolumeDown, QT_TRANSLATE_NOOP("QShortcut", "Volume Down") }, + { Qt::Key_VolumeMute, QT_TRANSLATE_NOOP("QShortcut", "Volume Mute") }, + { Qt::Key_VolumeUp, QT_TRANSLATE_NOOP("QShortcut", "Volume Up") }, + { Qt::Key_BassBoost, QT_TRANSLATE_NOOP("QShortcut", "Bass Boost") }, + { Qt::Key_BassUp, QT_TRANSLATE_NOOP("QShortcut", "Bass Up") }, + { Qt::Key_BassDown, QT_TRANSLATE_NOOP("QShortcut", "Bass Down") }, + { Qt::Key_TrebleUp, QT_TRANSLATE_NOOP("QShortcut", "Treble Up") }, + { Qt::Key_TrebleDown, QT_TRANSLATE_NOOP("QShortcut", "Treble Down") }, + { Qt::Key_MediaPlay, QT_TRANSLATE_NOOP("QShortcut", "Media Play") }, + { Qt::Key_MediaStop, QT_TRANSLATE_NOOP("QShortcut", "Media Stop") }, + { Qt::Key_MediaPrevious, QT_TRANSLATE_NOOP("QShortcut", "Media Previous") }, + { Qt::Key_MediaNext, QT_TRANSLATE_NOOP("QShortcut", "Media Next") }, + { Qt::Key_MediaRecord, QT_TRANSLATE_NOOP("QShortcut", "Media Record") }, + //: Media player pause button + { Qt::Key_MediaPause, QT_TRANSLATE_NOOP("QShortcut", "Media Pause") }, + //: Media player button to toggle between playing and paused + { Qt::Key_MediaTogglePlayPause, QT_TRANSLATE_NOOP("QShortcut", "Toggle Media Play/Pause") }, + { Qt::Key_HomePage, QT_TRANSLATE_NOOP("QShortcut", "Home Page") }, + { Qt::Key_Favorites, QT_TRANSLATE_NOOP("QShortcut", "Favorites") }, + { Qt::Key_Search, QT_TRANSLATE_NOOP("QShortcut", "Search") }, + { Qt::Key_Standby, QT_TRANSLATE_NOOP("QShortcut", "Standby") }, + { Qt::Key_OpenUrl, QT_TRANSLATE_NOOP("QShortcut", "Open URL") }, + { Qt::Key_LaunchMail, QT_TRANSLATE_NOOP("QShortcut", "Launch Mail") }, + { Qt::Key_LaunchMedia, QT_TRANSLATE_NOOP("QShortcut", "Launch Media") }, + { Qt::Key_Launch0, QT_TRANSLATE_NOOP("QShortcut", "Launch (0)") }, + { Qt::Key_Launch1, QT_TRANSLATE_NOOP("QShortcut", "Launch (1)") }, + { Qt::Key_Launch2, QT_TRANSLATE_NOOP("QShortcut", "Launch (2)") }, + { Qt::Key_Launch3, QT_TRANSLATE_NOOP("QShortcut", "Launch (3)") }, + { Qt::Key_Launch4, QT_TRANSLATE_NOOP("QShortcut", "Launch (4)") }, + { Qt::Key_Launch5, QT_TRANSLATE_NOOP("QShortcut", "Launch (5)") }, + { Qt::Key_Launch6, QT_TRANSLATE_NOOP("QShortcut", "Launch (6)") }, + { Qt::Key_Launch7, QT_TRANSLATE_NOOP("QShortcut", "Launch (7)") }, + { Qt::Key_Launch8, QT_TRANSLATE_NOOP("QShortcut", "Launch (8)") }, + { Qt::Key_Launch9, QT_TRANSLATE_NOOP("QShortcut", "Launch (9)") }, + { Qt::Key_LaunchA, QT_TRANSLATE_NOOP("QShortcut", "Launch (A)") }, + { Qt::Key_LaunchB, QT_TRANSLATE_NOOP("QShortcut", "Launch (B)") }, + { Qt::Key_LaunchC, QT_TRANSLATE_NOOP("QShortcut", "Launch (C)") }, + { Qt::Key_LaunchD, QT_TRANSLATE_NOOP("QShortcut", "Launch (D)") }, + { Qt::Key_LaunchE, QT_TRANSLATE_NOOP("QShortcut", "Launch (E)") }, + { Qt::Key_LaunchF, QT_TRANSLATE_NOOP("QShortcut", "Launch (F)") }, + { Qt::Key_MonBrightnessUp, QT_TRANSLATE_NOOP("QShortcut", "Monitor Brightness Up") }, + { Qt::Key_MonBrightnessDown, QT_TRANSLATE_NOOP("QShortcut", "Monitor Brightness Down") }, + { Qt::Key_KeyboardLightOnOff, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Light On/Off") }, + { Qt::Key_KeyboardBrightnessUp, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Brightness Up") }, + { Qt::Key_KeyboardBrightnessDown, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Brightness Down") }, + { Qt::Key_PowerOff, QT_TRANSLATE_NOOP("QShortcut", "Power Off") }, + { Qt::Key_WakeUp, QT_TRANSLATE_NOOP("QShortcut", "Wake Up") }, + { Qt::Key_Eject, QT_TRANSLATE_NOOP("QShortcut", "Eject") }, + { Qt::Key_ScreenSaver, QT_TRANSLATE_NOOP("QShortcut", "Screensaver") }, + { Qt::Key_WWW, QT_TRANSLATE_NOOP("QShortcut", "WWW") }, + { Qt::Key_Sleep, QT_TRANSLATE_NOOP("QShortcut", "Sleep") }, + { Qt::Key_LightBulb, QT_TRANSLATE_NOOP("QShortcut", "LightBulb") }, + { Qt::Key_Shop, QT_TRANSLATE_NOOP("QShortcut", "Shop") }, + { Qt::Key_History, QT_TRANSLATE_NOOP("QShortcut", "History") }, + { Qt::Key_AddFavorite, QT_TRANSLATE_NOOP("QShortcut", "Add Favorite") }, + { Qt::Key_HotLinks, QT_TRANSLATE_NOOP("QShortcut", "Hot Links") }, + { Qt::Key_BrightnessAdjust, QT_TRANSLATE_NOOP("QShortcut", "Adjust Brightness") }, + { Qt::Key_Finance, QT_TRANSLATE_NOOP("QShortcut", "Finance") }, + { Qt::Key_Community, QT_TRANSLATE_NOOP("QShortcut", "Community") }, + { Qt::Key_AudioRewind, QT_TRANSLATE_NOOP("QShortcut", "Audio Rewind") }, + { Qt::Key_BackForward, QT_TRANSLATE_NOOP("QShortcut", "Back Forward") }, + { Qt::Key_ApplicationLeft, QT_TRANSLATE_NOOP("QShortcut", "Application Left") }, + { Qt::Key_ApplicationRight, QT_TRANSLATE_NOOP("QShortcut", "Application Right") }, + { Qt::Key_Book, QT_TRANSLATE_NOOP("QShortcut", "Book") }, + { Qt::Key_CD, QT_TRANSLATE_NOOP("QShortcut", "CD") }, + { Qt::Key_Calculator, QT_TRANSLATE_NOOP("QShortcut", "Calculator") }, + { Qt::Key_Clear, QT_TRANSLATE_NOOP("QShortcut", "Clear") }, + { Qt::Key_ClearGrab, QT_TRANSLATE_NOOP("QShortcut", "Clear Grab") }, + { Qt::Key_Close, QT_TRANSLATE_NOOP("QShortcut", "Close") }, + { Qt::Key_Copy, QT_TRANSLATE_NOOP("QShortcut", "Copy") }, + { Qt::Key_Cut, QT_TRANSLATE_NOOP("QShortcut", "Cut") }, + { Qt::Key_Display, QT_TRANSLATE_NOOP("QShortcut", "Display") }, + { Qt::Key_DOS, QT_TRANSLATE_NOOP("QShortcut", "DOS") }, + { Qt::Key_Documents, QT_TRANSLATE_NOOP("QShortcut", "Documents") }, + { Qt::Key_Excel, QT_TRANSLATE_NOOP("QShortcut", "Spreadsheet") }, + { Qt::Key_Explorer, QT_TRANSLATE_NOOP("QShortcut", "Browser") }, + { Qt::Key_Game, QT_TRANSLATE_NOOP("QShortcut", "Game") }, + { Qt::Key_Go, QT_TRANSLATE_NOOP("QShortcut", "Go") }, + { Qt::Key_iTouch, QT_TRANSLATE_NOOP("QShortcut", "iTouch") }, + { Qt::Key_LogOff, QT_TRANSLATE_NOOP("QShortcut", "Logoff") }, + { Qt::Key_Market, QT_TRANSLATE_NOOP("QShortcut", "Market") }, + { Qt::Key_Meeting, QT_TRANSLATE_NOOP("QShortcut", "Meeting") }, + { Qt::Key_MenuKB, QT_TRANSLATE_NOOP("QShortcut", "Keyboard Menu") }, + { Qt::Key_MenuPB, QT_TRANSLATE_NOOP("QShortcut", "Menu PB") }, + { Qt::Key_MySites, QT_TRANSLATE_NOOP("QShortcut", "My Sites") }, + { Qt::Key_News, QT_TRANSLATE_NOOP("QShortcut", "News") }, + { Qt::Key_OfficeHome, QT_TRANSLATE_NOOP("QShortcut", "Home Office") }, + { Qt::Key_Option, QT_TRANSLATE_NOOP("QShortcut", "Option") }, + { Qt::Key_Paste, QT_TRANSLATE_NOOP("QShortcut", "Paste") }, + { Qt::Key_Phone, QT_TRANSLATE_NOOP("QShortcut", "Phone") }, + { Qt::Key_Reply, QT_TRANSLATE_NOOP("QShortcut", "Reply") }, + { Qt::Key_Reload, QT_TRANSLATE_NOOP("QShortcut", "Reload") }, + { Qt::Key_RotateWindows, QT_TRANSLATE_NOOP("QShortcut", "Rotate Windows") }, + { Qt::Key_RotationPB, QT_TRANSLATE_NOOP("QShortcut", "Rotation PB") }, + { Qt::Key_RotationKB, QT_TRANSLATE_NOOP("QShortcut", "Rotation KB") }, + { Qt::Key_Save, QT_TRANSLATE_NOOP("QShortcut", "Save") }, + { Qt::Key_Send, QT_TRANSLATE_NOOP("QShortcut", "Send") }, + { Qt::Key_Spell, QT_TRANSLATE_NOOP("QShortcut", "Spellchecker") }, + { Qt::Key_SplitScreen, QT_TRANSLATE_NOOP("QShortcut", "Split Screen") }, + { Qt::Key_Support, QT_TRANSLATE_NOOP("QShortcut", "Support") }, + { Qt::Key_TaskPane, QT_TRANSLATE_NOOP("QShortcut", "Task Panel") }, + { Qt::Key_Terminal, QT_TRANSLATE_NOOP("QShortcut", "Terminal") }, + { Qt::Key_Tools, QT_TRANSLATE_NOOP("QShortcut", "Tools") }, + { Qt::Key_Travel, QT_TRANSLATE_NOOP("QShortcut", "Travel") }, + { Qt::Key_Video, QT_TRANSLATE_NOOP("QShortcut", "Video") }, + { Qt::Key_Word, QT_TRANSLATE_NOOP("QShortcut", "Word Processor") }, + { Qt::Key_Xfer, QT_TRANSLATE_NOOP("QShortcut", "XFer") }, + { Qt::Key_ZoomIn, QT_TRANSLATE_NOOP("QShortcut", "Zoom In") }, + { Qt::Key_ZoomOut, QT_TRANSLATE_NOOP("QShortcut", "Zoom Out") }, + { Qt::Key_Away, QT_TRANSLATE_NOOP("QShortcut", "Away") }, + { Qt::Key_Messenger, QT_TRANSLATE_NOOP("QShortcut", "Messenger") }, + { Qt::Key_WebCam, QT_TRANSLATE_NOOP("QShortcut", "WebCam") }, + { Qt::Key_MailForward, QT_TRANSLATE_NOOP("QShortcut", "Mail Forward") }, + { Qt::Key_Pictures, QT_TRANSLATE_NOOP("QShortcut", "Pictures") }, + { Qt::Key_Music, QT_TRANSLATE_NOOP("QShortcut", "Music") }, + { Qt::Key_Battery, QT_TRANSLATE_NOOP("QShortcut", "Battery") }, + { Qt::Key_Bluetooth, QT_TRANSLATE_NOOP("QShortcut", "Bluetooth") }, + { Qt::Key_WLAN, QT_TRANSLATE_NOOP("QShortcut", "Wireless") }, + { Qt::Key_UWB, QT_TRANSLATE_NOOP("QShortcut", "Ultra Wide Band") }, + { Qt::Key_AudioForward, QT_TRANSLATE_NOOP("QShortcut", "Audio Forward") }, + { Qt::Key_AudioRepeat, QT_TRANSLATE_NOOP("QShortcut", "Audio Repeat") }, + { Qt::Key_AudioRandomPlay, QT_TRANSLATE_NOOP("QShortcut", "Audio Random Play") }, + { Qt::Key_Subtitle, QT_TRANSLATE_NOOP("QShortcut", "Subtitle") }, + { Qt::Key_AudioCycleTrack, QT_TRANSLATE_NOOP("QShortcut", "Audio Cycle Track") }, + { Qt::Key_Time, QT_TRANSLATE_NOOP("QShortcut", "Time") }, + { Qt::Key_Select, QT_TRANSLATE_NOOP("QShortcut", "Select") }, + { Qt::Key_View, QT_TRANSLATE_NOOP("QShortcut", "View") }, + { Qt::Key_TopMenu, QT_TRANSLATE_NOOP("QShortcut", "Top Menu") }, + { Qt::Key_Suspend, QT_TRANSLATE_NOOP("QShortcut", "Suspend") }, + { Qt::Key_Hibernate, QT_TRANSLATE_NOOP("QShortcut", "Hibernate") }, + + // -------------------------------------------------------------- + // More consistent namings + { Qt::Key_Print, QT_TRANSLATE_NOOP("QShortcut", "Print Screen") }, + { Qt::Key_PageUp, QT_TRANSLATE_NOOP("QShortcut", "Page Up") }, + { Qt::Key_PageDown, QT_TRANSLATE_NOOP("QShortcut", "Page Down") }, + { Qt::Key_CapsLock, QT_TRANSLATE_NOOP("QShortcut", "Caps Lock") }, + { Qt::Key_NumLock, QT_TRANSLATE_NOOP("QShortcut", "Num Lock") }, + { Qt::Key_NumLock, QT_TRANSLATE_NOOP("QShortcut", "Number Lock") }, + { Qt::Key_ScrollLock, QT_TRANSLATE_NOOP("QShortcut", "Scroll Lock") }, + { Qt::Key_Insert, QT_TRANSLATE_NOOP("QShortcut", "Insert") }, + { Qt::Key_Delete, QT_TRANSLATE_NOOP("QShortcut", "Delete") }, + { Qt::Key_Escape, QT_TRANSLATE_NOOP("QShortcut", "Escape") }, + { Qt::Key_SysReq, QT_TRANSLATE_NOOP("QShortcut", "System Request") }, + + // -------------------------------------------------------------- + // Keypad navigation keys + { Qt::Key_Select, QT_TRANSLATE_NOOP("QShortcut", "Select") }, + { Qt::Key_Yes, QT_TRANSLATE_NOOP("QShortcut", "Yes") }, + { Qt::Key_No, QT_TRANSLATE_NOOP("QShortcut", "No") }, + + // -------------------------------------------------------------- + // Device keys + { Qt::Key_Context1, QT_TRANSLATE_NOOP("QShortcut", "Context1") }, + { Qt::Key_Context2, QT_TRANSLATE_NOOP("QShortcut", "Context2") }, + { Qt::Key_Context3, QT_TRANSLATE_NOOP("QShortcut", "Context3") }, + { Qt::Key_Context4, QT_TRANSLATE_NOOP("QShortcut", "Context4") }, + //: Button to start a call (note: a separate button is used to end the call) + { Qt::Key_Call, QT_TRANSLATE_NOOP("QShortcut", "Call") }, + //: Button to end a call (note: a separate button is used to start the call) + { Qt::Key_Hangup, QT_TRANSLATE_NOOP("QShortcut", "Hangup") }, + //: Button that will hang up if we're in call, or make a call if we're not. + { Qt::Key_ToggleCallHangup, QT_TRANSLATE_NOOP("QShortcut", "Toggle Call/Hangup") }, + { Qt::Key_Flip, QT_TRANSLATE_NOOP("QShortcut", "Flip") }, + //: Button to trigger voice dialing + { Qt::Key_VoiceDial, QT_TRANSLATE_NOOP("QShortcut", "Voice Dial") }, + //: Button to redial the last number called + { Qt::Key_LastNumberRedial, QT_TRANSLATE_NOOP("QShortcut", "Last Number Redial") }, + //: Button to trigger the camera shutter (take a picture) + { Qt::Key_Camera, QT_TRANSLATE_NOOP("QShortcut", "Camera Shutter") }, + //: Button to focus the camera + { Qt::Key_CameraFocus, QT_TRANSLATE_NOOP("QShortcut", "Camera Focus") }, + + // -------------------------------------------------------------- + // Japanese keyboard support + { Qt::Key_Kanji, QT_TRANSLATE_NOOP("QShortcut", "Kanji") }, + { Qt::Key_Muhenkan, QT_TRANSLATE_NOOP("QShortcut", "Muhenkan") }, + { Qt::Key_Henkan, QT_TRANSLATE_NOOP("QShortcut", "Henkan") }, + { Qt::Key_Romaji, QT_TRANSLATE_NOOP("QShortcut", "Romaji") }, + { Qt::Key_Hiragana, QT_TRANSLATE_NOOP("QShortcut", "Hiragana") }, + { Qt::Key_Katakana, QT_TRANSLATE_NOOP("QShortcut", "Katakana") }, + { Qt::Key_Hiragana_Katakana,QT_TRANSLATE_NOOP("QShortcut", "Hiragana Katakana") }, + { Qt::Key_Zenkaku, QT_TRANSLATE_NOOP("QShortcut", "Zenkaku") }, + { Qt::Key_Hankaku, QT_TRANSLATE_NOOP("QShortcut", "Hankaku") }, + { Qt::Key_Zenkaku_Hankaku, QT_TRANSLATE_NOOP("QShortcut", "Zenkaku Hankaku") }, + { Qt::Key_Touroku, QT_TRANSLATE_NOOP("QShortcut", "Touroku") }, + { Qt::Key_Massyo, QT_TRANSLATE_NOOP("QShortcut", "Massyo") }, + { Qt::Key_Kana_Lock, QT_TRANSLATE_NOOP("QShortcut", "Kana Lock") }, + { Qt::Key_Kana_Shift, QT_TRANSLATE_NOOP("QShortcut", "Kana Shift") }, + { Qt::Key_Eisu_Shift, QT_TRANSLATE_NOOP("QShortcut", "Eisu Shift") }, + { Qt::Key_Eisu_toggle, QT_TRANSLATE_NOOP("QShortcut", "Eisu toggle") }, + { Qt::Key_Codeinput, QT_TRANSLATE_NOOP("QShortcut", "Code input") }, + { Qt::Key_MultipleCandidate,QT_TRANSLATE_NOOP("QShortcut", "Multiple Candidate") }, + { Qt::Key_PreviousCandidate,QT_TRANSLATE_NOOP("QShortcut", "Previous Candidate") }, + + // -------------------------------------------------------------- + // Korean keyboard support + { Qt::Key_Hangul, QT_TRANSLATE_NOOP("QShortcut", "Hangul") }, + { Qt::Key_Hangul_Start, QT_TRANSLATE_NOOP("QShortcut", "Hangul Start") }, + { Qt::Key_Hangul_End, QT_TRANSLATE_NOOP("QShortcut", "Hangul End") }, + { Qt::Key_Hangul_Hanja, QT_TRANSLATE_NOOP("QShortcut", "Hangul Hanja") }, + { Qt::Key_Hangul_Jamo, QT_TRANSLATE_NOOP("QShortcut", "Hangul Jamo") }, + { Qt::Key_Hangul_Romaja, QT_TRANSLATE_NOOP("QShortcut", "Hangul Romaja") }, + { Qt::Key_Hangul_Jeonja, QT_TRANSLATE_NOOP("QShortcut", "Hangul Jeonja") }, + { Qt::Key_Hangul_Banja, QT_TRANSLATE_NOOP("QShortcut", "Hangul Banja") }, + { Qt::Key_Hangul_PreHanja, QT_TRANSLATE_NOOP("QShortcut", "Hangul PreHanja") }, + { Qt::Key_Hangul_PostHanja,QT_TRANSLATE_NOOP("QShortcut", "Hangul PostHanja") }, + { Qt::Key_Hangul_Special, QT_TRANSLATE_NOOP("QShortcut", "Hangul Special") }, + + { 0, 0 } +}; + +//Table of key bindings. It must be sorted on key sequence. +//A priority of 1 indicates that this is the primary key binding when multiple are defined. + +const QKeyBinding QKeySequencePrivate::keyBindings[] = { +// StandardKey Priority Key Sequence Platforms + {QKeySequence::Back, 0, Qt::Key_Backspace, QApplicationPrivate::KB_Win}, + {QKeySequence::InsertParagraphSeparator,0, Qt::Key_Return, QApplicationPrivate::KB_All}, + {QKeySequence::InsertParagraphSeparator,0, Qt::Key_Enter, QApplicationPrivate::KB_All}, + {QKeySequence::Delete, 1, Qt::Key_Delete, QApplicationPrivate::KB_All}, + {QKeySequence::MoveToStartOfLine, 0, Qt::Key_Home, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::MoveToStartOfDocument, 0, Qt::Key_Home, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToEndOfLine, 0, Qt::Key_End, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::MoveToEndOfDocument, 0, Qt::Key_End, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToPreviousChar, 0, Qt::Key_Left, QApplicationPrivate::KB_All}, + {QKeySequence::MoveToPreviousLine, 0, Qt::Key_Up, QApplicationPrivate::KB_All}, + {QKeySequence::MoveToNextChar, 0, Qt::Key_Right, QApplicationPrivate::KB_All}, + {QKeySequence::MoveToNextLine, 0, Qt::Key_Down, QApplicationPrivate::KB_All}, + {QKeySequence::MoveToPreviousPage, 1, Qt::Key_PageUp, QApplicationPrivate::KB_All}, + {QKeySequence::MoveToNextPage, 1, Qt::Key_PageDown, QApplicationPrivate::KB_All}, + {QKeySequence::HelpContents, 0, Qt::Key_F1, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, + {QKeySequence::HelpContents, 0, Qt::Key_F2, QApplicationPrivate::KB_S60}, + {QKeySequence::FindNext, 0, Qt::Key_F3, QApplicationPrivate::KB_X11}, + {QKeySequence::FindNext, 1, Qt::Key_F3, QApplicationPrivate::KB_Win}, + {QKeySequence::Refresh, 0, Qt::Key_F5, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, + {QKeySequence::Undo, 0, Qt::Key_F14, QApplicationPrivate::KB_X11}, //Undo on sun keyboards + {QKeySequence::Copy, 0, Qt::Key_F16, QApplicationPrivate::KB_X11}, //Copy on sun keyboards + {QKeySequence::Paste, 0, Qt::Key_F18, QApplicationPrivate::KB_X11}, //Paste on sun keyboards + {QKeySequence::Cut, 0, Qt::Key_F20, QApplicationPrivate::KB_X11}, //Cut on sun keyboards + {QKeySequence::PreviousChild, 0, Qt::Key_Back, QApplicationPrivate::KB_All}, + {QKeySequence::NextChild, 0, Qt::Key_Forward, QApplicationPrivate::KB_All}, + {QKeySequence::Forward, 0, Qt::SHIFT | Qt::Key_Backspace, QApplicationPrivate::KB_Win}, + {QKeySequence::Delete, 0, Qt::SHIFT | Qt::Key_Backspace, QApplicationPrivate::KB_S60}, + {QKeySequence::InsertLineSeparator, 0, Qt::SHIFT | Qt::Key_Return, QApplicationPrivate::KB_All}, + {QKeySequence::InsertLineSeparator, 0, Qt::SHIFT | Qt::Key_Enter, QApplicationPrivate::KB_All}, + {QKeySequence::Paste, 0, Qt::SHIFT | Qt::Key_Insert, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, + {QKeySequence::Cut, 0, Qt::SHIFT | Qt::Key_Delete, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, //## Check if this should work on mac + {QKeySequence::SelectStartOfLine, 0, Qt::SHIFT | Qt::Key_Home, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::SelectStartOfDocument, 0, Qt::SHIFT | Qt::Key_Home, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectEndOfLine, 0, Qt::SHIFT | Qt::Key_End, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::SelectEndOfDocument, 0, Qt::SHIFT | Qt::Key_End, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectPreviousChar, 0, Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_All}, + {QKeySequence::SelectPreviousLine, 0, Qt::SHIFT | Qt::Key_Up, QApplicationPrivate::KB_All}, + {QKeySequence::SelectNextChar, 0, Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_All}, + {QKeySequence::SelectNextLine, 0, Qt::SHIFT | Qt::Key_Down, QApplicationPrivate::KB_All}, + {QKeySequence::SelectPreviousPage, 0, Qt::SHIFT | Qt::Key_PageUp, QApplicationPrivate::KB_All}, + {QKeySequence::SelectNextPage, 0, Qt::SHIFT | Qt::Key_PageDown, QApplicationPrivate::KB_All}, + {QKeySequence::WhatsThis, 1, Qt::SHIFT | Qt::Key_F1, QApplicationPrivate::KB_All}, + {QKeySequence::FindPrevious, 0, Qt::SHIFT | Qt::Key_F3, QApplicationPrivate::KB_X11}, + {QKeySequence::FindPrevious, 1, Qt::SHIFT | Qt::Key_F3, QApplicationPrivate::KB_Win}, + {QKeySequence::ZoomIn, 1, Qt::CTRL | Qt::Key_Plus, QApplicationPrivate::KB_All}, + {QKeySequence::NextChild, 0, Qt::CTRL | Qt::Key_Comma, QApplicationPrivate::KB_KDE}, + {QKeySequence::Preferences, 0, Qt::CTRL | Qt::Key_Comma, QApplicationPrivate::KB_Mac}, + {QKeySequence::ZoomOut, 1, Qt::CTRL | Qt::Key_Minus, QApplicationPrivate::KB_All}, + {QKeySequence::PreviousChild, 0, Qt::CTRL | Qt::Key_Period, QApplicationPrivate::KB_KDE}, + {QKeySequence::HelpContents, 1, Qt::CTRL | Qt::Key_Question, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectAll, 1, Qt::CTRL | Qt::Key_A, QApplicationPrivate::KB_All}, + {QKeySequence::Bold, 1, Qt::CTRL | Qt::Key_B, QApplicationPrivate::KB_All}, + {QKeySequence::Copy, 1, Qt::CTRL | Qt::Key_C, QApplicationPrivate::KB_All}, + {QKeySequence::Delete, 0, Qt::CTRL | Qt::Key_D, QApplicationPrivate::KB_X11}, //emacs (line edit only) + {QKeySequence::Find, 0, Qt::CTRL | Qt::Key_F, QApplicationPrivate::KB_All}, + {QKeySequence::FindNext, 1, Qt::CTRL | Qt::Key_G, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac}, + {QKeySequence::FindNext, 0, Qt::CTRL | Qt::Key_G, QApplicationPrivate::KB_Win}, + {QKeySequence::Replace, 0, Qt::CTRL | Qt::Key_H, QApplicationPrivate::KB_Win}, + {QKeySequence::Replace, 0, Qt::CTRL | Qt::Key_H, QApplicationPrivate::KB_Gnome}, + {QKeySequence::Italic, 0, Qt::CTRL | Qt::Key_I, QApplicationPrivate::KB_All}, + {QKeySequence::DeleteEndOfLine, 0, Qt::CTRL | Qt::Key_K, QApplicationPrivate::KB_X11}, //emacs (line edit only) + {QKeySequence::New, 1, Qt::CTRL | Qt::Key_N, QApplicationPrivate::KB_All}, + {QKeySequence::Open, 1, Qt::CTRL | Qt::Key_O, QApplicationPrivate::KB_All}, + {QKeySequence::Print, 1, Qt::CTRL | Qt::Key_P, QApplicationPrivate::KB_All}, + {QKeySequence::Quit, 0, Qt::CTRL | Qt::Key_Q, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_KDE | QApplicationPrivate::KB_Mac}, + {QKeySequence::Refresh, 1, Qt::CTRL | Qt::Key_R, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac}, + {QKeySequence::Replace, 0, Qt::CTRL | Qt::Key_R, QApplicationPrivate::KB_KDE}, + {QKeySequence::Save, 1, Qt::CTRL | Qt::Key_S, QApplicationPrivate::KB_All}, + {QKeySequence::AddTab, 0, Qt::CTRL | Qt::Key_T, QApplicationPrivate::KB_All}, + {QKeySequence::Underline, 1, Qt::CTRL | Qt::Key_U, QApplicationPrivate::KB_All}, + {QKeySequence::Paste, 1, Qt::CTRL | Qt::Key_V, QApplicationPrivate::KB_All}, + {QKeySequence::Close, 0, Qt::CTRL | Qt::Key_W, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, + {QKeySequence::Close, 1, Qt::CTRL | Qt::Key_W, QApplicationPrivate::KB_Mac}, + {QKeySequence::Cut, 1, Qt::CTRL | Qt::Key_X, QApplicationPrivate::KB_All}, + {QKeySequence::Redo, 1, Qt::CTRL | Qt::Key_Y, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_S60}, + {QKeySequence::Undo, 1, Qt::CTRL | Qt::Key_Z, QApplicationPrivate::KB_All}, + {QKeySequence::Back, 1, Qt::CTRL | Qt::Key_BracketLeft, QApplicationPrivate::KB_Mac}, + {QKeySequence::Forward, 1, Qt::CTRL | Qt::Key_BracketRight, QApplicationPrivate::KB_Mac}, + {QKeySequence::PreviousChild, 1, Qt::CTRL | Qt::Key_BraceLeft, QApplicationPrivate::KB_Mac}, + {QKeySequence::NextChild, 1, Qt::CTRL | Qt::Key_BraceRight, QApplicationPrivate::KB_Mac}, + {QKeySequence::NextChild, 1, Qt::CTRL | Qt::Key_Tab, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, + {QKeySequence::NextChild, 0, Qt::CTRL | Qt::Key_Tab, QApplicationPrivate::KB_Mac}, //different priority from above + {QKeySequence::DeleteStartOfWord, 0, Qt::CTRL | Qt::Key_Backspace, QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_Win}, + {QKeySequence::Copy, 0, Qt::CTRL | Qt::Key_Insert, QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_Win}, + {QKeySequence::DeleteEndOfWord, 0, Qt::CTRL | Qt::Key_Delete, QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_Win}, + {QKeySequence::MoveToStartOfDocument, 0, Qt::CTRL | Qt::Key_Home, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::MoveToEndOfDocument, 0, Qt::CTRL | Qt::Key_End, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::Back, 0, Qt::CTRL | Qt::Key_Left, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToPreviousWord, 0, Qt::CTRL | Qt::Key_Left, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::MoveToStartOfLine, 0, Qt::CTRL | Qt::Key_Left, QApplicationPrivate::KB_Mac }, + {QKeySequence::MoveToStartOfDocument, 1, Qt::CTRL | Qt::Key_Up, QApplicationPrivate::KB_Mac}, + {QKeySequence::Forward, 0, Qt::CTRL | Qt::Key_Right, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToEndOfLine, 0, Qt::CTRL | Qt::Key_Right, QApplicationPrivate::KB_Mac }, + {QKeySequence::MoveToNextWord, 0, Qt::CTRL | Qt::Key_Right, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::MoveToEndOfDocument, 1, Qt::CTRL | Qt::Key_Down, QApplicationPrivate::KB_Mac}, + {QKeySequence::Close, 1, Qt::CTRL | Qt::Key_F4, QApplicationPrivate::KB_Win}, + {QKeySequence::Close, 0, Qt::CTRL | Qt::Key_F4, QApplicationPrivate::KB_Mac}, + {QKeySequence::NextChild, 0, Qt::CTRL | Qt::Key_F6, QApplicationPrivate::KB_Win}, + {QKeySequence::FindPrevious, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_G, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac}, + {QKeySequence::FindPrevious, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_G, QApplicationPrivate::KB_Win}, + {QKeySequence::AddTab, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_N, QApplicationPrivate::KB_KDE}, + {QKeySequence::SaveAs, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_S, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac}, + {QKeySequence::Redo, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Z, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::Redo, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Z, QApplicationPrivate::KB_Mac}, + {QKeySequence::PreviousChild, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Backtab, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, + {QKeySequence::PreviousChild, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Backtab, QApplicationPrivate::KB_Mac },//different priority from above + {QKeySequence::Paste, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Insert, QApplicationPrivate::KB_X11}, + {QKeySequence::SelectStartOfDocument, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Home, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::SelectEndOfDocument, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_End, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::SelectPreviousWord, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::SelectStartOfLine, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_Mac }, + {QKeySequence::SelectStartOfDocument, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Up, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectNextWord, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, + {QKeySequence::SelectEndOfLine, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_Mac }, + {QKeySequence::SelectEndOfDocument, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Down, QApplicationPrivate::KB_Mac}, + {QKeySequence::PreviousChild, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_F6, QApplicationPrivate::KB_Win}, + {QKeySequence::Undo, 0, Qt::ALT | Qt::Key_Backspace, QApplicationPrivate::KB_Win}, + {QKeySequence::DeleteStartOfWord, 0, Qt::ALT | Qt::Key_Backspace, QApplicationPrivate::KB_Mac}, + {QKeySequence::DeleteEndOfWord, 0, Qt::ALT | Qt::Key_Delete, QApplicationPrivate::KB_Mac}, + {QKeySequence::Back, 1, Qt::ALT | Qt::Key_Left, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, + {QKeySequence::MoveToPreviousWord, 0, Qt::ALT | Qt::Key_Left, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToStartOfBlock, 0, Qt::ALT | Qt::Key_Up, QApplicationPrivate::KB_Mac}, //mac only + {QKeySequence::MoveToNextWord, 0, Qt::ALT | Qt::Key_Right, QApplicationPrivate::KB_Mac}, + {QKeySequence::Forward, 1, Qt::ALT | Qt::Key_Right, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, + {QKeySequence::MoveToEndOfBlock, 0, Qt::ALT | Qt::Key_Down, QApplicationPrivate::KB_Mac}, //mac only + {QKeySequence::MoveToPreviousPage, 0, Qt::ALT | Qt::Key_PageUp, QApplicationPrivate::KB_Mac }, + {QKeySequence::MoveToNextPage, 0, Qt::ALT | Qt::Key_PageDown, QApplicationPrivate::KB_Mac }, + {QKeySequence::Redo, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Backspace,QApplicationPrivate::KB_Win}, + {QKeySequence::SelectPreviousWord, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectStartOfBlock, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Up, QApplicationPrivate::KB_Mac}, //mac only + {QKeySequence::SelectNextWord, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectEndOfBlock, 0, Qt::ALT | Qt::SHIFT | Qt::Key_Down, QApplicationPrivate::KB_Mac}, //mac only + {QKeySequence::MoveToStartOfBlock, 0, Qt::META | Qt::Key_A, QApplicationPrivate::KB_Mac}, + {QKeySequence::Delete, 0, Qt::META | Qt::Key_D, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToEndOfBlock, 0, Qt::META | Qt::Key_E, QApplicationPrivate::KB_Mac}, + {QKeySequence::InsertLineSeparator, 0, Qt::META | Qt::Key_Return, QApplicationPrivate::KB_Mac}, + {QKeySequence::InsertLineSeparator, 0, Qt::META | Qt::Key_Enter, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToStartOfLine, 0, Qt::META | Qt::Key_Left, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToPreviousPage, 0, Qt::META | Qt::Key_Up, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToEndOfLine, 0, Qt::META | Qt::Key_Right, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToNextPage, 0, Qt::META | Qt::Key_Down, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToPreviousPage, 0, Qt::META | Qt::Key_PageUp, QApplicationPrivate::KB_Mac}, + {QKeySequence::MoveToNextPage, 0, Qt::META | Qt::Key_PageDown, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectStartOfBlock, 0, Qt::META | Qt::SHIFT | Qt::Key_A, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectEndOfBlock, 0, Qt::META | Qt::SHIFT | Qt::Key_E, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectStartOfLine, 0, Qt::META | Qt::SHIFT | Qt::Key_Left, QApplicationPrivate::KB_Mac}, + {QKeySequence::SelectEndOfLine, 0, Qt::META | Qt::SHIFT | Qt::Key_Right, QApplicationPrivate::KB_Mac} +}; + +const uint QKeySequencePrivate::numberOfKeyBindings = sizeof(QKeySequencePrivate::keyBindings)/(sizeof(QKeyBinding)); + + +/*! + \enum QKeySequence::StandardKey + \since 4.2 + + This enum represent standard key bindings. They can be used to + assign platform dependent keyboard shortcuts to a QAction. + + Note that the key bindings are platform dependent. The currently + bound shortcuts can be queried using keyBindings(). + + \value AddTab Add new tab. + \value Back Navigate back. + \value Bold Bold text. + \value Close Close document/tab. + \value Copy Copy. + \value Cut Cut. + \value Delete Delete. + \value DeleteEndOfLine Delete end of line. + \value DeleteEndOfWord Delete word from the end of the cursor. + \value DeleteStartOfWord Delete the beginning of a word up to the cursor. + \value Find Find in document. + \value FindNext Find next result. + \value FindPrevious Find previous result. + \value Forward Navigate forward. + \value HelpContents Open help contents. + \value InsertLineSeparator Insert a new line. + \value InsertParagraphSeparator Insert a new paragraph. + \value Italic Italic text. + \value MoveToEndOfBlock Move cursor to end of block. This shortcut is only used on the OS X. + \value MoveToEndOfDocument Move cursor to end of document. + \value MoveToEndOfLine Move cursor to end of line. + \value MoveToNextChar Move cursor to next character. + \value MoveToNextLine Move cursor to next line. + \value MoveToNextPage Move cursor to next page. + \value MoveToNextWord Move cursor to next word. + \value MoveToPreviousChar Move cursor to previous character. + \value MoveToPreviousLine Move cursor to previous line. + \value MoveToPreviousPage Move cursor to previous page. + \value MoveToPreviousWord Move cursor to previous word. + \value MoveToStartOfBlock Move cursor to start of a block. This shortcut is only used on OS X. + \value MoveToStartOfDocument Move cursor to start of document. + \value MoveToStartOfLine Move cursor to start of line. + \value New Create new document. + \value NextChild Navigate to next tab or child window. + \value Open Open document. + \value Paste Paste. + \value Preferences Open the preferences dialog. + \value PreviousChild Navigate to previous tab or child window. + \value Print Print document. + \value Quit Quit the application. + \value Redo Redo. + \value Refresh Refresh or reload current document. + \value Replace Find and replace. + \value SaveAs Save document after prompting the user for a file name. + \value Save Save document. + \value SelectAll Select all text. + \value SelectEndOfBlock Extend selection to the end of a text block. This shortcut is only used on OS X. + \value SelectEndOfDocument Extend selection to end of document. + \value SelectEndOfLine Extend selection to end of line. + \value SelectNextChar Extend selection to next character. + \value SelectNextLine Extend selection to next line. + \value SelectNextPage Extend selection to next page. + \value SelectNextWord Extend selection to next word. + \value SelectPreviousChar Extend selection to previous character. + \value SelectPreviousLine Extend selection to previous line. + \value SelectPreviousPage Extend selection to previous page. + \value SelectPreviousWord Extend selection to previous word. + \value SelectStartOfBlock Extend selection to the start of a text block. This shortcut is only used on OS X. + \value SelectStartOfDocument Extend selection to start of document. + \value SelectStartOfLine Extend selection to start of line. + \value Underline Underline text. + \value Undo Undo. + \value UnknownKey Unbound key. + \value WhatsThis Activate whats this. + \value ZoomIn Zoom in. + \value ZoomOut Zoom out. +*/ + +/*! + \since 4.2 + + Constructs a QKeySequence object for the given \a key. + The result will depend on the currently running platform. + + The resulting object will be based on the first element in the + list of key bindings for the \a key. +*/ +QKeySequence::QKeySequence(StandardKey key) +{ + const QList <QKeySequence> bindings = keyBindings(key); + //pick only the first/primary shortcut from current bindings + if (bindings.size() > 0) { + d = bindings.first().d; + d->ref.ref(); + } + else + d = new QKeySequencePrivate(); +} + + +/*! + Constructs an empty key sequence. +*/ +QKeySequence::QKeySequence() +{ + static QKeySequencePrivate shared_empty; + d = &shared_empty; + d->ref.ref(); +} + +/*! + Creates a key sequence from the \a key string. For example + "Ctrl+O" gives CTRL+'O'. The strings "Ctrl", + "Shift", "Alt" and "Meta" are recognized, as well as their + translated equivalents in the "QShortcut" context (using + QObject::tr()). + + Up to four key codes may be entered by separating them with + commas, e.g. "Alt+X,Ctrl+S,Q". + + \a key should be in NativeText format. + + This constructor is typically used with \link QObject::tr() tr + \endlink(), so that shortcut keys can be replaced in + translations: + + \snippet doc/src/snippets/code/src_gui_kernel_qkeysequence.cpp 2 + + Note the "File|Open" translator comment. It is by no means + necessary, but it provides some context for the human translator. +*/ +QKeySequence::QKeySequence(const QString &key) +{ + d = new QKeySequencePrivate(); + assign(key); +} + +/*! + \since 4.x + Creates a key sequence from the \a key string based on \a format. +*/ +QKeySequence::QKeySequence(const QString &key, QKeySequence::SequenceFormat format) +{ + d = new QKeySequencePrivate(); + assign(key, format); +} + +/*! + Constructs a key sequence with up to 4 keys \a k1, \a k2, + \a k3 and \a k4. + + The key codes are listed in Qt::Key and can be combined with + modifiers (see Qt::Modifier) such as Qt::SHIFT, Qt::CTRL, + Qt::ALT, or Qt::META. +*/ +QKeySequence::QKeySequence(int k1, int k2, int k3, int k4) +{ + d = new QKeySequencePrivate(); + d->key[0] = k1; + d->key[1] = k2; + d->key[2] = k3; + d->key[3] = k4; +} + +/*! + Copy constructor. Makes a copy of \a keysequence. + */ +QKeySequence::QKeySequence(const QKeySequence& keysequence) + : d(keysequence.d) +{ + d->ref.ref(); +} + +#ifdef Q_WS_MAC +static inline int maybeSwapShortcut(int shortcut) +{ + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + uint oldshortcut = shortcut; + shortcut &= ~(Qt::CTRL | Qt::META); + if (oldshortcut & Qt::CTRL) + shortcut |= Qt::META; + if (oldshortcut & Qt::META) + shortcut |= Qt::CTRL; + } + return shortcut; +} +#endif + +/*! + \since 4.2 + + Returns a list of key bindings for the given \a key. + The result of calling this function will vary based on the target platform. + The first element of the list indicates the primary shortcut for the given platform. + If the result contains more than one result, these can + be considered alternative shortcuts on the same platform for the given \a key. +*/ +QList<QKeySequence> QKeySequence::keyBindings(StandardKey key) +{ + uint platform = QApplicationPrivate::currentPlatform(); + QList <QKeySequence> list; + for (uint i = 0; i < QKeySequencePrivate::numberOfKeyBindings ; ++i) { + QKeyBinding keyBinding = QKeySequencePrivate::keyBindings[i]; + if (keyBinding.standardKey == key && (keyBinding.platform & platform)) { + uint shortcut = +#ifdef Q_WS_MAC + maybeSwapShortcut(QKeySequencePrivate::keyBindings[i].shortcut); +#else + QKeySequencePrivate::keyBindings[i].shortcut; +#endif + if (keyBinding.priority > 0) + list.prepend(QKeySequence(shortcut)); + else + list.append(QKeySequence(shortcut)); + } + } + return list; +} + +/*! + Destroys the key sequence. + */ +QKeySequence::~QKeySequence() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + \internal + KeySequences should never be modified, but rather just created. + Internally though we do need to modify to keep pace in event + delivery. +*/ + +void QKeySequence::setKey(int key, int index) +{ + Q_ASSERT_X(index >= 0 && index < 4, "QKeySequence::setKey", "index out of range"); + qAtomicDetach(d); + d->key[index] = key; +} + +/*! + Returns the number of keys in the key sequence. + The maximum is 4. + */ +uint QKeySequence::count() const +{ + if (!d->key[0]) + return 0; + if (!d->key[1]) + return 1; + if (!d->key[2]) + return 2; + if (!d->key[3]) + return 3; + return 4; +} + + +/*! + Returns true if the key sequence is empty; otherwise returns + false. +*/ +bool QKeySequence::isEmpty() const +{ + return !d->key[0]; +} + + +/*! + Returns the shortcut key sequence for the mnemonic in \a text, + or an empty key sequence if no mnemonics are found. + + For example, mnemonic("E&xit") returns \c{Qt::ALT+Qt::Key_X}, + mnemonic("&Quit") returns \c{ALT+Key_Q}, and mnemonic("Quit") + returns an empty QKeySequence. + + We provide a \l{accelerators.html}{list of common mnemonics} + in English. At the time of writing, Microsoft and Open Group do + not appear to have issued equivalent recommendations for other + languages. + + \sa qt_set_sequence_auto_mnemonic() +*/ +QKeySequence QKeySequence::mnemonic(const QString &text) +{ + QKeySequence ret; + + if(qt_sequence_no_mnemonics) + return ret; + + bool found = false; + int p = 0; + while (p >= 0) { + p = text.indexOf(QLatin1Char('&'), p) + 1; + if (p <= 0 || p >= (int)text.length()) + break; + if (text.at(p) != QLatin1Char('&')) { + QChar c = text.at(p); + if (c.isPrint()) { + if (!found) { + c = c.toUpper(); + ret = QKeySequence(c.unicode() + Qt::ALT); +#ifdef QT_NO_DEBUG + return ret; +#else + found = true; + } else { + qWarning("QKeySequence::mnemonic: \"%s\" contains multiple occurrences of '&'", qPrintable(text)); +#endif + } + } + } + p++; + } + return ret; +} + +/*! + \fn int QKeySequence::assign(const QString &keys) + + Adds the given \a keys to the key sequence. \a keys may + contain up to four key codes, provided they are separated by a + comma; for example, "Alt+X,Ctrl+S,Z". The return value is the + number of key codes added. + \a keys should be in NativeText format. +*/ +int QKeySequence::assign(const QString &ks) +{ + return assign(ks, NativeText); +} + +/*! + \fn int QKeySequence::assign(const QString &keys, QKeySequence::SequenceFormat format) + \since 4.x + + Adds the given \a keys to the key sequence (based on \a format). + \a keys may contain up to four key codes, provided they are + separated by a comma; for example, "Alt+X,Ctrl+S,Z". The return + value is the number of key codes added. +*/ +int QKeySequence::assign(const QString &ks, QKeySequence::SequenceFormat format) +{ + QString keyseq = ks; + QString part; + int n = 0; + int p = 0, diff = 0; + + // Run through the whole string, but stop + // if we have 4 keys before the end. + while (keyseq.length() && n < 4) { + // We MUST use something to separate each sequence, and space + // does not cut it, since some of the key names have space + // in them.. (Let's hope no one translate with a comma in it:) + p = keyseq.indexOf(QLatin1Char(',')); + if (-1 != p) { + if (p == keyseq.count() - 1) { // Last comma 'Ctrl+,' + p = -1; + } else { + if (QLatin1Char(',') == keyseq.at(p+1)) // e.g. 'Ctrl+,, Shift+,,' + p++; + if (QLatin1Char(' ') == keyseq.at(p+1)) { // Space after comma + diff = 1; + p++; + } else { + diff = 0; + } + } + } + part = keyseq.left(-1 == p ? keyseq.length() : p - diff); + keyseq = keyseq.right(-1 == p ? 0 : keyseq.length() - (p + 1)); + d->key[n] = QKeySequencePrivate::decodeString(part, format); + ++n; + } + return n; +} + +struct QModifKeyName { + QModifKeyName() { } + QModifKeyName(int q, QChar n) : qt_key(q), name(n) { } + QModifKeyName(int q, const QString &n) : qt_key(q), name(n) { } + int qt_key; + QString name; +}; + +Q_GLOBAL_STATIC(QList<QModifKeyName>, globalModifs) +Q_GLOBAL_STATIC(QList<QModifKeyName>, globalPortableModifs) + +/*! + Constructs a single key from the string \a str. +*/ +int QKeySequence::decodeString(const QString &str) +{ + return QKeySequencePrivate::decodeString(str, NativeText); +} + +int QKeySequencePrivate::decodeString(const QString &str, QKeySequence::SequenceFormat format) +{ + int ret = 0; + QString accel = str.toLower(); + bool nativeText = (format == QKeySequence::NativeText); + + QList<QModifKeyName> *gmodifs; + if (nativeText) { + gmodifs = globalModifs(); + if (gmodifs->isEmpty()) { +#ifdef Q_WS_MAC + const bool dontSwap = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta); + if (dontSwap) + *gmodifs << QModifKeyName(Qt::META, QChar(kCommandUnicode)); + else + *gmodifs << QModifKeyName(Qt::CTRL, QChar(kCommandUnicode)); + *gmodifs << QModifKeyName(Qt::ALT, QChar(kOptionUnicode)); + if (dontSwap) + *gmodifs << QModifKeyName(Qt::CTRL, QChar(kControlUnicode)); + else + *gmodifs << QModifKeyName(Qt::META, QChar(kControlUnicode)); + *gmodifs << QModifKeyName(Qt::SHIFT, QChar(kShiftUnicode)); +#endif + *gmodifs << QModifKeyName(Qt::CTRL, QLatin1String("ctrl+")) + << QModifKeyName(Qt::SHIFT, QLatin1String("shift+")) + << QModifKeyName(Qt::ALT, QLatin1String("alt+")) + << QModifKeyName(Qt::META, QLatin1String("meta+")); + } + } else { + gmodifs = globalPortableModifs(); + if (gmodifs->isEmpty()) { + *gmodifs << QModifKeyName(Qt::CTRL, QLatin1String("ctrl+")) + << QModifKeyName(Qt::SHIFT, QLatin1String("shift+")) + << QModifKeyName(Qt::ALT, QLatin1String("alt+")) + << QModifKeyName(Qt::META, QLatin1String("meta+")); + } + } + if (!gmodifs) return ret; + + + QList<QModifKeyName> modifs; + if (nativeText) { + modifs << QModifKeyName(Qt::CTRL, QShortcut::tr("Ctrl").toLower().append(QLatin1Char('+'))) + << QModifKeyName(Qt::SHIFT, QShortcut::tr("Shift").toLower().append(QLatin1Char('+'))) + << QModifKeyName(Qt::ALT, QShortcut::tr("Alt").toLower().append(QLatin1Char('+'))) + << QModifKeyName(Qt::META, QShortcut::tr("Meta").toLower().append(QLatin1Char('+'))); + } + modifs += *gmodifs; // Test non-translated ones last + + QString sl = accel; +#ifdef Q_WS_MAC + for (int i = 0; i < modifs.size(); ++i) { + const QModifKeyName &mkf = modifs.at(i); + if (sl.contains(mkf.name)) { + ret |= mkf.qt_key; + accel.remove(mkf.name); + sl = accel; + } + } +#else + int i = 0; + int lastI = 0; + while ((i = sl.indexOf(QLatin1Char('+'), i + 1)) != -1) { + const QString sub = sl.mid(lastI, i - lastI + 1); + // Just shortcut the check here if we only have one character. + // Rational: A modifier will contain the name AND +, so longer than 1, a length of 1 is just + // the remaining part of the shortcut (ei. The 'C' in "Ctrl+C"), so no need to check that. + if (sub.length() > 1) { + for (int j = 0; j < modifs.size(); ++j) { + const QModifKeyName &mkf = modifs.at(j); + if (sub == mkf.name) { + ret |= mkf.qt_key; + break; // Shortcut, since if we find an other it would/should just be a dup + } + } + } + lastI = i + 1; + } +#endif + + int p = accel.lastIndexOf(QLatin1Char('+'), str.length() - 2); // -2 so that Ctrl++ works + if(p > 0) + accel = accel.mid(p + 1); + + int fnum = 0; + if (accel.length() == 1) { +#ifdef Q_WS_MAC + int qtKey = qtkeyForMacSymbol(accel[0]); + if (qtKey != -1) { + ret |= qtKey; + } else +#endif + { + ret |= accel[0].toUpper().unicode(); + } + } else if (accel[0] == QLatin1Char('f') && (fnum = accel.mid(1).toInt()) && (fnum >= 1) && (fnum <= 35)) { + ret |= Qt::Key_F1 + fnum - 1; + } else { + // For NativeText, check the traslation table first, + // if we don't find anything then try it out with just the untranlated stuff. + // PortableText will only try the untranlated table. + bool found = false; + for (int tran = 0; tran < 2; ++tran) { + if (!nativeText) + ++tran; + for (int i = 0; keyname[i].name; ++i) { + QString keyName(tran == 0 + ? QShortcut::tr(keyname[i].name) + : QString::fromLatin1(keyname[i].name)); + if (accel == keyName.toLower()) { + ret |= keyname[i].key; + found = true; + break; + } + } + if (found) + break; + } + } + return ret; +} + +/*! + Creates a shortcut string for \a key. For example, + Qt::CTRL+Qt::Key_O gives "Ctrl+O". The strings, "Ctrl", "Shift", etc. are + translated (using QObject::tr()) in the "QShortcut" context. + */ +QString QKeySequence::encodeString(int key) +{ + return QKeySequencePrivate::encodeString(key, NativeText); +} + +static inline void addKey(QString &str, const QString &theKey, QKeySequence::SequenceFormat format) +{ + if (!str.isEmpty()) + str += (format == QKeySequence::NativeText) ? QShortcut::tr("+") + : QString::fromLatin1("+"); + str += theKey; +} + +QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat format) +{ + bool nativeText = (format == QKeySequence::NativeText); + QString s; +#if defined(Q_WS_MAC) + if (nativeText) { + // On Mac OS X the order (by default) is Meta, Alt, Shift, Control. + // If the AA_MacDontSwapCtrlAndMeta is enabled, then the order + // is Ctrl, Alt, Shift, Meta. The macSymbolForQtKey does this swap + // for us, which means that we have to adjust our order here. + // The upshot is a lot more infrastructure to keep the number of + // if tests down and the code relatively clean. + static const int ModifierOrder[] = { Qt::META, Qt::ALT, Qt::SHIFT, Qt::CTRL, 0 }; + static const int QtKeyOrder[] = { Qt::Key_Meta, Qt::Key_Alt, Qt::Key_Shift, Qt::Key_Control, 0 }; + static const int DontSwapModifierOrder[] = { Qt::CTRL, Qt::ALT, Qt::SHIFT, Qt::META, 0 }; + static const int DontSwapQtKeyOrder[] = { Qt::Key_Control, Qt::Key_Alt, Qt::Key_Shift, Qt::Key_Meta, 0 }; + const int *modifierOrder; + const int *qtkeyOrder; + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + modifierOrder = DontSwapModifierOrder; + qtkeyOrder = DontSwapQtKeyOrder; + } else { + modifierOrder = ModifierOrder; + qtkeyOrder = QtKeyOrder; + } + + for (int i = 0; modifierOrder[i] != 0; ++i) { + if (key & modifierOrder[i]) + s += qt_macSymbolForQtKey(qtkeyOrder[i]); + } + } else +#endif + { + // On other systems the order is Meta, Control, Alt, Shift + if ((key & Qt::META) == Qt::META) + s = nativeText ? QShortcut::tr("Meta") : QString::fromLatin1("Meta"); + if ((key & Qt::CTRL) == Qt::CTRL) + addKey(s, nativeText ? QShortcut::tr("Ctrl") : QString::fromLatin1("Ctrl"), format); + if ((key & Qt::ALT) == Qt::ALT) + addKey(s, nativeText ? QShortcut::tr("Alt") : QString::fromLatin1("Alt"), format); + if ((key & Qt::SHIFT) == Qt::SHIFT) + addKey(s, nativeText ? QShortcut::tr("Shift") : QString::fromLatin1("Shift"), format); + } + + + key &= ~(Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier); + QString p; + + if (key && key < Qt::Key_Escape && key != Qt::Key_Space) { + if (key < 0x10000) { + p = QChar(key & 0xffff).toUpper(); + } else { + p = QChar((key-0x10000)/0x400+0xd800); + p += QChar((key-0x10000)%400+0xdc00); + } + } else if (key >= Qt::Key_F1 && key <= Qt::Key_F35) { + p = nativeText ? QShortcut::tr("F%1").arg(key - Qt::Key_F1 + 1) + : QString::fromLatin1("F%1").arg(key - Qt::Key_F1 + 1); + } else if (key) { + int i=0; +#if defined(Q_WS_MAC) + if (nativeText) { + QChar ch = qt_macSymbolForQtKey(key); + if (!ch.isNull()) + p = ch; + else + goto NonSymbol; + } else +#endif + { +#ifdef Q_WS_MAC +NonSymbol: +#endif + while (keyname[i].name) { + if (key == keyname[i].key) { + p = nativeText ? QShortcut::tr(keyname[i].name) + : QString::fromLatin1(keyname[i].name); + break; + } + ++i; + } + // If we can't find the actual translatable keyname, + // fall back on the unicode representation of it... + // Or else characters like Qt::Key_aring may not get displayed + // (Really depends on you locale) + if (!keyname[i].name) { + if (key < 0x10000) { + p = QChar(key & 0xffff).toUpper(); + } else { + p = QChar((key-0x10000)/0x400+0xd800); + p += QChar((key-0x10000)%400+0xdc00); + } + } + } + } + +#ifdef Q_WS_MAC + if (nativeText) + s += p; + else +#endif + addKey(s, p, format); + return s; +} +/*! + Matches the sequence with \a seq. Returns ExactMatch if + successful, PartialMatch if \a seq matches incompletely, + and NoMatch if the sequences have nothing in common. + Returns NoMatch if \a seq is shorter. +*/ +QKeySequence::SequenceMatch QKeySequence::matches(const QKeySequence &seq) const +{ + uint userN = count(), + seqN = seq.count(); + + if (userN > seqN) + return NoMatch; + + // If equal in length, we have a potential ExactMatch sequence, + // else we already know it can only be partial. + SequenceMatch match = (userN == seqN ? ExactMatch : PartialMatch); + + for (uint i = 0; i < userN; ++i) { + int userKey = (*this)[i], + sequenceKey = seq[i]; + if (userKey != sequenceKey) + return NoMatch; + } + return match; +} + + +/*! + \obsolete + + Use toString() instead. + + Returns the key sequence as a QString. This is equivalent to + calling toString(QKeySequence::NativeText). Note that the + result is not platform independent. +*/ +QKeySequence::operator QString() const +{ + return QKeySequence::toString(QKeySequence::NativeText); +} + +/*! + Returns the key sequence as a QVariant +*/ +QKeySequence::operator QVariant() const +{ + return QVariant(QVariant::KeySequence, this); +} + +/*! + \obsolete + For backward compatibility: returns the first keycode + as integer. If the key sequence is empty, 0 is returned. + */ +QKeySequence::operator int () const +{ + if (1 <= count()) + return d->key[0]; + return 0; +} + + +/*! + Returns a reference to the element at position \a index in the key + sequence. This can only be used to read an element. + */ +int QKeySequence::operator[](uint index) const +{ + Q_ASSERT_X(index < 4, "QKeySequence::operator[]", "index out of range"); + return d->key[index]; +} + + +/*! + Assignment operator. Assigns the \a other key sequence to this + object. + */ +QKeySequence &QKeySequence::operator=(const QKeySequence &other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + \fn void QKeySequence::swap(QKeySequence &other) + \since 4.8 + + Swaps key sequence \a other with this key sequence. This operation is very + fast and never fails. +*/ + +/*! + \fn bool QKeySequence::operator!=(const QKeySequence &other) const + + Returns true if this key sequence is not equal to the \a other + key sequence; otherwise returns false. +*/ + + +/*! + Returns true if this key sequence is equal to the \a other + key sequence; otherwise returns false. + */ +bool QKeySequence::operator==(const QKeySequence &other) const +{ + return (d->key[0] == other.d->key[0] && + d->key[1] == other.d->key[1] && + d->key[2] == other.d->key[2] && + d->key[3] == other.d->key[3]); +} + + +/*! + Provides an arbitrary comparison of this key sequence and + \a other key sequence. All that is guaranteed is that the + operator returns false if both key sequences are equal and + that (ks1 \< ks2) == !( ks2 \< ks1) if the key sequences + are not equal. + + This function is useful in some circumstances, for example + if you want to use QKeySequence objects as keys in a QMap. + + \sa operator==() operator!=() operator>() operator<=() operator>=() +*/ +bool QKeySequence::operator< (const QKeySequence &other) const +{ + for (int i = 0; i < 4; ++i) + if (d->key[i] != other.d->key[i]) + return d->key[i] < other.d->key[i]; + return false; +} + +/*! + \fn bool QKeySequence::operator> (const QKeySequence &other) const + + Returns true if this key sequence is larger than the \a other key + sequence; otherwise returns false. + + \sa operator==() operator!=() operator<() operator<=() operator>=() +*/ + +/*! + \fn bool QKeySequence::operator<= (const QKeySequence &other) const + + Returns true if this key sequence is smaller or equal to the + \a other key sequence; otherwise returns false. + + \sa operator==() operator!=() operator<() operator>() operator>=() +*/ + +/*! + \fn bool QKeySequence::operator>= (const QKeySequence &other) const + + Returns true if this key sequence is larger or equal to the + \a other key sequence; otherwise returns false. + + \sa operator==() operator!=() operator<() operator>() operator<=() +*/ + +/*! + \internal +*/ +bool QKeySequence::isDetached() const +{ + return d->ref == 1; +} + +/*! + \since 4.1 + + Return a string representation of the key sequence, + based on \a format. + + For example, the value Qt::CTRL+Qt::Key_O results in "Ctrl+O". + If the key sequence has multiple key codes, each is separated + by commas in the string returned, such as "Alt+X, Ctrl+Y, Z". + The strings, "Ctrl", "Shift", etc. are translated using + QObject::tr() in the "QShortcut" context. + + If the key sequence has no keys, an empty string is returned. + + On Mac OS X, the string returned resembles the sequence that is + shown in the menu bar. + + \sa fromString() +*/ +QString QKeySequence::toString(SequenceFormat format) const +{ + QString finalString; + // A standard string, with no translation or anything like that. In some ways it will + // look like our latin case on Windows and X11 + int end = count(); + for (int i = 0; i < end; ++i) { + finalString += d->encodeString(d->key[i], format); + finalString += QLatin1String(", "); + } + finalString.truncate(finalString.length() - 2); + return finalString; +} + +/*! + \since 4.1 + + Return a QKeySequence from the string \a str based on \a format. + + \sa toString() +*/ +QKeySequence QKeySequence::fromString(const QString &str, SequenceFormat format) +{ + return QKeySequence(str, format); +} + +/***************************************************************************** + QKeySequence stream functions + *****************************************************************************/ +#if !defined(QT_NO_DATASTREAM) +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QKeySequence &sequence) + \relates QKeySequence + + Writes the key \a sequence to the \a stream. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ +QDataStream &operator<<(QDataStream &s, const QKeySequence &keysequence) +{ + QList<quint32> list; + list << keysequence.d->key[0]; + + if (s.version() >= 5 && keysequence.count() > 1) { + list << keysequence.d->key[1]; + list << keysequence.d->key[2]; + list << keysequence.d->key[3]; + } + s << list; + return s; +} + + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QKeySequence &sequence) + \relates QKeySequence + + Reads a key sequence from the \a stream into the key \a sequence. + + \sa \link datastreamformat.html Format of the QDataStream operators \endlink +*/ +QDataStream &operator>>(QDataStream &s, QKeySequence &keysequence) +{ + qAtomicDetach(keysequence.d); + QList<quint32> list; + s >> list; + for (int i = 0; i < 4; ++i) + keysequence.d->key[i] = list.value(i); + return s; +} + +#endif //QT_NO_DATASTREAM + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QKeySequence &p) +{ +#ifndef Q_BROKEN_DEBUG_STREAM + dbg.nospace() << "QKeySequence(" << p.toString() << ')'; + return dbg.space(); +#else + qWarning("This compiler doesn't support streaming QKeySequence to QDebug"); + return dbg; + Q_UNUSED(p); +#endif +} +#endif + +#endif // QT_NO_SHORTCUT + + +/*! + \typedef QKeySequence::DataPtr + \internal +*/ + + /*! + \fn DataPtr &QKeySequence::data_ptr() + \internal +*/ + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qkeysequence.h b/src/gui/guikernel/qkeysequence.h new file mode 100644 index 0000000000..c61501036a --- /dev/null +++ b/src/gui/guikernel/qkeysequence.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QKEYSEQUENCE_H +#define QKEYSEQUENCE_H + +#include <QtCore/qnamespace.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_SHORTCUT + +/***************************************************************************** + QKeySequence stream functions + *****************************************************************************/ +#ifndef QT_NO_DATASTREAM +class QKeySequence; +Q_GUI_EXPORT QDataStream &operator<<(QDataStream &in, const QKeySequence &ks); +Q_GUI_EXPORT QDataStream &operator>>(QDataStream &out, QKeySequence &ks); +#endif + +#ifdef qdoc +void qt_set_sequence_auto_mnemonic(bool b); +#endif + +class QVariant; +class QKeySequencePrivate; + +class Q_GUI_EXPORT QKeySequence +{ +public: + enum StandardKey { + UnknownKey, + HelpContents, + WhatsThis, + Open, + Close, + Save, + New, + Delete, + Cut, + Copy, + Paste, + Undo, + Redo, + Back, + Forward, + Refresh, + ZoomIn, + ZoomOut, + Print, + AddTab, + NextChild, + PreviousChild, + Find, + FindNext, + FindPrevious, + Replace, + SelectAll, + Bold, + Italic, + Underline, + MoveToNextChar, + MoveToPreviousChar, + MoveToNextWord, + MoveToPreviousWord, + MoveToNextLine, + MoveToPreviousLine, + MoveToNextPage, + MoveToPreviousPage, + MoveToStartOfLine, + MoveToEndOfLine, + MoveToStartOfBlock, + MoveToEndOfBlock, + MoveToStartOfDocument, + MoveToEndOfDocument, + SelectNextChar, + SelectPreviousChar, + SelectNextWord, + SelectPreviousWord, + SelectNextLine, + SelectPreviousLine, + SelectNextPage, + SelectPreviousPage, + SelectStartOfLine, + SelectEndOfLine, + SelectStartOfBlock, + SelectEndOfBlock, + SelectStartOfDocument, + SelectEndOfDocument, + DeleteStartOfWord, + DeleteEndOfWord, + DeleteEndOfLine, + InsertParagraphSeparator, + InsertLineSeparator, + SaveAs, + Preferences, + Quit + }; + + enum SequenceFormat { + NativeText, + PortableText + }; + + QKeySequence(); + QKeySequence(const QString &key); + QKeySequence(const QString &key, SequenceFormat format); + QKeySequence(int k1, int k2 = 0, int k3 = 0, int k4 = 0); + QKeySequence(const QKeySequence &ks); + QKeySequence(StandardKey key); + ~QKeySequence(); + + uint count() const; // ### Qt 5: return 'int' + bool isEmpty() const; + + enum SequenceMatch { + NoMatch, + PartialMatch, + ExactMatch + }; + + QString toString(SequenceFormat format = PortableText) const; + static QKeySequence fromString(const QString &str, SequenceFormat format = PortableText); + + SequenceMatch matches(const QKeySequence &seq) const; + static QKeySequence mnemonic(const QString &text); + static QList<QKeySequence> keyBindings(StandardKey key); + + // ### Qt 5: kill 'operator QString' - it's evil + operator QString() const; + operator QVariant() const; + operator int() const; + int operator[](uint i) const; + QKeySequence &operator=(const QKeySequence &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QKeySequence &operator=(QKeySequence &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QKeySequence &other) { qSwap(d, other.d); } + bool operator==(const QKeySequence &other) const; + inline bool operator!= (const QKeySequence &other) const + { return !(*this == other); } + bool operator< (const QKeySequence &ks) const; + inline bool operator> (const QKeySequence &other) const + { return other < *this; } + inline bool operator<= (const QKeySequence &other) const + { return !(other < *this); } + inline bool operator>= (const QKeySequence &other) const + { return !(*this < other); } + + bool isDetached() const; +private: + static int decodeString(const QString &ks); + static QString encodeString(int key); + int assign(const QString &str); + int assign(const QString &str, SequenceFormat format); + void setKey(int key, int index); + + QKeySequencePrivate *d; + + friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &in, const QKeySequence &ks); + friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &in, QKeySequence &ks); + friend class Q3AccelManager; + friend class QShortcutMap; + friend class QShortcut; + +public: + typedef QKeySequencePrivate * DataPtr; + inline DataPtr &data_ptr() { return d; } +}; +Q_DECLARE_TYPEINFO(QKeySequence, Q_MOVABLE_TYPE); +Q_DECLARE_SHARED(QKeySequence) + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug, const QKeySequence &); +#endif + +#else + +class Q_GUI_EXPORT QKeySequence +{ +public: + QKeySequence() {} + QKeySequence(int) {} +}; + +#endif // QT_NO_SHORTCUT + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKEYSEQUENCE_H diff --git a/src/gui/guikernel/qkeysequence_p.h b/src/gui/guikernel/qkeysequence_p.h new file mode 100644 index 0000000000..c1e5977663 --- /dev/null +++ b/src/gui/guikernel/qkeysequence_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QKEYSEQUENCE_P_H +#define QKEYSEQUENCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qkeysequence.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SHORTCUT +struct Q_AUTOTEST_EXPORT QKeyBinding +{ + QKeySequence::StandardKey standardKey; + uchar priority; + uint shortcut; + uint platform; +}; + +class Q_AUTOTEST_EXPORT QKeySequencePrivate +{ +public: + inline QKeySequencePrivate() + { + ref = 1; + key[0] = key[1] = key[2] = key[3] = 0; + } + inline QKeySequencePrivate(const QKeySequencePrivate ©) + { + ref = 1; + key[0] = copy.key[0]; + key[1] = copy.key[1]; + key[2] = copy.key[2]; + key[3] = copy.key[3]; + } + QAtomicInt ref; + int key[4]; + static QString encodeString(int key, QKeySequence::SequenceFormat format); + static int decodeString(const QString &keyStr, QKeySequence::SequenceFormat format); + + static const QKeyBinding keyBindings[]; + static const uint numberOfKeyBindings; + +}; +#endif // QT_NO_SHORTCUT + +QT_END_NAMESPACE + +#endif //QKEYSEQUENCE_P_H diff --git a/src/gui/guikernel/qmime.cpp b/src/gui/guikernel/qmime.cpp new file mode 100644 index 0000000000..4e15ddf624 --- /dev/null +++ b/src/gui/guikernel/qmime.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmime.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QMimeSource + \brief The QMimeSource class is an abstraction of objects that + provided formatted data of a certain MIME type. + + \obsolete + + The preferred approach to drag and drop is to use QDrag in + conjunction with QMimeData. See \l{Drag and Drop} for details. + + \sa QMimeData, QDrag +*/ + +/*! + Destroys the MIME source. +*/ +QMimeSource::~QMimeSource() +{ +} + +/*! + \fn const char *QMimeSource::format(int i) const + + Returns the (\a i - 1)-th supported MIME format, or 0. +*/ + +/*! + \fn QByteArray QMimeSource::encodedData(const char *format) const + + Returns the encoded data of this object in the specified MIME + \a format. +*/ + +/*! + Returns true if the object can provide the data in format \a + mimeType; otherwise returns false. + + If you inherit from QMimeSource, for consistency reasons it is + better to implement the more abstract canDecode() functions such + as QTextDrag::canDecode() and QImageDrag::canDecode(). +*/ +bool QMimeSource::provides(const char* mimeType) const +{ + const char* fmt; + for (int i=0; (fmt = format(i)); i++) { + if (!qstricmp(mimeType,fmt)) + return true; + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qmime.h b/src/gui/guikernel/qmime.h new file mode 100644 index 0000000000..a791f68cf0 --- /dev/null +++ b/src/gui/guikernel/qmime.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMIME_H +#define QMIME_H + +#include <QtCore/qmimedata.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QMimeSource +{ +public: + virtual ~QMimeSource(); + virtual const char* format(int n = 0) const = 0; + virtual bool provides(const char*) const; + virtual QByteArray encodedData(const char*) const = 0; +}; + + +#if defined(Q_WS_WIN) + +QT_BEGIN_INCLUDE_NAMESPACE +typedef struct tagFORMATETC FORMATETC; +typedef struct tagSTGMEDIUM STGMEDIUM; +struct IDataObject; + +#include <QtCore/qvariant.h> +QT_END_INCLUDE_NAMESPACE + +/* + Encapsulation of conversion between MIME and Windows CLIPFORMAT. + Not need on X11, as the underlying protocol uses the MIME standard + directly. +*/ + +class Q_GUI_EXPORT QWindowsMime +{ +public: + QWindowsMime(); + virtual ~QWindowsMime(); + + // for converting from Qt + virtual bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const = 0; + virtual bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const = 0; + virtual QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const = 0; + + // for converting to Qt + virtual bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const = 0; + virtual QVariant convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const = 0; + virtual QString mimeForFormat(const FORMATETC &formatetc) const = 0; + + static int registerMimeType(const QString &mime); + +private: + friend class QClipboardWatcher; + friend class QDragManager; + friend class QDropData; + friend class QOleDataObject; + + static QWindowsMime *converterToMime(const QString &mimeType, IDataObject *pDataObj); + static QStringList allMimesForFormats(IDataObject *pDataObj); + static QWindowsMime *converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData); + static QVector<FORMATETC> allFormatsForMime(const QMimeData *mimeData); +}; + +#endif +#if defined(Q_WS_MAC) + +/* + Encapsulation of conversion between MIME and Mac flavor. + Not needed on X11, as the underlying protocol uses the MIME standard + directly. +*/ + +class Q_GUI_EXPORT QMacMime { //Obsolete + char type; +public: + enum QMacMimeType { MIME_DND=0x01, MIME_CLIP=0x02, MIME_QT_CONVERTOR=0x04, MIME_ALL=MIME_DND|MIME_CLIP }; + explicit QMacMime(char) { } + virtual ~QMacMime() { } + + static void initialize() { } + + static QList<QMacMime*> all(QMacMimeType) { return QList<QMacMime*>(); } + static QMacMime *convertor(QMacMimeType, const QString &, int) { return 0; } + static QString flavorToMime(QMacMimeType, int) { return QString(); } + + virtual QString convertorName()=0; + virtual int countFlavors()=0; + virtual int flavor(int index)=0; + virtual bool canConvert(const QString &mime, int flav)=0; + virtual QString mimeFor(int flav)=0; + virtual int flavorFor(const QString &mime)=0; + virtual QVariant convertToMime(const QString &mime, QList<QByteArray> data, int flav)=0; + virtual QList<QByteArray> convertFromMime(const QString &mime, QVariant data, int flav)=0; +}; + +class Q_GUI_EXPORT QMacPasteboardMime { + char type; +public: + enum QMacPasteboardMimeType { MIME_DND=0x01, + MIME_CLIP=0x02, + MIME_QT_CONVERTOR=0x04, + MIME_QT3_CONVERTOR=0x08, + MIME_ALL=MIME_DND|MIME_CLIP + }; + explicit QMacPasteboardMime(char); + virtual ~QMacPasteboardMime(); + + static void initialize(); + + static QList<QMacPasteboardMime*> all(uchar); + static QMacPasteboardMime *convertor(uchar, const QString &mime, QString flav); + static QString flavorToMime(uchar, QString flav); + + virtual QString convertorName() = 0; + + virtual bool canConvert(const QString &mime, QString flav) = 0; + virtual QString mimeFor(QString flav) = 0; + virtual QString flavorFor(const QString &mime) = 0; + virtual QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav) = 0; + virtual QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav) = 0; +}; + +// ### Qt 5: Add const QStringList& QMacPasteboardMime::supportedFlavours() +Q_GUI_EXPORT void qRegisterDraggedTypes(const QStringList &types); +#endif // Q_WS_MAC + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMIME_H diff --git a/src/gui/guikernel/qmime_mac.cpp b/src/gui/guikernel/qmime_mac.cpp new file mode 100644 index 0000000000..d6f6222c23 --- /dev/null +++ b/src/gui/guikernel/qmime_mac.cpp @@ -0,0 +1,1310 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmime.h" + +//#define USE_INTERNET_CONFIG + +#ifndef USE_INTERNET_CONFIG +# include "qfile.h" +# include "qfileinfo.h" +# include "qtextstream.h" +# include "qdir.h" +# include <unistd.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <sys/fcntl.h> +#endif + +#include "qdebug.h" +#include "qpixmap.h" +#include "qimagewriter.h" +#include "qimagereader.h" +#include "qdatastream.h" +#include "qbuffer.h" +#include "qdatetime.h" +#include "qapplication_p.h" +#include "qtextcodec.h" +#include "qregexp.h" +#include "qurl.h" +#include "qmap.h" +#include <private/qt_mac_p.h> + + +#ifdef Q_WS_MAC32 +#include <QuickTime/QuickTime.h> +#include <qlibrary.h> +#endif + +QT_BEGIN_NAMESPACE + +extern CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0); // qpaintengine_mac.cpp + +typedef QList<QMacPasteboardMime*> MimeList; +Q_GLOBAL_STATIC(MimeList, globalMimeList) + +static void cleanup_mimes() +{ + MimeList *mimes = globalMimeList(); + while (!mimes->isEmpty()) + delete mimes->takeFirst(); +} + +Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList) + +/*! + \fn void qRegisterDraggedTypes(const QStringList &types) + \relates QMacPasteboardMime + + Registers the given \a types as custom pasteboard types. + + This function should be called to enable the Drag and Drop events + for custom pasteboard types on Cocoa implementations. This is required + in addition to a QMacPasteboardMime subclass implementation. By default + drag and drop is enabled for all standard pasteboard types. + + \sa QMacPasteboardMime +*/ +Q_GUI_EXPORT void qRegisterDraggedTypes(const QStringList &types) +{ + (*globalDraggedTypesList()) += types; +} + +const QStringList& qEnabledDraggedTypes() +{ + return (*globalDraggedTypesList()); +} + + +/***************************************************************************** + QDnD debug facilities + *****************************************************************************/ +//#define DEBUG_MIME_MAPS + +//functions +extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp +extern void qt_mac_from_pascal_string(QString, Str255, TextEncoding encoding=0, int len=-1); //qglobal.cpp + +ScrapFlavorType qt_mac_mime_type = 'CUTE'; +CFStringRef qt_mac_mime_typeUTI = CFSTR("com.pasteboard.trolltech.marker"); + +/*! + \class QMacPasteboardMime + \brief The QMacPasteboardMime class converts between a MIME type and a + \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform + Type Identifier (UTI)} format. + \since 4.2 + + \ingroup draganddrop + + Qt's drag and drop and clipboard facilities use the MIME + standard. On X11, this maps trivially to the Xdnd protocol. On + Mac, although some applications use MIME to describe clipboard + contents, it is more common to use Apple's UTI format. + + QMacPasteboardMime's role is to bridge the gap between MIME and UTI; + By subclasses this class, one can extend Qt's drag and drop + and clipboard handling to convert to and from unsupported, or proprietary, UTI formats. + + A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation. + + Qt has predefined support for the following UTIs: + \list + \i public.utf8-plain-text - converts to "text/plain" + \i public.utf16-plain-text - converts to "text/plain" + \i public.html - converts to "text/html" + \i public.url - converts to "text/uri-list" + \i public.file-url - converts to "text/uri-list" + \i public.tiff - converts to "application/x-qt-image" + \i public.vcard - converts to "text/plain" + \i com.apple.traditional-mac-plain-text - converts to "text/plain" + \i com.apple.pict - converts to "application/x-qt-image" + \endlist + + When working with MIME data, Qt will interate through all instances of QMacPasteboardMime to + find an instance that can convert to, or from, a specific MIME type. It will do this by calling + canConvert() on each instance, starting with (and choosing) the last created instance first. + The actual conversions will be done by using convertToMime() and convertFromMime(). + + \note The API uses the term "flavor" in some cases. This is for backwards + compatibility reasons, and should now be understood as UTIs. +*/ + +/*! \enum QMacPasteboardMime::QMacPasteboardMimeType + \internal +*/ + +/*! + Constructs a new conversion object of type \a t, adding it to the + globally accessed list of available convertors. +*/ +QMacPasteboardMime::QMacPasteboardMime(char t) : type(t) +{ + globalMimeList()->append(this); +} + +/*! + Destroys a conversion object, removing it from the global + list of available convertors. +*/ +QMacPasteboardMime::~QMacPasteboardMime() +{ + if(!QApplication::closingDown()) + globalMimeList()->removeAll(this); +} + +class QMacPasteboardMimeAny : public QMacPasteboardMime { +private: + +public: + QMacPasteboardMimeAny() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) { + } + ~QMacPasteboardMimeAny() { + } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeAny::convertorName() +{ + return QLatin1String("Any-Mime"); +} + +QString QMacPasteboardMimeAny::flavorFor(const QString &mime) +{ + // do not handle the mime type name in the drag pasteboard + if(mime == QLatin1String("application/x-qt-mime-type-name")) + return QString(); + QString ret = QLatin1String("com.trolltech.anymime.") + mime; + return ret.replace(QLatin1Char('/'), QLatin1String("--")); +} + +QString QMacPasteboardMimeAny::mimeFor(QString flav) +{ + const QString any_prefix = QLatin1String("com.trolltech.anymime."); + if(flav.size() > any_prefix.length() && flav.startsWith(any_prefix)) + return flav.mid(any_prefix.length()).replace(QLatin1String("--"), QLatin1String("/")); + return QString(); +} + +bool QMacPasteboardMimeAny::canConvert(const QString &mime, QString flav) +{ + return mimeFor(flav) == mime; +} + +QVariant QMacPasteboardMimeAny::convertToMime(const QString &mime, QList<QByteArray> data, QString) +{ + if(data.count() > 1) + qWarning("QMacPasteboardMimeAny: Cannot handle multiple member data"); + QVariant ret; + if (mime == QLatin1String("text/plain")) + ret = QString::fromUtf8(data.first()); + else + ret = data.first(); + return ret; +} + +QList<QByteArray> QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString) +{ + QList<QByteArray> ret; + if (mime == QLatin1String("text/plain")) + ret.append(data.toString().toUtf8()); + else + ret.append(data.toByteArray()); + return ret; +} + +class QMacPasteboardMimeTypeName : public QMacPasteboardMime { +private: + +public: + QMacPasteboardMimeTypeName() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) { + } + ~QMacPasteboardMimeTypeName() { + } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeTypeName::convertorName() +{ + return QLatin1String("Qt-Mime-Type"); +} + +QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime) +{ + if(mime == QLatin1String("application/x-qt-mime-type-name")) + return QLatin1String("com.trolltech.qt.MimeTypeName"); + return QString(); +} + +QString QMacPasteboardMimeTypeName::mimeFor(QString) +{ + return QString(); +} + +bool QMacPasteboardMimeTypeName::canConvert(const QString &, QString) +{ + return false; +} + +QVariant QMacPasteboardMimeTypeName::convertToMime(const QString &, QList<QByteArray>, QString) +{ + QVariant ret; + return ret; +} + +QList<QByteArray> QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString) +{ + QList<QByteArray> ret; + ret.append(QString("x-qt-mime-type-name").toUtf8()); + return ret; +} + +class QMacPasteboardMimePlainText : public QMacPasteboardMime { +public: + QMacPasteboardMimePlainText() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimePlainText::convertorName() +{ + return QLatin1String("PlainText"); +} + +QString QMacPasteboardMimePlainText::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("text/plain")) + return QLatin1String("com.apple.traditional-mac-plain-text"); + return QString(); +} + +QString QMacPasteboardMimePlainText::mimeFor(QString flav) +{ + if (flav == QLatin1String("com.apple.traditional-mac-plain-text")) + return QLatin1String("text/plain"); + return QString(); +} + +bool QMacPasteboardMimePlainText::canConvert(const QString &mime, QString flav) +{ + return flavorFor(mime) == flav; +} + +QVariant QMacPasteboardMimePlainText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor) +{ + if(data.count() > 1) + qWarning("QMacPasteboardMimePlainText: Cannot handle multiple member data"); + const QByteArray &firstData = data.first(); + QVariant ret; + if(flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text"))) { + QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast<const UInt8 *>(firstData.constData()), + firstData.size(), CFStringGetSystemEncoding(), false)); + ret = QString(str); + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return ret; +} + +QList<QByteArray> QMacPasteboardMimePlainText::convertFromMime(const QString &, QVariant data, QString flavor) +{ + QList<QByteArray> ret; + QString string = data.toString(); + if(flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text"))) + ret.append(string.toLatin1()); + return ret; +} + +class QMacPasteboardMimeUnicodeText : public QMacPasteboardMime { +public: + QMacPasteboardMimeUnicodeText() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeUnicodeText::convertorName() +{ + return QLatin1String("UnicodeText"); +} + +QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("text/plain")) + return QLatin1String("public.utf16-plain-text"); + int i = mime.indexOf(QLatin1String("charset=")); + if (i >= 0) { + QString cs(mime.mid(i+8).toLower()); + i = cs.indexOf(QLatin1Char(';')); + if (i>=0) + cs = cs.left(i); + if (cs == QLatin1String("system")) + return QLatin1String("public.utf8-plain-text"); + else if (cs == QLatin1String("iso-10646-ucs-2") + || cs == QLatin1String("utf16")) + return QLatin1String("public.utf16-plain-text"); + } + return QString(); +} + +QString QMacPasteboardMimeUnicodeText::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.utf16-plain-text") || flav == QLatin1String("public.utf8-plain-text")) + return QLatin1String("text/plain"); + return QString(); +} + +bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav) +{ + return flavorFor(mime) == flav; +} + +QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor) +{ + if(data.count() > 1) + qWarning("QMacPasteboardMimeUnicodeText: Cannot handle multiple member data"); + const QByteArray &firstData = data.first(); + // I can only handle two types (system and unicode) so deal with them that way + QVariant ret; + if(flavor == QLatin1String("public.utf8-plain-text")) { + QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast<const UInt8 *>(firstData.constData()), + firstData.size(), CFStringGetSystemEncoding(), false)); + ret = QString(str); + } else if (flavor == QLatin1String("public.utf16-plain-text")) { + ret = QString(reinterpret_cast<const QChar *>(firstData.constData()), + firstData.size() / sizeof(QChar)); + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return ret; +} + +QList<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor) +{ + QList<QByteArray> ret; + QString string = data.toString(); + if(flavor == QLatin1String("public.utf8-plain-text")) + ret.append(string.toUtf8()); + else if (flavor == QLatin1String("public.utf16-plain-text")) + ret.append(QByteArray((char*)string.utf16(), string.length()*2)); + return ret; +} + +class QMacPasteboardMimeHTMLText : public QMacPasteboardMime { +public: + QMacPasteboardMimeHTMLText() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeHTMLText::convertorName() +{ + return QLatin1String("HTML"); +} + +QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("text/html")) + return QLatin1String("public.html"); + return QString(); +} + +QString QMacPasteboardMimeHTMLText::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.html")) + return QLatin1String("text/html"); + return QString(); +} + +bool QMacPasteboardMimeHTMLText::canConvert(const QString &mime, QString flav) +{ + return flavorFor(mime) == flav; +} + +QVariant QMacPasteboardMimeHTMLText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor) +{ + if (!canConvert(mimeType, flavor)) + return QVariant(); + if (data.count() > 1) + qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data"); + return data.first(); +} + +QList<QByteArray> QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor) +{ + QList<QByteArray> ret; + if (!canConvert(mime, flavor)) + return ret; + ret.append(data.toByteArray()); + return ret; +} + + +#ifdef Q_WS_MAC32 + +// This can be removed once 10.6 is the minimum (or we have to require 64-bit) whichever comes first. + +typedef ComponentResult (*PtrGraphicsImportSetDataHandle)(GraphicsImportComponent, Handle); +typedef ComponentResult (*PtrGraphicsImportCreateCGImage)(GraphicsImportComponent, CGImageRef*, UInt32); +typedef ComponentResult (*PtrGraphicsExportSetInputCGImage)(GraphicsExportComponent, CGImageRef); +typedef ComponentResult (*PtrGraphicsExportSetOutputHandle)(GraphicsExportComponent, Handle); +typedef ComponentResult (*PtrGraphicsExportDoExport)(GraphicsExportComponent, unsigned long *); + +static PtrGraphicsImportSetDataHandle ptrGraphicsImportSetDataHandle = 0; +static PtrGraphicsImportCreateCGImage ptrGraphicsImportCreateCGImage = 0; +static PtrGraphicsExportSetInputCGImage ptrGraphicsExportSetInputCGImage = 0; +static PtrGraphicsExportSetOutputHandle ptrGraphicsExportSetOutputHandle = 0; +static PtrGraphicsExportDoExport ptrGraphicsExportDoExport = 0; + +static bool resolveMimeQuickTimeSymbols() +{ + if (ptrGraphicsImportSetDataHandle == 0) { + QLibrary library(QLatin1String("/System/Library/Frameworks/QuickTime.framework/QuickTime")); + ptrGraphicsImportSetDataHandle = reinterpret_cast<PtrGraphicsImportSetDataHandle>(library.resolve("GraphicsImportSetDataHandle")); + ptrGraphicsImportCreateCGImage = reinterpret_cast<PtrGraphicsImportCreateCGImage>(library.resolve("GraphicsImportCreateCGImage")); + ptrGraphicsExportSetInputCGImage = reinterpret_cast<PtrGraphicsExportSetInputCGImage>(library.resolve("GraphicsExportSetInputCGImage")); + ptrGraphicsExportSetOutputHandle = reinterpret_cast<PtrGraphicsExportSetOutputHandle>(library.resolve("GraphicsExportSetOutputHandle")); + ptrGraphicsExportDoExport = reinterpret_cast<PtrGraphicsExportDoExport>(library.resolve("GraphicsExportDoExport")); + } + + return ptrGraphicsImportSetDataHandle != 0 + && ptrGraphicsImportCreateCGImage != 0 && ptrGraphicsExportSetInputCGImage != 0 + && ptrGraphicsExportSetOutputHandle != 0 && ptrGraphicsExportDoExport != 0; +} + +class QMacPasteboardMimePict : public QMacPasteboardMime { +public: + QMacPasteboardMimePict() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimePict::convertorName() +{ + return QLatin1String("Pict"); +} + +QString QMacPasteboardMimePict::flavorFor(const QString &mime) +{ + if(mime.startsWith(QLatin1String("application/x-qt-image"))) + return QLatin1String("com.apple.pict"); + return QString(); +} + +QString QMacPasteboardMimePict::mimeFor(QString flav) +{ + if(flav == QLatin1String("com.apple.pict")) + return QLatin1String("application/x-qt-image"); + return QString(); +} + +bool QMacPasteboardMimePict::canConvert(const QString &mime, QString flav) +{ + return flav == QLatin1String("com.apple.pict") + && mime == QLatin1String("application/x-qt-image"); +} + + +QVariant QMacPasteboardMimePict::convertToMime(const QString &mime, QList<QByteArray> data, QString flav) +{ + if(data.count() > 1) + qWarning("QMacPasteboardMimePict: Cannot handle multiple member data"); + QVariant ret; + if (!resolveMimeQuickTimeSymbols()) + return ret; + + if(!canConvert(mime, flav)) + return ret; + const QByteArray &a = data.first(); + + // This function expects the 512 header (just to skip it, so create the extra space for it). + Handle pic = NewHandle(a.size() + 512); + memcpy(*pic + 512, a.constData(), a.size()); + + GraphicsImportComponent graphicsImporter; + ComponentResult result = OpenADefaultComponent(GraphicsImporterComponentType, + kQTFileTypePicture, &graphicsImporter); + QCFType<CGImageRef> cgImage; + if (!result) + result = ptrGraphicsImportSetDataHandle(graphicsImporter, pic); + if (!result) + result = ptrGraphicsImportCreateCGImage(graphicsImporter, &cgImage, + kGraphicsImportCreateCGImageUsingCurrentSettings); + if (!result) + ret = QVariant(QPixmap::fromMacCGImageRef(cgImage).toImage()); + CloseComponent(graphicsImporter); + DisposeHandle(pic); + return ret; +} + +QList<QByteArray> QMacPasteboardMimePict::convertFromMime(const QString &mime, QVariant variant, + QString flav) +{ + QList<QByteArray> ret; + if (!resolveMimeQuickTimeSymbols()) + return ret; + + if (!canConvert(mime, flav)) + return ret; + QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(qvariant_cast<QImage>(variant)); + Handle pic = NewHandle(0); + GraphicsExportComponent graphicsExporter; + ComponentResult result = OpenADefaultComponent(GraphicsExporterComponentType, + kQTFileTypePicture, &graphicsExporter); + if (!result) { + unsigned long sizeWritten; + result = ptrGraphicsExportSetInputCGImage(graphicsExporter, cgimage); + if (!result) + result = ptrGraphicsExportSetOutputHandle(graphicsExporter, pic); + if (!result) + result = ptrGraphicsExportDoExport(graphicsExporter, &sizeWritten); + + CloseComponent(graphicsExporter); + } + + int size = GetHandleSize((Handle)pic); + // Skip the Picture File header (512 bytes) and feed the raw data + QByteArray ar(reinterpret_cast<char *>(*pic + 512), size - 512); + ret.append(ar); + DisposeHandle(pic); + return ret; +} + + +#endif //Q_WS_MAC32 + +class QMacPasteboardMimeTiff : public QMacPasteboardMime { +public: + QMacPasteboardMimeTiff() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeTiff::convertorName() +{ + return QLatin1String("Tiff"); +} + +QString QMacPasteboardMimeTiff::flavorFor(const QString &mime) +{ + if(mime.startsWith(QLatin1String("application/x-qt-image"))) + return QLatin1String("public.tiff"); + return QString(); +} + +QString QMacPasteboardMimeTiff::mimeFor(QString flav) +{ + if(flav == QLatin1String("public.tiff")) + return QLatin1String("application/x-qt-image"); + return QString(); +} + +bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav) +{ + return flav == QLatin1String("public.tiff") && mime == QLatin1String("application/x-qt-image"); +} + +QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList<QByteArray> data, QString flav) +{ + if(data.count() > 1) + qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data"); + QVariant ret; + if (!canConvert(mime, flav)) + return ret; + const QByteArray &a = data.first(); + QCFType<CGImageRef> image; + QCFType<CFDataRef> tiffData = CFDataCreateWithBytesNoCopy(0, + reinterpret_cast<const UInt8 *>(a.constData()), + a.size(), kCFAllocatorNull); + QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0); + image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0); + + if (image != 0) + ret = QVariant(QPixmap::fromMacCGImageRef(image).toImage()); + return ret; +} + +QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav) +{ + QList<QByteArray> ret; + if (!canConvert(mime, flav)) + return ret; + + QImage img = qvariant_cast<QImage>(variant); + QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(img); +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { + QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0); + QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0); + if (imageDestination != 0) { + CFTypeRef keys[2]; + QCFType<CFTypeRef> values[2]; + QCFType<CFDictionaryRef> options; + keys[0] = kCGImagePropertyPixelWidth; + keys[1] = kCGImagePropertyPixelHeight; + int width = img.width(); + int height = img.height(); + values[0] = CFNumberCreate(0, kCFNumberIntType, &width); + values[1] = CFNumberCreate(0, kCFNumberIntType, &height); + options = CFDictionaryCreate(0, reinterpret_cast<const void **>(keys), + reinterpret_cast<const void **>(values), 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CGImageDestinationAddImage(imageDestination, cgimage, options); + CGImageDestinationFinalize(imageDestination); + } + QByteArray ar(CFDataGetLength(data), 0); + CFDataGetBytes(data, + CFRangeMake(0, ar.size()), + reinterpret_cast<UInt8 *>(ar.data())); + ret.append(ar); + } else +#endif + { +#ifdef Q_WS_MAC32 + Handle tiff = NewHandle(0); + if (resolveMimeQuickTimeSymbols()) { + GraphicsExportComponent graphicsExporter; + ComponentResult result = OpenADefaultComponent(GraphicsExporterComponentType, + kQTFileTypeTIFF, &graphicsExporter); + if (!result) { + unsigned long sizeWritten; + result = ptrGraphicsExportSetInputCGImage(graphicsExporter, cgimage); + if (!result) + result = ptrGraphicsExportSetOutputHandle(graphicsExporter, tiff); + if (!result) + result = ptrGraphicsExportDoExport(graphicsExporter, &sizeWritten); + + CloseComponent(graphicsExporter); + } + } + int size = GetHandleSize((Handle)tiff); + QByteArray ar(reinterpret_cast<char *>(*tiff), size); + ret.append(ar); + DisposeHandle(tiff); +#endif + } + return ret; +} + + +class QMacPasteboardMimeFileUri : public QMacPasteboardMime { +public: + QMacPasteboardMimeFileUri() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeFileUri::convertorName() +{ + return QLatin1String("FileURL"); +} + +QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("text/uri-list")) + return QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0)); + return QString(); +} + +QString QMacPasteboardMimeFileUri::mimeFor(QString flav) +{ + if (flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0))) + return QLatin1String("text/uri-list"); + return QString(); +} + +bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav) +{ + return mime == QLatin1String("text/uri-list") + && flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0)); +} + +QVariant QMacPasteboardMimeFileUri::convertToMime(const QString &mime, QList<QByteArray> data, QString flav) +{ + if(!canConvert(mime, flav)) + return QVariant(); + QList<QVariant> ret; + for(int i = 0; i < data.size(); ++i) { + QUrl url = QUrl::fromEncoded(data.at(i)); + if (url.host().toLower() == QLatin1String("localhost")) + url.setHost(QString()); + url.setPath(url.path().normalized(QString::NormalizationForm_C)); + ret.append(url); + } + return QVariant(ret); +} + +QList<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav) +{ + QList<QByteArray> ret; + if (!canConvert(mime, flav)) + return ret; + QList<QVariant> urls = data.toList(); + for(int i = 0; i < urls.size(); ++i) { + QUrl url = urls.at(i).toUrl(); + if (url.scheme().isEmpty()) + url.setScheme(QLatin1String("file")); + if (url.scheme().toLower() == QLatin1String("file")) { + if (url.host().isEmpty()) + url.setHost(QLatin1String("localhost")); + url.setPath(url.path().normalized(QString::NormalizationForm_D)); + } + ret.append(url.toEncoded()); + } + return ret; +} + +class QMacPasteboardMimeUrl : public QMacPasteboardMime { +public: + QMacPasteboardMimeUrl() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeUrl::convertorName() +{ + return QLatin1String("URL"); +} + +QString QMacPasteboardMimeUrl::flavorFor(const QString &mime) +{ + if(mime.startsWith(QLatin1String("text/uri-list"))) + return QLatin1String("public.url"); + return QString(); +} + +QString QMacPasteboardMimeUrl::mimeFor(QString flav) +{ + if(flav == QLatin1String("public.url")) + return QLatin1String("text/uri-list"); + return QString(); +} + +bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav) +{ + return flav == QLatin1String("public.url") + && mime == QLatin1String("text/uri-list"); +} + +QVariant QMacPasteboardMimeUrl::convertToMime(const QString &mime, QList<QByteArray> data, QString flav) +{ + if(!canConvert(mime, flav)) + return QVariant(); + + QList<QVariant> ret; + for (int i=0; i<data.size(); ++i) { + QUrl url = QUrl::fromEncoded(data.at(i)); + if (url.host().toLower() == QLatin1String("localhost")) + url.setHost(QString()); + url.setPath(url.path().normalized(QString::NormalizationForm_C)); + ret.append(url); + } + return QVariant(ret); +} + +QList<QByteArray> QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QVariant data, QString flav) +{ + QList<QByteArray> ret; + if (!canConvert(mime, flav)) + return ret; + + QList<QVariant> urls = data.toList(); + for(int i=0; i<urls.size(); ++i) { + QUrl url = urls.at(i).toUrl(); + if (url.scheme().isEmpty()) + url.setScheme(QLatin1String("file")); + if (url.scheme().toLower() == QLatin1String("file")) { + if (url.host().isEmpty()) + url.setHost(QLatin1String("localhost")); + url.setPath(url.path().normalized(QString::NormalizationForm_D)); + } + ret.append(url.toEncoded()); + } + return ret; +} + +class QMacPasteboardMimeVCard : public QMacPasteboardMime +{ +public: + QMacPasteboardMimeVCard() : QMacPasteboardMime(MIME_ALL){ } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeVCard::convertorName() +{ + return QString("VCard"); +} + +bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav) +{ + return mimeFor(flav) == mime; +} + +QString QMacPasteboardMimeVCard::flavorFor(const QString &mime) +{ + if(mime.startsWith(QLatin1String("text/plain"))) + return QLatin1String("public.vcard"); + return QString(); +} + +QString QMacPasteboardMimeVCard::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.vcard")) + return QLatin1String("text/plain"); + return QString(); +} + +QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString) +{ + QByteArray cards; + if (mime == QLatin1String("text/plain")) { + for (int i=0; i<data.size(); ++i) + cards += data[i]; + } + return QVariant(cards); +} + +QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString) +{ + QList<QByteArray> ret; + if (mime == QLatin1String("text/plain")) + ret.append(data.toString().toUtf8()); + return ret; +} + +#ifdef QT3_SUPPORT +class QMacPasteboardMimeQt3Any : public QMacPasteboardMime { +private: + int current_max; + QFile library_file; + QDateTime mime_registry_loaded; + QMap<QString, int> mime_registry; + int registerMimeType(const QString &mime); + bool loadMimeRegistry(); + +public: + QMacPasteboardMimeQt3Any() : QMacPasteboardMime(MIME_QT3_CONVERTOR) { + current_max = 'QT00'; + } + ~QMacPasteboardMimeQt3Any() { + } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); + QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +static bool qt_mac_openMimeRegistry(bool global, QIODevice::OpenMode mode, QFile &file) +{ + QString dir = QLatin1String("/Library/Qt"); + if(!global) + dir.prepend(QDir::homePath()); + file.setFileName(dir + QLatin1String("/.mime_types")); + if(mode != QIODevice::ReadOnly) { + if(!QFile::exists(dir)) { + // Do it with a system call as I don't see much worth in + // doing it with QDir since we have to chmod anyway. + bool success = ::mkdir(dir.toLocal8Bit().constData(), S_IRUSR | S_IWUSR | S_IXUSR) == 0; + if (success) + success = ::chmod(dir.toLocal8Bit().constData(), S_IRUSR | S_IWUSR | S_IXUSR + | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) == 0; + if (!success) + return false; + } + if (!file.exists()) { + // Create the file and chmod it so that everyone can write to it. + int fd = ::open(file.fileName().toLocal8Bit().constData(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + bool success = fd != -1; + if (success) + success = ::fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) == 0; + if (fd != -1) + ::close(fd); + if(!success) + return false; + } + } + return file.open(mode); +} + +static void qt_mac_loadMimeRegistry(QFile &file, QMap<QString, int> ®istry, int &max) +{ + file.reset(); + QTextStream stream(&file); + while(!stream.atEnd()) { + QString mime = stream.readLine(); + int mactype = stream.readLine().toInt(); + if(mactype > max) + max = mactype; + registry.insert(mime, mactype); + } +} + +bool QMacPasteboardMimeQt3Any::loadMimeRegistry() +{ + if(!library_file.isOpen()) { + if(!qt_mac_openMimeRegistry(true, QIODevice::ReadWrite, library_file)) { + QFile global; + if(qt_mac_openMimeRegistry(true, QIODevice::ReadOnly, global)) { + qt_mac_loadMimeRegistry(global, mime_registry, current_max); + global.close(); + } + if(!qt_mac_openMimeRegistry(false, QIODevice::ReadWrite, library_file)) { + qWarning("QMacPasteboardMimeAnyQt3Mime: Failure to open mime resources %s -- %s", library_file.fileName().toLatin1().constData(), + library_file.errorString().toLatin1().constData()); + return false; + } + } + } + + QFileInfo fi(library_file); + if(!mime_registry_loaded.isNull() && mime_registry_loaded == fi.lastModified()) + return true; + mime_registry_loaded = fi.lastModified(); + qt_mac_loadMimeRegistry(library_file, mime_registry, current_max); + return true; +} + +int QMacPasteboardMimeQt3Any::registerMimeType(const QString &mime) +{ + if(!mime_registry.contains(mime)) { + if(!loadMimeRegistry()) { + qWarning("QMacPasteboardMimeAnyQt3Mime: Internal error"); + return 0; + } + if(!mime_registry.contains(mime)) { + if(!library_file.isOpen()) { + if(!library_file.open(QIODevice::WriteOnly)) { + qWarning("QMacPasteboardMimeAnyQt3Mime: Failure to open %s -- %s", library_file.fileName().toLatin1().constData(), + library_file.errorString().toLatin1().constData()); + return false; + } + } + int ret = ++current_max; + mime_registry_loaded = QFileInfo(library_file).lastModified(); + QTextStream stream(&library_file); + stream << mime << endl; + stream << ret << endl; + mime_registry.insert(mime, ret); + library_file.flush(); //flush and set mtime + return ret; + } + } + return mime_registry[mime]; +} + +QString QMacPasteboardMimeQt3Any::convertorName() +{ + return QLatin1String("Qt3-Any-Mime"); +} + +QString QMacPasteboardMimeQt3Any::flavorFor(const QString &mime) +{ + const int os_flav = registerMimeType(mime); + QCFType<CFArrayRef> ids = UTTypeCreateAllIdentifiersForTag(0, kUTTagClassOSType, + QCFString(UTCreateStringForOSType(os_flav))); + if(ids) { + const int type_count = CFArrayGetCount(ids); + if(type_count) { + if(type_count > 1) + qDebug("Can't happen!"); + return QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(ids, 0)); + } + } + return QString(); +} + +QString QMacPasteboardMimeQt3Any::mimeFor(QString flav) +{ + loadMimeRegistry(); + const int os_flav = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(QCFString(flav), kUTTagClassOSType)); + for(QMap<QString, int>::const_iterator it = mime_registry.constBegin(); + it != mime_registry.constEnd(); ++it) { + if(it.value() == os_flav) + return QString::fromLatin1(it.key().toLatin1()); + } + return QString(); +} + +bool QMacPasteboardMimeQt3Any::canConvert(const QString &mime, QString flav) +{ + loadMimeRegistry(); + const int os_flav = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(QCFString(flav), kUTTagClassOSType)); + if(mime_registry.contains(mime) && mime_registry[mime] == os_flav) + return true; + return false; +} + +QVariant QMacPasteboardMimeQt3Any::convertToMime(const QString &, QList<QByteArray>, QString) +{ + qWarning("QMacPasteboardMimeAnyQt3Mime: Cannot write anything!"); + return QVariant(); +} + +QList<QByteArray> QMacPasteboardMimeQt3Any::convertFromMime(const QString &mime, QVariant data, QString) +{ + QList<QByteArray> ret; + if (mime == QLatin1String("text/plain")) { + ret.append(data.toString().toUtf8()); + } else { + ret.append(data.toByteArray()); + } + return ret; +} +#endif + +/*! + \internal + + This is an internal function. +*/ +void QMacPasteboardMime::initialize() +{ + if(globalMimeList()->isEmpty()) { + qAddPostRoutine(cleanup_mimes); + + //standard types that we wrap + new QMacPasteboardMimeTiff; +#ifdef Q_WS_MAC32 + // 10.6 does automatic synthesis to and from PICT to standard image types (like TIFF), + // so don't bother doing it ourselves, especially since it's not available in 64-bit. + if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6) + new QMacPasteboardMimePict; +#endif + new QMacPasteboardMimeUnicodeText; + new QMacPasteboardMimePlainText; + new QMacPasteboardMimeHTMLText; + new QMacPasteboardMimeFileUri; + new QMacPasteboardMimeUrl; + new QMacPasteboardMimeTypeName; + new QMacPasteboardMimeVCard; + //make sure our "non-standard" types are always last! --Sam + new QMacPasteboardMimeAny; +#ifdef QT3_SUPPORT + new QMacPasteboardMimeQt3Any; +#endif + } +} + +/*! + Returns the most-recently created QMacPasteboardMime of type \a t that can convert + between the \a mime and \a flav formats. Returns 0 if no such convertor + exists. +*/ +QMacPasteboardMime* +QMacPasteboardMime::convertor(uchar t, const QString &mime, QString flav) +{ + MimeList *mimes = globalMimeList(); + for(MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { +#ifdef DEBUG_MIME_MAPS + qDebug("QMacPasteboardMime::convertor: seeing if %s (%d) can convert %s to %d[%c%c%c%c] [%d]", + (*it)->convertorName().toLatin1().constData(), + (*it)->type & t, mime.toLatin1().constData(), + flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF, + (*it)->canConvert(mime,flav)); + for(int i = 0; i < (*it)->countFlavors(); ++i) { + int f = (*it)->flavor(i); + qDebug(" %d) %d[%c%c%c%c] [%s]", i, f, + (f >> 24) & 0xFF, (f >> 16) & 0xFF, (f >> 8) & 0xFF, (f) & 0xFF, + (*it)->convertorName().toLatin1().constData()); + } +#endif + if(((*it)->type & t) && (*it)->canConvert(mime, flav)) + return (*it); + } + return 0; +} +/*! + Returns a MIME type of type \a t for \a flav, or 0 if none exists. +*/ +QString QMacPasteboardMime::flavorToMime(uchar t, QString flav) +{ + MimeList *mimes = globalMimeList(); + for(MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { +#ifdef DEBUG_MIME_MAPS + qDebug("QMacMIme::flavorToMime: attempting %s (%d) for flavor %d[%c%c%c%c] [%s]", + (*it)->convertorName().toLatin1().constData(), + (*it)->type & t, flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF, + (*it)->mimeFor(flav).toLatin1().constData()); + +#endif + if((*it)->type & t) { + QString mimeType = (*it)->mimeFor(flav); + if(!mimeType.isNull()) + return mimeType; + } + } + return QString(); +} + +/*! + Returns a list of all currently defined QMacPasteboardMime objects of type \a t. +*/ +QList<QMacPasteboardMime*> QMacPasteboardMime::all(uchar t) +{ + MimeList ret; + MimeList *mimes = globalMimeList(); + for(MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { + if((*it)->type & t) + ret.append((*it)); + } + return ret; +} + + +/*! + \fn QString QMacPasteboardMime::convertorName() + + Returns a name for the convertor. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn bool QMacPasteboardMime::canConvert(const QString &mime, QString flav) + + Returns true if the convertor can convert (both ways) between + \a mime and \a flav; otherwise returns false. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QString QMacPasteboardMime::mimeFor(QString flav) + + Returns the MIME UTI used for Mac flavor \a flav, or 0 if this + convertor does not support \a flav. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QString QMacPasteboardMime::flavorFor(const QString &mime) + + Returns the Mac UTI used for MIME type \a mime, or 0 if this + convertor does not support \a mime. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QVariant QMacPasteboardMime::convertToMime(const QString &mime, QList<QByteArray> data, QString flav) + + Returns \a data converted from Mac UTI \a flav to MIME type \a + mime. + + Note that Mac flavors must all be self-terminating. The input \a + data may contain trailing data. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QList<QByteArray> QMacPasteboardMime::convertFromMime(const QString &mime, QVariant data, QString flav) + + Returns \a data converted from MIME type \a mime + to Mac UTI \a flav. + + Note that Mac flavors must all be self-terminating. The return + value may contain trailing data. + + All subclasses must reimplement this pure virtual function. +*/ + + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qmime_win.cpp b/src/gui/guikernel/qmime_win.cpp new file mode 100644 index 0000000000..feb8b78eca --- /dev/null +++ b/src/gui/guikernel/qmime_win.cpp @@ -0,0 +1,1556 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmime.h" + +#include "qimagereader.h" +#include "qimagewriter.h" +#include "qdatastream.h" +#include "qbuffer.h" +#include "qt_windows.h" +#include "qapplication_p.h" +#include "qtextcodec.h" +#include "qregexp.h" +#include "qalgorithms.h" +#include "qmap.h" +#include "qdnd_p.h" +#include <shlobj.h> +#include "qurl.h" +#include "qvariant.h" +#include "qtextdocument.h" +#include "qdir.h" + +#if defined(Q_OS_WINCE) +#include "qguifunctions_wince.h" +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_IMAGEFORMAT_BMP +#ifndef CF_DIBV5 +#define CF_DIBV5 17 +#endif +/* The MSVC compilers allows multi-byte characters, that has the behavior of + * that each character gets shifted into position. 0x73524742 below is for MSVC + * equivalent to doing 'sRGB', but this does of course not work + * on conformant C++ compilers. */ +#define BMP_LCS_sRGB 0x73524742 +#define BMP_LCS_GM_IMAGES 0x00000004L + +struct _CIEXYZ { + long ciexyzX, ciexyzY, ciexyzZ; +}; + +struct _CIEXYZTRIPLE { + _CIEXYZ ciexyzRed, ciexyzGreen, ciexyzBlue; +}; + +struct BMP_BITMAPV5HEADER { + DWORD bV5Size; + LONG bV5Width; + LONG bV5Height; + WORD bV5Planes; + WORD bV5BitCount; + DWORD bV5Compression; + DWORD bV5SizeImage; + LONG bV5XPelsPerMeter; + LONG bV5YPelsPerMeter; + DWORD bV5ClrUsed; + DWORD bV5ClrImportant; + DWORD bV5RedMask; + DWORD bV5GreenMask; + DWORD bV5BlueMask; + DWORD bV5AlphaMask; + DWORD bV5CSType; + _CIEXYZTRIPLE bV5Endpoints; + DWORD bV5GammaRed; + DWORD bV5GammaGreen; + DWORD bV5GammaBlue; + DWORD bV5Intent; + DWORD bV5ProfileData; + DWORD bV5ProfileSize; + DWORD bV5Reserved; +}; +static const int BMP_BITFIELDS = 3; + +extern bool qt_read_dib(QDataStream&, QImage&); // qimage.cpp +extern bool qt_write_dib(QDataStream&, QImage); // qimage.cpp +static bool qt_write_dibv5(QDataStream &s, QImage image); +static bool qt_read_dibv5(QDataStream &s, QImage &image); +#endif + +//#define QMIME_DEBUG + + +// helpers for using global memory + +static int getCf(const FORMATETC &formatetc) +{ + return formatetc.cfFormat; +} + +static FORMATETC setCf(int cf) +{ + FORMATETC formatetc; + formatetc.cfFormat = cf; + formatetc.dwAspect = DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.ptd = NULL; + formatetc.tymed = TYMED_HGLOBAL; + return formatetc; +} + +static bool setData(const QByteArray &data, STGMEDIUM *pmedium) +{ + HGLOBAL hData = GlobalAlloc(0, data.size()); + if (!hData) + return false; + + void *out = GlobalLock(hData); + memcpy(out, data.data(), data.size()); + GlobalUnlock(hData); + pmedium->tymed = TYMED_HGLOBAL; + pmedium->hGlobal = hData; + pmedium->pUnkForRelease = 0; + return true; +} + +static QByteArray getData(int cf, IDataObject *pDataObj) +{ + QByteArray data; + FORMATETC formatetc = setCf(cf); + STGMEDIUM s; + if (pDataObj->GetData(&formatetc, &s) == S_OK) { + DWORD * val = (DWORD*)GlobalLock(s.hGlobal); + data = QByteArray::fromRawData((char*)val, GlobalSize(s.hGlobal)); + data.detach(); + GlobalUnlock(s.hGlobal); + ReleaseStgMedium(&s); + } else { + //Try reading IStream data + formatetc.tymed = TYMED_ISTREAM; + if (pDataObj->GetData(&formatetc, &s) == S_OK) { + char szBuffer[4096]; + ULONG actualRead = 0; + LARGE_INTEGER pos = {{0, 0}}; + //Move to front (can fail depending on the data model implemented) + HRESULT hr = s.pstm->Seek(pos, STREAM_SEEK_SET, NULL); + while(SUCCEEDED(hr)){ + hr = s.pstm->Read(szBuffer, sizeof(szBuffer), &actualRead); + if (SUCCEEDED(hr) && actualRead > 0) { + data += QByteArray::fromRawData(szBuffer, actualRead); + } + if (actualRead != sizeof(szBuffer)) + break; + } + data.detach(); + ReleaseStgMedium(&s); + } + } + return data; +} + +static bool canGetData(int cf, IDataObject * pDataObj) +{ + FORMATETC formatetc = setCf(cf); + if (pDataObj->QueryGetData(&formatetc) != S_OK){ + formatetc.tymed = TYMED_ISTREAM; + return pDataObj->QueryGetData(&formatetc) == S_OK; + } + return true; +} + +class QWindowsMimeList +{ +public: + QWindowsMimeList(); + ~QWindowsMimeList(); + void addWindowsMime(QWindowsMime * mime); + void removeWindowsMime(QWindowsMime * mime); + QList<QWindowsMime*> windowsMimes(); + +private: + void init(); + bool initialized; + QList<QWindowsMime*> mimes; +}; + +Q_GLOBAL_STATIC(QWindowsMimeList, theMimeList); + + +/*! + \class QWindowsMime + \brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats. + \ingroup draganddrop + + Qt's drag-and-drop and clipboard facilities use the MIME standard. + On X11, this maps trivially to the Xdnd protocol, but on Windows + although some applications use MIME types to describe clipboard + formats, others use arbitrary non-standardized naming conventions, + or unnamed built-in formats of Windows. + + By instantiating subclasses of QWindowsMime that provide conversions + between Windows Clipboard and MIME formats, you can convert + proprietary clipboard formats to MIME formats. + + Qt has predefined support for the following Windows Clipboard formats: + + \table + \header \o Windows Format \o Equivalent MIME type + \row \o \c CF_UNICODETEXT \o \c text/plain + \row \o \c CF_TEXT \o \c text/plain + \row \o \c CF_DIB \o \c{image/xyz}, where \c xyz is + a \l{QImageWriter::supportedImageFormats()}{Qt image format} + \row \o \c CF_HDROP \o \c text/uri-list + \row \o \c CF_INETURL \o \c text/uri-list + \row \o \c CF_HTML \o \c text/html + \endtable + + An example use of this class would be to map the Windows Metafile + clipboard format (\c CF_METAFILEPICT) to and from the MIME type + \c{image/x-wmf}. This conversion might simply be adding or removing + a header, or even just passing on the data. See \l{Drag and Drop} + for more information on choosing and definition MIME types. + + You can check if a MIME type is convertible using canConvertFromMime() and + can perform conversions with convertToMime() and convertFromMime(). +*/ + +/*! +Constructs a new conversion object, adding it to the globally accessed +list of available converters. +*/ +QWindowsMime::QWindowsMime() +{ + theMimeList()->addWindowsMime(this); +} + +/*! +Destroys a conversion object, removing it from the global +list of available converters. +*/ +QWindowsMime::~QWindowsMime() +{ + theMimeList()->removeWindowsMime(this); +} + + +/*! + Registers the MIME type \a mime, and returns an ID number + identifying the format on Windows. +*/ +int QWindowsMime::registerMimeType(const QString &mime) +{ + int f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16())); + if (!f) + qErrnoWarning("QWindowsMime::registerMimeType: Failed to register clipboard format"); + + return f; +} + + +/*! +\fn bool QWindowsMime::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const + + Returns true if the converter can convert from the \a mimeData to + the format specified in \a formatetc. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn bool QWindowsMime::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const + + Returns true if the converter can convert to the \a mimeType from + the available formats in \a pDataObj. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! +\fn QString QWindowsMime::mimeForFormat(const FORMATETC &formatetc) const + + Returns the mime type that will be created form the format specified + in \a formatetc, or an empty string if this converter does not support + \a formatetc. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! +\fn QVector<FORMATETC> QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const + + Returns a QVector of FORMATETC structures representing the different windows clipboard + formats that can be provided for the \a mimeType from the \a mimeData. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QVariant QWindowsMime::convertToMime(const QString &mimeType, IDataObject *pDataObj, + QVariant::Type preferredType) const + + Returns a QVariant containing the converted data for \a mimeType from \a pDataObj. + If possible the QVariant should be of the \a preferredType to avoid needless conversions. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! +\fn bool QWindowsMime::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const + + Convert the \a mimeData to the format specified in \a formatetc. + The converted data should then be placed in \a pmedium structure. + + Return true if the conversion was successful. + + All subclasses must reimplement this pure virtual function. +*/ + + +QWindowsMime *QWindowsMime::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) +{ + QList<QWindowsMime*> mimes = theMimeList()->windowsMimes(); + for (int i=mimes.size()-1; i>=0; --i) { + if (mimes.at(i)->canConvertFromMime(formatetc, mimeData)) + return mimes.at(i); + } + return 0; +} + +QWindowsMime *QWindowsMime::converterToMime(const QString &mimeType, IDataObject *pDataObj) +{ + QList<QWindowsMime*> mimes = theMimeList()->windowsMimes(); + for (int i=mimes.size()-1; i>=0; --i) { + if (mimes.at(i)->canConvertToMime(mimeType, pDataObj)) + return mimes.at(i); + } + return 0; +} + +QVector<FORMATETC> QWindowsMime::allFormatsForMime(const QMimeData *mimeData) +{ + QList<QWindowsMime*> mimes = theMimeList()->windowsMimes(); + QVector<FORMATETC> formatics; + formatics.reserve(20); +#ifndef QT_NO_DRAGANDDROP + QStringList formats = QInternalMimeData::formatsHelper(mimeData); + for (int f=0; f<formats.size(); ++f) { + for (int i=mimes.size()-1; i>=0; --i) + formatics += mimes.at(i)->formatsForMime(formats.at(f), mimeData); + } +#else + Q_UNUSED(mimeData); +#endif //QT_NO_DRAGANDDROP + return formatics; +} + +QStringList QWindowsMime::allMimesForFormats(IDataObject *pDataObj) +{ + QList<QWindowsMime*> mimes = theMimeList()->windowsMimes(); + QStringList formats; + LPENUMFORMATETC FAR fmtenum; + HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &fmtenum); + + if (hr == NOERROR) { + FORMATETC fmtetc; + while (S_OK == fmtenum->Next(1, &fmtetc, 0)) { +#if defined(QMIME_DEBUG) && !defined(Q_OS_WINCE) + qDebug("QWindowsMime::allMimesForFormats()"); + wchar_t buf[256] = {0}; + GetClipboardFormatName(fmtetc.cfFormat, buf, 255); + qDebug("CF = %d : %s", fmtetc.cfFormat, QString::fromWCharArray(buf)); +#endif + for (int i=mimes.size()-1; i>=0; --i) { + QString format = mimes.at(i)->mimeForFormat(fmtetc); + if (!format.isEmpty() && !formats.contains(format)) { + formats += format; + } + } + // as documented in MSDN to avoid possible memleak + if (fmtetc.ptd) + CoTaskMemFree(fmtetc.ptd); + } + fmtenum->Release(); + } + + return formats; +} + + +class QWindowsMimeText : public QWindowsMime +{ +public: + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const; + QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; +}; + +bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + int cf = getCf(formatetc); + return (cf == CF_UNICODETEXT || cf == CF_TEXT) && mimeData->hasText(); +} + +/* +text/plain is defined as using CRLF, but so many programs don't, +and programmers just look for '\n' in strings. +Windows really needs CRLF, so we ensure it here. +*/ +bool QWindowsMimeText::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const +{ + if (canConvertFromMime(formatetc, mimeData)) { + QByteArray data; + int cf = getCf(formatetc); + if (cf == CF_TEXT) { + data = mimeData->text().toLocal8Bit(); + // Anticipate required space for CRLFs at 1/40 + int maxsize=data.size()+data.size()/40+3; + QByteArray r(maxsize, '\0'); + char* o = r.data(); + const char* d = data.data(); + const int s = data.size(); + bool cr=false; + int j=0; + for (int i=0; i<s; i++) { + char c = d[i]; + if (c=='\r') + cr=true; + else { + if (c=='\n') { + if (!cr) + o[j++]='\r'; + } + cr=false; + } + o[j++]=c; + if (j+3 >= maxsize) { + maxsize += maxsize/4; + r.resize(maxsize); + o = r.data(); + } + } + o[j]=0; + return setData(r, pmedium); + } else if (cf == CF_UNICODETEXT) { + QString str = mimeData->text(); + const QChar *u = str.unicode(); + QString res; + const int s = str.length(); + int maxsize = s + s/40 + 3; + res.resize(maxsize); + int ri = 0; + bool cr = false; + for (int i=0; i < s; ++i) { + if (*u == QLatin1Char('\r')) + cr = true; + else { + if (*u == QLatin1Char('\n') && !cr) + res[ri++] = QLatin1Char('\r'); + cr = false; + } + res[ri++] = *u; + if (ri+3 >= maxsize) { + maxsize += maxsize/4; + res.resize(maxsize); + } + ++u; + } + res.truncate(ri); + const int byteLength = res.length() * sizeof(ushort); + QByteArray r(byteLength + 2, '\0'); + memcpy(r.data(), res.unicode(), byteLength); + r[byteLength] = 0; + r[byteLength+1] = 0; + return setData(r, pmedium); + } + } + return false; +} + +bool QWindowsMimeText::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + return mimeType.startsWith(QLatin1String("text/plain")) + && (canGetData(CF_UNICODETEXT, pDataObj) + || canGetData(CF_TEXT, pDataObj)); +} + +QString QWindowsMimeText::mimeForFormat(const FORMATETC &formatetc) const +{ + int cf = getCf(formatetc); + if (cf == CF_UNICODETEXT || cf == CF_TEXT) + return QLatin1String("text/plain"); + return QString(); +} + + +QVector<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector<FORMATETC> formatics; + if (mimeType.startsWith(QLatin1String("text/plain")) && mimeData->hasText()) { + formatics += setCf(CF_UNICODETEXT); + formatics += setCf(CF_TEXT); + } + return formatics; +} + +QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const +{ + QVariant ret; + + if (canConvertToMime(mime, pDataObj)) { + QString str; + QByteArray data = getData(CF_UNICODETEXT, pDataObj); + if (!data.isEmpty()) { + str = QString::fromWCharArray((const wchar_t *)data.data()); + str.replace(QLatin1String("\r\n"), QLatin1String("\n")); + } else { + data = getData(CF_TEXT, pDataObj); + if (!data.isEmpty()) { + const char* d = data.data(); + const int s = qstrlen(d); + QByteArray r(data.size()+1, '\0'); + char* o = r.data(); + int j=0; + for (int i=0; i<s; i++) { + char c = d[i]; + if (c!='\r') + o[j++]=c; + } + o[j]=0; + str = QString::fromLocal8Bit(r); + } + } + if (preferredType == QVariant::String) + ret = str; + else + ret = str.toUtf8(); + } + + return ret; +} + +class QWindowsMimeURI : public QWindowsMime +{ +public: + QWindowsMimeURI(); + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const; + QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; +private: + int CF_INETURL_W; // wide char version + int CF_INETURL; +}; + +QWindowsMimeURI::QWindowsMimeURI() +{ + CF_INETURL_W = QWindowsMime::registerMimeType(QLatin1String("UniformResourceLocatorW")); + CF_INETURL = QWindowsMime::registerMimeType(QLatin1String("UniformResourceLocator")); +} + +bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + if (getCf(formatetc) == CF_HDROP) { + QList<QUrl> urls = mimeData->urls(); + for (int i=0; i<urls.size(); i++) { + if (!urls.at(i).toLocalFile().isEmpty()) + return true; + } + } + return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasFormat(QLatin1String("text/uri-list")); +} + +bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const +{ + if (canConvertFromMime(formatetc, mimeData)) { + if (getCf(formatetc) == CF_HDROP) { + QList<QUrl> urls = mimeData->urls(); + QStringList fileNames; + int size = sizeof(DROPFILES)+2; + for (int i=0; i<urls.size(); i++) { + QString fn = QDir::toNativeSeparators(urls.at(i).toLocalFile()); + if (!fn.isEmpty()) { + size += sizeof(ushort) * (fn.length() + 1); + fileNames.append(fn); + } + } + + QByteArray result(size, '\0'); + DROPFILES* d = (DROPFILES*)result.data(); + d->pFiles = sizeof(DROPFILES); + GetCursorPos(&d->pt); // try + d->fNC = true; + char* files = ((char*)d) + d->pFiles; + + d->fWide = true; + wchar_t* f = (wchar_t*)files; + for (int i=0; i<fileNames.size(); i++) { + int l = fileNames.at(i).length(); + memcpy(f, fileNames.at(i).utf16(), l * sizeof(ushort)); + f += l; + *f++ = 0; + } + *f = 0; + + return setData(result, pmedium); + } else if (getCf(formatetc) == CF_INETURL_W) { + QList<QUrl> urls = mimeData->urls(); + QByteArray result; + if (!urls.isEmpty()) { + QString url = urls.at(0).toString(); + result = QByteArray((const char *)url.utf16(), url.length() * sizeof(ushort)); + } + result.append('\0'); + result.append('\0'); + return setData(result, pmedium); + } else if (getCf(formatetc) == CF_INETURL) { + QList<QUrl> urls = mimeData->urls(); + QByteArray result; + if (!urls.isEmpty()) + result = urls.at(0).toString().toLocal8Bit(); + return setData(result, pmedium); + } + } + + return false; +} + +bool QWindowsMimeURI::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + return mimeType == QLatin1String("text/uri-list") + && (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj)); +} + +QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const +{ + QString format; + if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) + format = QLatin1String("text/uri-list"); + return format; +} + +QVector<FORMATETC> QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector<FORMATETC> formatics; + if (mimeType == QLatin1String("text/uri-list")) { + if (canConvertFromMime(setCf(CF_HDROP), mimeData)) + formatics += setCf(CF_HDROP); + if (canConvertFromMime(setCf(CF_INETURL_W), mimeData)) + formatics += setCf(CF_INETURL_W); + if (canConvertFromMime(setCf(CF_INETURL), mimeData)) + formatics += setCf(CF_INETURL); + } + return formatics; +} + +QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const +{ + if (mimeType == QLatin1String("text/uri-list")) { + if (canGetData(CF_HDROP, pDataObj)) { + QByteArray texturi; + QList<QVariant> urls; + + QByteArray data = getData(CF_HDROP, pDataObj); + if (data.isEmpty()) + return QVariant(); + + LPDROPFILES hdrop = (LPDROPFILES)data.data(); + if (hdrop->fWide) { + const wchar_t* filesw = (const wchar_t *)(data.data() + hdrop->pFiles); + int i = 0; + while (filesw[i]) { + QString fileurl = QString::fromWCharArray(filesw + i); + urls += QUrl::fromLocalFile(fileurl); + i += fileurl.length()+1; + } + } else { + const char* files = (const char *)data.data() + hdrop->pFiles; + int i=0; + while (files[i]) { + urls += QUrl::fromLocalFile(QString::fromLocal8Bit(files+i)); + i += int(strlen(files+i))+1; + } + } + + if (preferredType == QVariant::Url && urls.size() == 1) + return urls.at(0); + else if (!urls.isEmpty()) + return urls; + } else if (canGetData(CF_INETURL_W, pDataObj)) { + QByteArray data = getData(CF_INETURL_W, pDataObj); + if (data.isEmpty()) + return QVariant(); + return QUrl(QString::fromWCharArray((const wchar_t *)data.constData())); + } else if (canGetData(CF_INETURL, pDataObj)) { + QByteArray data = getData(CF_INETURL, pDataObj); + if (data.isEmpty()) + return QVariant(); + return QUrl(QString::fromLocal8Bit(data.constData())); + } + } + return QVariant(); +} + +class QWindowsMimeHtml : public QWindowsMime +{ +public: + QWindowsMimeHtml(); + + // for converting from Qt + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; + QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; + + // for converting to Qt + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; + +private: + int CF_HTML; +}; + +QWindowsMimeHtml::QWindowsMimeHtml() +{ + CF_HTML = QWindowsMime::registerMimeType(QLatin1String("HTML Format")); +} + +QVector<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector<FORMATETC> formatetcs; + if (mimeType == QLatin1String("text/html") && (!mimeData->html().isEmpty())) + formatetcs += setCf(CF_HTML); + return formatetcs; +} + +QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const +{ + if (getCf(formatetc) == CF_HTML) + return QLatin1String("text/html"); + return QString(); +} + +bool QWindowsMimeHtml::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + return mimeType == QLatin1String("text/html") && canGetData(CF_HTML, pDataObj); +} + + +bool QWindowsMimeHtml::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + return getCf(formatetc) == CF_HTML && (!mimeData->html().isEmpty()); +} + +/* +The windows HTML clipboard format is as follows (xxxxxxxxxx is a 10 integer number giving the positions +in bytes). Charset used is mostly utf8, but can be different, ie. we have to look for the <meta> charset tag + + Version: 1.0 + StartHTML:xxxxxxxxxx + EndHTML:xxxxxxxxxx + StartFragment:xxxxxxxxxx + EndFragment:xxxxxxxxxx + ...html... + +*/ +QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const +{ + Q_UNUSED(preferredType); + QVariant result; + if (canConvertToMime(mime, pDataObj)) { + QByteArray html = getData(CF_HTML, pDataObj); +#ifdef QMIME_DEBUG + qDebug("QWindowsMimeHtml::convertToMime"); + qDebug("raw :"); + qDebug(html); +#endif + int start = html.indexOf("StartFragment:"); + int end = html.indexOf("EndFragment:"); + + if (start != -1) { + int startOffset = start + 14; + int i = startOffset; + while (html.at(i) != '\r' && html.at(i) != '\n') + ++i; + QByteArray bytecount = html.mid(startOffset, i - startOffset); + start = bytecount.toInt(); + } + + if (end != -1) { + int endOffset = end + 12; + int i = endOffset ; + while (html.at(i) != '\r' && html.at(i) != '\n') + ++i; + QByteArray bytecount = html.mid(endOffset , i - endOffset); + end = bytecount.toInt(); + } + + if (end > start && start > 0) { + html = "<!--StartFragment-->" + html.mid(start, end - start); + html += "<!--EndFragment-->"; + html.replace('\r', ""); + result = QString::fromUtf8(html); + } + } + return result; +} + +bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const +{ + if (canConvertFromMime(formatetc, mimeData)) { + QByteArray data = mimeData->html().toUtf8(); + QByteArray result = + "Version:1.0\r\n" // 0-12 + "StartHTML:0000000105\r\n" // 13-35 + "EndHTML:0000000000\r\n" // 36-55 + "StartFragment:0000000000\r\n" // 58-86 + "EndFragment:0000000000\r\n\r\n"; // 87-105 + + if (data.indexOf("<!--StartFragment-->") == -1) + result += "<!--StartFragment-->"; + result += data; + if (data.indexOf("<!--EndFragment-->") == -1) + result += "<!--EndFragment-->"; + + // set the correct number for EndHTML + QByteArray pos = QString::number(result.size()).toLatin1(); + memcpy((char *)(result.data() + 53 - pos.length()), pos.constData(), pos.length()); + + // set correct numbers for StartFragment and EndFragment + pos = QString::number(result.indexOf("<!--StartFragment-->") + 20).toLatin1(); + memcpy((char *)(result.data() + 79 - pos.length()), pos.constData(), pos.length()); + pos = QString::number(result.indexOf("<!--EndFragment-->")).toLatin1(); + memcpy((char *)(result.data() + 103 - pos.length()), pos.constData(), pos.length()); + + return setData(result, pmedium); + } + return false; +} + + +#ifndef QT_NO_IMAGEFORMAT_BMP +class QWindowsMimeImage : public QWindowsMime +{ +public: + QWindowsMimeImage(); + // for converting from Qt + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; + QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; + + // for converting to Qt + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; +private: + bool hasOriginalDIBV5(IDataObject *pDataObj) const; + UINT CF_PNG; +}; + +QWindowsMimeImage::QWindowsMimeImage() +{ + CF_PNG = RegisterClipboardFormat(L"PNG"); +} + +QVector<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector<FORMATETC> formatetcs; + if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) { + //add DIBV5 if image has alpha channel + QImage image = qvariant_cast<QImage>(mimeData->imageData()); + if (!image.isNull() && image.hasAlphaChannel()) + formatetcs += setCf(CF_DIBV5); + formatetcs += setCf(CF_DIB); + } + return formatetcs; +} + +QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const +{ + int cf = getCf(formatetc); + if (cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG)) + return QLatin1String("application/x-qt-image"); + return QString(); +} + +bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + if ((mimeType == QLatin1String("application/x-qt-image")) && + (canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj))) + return true; + return false; +} + +bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + int cf = getCf(formatetc); + if (mimeData->hasImage()) { + if (cf == CF_DIB) + return true; + else if (cf == CF_DIBV5) { + //support DIBV5 conversion only if the image has alpha channel + QImage image = qvariant_cast<QImage>(mimeData->imageData()); + if (!image.isNull() && image.hasAlphaChannel()) + return true; + } + } + return false; +} + +bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const +{ + int cf = getCf(formatetc); + if ((cf == CF_DIB || cf == CF_DIBV5) && mimeData->hasImage()) { + QImage img = qvariant_cast<QImage>(mimeData->imageData()); + if (img.isNull()) + return false; + QByteArray ba; + QDataStream s(&ba, QIODevice::WriteOnly); + s.setByteOrder(QDataStream::LittleEndian);// Intel byte order #### + if (cf == CF_DIB) { + if (img.format() > QImage::Format_ARGB32) + img = img.convertToFormat(QImage::Format_RGB32); + if (qt_write_dib(s, img)) + return setData(ba, pmedium); + } else { + if (qt_write_dibv5(s, img)) + return setData(ba, pmedium); + } + } + return false; +} + +bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const +{ + bool isSynthesized = true; + IEnumFORMATETC *pEnum =NULL; + HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum); + if (res == S_OK && pEnum) { + FORMATETC fc; + while ((res = pEnum->Next(1, &fc, 0)) == S_OK) { + if (fc.ptd) + CoTaskMemFree(fc.ptd); + if (fc.cfFormat == CF_DIB) + break; + else if (fc.cfFormat == CF_DIBV5) { + isSynthesized = false; + break; + } + } + pEnum->Release(); + } + return !isSynthesized; +} + +QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const +{ + Q_UNUSED(preferredType); + QVariant result; + if (mimeType != QLatin1String("application/x-qt-image")) + return result; + //Try to convert from a format which has more data + //DIBV5, use only if its is not synthesized + if (canGetData(CF_DIBV5, pDataObj) && hasOriginalDIBV5(pDataObj)) { + QImage img; + QByteArray data = getData(CF_DIBV5, pDataObj); + QDataStream s(&data, QIODevice::ReadOnly); + s.setByteOrder(QDataStream::LittleEndian); + if (qt_read_dibv5(s, img)) { // #### supports only 32bit DIBV5 + return img; + } + } + //PNG, MS Office place this (undocumented) + if (canGetData(CF_PNG, pDataObj)) { + QImage img; + QByteArray data = getData(CF_PNG, pDataObj); + if (img.loadFromData(data, "PNG")) { + return img; + } + } + //Fallback to DIB + if (canGetData(CF_DIB, pDataObj)) { + QImage img; + QByteArray data = getData(CF_DIB, pDataObj); + QDataStream s(&data, QIODevice::ReadOnly); + s.setByteOrder(QDataStream::LittleEndian);// Intel byte order #### + if (qt_read_dib(s, img)) { // ##### encaps "-14" + return img; + } + } + // Failed + return result; +} +#endif + +class QBuiltInMimes : public QWindowsMime +{ +public: + QBuiltInMimes(); + + // for converting from Qt + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; + QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; + + // for converting to Qt + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; + +private: + QMap<int, QString> outFormats; + QMap<int, QString> inFormats; +}; + +QBuiltInMimes::QBuiltInMimes() +: QWindowsMime() +{ + outFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color")); + inFormats.insert(QWindowsMime::registerMimeType(QLatin1String("application/x-color")), QLatin1String("application/x-color")); +} + +bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + // really check + return formatetc.tymed & TYMED_HGLOBAL + && outFormats.contains(formatetc.cfFormat) + && mimeData->formats().contains(outFormats.value(formatetc.cfFormat)); +} + +bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const +{ + if (canConvertFromMime(formatetc, mimeData)) { + QByteArray data; + if (outFormats.value(getCf(formatetc)) == QLatin1String("text/html")) { + // text/html is in wide chars on windows (compatible with mozillia) + QString html = mimeData->html(); + // same code as in the text converter up above + const QChar *u = html.unicode(); + QString res; + const int s = html.length(); + int maxsize = s + s/40 + 3; + res.resize(maxsize); + int ri = 0; + bool cr = false; + for (int i=0; i < s; ++i) { + if (*u == QLatin1Char('\r')) + cr = true; + else { + if (*u == QLatin1Char('\n') && !cr) + res[ri++] = QLatin1Char('\r'); + cr = false; + } + res[ri++] = *u; + if (ri+3 >= maxsize) { + maxsize += maxsize/4; + res.resize(maxsize); + } + ++u; + } + res.truncate(ri); + const int byteLength = res.length() * sizeof(ushort); + QByteArray r(byteLength + 2, '\0'); + memcpy(r.data(), res.unicode(), byteLength); + r[byteLength] = 0; + r[byteLength+1] = 0; + data = r; + } else { +#ifndef QT_NO_DRAGANDDROP + data = QInternalMimeData::renderDataHelper(outFormats.value(getCf(formatetc)), mimeData); +#endif //QT_NO_DRAGANDDROP + } + return setData(data, pmedium); + } + return false; +} + +QVector<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector<FORMATETC> formatetcs; + if (!outFormats.keys(mimeType).isEmpty() && mimeData->formats().contains(mimeType)) + formatetcs += setCf(outFormats.key(mimeType)); + return formatetcs; +} + +bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + return (!inFormats.keys(mimeType).isEmpty()) + && canGetData(inFormats.key(mimeType), pDataObj); +} + +QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const +{ + QVariant val; + if (canConvertToMime(mimeType, pDataObj)) { + QByteArray data = getData(inFormats.key(mimeType), pDataObj); + if (!data.isEmpty()) { +#ifdef QMIME_DEBUG + qDebug("QBuiltInMimes::convertToMime()"); +#endif + if (mimeType == QLatin1String("text/html") && preferredType == QVariant::String) { + // text/html is in wide chars on windows (compatible with Mozilla) + val = QString::fromWCharArray((const wchar_t *)data.data()); + } else { + val = data; // it should be enough to return the data and let QMimeData do the rest. + } + } + } + return val; +} + +QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const +{ + return inFormats.value(getCf(formatetc)); +} + + +class QLastResortMimes : public QWindowsMime +{ +public: + + QLastResortMimes(); + // for converting from Qt + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; + QVector<FORMATETC> formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; + + // for converting to Qt + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; + +private: + QMap<int, QString> formats; + static QStringList ianaTypes; + static QStringList excludeList; +}; + +QStringList QLastResortMimes::ianaTypes; +QStringList QLastResortMimes::excludeList; + +QLastResortMimes::QLastResortMimes() +{ + //MIME Media-Types + if (!ianaTypes.size()) { + ianaTypes.append(QLatin1String("application/")); + ianaTypes.append(QLatin1String("audio/")); + ianaTypes.append(QLatin1String("example/")); + ianaTypes.append(QLatin1String("image/")); + ianaTypes.append(QLatin1String("message/")); + ianaTypes.append(QLatin1String("model/")); + ianaTypes.append(QLatin1String("multipart/")); + ianaTypes.append(QLatin1String("text/")); + ianaTypes.append(QLatin1String("video/")); + } + //Types handled by other classes + if (!excludeList.size()) { + excludeList.append(QLatin1String("HTML Format")); + excludeList.append(QLatin1String("UniformResourceLocator")); + excludeList.append(QLatin1String("text/html")); + excludeList.append(QLatin1String("text/plain")); + excludeList.append(QLatin1String("text/uri-list")); + excludeList.append(QLatin1String("application/x-qt-image")); + excludeList.append(QLatin1String("application/x-color")); + } +} + +bool QLastResortMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + // really check +#ifndef QT_NO_DRAGANDDROP + return formatetc.tymed & TYMED_HGLOBAL + && (formats.contains(formatetc.cfFormat) + && QInternalMimeData::hasFormatHelper(formats.value(formatetc.cfFormat), mimeData)); +#else + Q_UNUSED(mimeData); + Q_UNUSED(formatetc); + return formatetc.tymed & TYMED_HGLOBAL + && formats.contains(formatetc.cfFormat); +#endif //QT_NO_DRAGANDDROP +} + +bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const +{ +#ifndef QT_NO_DRAGANDDROP + return canConvertFromMime(formatetc, mimeData) + && setData(QInternalMimeData::renderDataHelper(formats.value(getCf(formatetc)), mimeData), pmedium); +#else + Q_UNUSED(mimeData); + Q_UNUSED(formatetc); + Q_UNUSED(pmedium); + return false; +#endif //QT_NO_DRAGANDDROP +} + +QVector<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const +{ + QVector<FORMATETC> formatetcs; + if (!formats.keys(mimeType).isEmpty()) { + formatetcs += setCf(formats.key(mimeType)); + } else if (!excludeList.contains(mimeType, Qt::CaseInsensitive)){ + // register any other available formats + int cf = QWindowsMime::registerMimeType(mimeType); + QLastResortMimes *that = const_cast<QLastResortMimes *>(this); + that->formats.insert(cf, mimeType); + formatetcs += setCf(cf); + } + return formatetcs; +} +static const char x_qt_windows_mime[] = "application/x-qt-windows-mime;value=\""; + +static bool isCustomMimeType(const QString &mimeType) +{ + return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive); +} + +static QString customMimeType(const QString &mimeType) +{ + int len = sizeof(x_qt_windows_mime) - 1; + int n = mimeType.lastIndexOf(QLatin1Char('\"'))-len; + return mimeType.mid(len, n); +} + +bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + if (isCustomMimeType(mimeType)) { + QString clipFormat = customMimeType(mimeType); + int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); + return canGetData(cf, pDataObj); + } else if (formats.keys(mimeType).isEmpty()) { + // if it is not in there then register it an see if we can get it + int cf = QWindowsMime::registerMimeType(mimeType); + return canGetData(cf, pDataObj); + } else { + return canGetData(formats.key(mimeType), pDataObj); + } + return false; +} + +QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const +{ + Q_UNUSED(preferredType); + QVariant val; + if (canConvertToMime(mimeType, pDataObj)) { + QByteArray data; + if (isCustomMimeType(mimeType)) { + QString clipFormat = customMimeType(mimeType); + int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); + data = getData(cf, pDataObj); + } else if (formats.keys(mimeType).isEmpty()) { + int cf = QWindowsMime::registerMimeType(mimeType); + data = getData(cf, pDataObj); + } else { + data = getData(formats.key(mimeType), pDataObj); + } + if (!data.isEmpty()) + val = data; // it should be enough to return the data and let QMimeData do the rest. + } + return val; +} + +QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const +{ + QString format = formats.value(getCf(formatetc)); + if (!format.isEmpty()) + return format; + + wchar_t buffer[256]; + int len = GetClipboardFormatName(getCf(formatetc), buffer, 256); + + if (len) { + QString clipFormat = QString::fromWCharArray(buffer, len); +#ifndef QT_NO_DRAGANDDROP + if (QInternalMimeData::canReadData(clipFormat)) + format = clipFormat; + else if((formatetc.cfFormat >= 0xC000)){ + //create the mime as custom. not registered. + if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) { + //check if this is a mime type + bool ianaType = false; + int sz = ianaTypes.size(); + for (int i = 0; i < sz; i++) { + if (clipFormat.startsWith(ianaTypes[i], Qt::CaseInsensitive)) { + ianaType = true; + break; + } + } + if (!ianaType) + format = QLatin1String(x_qt_windows_mime) + clipFormat + QLatin1Char('\"'); + else + format = clipFormat; + } + } +#endif //QT_NO_DRAGANDDROP + } + + return format; +} + +QWindowsMimeList::QWindowsMimeList() + : initialized(false) +{ +} + +QWindowsMimeList::~QWindowsMimeList() +{ + while (mimes.size()) + delete mimes.first(); +} + + +void QWindowsMimeList::init() +{ + if (!initialized) { + initialized = true; +#ifndef QT_NO_IMAGEFORMAT_BMP + new QWindowsMimeImage; +#endif + new QLastResortMimes; + new QWindowsMimeText; + new QWindowsMimeURI; + + new QWindowsMimeHtml; + new QBuiltInMimes; + } +} + +void QWindowsMimeList::addWindowsMime(QWindowsMime * mime) +{ + init(); + mimes.append(mime); +} + +void QWindowsMimeList::removeWindowsMime(QWindowsMime * mime) +{ + init(); + mimes.removeAll(mime); +} + +QList<QWindowsMime*> QWindowsMimeList::windowsMimes() +{ + init(); + return mimes; +} + +#ifndef QT_NO_IMAGEFORMAT_BMP +static bool qt_write_dibv5(QDataStream &s, QImage image) +{ + QIODevice* d = s.device(); + if (!d->isWritable()) + return false; + + //depth will be always 32 + int bpl_bmp = image.width()*4; + + BMP_BITMAPV5HEADER bi ={0}; + bi.bV5Size = sizeof(BMP_BITMAPV5HEADER); + bi.bV5Width = image.width(); + bi.bV5Height = image.height(); + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5SizeImage = bpl_bmp*image.height(); + bi.bV5XPelsPerMeter = 0; + bi.bV5YPelsPerMeter = 0; + bi.bV5ClrUsed = 0; + bi.bV5ClrImportant = 0; + bi.bV5BlueMask = 0x000000ff; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5RedMask = 0x00ff0000; + bi.bV5AlphaMask = 0xff000000; + bi.bV5CSType = BMP_LCS_sRGB; //LCS_sRGB + bi.bV5Intent = BMP_LCS_GM_IMAGES; //LCS_GM_IMAGES + + d->write(reinterpret_cast<const char*>(&bi), bi.bV5Size); + if (s.status() != QDataStream::Ok) + return false; + + DWORD colorSpace[3] = {0x00ff0000,0x0000ff00,0x000000ff}; + d->write(reinterpret_cast<const char*>(colorSpace), sizeof(colorSpace)); + if (s.status() != QDataStream::Ok) + return false; + + if (image.format() != QImage::Format_ARGB32) + image = image.convertToFormat(QImage::Format_ARGB32); + + uchar *buf = new uchar[bpl_bmp]; + uchar *b; + + memset(buf, 0, bpl_bmp); + for (int y=image.height()-1; y>=0; y--) { + // write the image bits + QRgb *p = (QRgb *)image.scanLine(y); + QRgb *end = p + image.width(); + b = buf; + while (p < end) { + int alpha = qAlpha(*p); + if (alpha) { + *b++ = qBlue(*p); + *b++ = qGreen(*p); + *b++ = qRed(*p); + } else { + //white for fully transparent pixels. + *b++ = 0xff; + *b++ = 0xff; + *b++ = 0xff; + } + *b++ = alpha; + p++; + } + d->write((char*)buf, bpl_bmp); + if (s.status() != QDataStream::Ok) { + delete[] buf; + return false; + } + } + delete[] buf; + return true; +} + +static int calc_shift(int mask) +{ + int result = 0; + while (!(mask & 1)) { + result++; + mask >>= 1; + } + return result; +} + +//Supports only 32 bit DIBV5 +static bool qt_read_dibv5(QDataStream &s, QImage &image) +{ + BMP_BITMAPV5HEADER bi; + QIODevice* d = s.device(); + if (d->atEnd()) + return false; + + d->read((char *)&bi, sizeof(bi)); // read BITMAPV5HEADER header + if (s.status() != QDataStream::Ok) + return false; + + int nbits = bi.bV5BitCount; + int comp = bi.bV5Compression; + if (nbits != 32 || bi.bV5Planes != 1 || comp != BMP_BITFIELDS) + return false; //Unsupported DIBV5 format + + int w = bi.bV5Width, h = bi.bV5Height; + int red_mask = bi.bV5RedMask; + int green_mask = bi.bV5GreenMask; + int blue_mask = bi.bV5BlueMask; + int alpha_mask = bi.bV5AlphaMask; + int red_shift = 0; + int green_shift = 0; + int blue_shift = 0; + int alpha_shift = 0; + QImage::Format format = QImage::Format_ARGB32; + + if (bi.bV5Height < 0) + h = -h; // support images with negative height + if (image.size() != QSize(w, h) || image.format() != format) { + image = QImage(w, h, format); + if (image.isNull()) // could not create image + return false; + } + image.setDotsPerMeterX(bi.bV5XPelsPerMeter); + image.setDotsPerMeterY(bi.bV5YPelsPerMeter); + // read color table + DWORD colorSpace[3]; + if (d->read((char *)colorSpace, sizeof(colorSpace)) != sizeof(colorSpace)) + return false; + + red_shift = calc_shift(red_mask); + green_shift = calc_shift(green_mask); + blue_shift = calc_shift(blue_mask); + if (alpha_mask) { + alpha_shift = calc_shift(alpha_mask); + } + + int bpl = image.bytesPerLine(); + uchar *data = image.bits(); + register QRgb *p; + QRgb *end; + uchar *buf24 = new uchar[bpl]; + int bpl24 = ((w*nbits+31)/32)*4; + uchar *b; + unsigned int c; + + while (--h >= 0) { + p = (QRgb *)(data + h*bpl); + end = p + w; + if (d->read((char *)buf24,bpl24) != bpl24) + break; + b = buf24; + while (p < end) { + c = *b | (*(b+1))<<8 | (*(b+2))<<16 | (*(b+3))<<24; + *p++ = qRgba(((c & red_mask) >> red_shift) , + ((c & green_mask) >> green_shift), + ((c & blue_mask) >> blue_shift), + ((c & alpha_mask) >> alpha_shift)); + b += 4; + } + } + delete[] buf24; + + if (bi.bV5Height < 0) { + // Flip the image + uchar *buf = new uchar[bpl]; + h = -bi.bV5Height; + for (int y = 0; y < h/2; ++y) { + memcpy(buf, data + y*bpl, bpl); + memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl); + memcpy(data + (h-y-1)*bpl, buf, bpl); + } + delete [] buf; + } + + return true; +} + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qmotifdnd_x11.cpp b/src/gui/guikernel/qmotifdnd_x11.cpp new file mode 100644 index 0000000000..eef4cc470b --- /dev/null +++ b/src/gui/guikernel/qmotifdnd_x11.cpp @@ -0,0 +1,1031 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* The following copyright notice pertains to the code as contributed +to Qt, not to Nokia's modifications. It is replicated +in doc/dnd.doc, where the documentation system can see it. */ + +/* Copyright 1996 Daniel Dardailler. + + Permission to use, copy, modify, distribute, and sell this software + for any purpose is hereby granted without fee, provided that the above + copyright notice appear in all copies and that both that copyright + notice and this permission notice appear in supporting documentation, + and that the name of Daniel Dardailler not be used in advertising or + publicity pertaining to distribution of the software without specific, + written prior permission. Daniel Dardailler makes no representations + about the suitability of this software for any purpose. It is + provided "as is" without express or implied warranty. + + Modifications Copyright 1999 Matt Koss, under the same license as + above. +************************************************************/ + +/***********************************************************/ +/* Motif Drag&Drop Dynamic Protocol messaging API code */ +/* Only requires Xlib layer - not MT safe */ +/* Author: Daniel Dardailler, daniel@x.org */ +/* Adapted by: Matt Koss, koss@napri.sk */ +/* Further adaptions by: Nokia Corporation and/or its subsidiary(-ies) */ +/***********************************************************/ + +#include "qplatformdefs.h" + +#include "qapplication.h" + +#ifndef QT_NO_DRAGANDDROP + +#include "qdebug.h" +#include "qtextcodec.h" +#include "qwidget.h" +#include "qevent.h" +#include "qt_x11_p.h" +#include "qx11info_x11.h" +#include "qiodevice.h" +#include "qdnd_p.h" + +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +static Window sourceWindow = XNone; +static QWidget *dropWidget = 0; +static Qt::DropAction lastAcceptedAction = Qt::IgnoreAction; + +static Atom Dnd_selection = 0; +static Time Dnd_selection_time; + +static Atom * src_targets ; +static ushort num_src_targets ; + +// Motif definitions +#define DndVersion 1 +#define DndRevision 0 +#define DndIncludeVersion (DndVersion * 10 + DndRevision) + +/* The following values are used in the DndData structure */ + +/* protocol style */ +#define DND_DRAG_NONE 0 +#define DND_DRAG_DROP_ONLY 1 +#define DND_DRAG_DYNAMIC 5 + +/* message type */ +#define DND_TOP_LEVEL_ENTER 0 +#define DND_TOP_LEVEL_LEAVE 1 +#define DND_DRAG_MOTION 2 +#define DND_DROP_SITE_ENTER 3 +#define DND_DROP_SITE_LEAVE 4 +#define DND_DROP_START 5 +#define DND_OPERATION_CHANGED 8 + +/* operation(s) */ +#define DND_NOOP 0L +#define DND_MOVE (1L << 0) +#define DND_COPY (1L << 1) +#define DND_LINK (1L << 2) + +static Qt::DropActions DndOperationsToQtDropActions(uchar op) +{ + Qt::DropActions actions = Qt::IgnoreAction; + if (op | DND_MOVE) + actions |= Qt::MoveAction; + if (op | DND_COPY) + actions |= Qt::CopyAction; + if (op | DND_LINK) + actions |= Qt::LinkAction; + return actions; +} + +static uchar QtDropActionToDndOperation(Qt::DropAction action) +{ + switch (action & Qt::ActionMask) { + case Qt::CopyAction: + default: + return DND_COPY; + case Qt::MoveAction: + return DND_MOVE; + case Qt::LinkAction: + return DND_LINK; + } +} + + +/* status */ +#define DND_NO_DROP_SITE 1 +#define DND_INVALID_DROP_SITE 2 +#define DND_VALID_DROP_SITE 3 + +/* completion */ +#define DND_DROP 0 +#define DND_DROP_HELP 1 +#define DND_DROP_CANCEL 2 + +#define BYTE unsigned char +#define CARD32 unsigned int +#define CARD16 unsigned short +#define INT16 signed short + +/* Client side structure used in the API */ +typedef struct { + unsigned char reason; /* message type: DND_TOP_LEVEL_ENTER, etc */ + Time time ; + unsigned char operation; + unsigned char operations; + unsigned char status; + unsigned char completion; + short x ; + short y ; + Window src_window ; + Atom property ; +} DndData ; + + +typedef struct _DndSrcProp { + BYTE byte_order ; + BYTE protocol_version ; + CARD16 target_index ; + CARD32 selection ; +} DndSrcProp ; + +typedef struct _DndReceiverProp { + BYTE byte_order ; + BYTE protocol_version ; + BYTE protocol_style ; + BYTE pad1; + CARD32 proxy_window; + CARD16 num_drop_sites ; + CARD16 pad2; + CARD32 total_size; +} DndReceiverProp ; + +/* need to use some union hack since window and property are in + different order depending on the message ... */ +typedef struct _DndTop { + CARD32 src_window; + CARD32 property; +} DndTop ; + +typedef struct _DndPot { + INT16 x; + INT16 y; + CARD32 property; + CARD32 src_window; +} DndPot ; + +typedef struct _DndMessage { + BYTE reason; + BYTE byte_order; + CARD16 flags; + CARD32 time; + union { + DndTop top ; + DndPot pot ; + } data ; +} DndMessage ; + +typedef struct { + BYTE byte_order; + BYTE protocol_version; + CARD16 num_target_lists; + CARD32 data_size; + /* then come series of CARD16,CARD32,CARD32,CARD32... */ +} DndTargets; + + +/* protocol version */ +#define DND_PROTOCOL_VERSION 0 + + +#define DND_EVENT_TYPE_MASK ((BYTE)0x80) +#define DND_EVENT_TYPE_SHIFT 7 +#define DND_CLEAR_EVENT_TYPE ((BYTE)0x7F) + +/* message_type is data[0] of the client_message + this return 1 (receiver bit up) or 0 (initiator) */ +#define DND_GET_EVENT_TYPE(message_type) \ +((char) (((message_type) & DND_EVENT_TYPE_MASK) >> DND_EVENT_TYPE_SHIFT)) + +/* event_type can be 0 (initiator) or 1 (receiver) */ +#define DND_SET_EVENT_TYPE(event_type) \ +(((BYTE)(event_type) << DND_EVENT_TYPE_SHIFT) & DND_EVENT_TYPE_MASK) + + +#define DND_OPERATION_MASK ((CARD16) 0x000F) +#define DND_OPERATION_SHIFT 0 +#define DND_STATUS_MASK ((CARD16) 0x00F0) +#define DND_STATUS_SHIFT 4 +#define DND_OPERATIONS_MASK ((CARD16) 0x0F00) +#define DND_OPERATIONS_SHIFT 8 +#define DND_COMPLETION_MASK ((CARD16) 0xF000) +#define DND_COMPLETION_SHIFT 12 + +#define DND_GET_OPERATION(flags) \ +((unsigned char) \ +(((flags) & DND_OPERATION_MASK) >> DND_OPERATION_SHIFT)) + +#define DND_SET_OPERATION(operation) \ +(((CARD16)(operation) << DND_OPERATION_SHIFT)\ +& DND_OPERATION_MASK) + +#define DND_GET_STATUS(flags) \ +((unsigned char) \ +(((flags) & DND_STATUS_MASK) >> DND_STATUS_SHIFT)) + +#define DND_SET_STATUS(status) \ +(((CARD16)(status) << DND_STATUS_SHIFT)\ +& DND_STATUS_MASK) + +#define DND_GET_OPERATIONS(flags) \ +((unsigned char) \ +(((flags) & DND_OPERATIONS_MASK) >> DND_OPERATIONS_SHIFT)) + +#define DND_SET_OPERATIONS(operation) \ +(((CARD16)(operation) << DND_OPERATIONS_SHIFT)\ +& DND_OPERATIONS_MASK) + +#define DND_GET_COMPLETION(flags) \ +((unsigned char) \ +(((flags) & DND_COMPLETION_MASK) >> DND_COMPLETION_SHIFT)) + +#define DND_SET_COMPLETION(completion) \ +(((CARD16)(completion) << DND_COMPLETION_SHIFT)\ +& DND_COMPLETION_MASK) + + +#define SWAP4BYTES(l) {\ +struct { unsigned t :32;} bit32;\ +char n, *tp = (char *) &bit32;\ +bit32.t = l;\ +n = tp[0]; tp[0] = tp[3]; tp[3] = n;\ +n = tp[1]; tp[1] = tp[2]; tp[2] = n;\ +l = bit32.t;\ +} + +#define SWAP2BYTES(s) {\ +struct { unsigned t :16; } bit16;\ +char n, *tp = (char *) &bit16;\ +bit16.t = s;\ +n = tp[0]; tp[0] = tp[1]; tp[1] = n;\ +s = bit16.t;\ +} + + +/** Private extern functions */ + +static unsigned char DndByteOrder (); + + +/***** Targets/Index stuff */ + +typedef struct { + int num_targets; + Atom *targets; +} DndTargetsTableEntryRec, * DndTargetsTableEntry; + +typedef struct { + int num_entries; + DndTargetsTableEntry entries; +} DndTargetsTableRec, * DndTargetsTable; + + +static ushort _DndIndexToTargets(Display * display, + int index, + Atom ** targets); + +extern void qt_x11_intern_atom(const char *, Atom *); + +///////////////////////////////////////////////////////////////// + +static unsigned char DndByteOrder () +{ + static unsigned char byte_order = 0; + + if (!byte_order) { + unsigned int endian = 1; + byte_order = (*((char *)&endian))?'l':'B'; + } + return byte_order ; +} + + + +static void DndReadSourceProperty(Display * dpy, + Window window, Atom dnd_selection, + Atom ** targets, unsigned short * num_targets) +{ + unsigned char *retval = 0; + Atom type ; + int format ; + unsigned long bytesafter, lengthRtn; + + if ((XGetWindowProperty (dpy, window, dnd_selection, 0L, 100000L, + False, ATOM(_MOTIF_DRAG_INITIATOR_INFO), &type, + &format, &lengthRtn, &bytesafter, + &retval) != Success) + || (type == XNone)) { + *num_targets = 0; + return ; + } + + DndSrcProp * src_prop = (DndSrcProp *)retval; + + if (src_prop->byte_order != DndByteOrder()) { + SWAP2BYTES(src_prop->target_index); + SWAP4BYTES(src_prop->selection); + } + + *num_targets = _DndIndexToTargets(dpy, src_prop->target_index, targets); + + XFree((char*)src_prop); +} + + +/* Position the _MOTIF_DRAG_RECEIVER_INFO property on the dropsite window. + Called by the receiver of the drop to indicate the + supported protocol style : dynamic, drop_only or none */ +static void DndWriteReceiverProperty(Display * dpy, Window window, + unsigned char protocol_style) +{ + DndReceiverProp receiver_prop; + + // squelch potential valgrind errors about uninitialized reads + memset(&receiver_prop, 0, sizeof(receiver_prop)); + + receiver_prop.byte_order = DndByteOrder() ; + receiver_prop.protocol_version = DND_PROTOCOL_VERSION; + receiver_prop.protocol_style = protocol_style ; + receiver_prop.proxy_window = XNone ; + receiver_prop.num_drop_sites = 0 ; + receiver_prop.total_size = sizeof(DndReceiverProp); + + /* write the buffer to the property */ + XChangeProperty (dpy, window, ATOM(_MOTIF_DRAG_RECEIVER_INFO), ATOM(_MOTIF_DRAG_RECEIVER_INFO), + 8, PropModeReplace, + (unsigned char *)&receiver_prop, + sizeof(DndReceiverProp)); +} + + +/* protocol style equiv (preregister stuff really) */ +#define DND_DRAG_DROP_ONLY_EQUIV 3 +#define DND_DRAG_DYNAMIC_EQUIV1 2 +#define DND_DRAG_DYNAMIC_EQUIV2 4 + + +/* Produce a client message to be sent by the caller */ +static void DndFillClientMessage(Display * dpy, Window window, + XClientMessageEvent *cm, + DndData * dnd_data, + char receiver) +{ + DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ; + + cm->display = dpy; + cm->type = ClientMessage; + cm->serial = LastKnownRequestProcessed(dpy); + cm->send_event = True; + cm->window = window; + cm->format = 8; + cm->message_type = ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE); + + dnd_message->reason = dnd_data->reason | DND_SET_EVENT_TYPE(receiver); + + dnd_message->byte_order = DndByteOrder(); + + /* we're filling in flags with more stuff that necessary, + depending on the reason, but it doesn't matter */ + dnd_message->flags = 0 ; + dnd_message->flags |= DND_SET_STATUS(dnd_data->status) ; + dnd_message->flags |= DND_SET_OPERATION(dnd_data->operation) ; + dnd_message->flags |= DND_SET_OPERATIONS(dnd_data->operations) ; + dnd_message->flags |= DND_SET_COMPLETION(dnd_data->completion) ; + + dnd_message->time = dnd_data->time ; + + switch(dnd_data->reason) { + case DND_DROP_SITE_LEAVE: break ; + case DND_TOP_LEVEL_ENTER: + case DND_TOP_LEVEL_LEAVE: + dnd_message->data.top.src_window = dnd_data->src_window ; + dnd_message->data.top.property = dnd_data->property ; + break ; /* cannot fall through since the byte layout is different in + both set of messages, see top and pot union stuff */ + + case DND_DRAG_MOTION: + case DND_OPERATION_CHANGED: + case DND_DROP_SITE_ENTER: + case DND_DROP_START: + dnd_message->data.pot.x = dnd_data->x ; /* mouse position */ + dnd_message->data.pot.y = dnd_data->y ; + dnd_message->data.pot.src_window = dnd_data->src_window ; + dnd_message->data.pot.property = dnd_data->property ; + break ; + default: + break ; + } + +} + +static Bool DndParseClientMessage(XClientMessageEvent *cm, DndData * dnd_data, + char * receiver) +{ + DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ; + + if (cm->message_type != ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE)) { + return False ; + } + + if (dnd_message->byte_order != DndByteOrder()) { + SWAP2BYTES(dnd_message->flags); + SWAP4BYTES(dnd_message->time); + } /* do the rest in the switch */ + + dnd_data->reason = dnd_message->reason ; + if (DND_GET_EVENT_TYPE(dnd_data->reason)) + *receiver = 1 ; + else + *receiver = 0 ; + dnd_data->reason &= DND_CLEAR_EVENT_TYPE ; + + dnd_data->time = dnd_message->time ; + + /* we're reading in more stuff that necessary. but who cares */ + dnd_data->status = DND_GET_STATUS(dnd_message->flags) ; + dnd_data->operation = DND_GET_OPERATION(dnd_message->flags) ; + dnd_data->operations = DND_GET_OPERATIONS(dnd_message->flags) ; + dnd_data->completion = DND_GET_COMPLETION(dnd_message->flags) ; + + switch(dnd_data->reason) { + case DND_TOP_LEVEL_ENTER: + case DND_TOP_LEVEL_LEAVE: + if (dnd_message->byte_order != DndByteOrder()) { + SWAP4BYTES(dnd_message->data.top.src_window); + SWAP4BYTES(dnd_message->data.top.property); + } + dnd_data->src_window = dnd_message->data.top.src_window ; + dnd_data->property = dnd_message->data.top.property ; + break ; /* cannot fall through, see above comment in write msg */ + + case DND_DRAG_MOTION: + case DND_OPERATION_CHANGED: + case DND_DROP_SITE_ENTER: + case DND_DROP_START: + if (dnd_message->byte_order != DndByteOrder()) { + SWAP2BYTES(dnd_message->data.pot.x); + SWAP2BYTES(dnd_message->data.pot.y); + SWAP4BYTES(dnd_message->data.pot.property); + SWAP4BYTES(dnd_message->data.pot.src_window); + } + dnd_data->x = dnd_message->data.pot.x ; + dnd_data->y = dnd_message->data.pot.y ; + dnd_data->property = dnd_message->data.pot.property ; + dnd_data->src_window = dnd_message->data.pot.src_window ; + break ; + + case DND_DROP_SITE_LEAVE: + break; + default: + break ; + } + + return True ; +} + + +static Window MotifWindow(Display *display) +{ + Atom type; + int format; + unsigned long size; + unsigned long bytes_after; + unsigned char *property = 0; + Window motif_window ; + + /* this version does no caching, so it's slow: round trip each time */ + + if ((XGetWindowProperty (display, RootWindow(display, 0), + ATOM(_MOTIF_DRAG_WINDOW), + 0L, 100000L, False, AnyPropertyType, + &type, &format, &size, &bytes_after, + &property) == Success) && + (type != XNone)) { + motif_window = *(Window *)property; + } else { + XSetWindowAttributes sAttributes; + + /* really, this should be done on a separate connection, + with XSetCloseDownMode (RetainPermanent), so that + others don't have to recreate it; hopefully, some real + Motif application will be around to do it */ + + sAttributes.override_redirect = True; + sAttributes.event_mask = PropertyChangeMask; + motif_window = XCreateWindow (display, + RootWindow (display, 0), + -170, -560, 1, 1, 0, 0, + InputOnly, CopyFromParent, + (CWOverrideRedirect |CWEventMask), + &sAttributes); + XMapWindow (display, motif_window); + } + + if (property) { + XFree ((char *)property); + } + + return (motif_window); +} + + +static DndTargetsTable TargetsTable(Display *display) +{ + Atom type; + int format; + unsigned long size; + unsigned long bytes_after; + Window motif_window = MotifWindow(display) ; + unsigned char *retval; + DndTargetsTable targets_table ; + int i,j ; + char * target_data ; + + /* this version does no caching, so it's slow: round trip each time */ + /* ideally, register for property notify on this target_list + atom and update when necessary only */ + + if ((XGetWindowProperty (display, motif_window, + ATOM(_MOTIF_DRAG_TARGETS), 0L, 100000L, + False, ATOM(_MOTIF_DRAG_TARGETS), + &type, &format, &size, &bytes_after, + &retval) != Success) || + type == XNone) { + qWarning("QMotifDND: Cannot get property on Motif window"); + return 0; + } + + DndTargets * target_prop = (DndTargets *)retval; + + if (target_prop->protocol_version != DND_PROTOCOL_VERSION) { + qWarning("QMotifDND: Protocol mismatch"); + } + + if (target_prop->byte_order != DndByteOrder()) { + /* need to swap num_target_lists and size */ + SWAP2BYTES(target_prop->num_target_lists); + SWAP4BYTES(target_prop->data_size); + } + + /* now parse DndTarget prop data in a TargetsTable */ + + targets_table = (DndTargetsTable)malloc(sizeof(DndTargetsTableRec)); + targets_table->num_entries = target_prop->num_target_lists ; + targets_table->entries = (DndTargetsTableEntry) + malloc(sizeof(DndTargetsTableEntryRec) * target_prop->num_target_lists); + + target_data = (char*)target_prop + sizeof(*target_prop) ; + + for (i = 0 ; i < targets_table->num_entries; i++) { + CARD16 num_targets ; + CARD32 atom ; + + memcpy(&num_targets, target_data, 2); + target_data += 2; + + /* potential swap needed here */ + if (target_prop->byte_order != DndByteOrder()) + SWAP2BYTES(num_targets); + + targets_table->entries[i].num_targets = num_targets ; + targets_table->entries[i].targets = (Atom *) + malloc(sizeof(Atom) * targets_table->entries[i].num_targets); + + + for (j = 0; j < num_targets; j++) { + memcpy(&atom, target_data, 4); + target_data += 4; + + /* another potential swap needed here */ + if (target_prop->byte_order != DndByteOrder()) + SWAP4BYTES(atom); + + targets_table->entries[i].targets[j] = (Atom) atom ; + } + } + + if (target_prop) { + XFree((char *)target_prop); + } + + return targets_table ; +} + + +static ushort _DndIndexToTargets(Display * display, + int index, + Atom ** targets) +{ + DndTargetsTable targets_table; + int i ; + + /* again, slow: no caching here, alloc/free each time */ + + if (!(targets_table = TargetsTable (display)) || + (index >= targets_table->num_entries)) { + if (targets_table) + XFree((char*)targets_table); + return 0; + } + + /* transfer the correct target list index */ + *targets = (Atom*)malloc(sizeof(Atom)*targets_table-> + entries[index].num_targets); + memcpy((char*)*targets, + (char*)targets_table->entries[index].targets, + sizeof(Atom)*targets_table->entries[index].num_targets); + + /* free the target table and its guts */ + for (i=0 ; i < targets_table->num_entries; i++) + XFree((char*)targets_table->entries[i].targets); + + int tmp = targets_table->entries[index].num_targets; + XFree((char*)targets_table); + + return tmp; // targets_table->entries[index].num_targets; +} + + +QByteArray QX11Data::motifdndFormat(int n) +{ + if (!motifdnd_active) + return 0; // should not happen + + if (n >= num_src_targets) + return 0; + + Atom target = src_targets[n]; + + if (target == XA_STRING) + return "text/plain;charset=ISO-8859-1"; + if (target == ATOM(UTF8_STRING)) + return "text/plain;charset=UTF-8"; + if (target == ATOM(COMPOUND_TEXT)) + return QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name(); + if (target == ATOM(TEXT)) + return "text/plain"; + + return ("x-motif-dnd/" + X11->xdndAtomToString(target)); +} + + +QVariant QX11Data::motifdndObtainData(const char *mimeType) +{ + QByteArray result; + + if (Dnd_selection == 0 || !dropWidget) + return result; + + // try to convert the selection to the requested property + // qDebug("trying to convert to '%s'", mimeType); + + int n=0; + QByteArray f; + do { + f = motifdndFormat(n); + if (f.isEmpty()) + return result; + n++; + } while(qstricmp(mimeType, f.data())); + + Atom conversion_type = XNone; + if (f == "text/plain;charset=ISO-8859-1") { + conversion_type = XA_STRING; + } else if (f == "text/plain;charset=UTF-8") { + conversion_type = ATOM(UTF8_STRING); + } else if (f == (QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name())) { + conversion_type = ATOM(COMPOUND_TEXT); + } else if (f == "text/plain") { + conversion_type = ATOM(TEXT); + } else if (f.startsWith("x-motif-dnd/")) { + // strip off the "x-motif-dnd/" prefix + conversion_type = X11->xdndStringToAtom(f.remove(0, 12)); + } + + if (XGetSelectionOwner(X11->display, Dnd_selection) == XNone) { + return result; // should never happen? + } + + QWidget* tw = dropWidget; + if ((dropWidget->windowType() == Qt::Desktop)) { + tw = new QWidget; + } + + // convert selection to the appropriate type + XConvertSelection (X11->display, Dnd_selection, conversion_type, + Dnd_selection, tw->internalWinId(), Dnd_selection_time); + + XFlush(X11->display); + + XEvent xevent; + bool got=X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000); + if (got) { + Atom type; + + if (X11->clipboardReadProperty(tw->internalWinId(), Dnd_selection, true, &result, 0, &type, 0)) { + } + } + + // we have to convert selection in order to indicate success to the initiator + XConvertSelection (X11->display, Dnd_selection, ATOM(XmTRANSFER_SUCCESS), + Dnd_selection, tw->internalWinId(), Dnd_selection_time); + + // wait again for SelectionNotify event + X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000); + + if ((dropWidget->windowType() == Qt::Desktop)) { + delete tw; + } + + return result; +} + + +void QX11Data::motifdndEnable(QWidget *widget, bool) +{ + DndWriteReceiverProperty(display, widget->internalWinId(), DND_DRAG_DYNAMIC); +} + + +void QX11Data::motifdndHandle(QWidget *widget, const XEvent * xe, bool /* passive */) +{ + XEvent event = *xe; + XClientMessageEvent cm ; + DndData dnd_data ; + char receiver ; + + if (!(DndParseClientMessage ((XClientMessageEvent*)&event, + &dnd_data, &receiver))) { + return; + } + + switch (dnd_data.reason) { + + case DND_DRAG_MOTION: + { + QPoint p = widget->mapFromGlobal(QPoint(dnd_data.x, dnd_data.y)); + QWidget *c = widget->childAt(p); + + if (!c || !c->acceptDrops()) { + // not over a drop site + if (dropWidget) { + QDragLeaveEvent dragLeaveEvent; + QApplication::sendEvent(dropWidget, &dragLeaveEvent); + + dropWidget = 0; + lastAcceptedAction = Qt::IgnoreAction; + + dnd_data.reason = DND_DROP_SITE_LEAVE; + dnd_data.time = X11->time; + DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver); + XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ; + } else { + dnd_data.reason = DND_DRAG_MOTION; + dnd_data.status = DND_NO_DROP_SITE; + dnd_data.time = X11->time; + dnd_data.operation = DND_NOOP; + dnd_data.operations = DND_NOOP; + DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver); + XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ; + } + } else { + Q_ASSERT(c != 0); + p = c->mapFrom(widget, p); + + if (dropWidget != c) { + if (dropWidget) { + QDragLeaveEvent le; + QApplication::sendEvent(dropWidget, &le); + } + + dropWidget = c; + lastAcceptedAction = Qt::IgnoreAction; + + const Qt::DropActions possibleActions = + DndOperationsToQtDropActions(dnd_data.operations); + QDragEnterEvent de(p, possibleActions, QDragManager::self()->dropData, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + QApplication::sendEvent(dropWidget, &de); + + dnd_data.reason = DND_DROP_SITE_ENTER; + dnd_data.time = X11->time; + if (de.isAccepted()) { + lastAcceptedAction = de.dropAction(); + + dnd_data.status = DND_VALID_DROP_SITE; + dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction); + } else { + dnd_data.status = DND_INVALID_DROP_SITE; + dnd_data.operation = DND_NOOP; + dnd_data.operations = DND_NOOP; + } + DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver); + XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm); + } else { + const Qt::DropActions possibleActions = + DndOperationsToQtDropActions(dnd_data.operations); + QDragMoveEvent me(p, possibleActions, QDragManager::self()->dropData, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + if (lastAcceptedAction != Qt::IgnoreAction) { + me.setDropAction(lastAcceptedAction); + me.accept(); + } + QApplication::sendEvent(dropWidget, &me); + + dnd_data.reason = DND_DRAG_MOTION; + dnd_data.time = X11->time; + + if (me.isAccepted()) { + lastAcceptedAction = me.dropAction(); + + dnd_data.status = DND_VALID_DROP_SITE; + dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction); + } else { + dnd_data.status = DND_INVALID_DROP_SITE; + dnd_data.operation = DND_NOOP; + dnd_data.operations = DND_NOOP; + } + + DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver); + XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm); + } + } + + break; + } + + case DND_TOP_LEVEL_ENTER: + { + /* get the size of our drop site for later use */ + + motifdnd_active = true; + sourceWindow = dnd_data.src_window; + + /* no answer needed, just read source property */ + DndReadSourceProperty (event.xclient.display, + sourceWindow, + dnd_data.property, + &src_targets, &num_src_targets); + + break; + } + + case DND_TOP_LEVEL_LEAVE: + { + XEvent nextEvent; + if (XCheckTypedWindowEvent(X11->display, widget->winId(), ClientMessage, &nextEvent)) { + // we just want to check, not eat (should use XPeekIfEvent) + XPutBackEvent(X11->display, &nextEvent); + + if (DndParseClientMessage (&nextEvent.xclient, &dnd_data, &receiver) + && dnd_data.reason == DND_DROP_START) { + // expecting drop next, keeping DnD alive + break; + } + } + + // not expecting drop, need to send drag leave events and such here + if (dropWidget) { + QDragLeaveEvent le; + QApplication::sendEvent(dropWidget, &le); + } + + sourceWindow = XNone; + dropWidget = 0; + lastAcceptedAction = Qt::IgnoreAction; + + motifdnd_active = false; + + break; + } + + case DND_OPERATION_CHANGED: + // ### need to echo + break; + + case DND_DROP_START: + { + Q_ASSERT(motifdnd_active); + Q_ASSERT(sourceWindow == dnd_data.src_window); + + if (!dropWidget || lastAcceptedAction == Qt::IgnoreAction) { + // echo DROP_START + dnd_data.reason = DND_DROP_START; + dnd_data.status = DND_NO_DROP_SITE; + dnd_data.operation = DND_NOOP; + dnd_data.operations = DND_NOOP; + DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0); + XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm); + + // we have to convert selection in order to indicate failure to the initiator + XConvertSelection (X11->display, dnd_data.property, ATOM(XmTRANSFER_FAILURE), + dnd_data.property, dnd_data.src_window, dnd_data.time); + + if (dropWidget) { + QDragLeaveEvent e; + QApplication::sendEvent(dropWidget, &e); + } + + motifdnd_active = false; + sourceWindow = XNone; + dropWidget = 0; + lastAcceptedAction = Qt::IgnoreAction; + + return; + } + + // store selection and its time + Dnd_selection = dnd_data.property; + Dnd_selection_time = dnd_data.time; + + QPoint p(dnd_data.x, dnd_data.y); + QDropEvent de(dropWidget->mapFromGlobal(p), Qt::CopyAction, QDragManager::self()->dropData, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + if (lastAcceptedAction != Qt::IgnoreAction) { + de.setDropAction(lastAcceptedAction); + de.accept(); + } + QApplication::sendEvent(dropWidget, &de); + + // reset + Dnd_selection = XNone; + Dnd_selection_time = 0; + + // echo DROP_START depending on the result of the dropEvent + if (de.isAccepted()) { + dnd_data.reason = DND_DROP_START; + dnd_data.status = DND_VALID_DROP_SITE; + dnd_data.operation = QtDropActionToDndOperation(de.dropAction()); + } else { + dnd_data.reason = DND_DROP_START; + dnd_data.status = DND_NO_DROP_SITE; + dnd_data.operation = DND_NOOP; + dnd_data.operations = DND_NOOP; + } + DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0); + XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm); + + sourceWindow = XNone; + dropWidget = 0; + lastAcceptedAction = Qt::IgnoreAction; + + motifdnd_active = false; + + break; + } + + default: + break; + } // end of switch (dnd_data.reason) +} + +QT_END_NAMESPACE + +#endif // QT_NO_DRAGANDDROP diff --git a/src/gui/guikernel/qole_win.cpp b/src/gui/guikernel/qole_win.cpp new file mode 100644 index 0000000000..24e2d5b292 --- /dev/null +++ b/src/gui/guikernel/qole_win.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdnd_p.h" + +#if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD)) + +#if defined(Q_OS_WINCE) +#include <shlobj.h> +#include "qguifunctions_wince.h" +#endif + +QT_BEGIN_NAMESPACE + +QOleEnumFmtEtc::QOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs) +{ + m_isNull = false; + m_dwRefs = 1; + m_nIndex = 0; + + for (int idx = 0; idx < fmtetcs.count(); ++idx) { + LPFORMATETC destetc = new FORMATETC(); + if (copyFormatEtc(destetc, (LPFORMATETC)&(fmtetcs.at(idx)))) { + m_lpfmtetcs.append(destetc); + } else { + m_isNull = true; + delete destetc; + break; + } + } +} + +QOleEnumFmtEtc::QOleEnumFmtEtc(const QVector<LPFORMATETC> &lpfmtetcs) +{ + m_isNull = false; + m_dwRefs = 1; + m_nIndex = 0; + + for (int idx = 0; idx < lpfmtetcs.count(); ++idx) { + LPFORMATETC srcetc = lpfmtetcs.at(idx); + LPFORMATETC destetc = new FORMATETC(); + if (copyFormatEtc(destetc, srcetc)) { + m_lpfmtetcs.append(destetc); + } else { + m_isNull = true; + delete destetc; + break; + } + } +} + +QOleEnumFmtEtc::~QOleEnumFmtEtc() +{ + LPMALLOC pmalloc; + +#if !defined(Q_OS_WINCE) + if (CoGetMalloc(MEMCTX_TASK, &pmalloc) == NOERROR) { +#else + if (SHGetMalloc(&pmalloc) == NOERROR) { +#endif + for (int idx = 0; idx < m_lpfmtetcs.count(); ++idx) { + LPFORMATETC tmpetc = m_lpfmtetcs.at(idx); + if (tmpetc->ptd) + pmalloc->Free(tmpetc->ptd); + delete tmpetc; + } + + pmalloc->Release(); + } + m_lpfmtetcs.clear(); +} + +bool QOleEnumFmtEtc::isNull() const +{ + return m_isNull; +} + +// IUnknown methods +STDMETHODIMP +QOleEnumFmtEtc::QueryInterface(REFIID riid, void FAR* FAR* ppvObj) +{ + if (riid == IID_IUnknown || riid == IID_IEnumFORMATETC) { + *ppvObj = this; + AddRef(); + return NOERROR; + } + *ppvObj = NULL; + return ResultFromScode(E_NOINTERFACE); +} + +STDMETHODIMP_(ULONG) +QOleEnumFmtEtc::AddRef(void) +{ + return ++m_dwRefs; +} + +STDMETHODIMP_(ULONG) +QOleEnumFmtEtc::Release(void) +{ + if (--m_dwRefs == 0) { + delete this; + return 0; + } + return m_dwRefs; +} + +// IEnumFORMATETC methods +STDMETHODIMP +QOleEnumFmtEtc::Next(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched) +{ + ULONG i=0; + ULONG nOffset; + + if (rgelt == NULL) + return ResultFromScode(E_INVALIDARG); + + while (i < celt) { + nOffset = m_nIndex + i; + + if (nOffset < ULONG(m_lpfmtetcs.count())) { + copyFormatEtc((LPFORMATETC)&(rgelt[i]), m_lpfmtetcs.at(nOffset)); + i++; + } else { + break; + } + } + + m_nIndex += (WORD)i; + + if (pceltFetched != NULL) + *pceltFetched = i; + + if (i != celt) + return ResultFromScode(S_FALSE); + + return NOERROR; +} + +STDMETHODIMP +QOleEnumFmtEtc::Skip(ULONG celt) +{ + ULONG i=0; + ULONG nOffset; + + while (i < celt) { + nOffset = m_nIndex + i; + + if (nOffset < ULONG(m_lpfmtetcs.count())) { + i++; + } else { + break; + } + } + + m_nIndex += (WORD)i; + + if (i != celt) + return ResultFromScode(S_FALSE); + + return NOERROR; +} + +STDMETHODIMP +QOleEnumFmtEtc::Reset() +{ + m_nIndex = 0; + return NOERROR; +} + +STDMETHODIMP +QOleEnumFmtEtc::Clone(LPENUMFORMATETC FAR* newEnum) +{ + if (newEnum == NULL) + return ResultFromScode(E_INVALIDARG); + + QOleEnumFmtEtc *result = new QOleEnumFmtEtc(m_lpfmtetcs); + result->m_nIndex = m_nIndex; + + if (result->isNull()) { + delete result; + return ResultFromScode(E_OUTOFMEMORY); + } else { + *newEnum = result; + } + + return NOERROR; +} + +bool QOleEnumFmtEtc::copyFormatEtc(LPFORMATETC dest, LPFORMATETC src) const +{ + if (dest == NULL || src == NULL) + return false; + + *dest = *src; + + if (src->ptd) { + LPVOID pout; + LPMALLOC pmalloc; + +#if !defined(Q_OS_WINCE) + if (CoGetMalloc(MEMCTX_TASK, &pmalloc) != NOERROR) +#else + if (SHGetMalloc(&pmalloc) != NOERROR) +#endif + return false; + + pout = (LPVOID)pmalloc->Alloc(src->ptd->tdSize); + memcpy(dest->ptd, src->ptd, size_t(src->ptd->tdSize)); + + pmalloc->Release(); + } + + return true; +} + +QT_END_NAMESPACE +#endif // QT_NO_DRAGANDDROP && QT_NO_CLIPBOARD diff --git a/src/gui/guikernel/qplatformclipboard_qpa.cpp b/src/gui/guikernel/qplatformclipboard_qpa.cpp new file mode 100644 index 0000000000..957a4dfd2e --- /dev/null +++ b/src/gui/guikernel/qplatformclipboard_qpa.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qplatformclipboard_qpa.h" + +#ifndef QT_NO_CLIPBOARD + +QT_BEGIN_NAMESPACE + +class QClipboardData +{ +public: + QClipboardData(); + ~QClipboardData(); + + void setSource(QMimeData* s) + { + if (s == src) + return; + delete src; + src = s; + } + QMimeData* source() + { return src; } + +private: + QMimeData* src; +}; + +QClipboardData::QClipboardData() +{ + src = 0; +} + +QClipboardData::~QClipboardData() +{ + delete src; +} + +Q_GLOBAL_STATIC(QClipboardData,q_clipboardData); + +QPlatformClipboard::~QPlatformClipboard() +{ + +} + +const QMimeData *QPlatformClipboard::mimeData(QClipboard::Mode mode) const +{ + //we know its clipboard + Q_UNUSED(mode); + return q_clipboardData()->source(); +} + +void QPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + //we know its clipboard + Q_UNUSED(mode); + q_clipboardData()->setSource(data); +} + +bool QPlatformClipboard::supportsMode(QClipboard::Mode mode) const +{ + return mode == QClipboard::Clipboard; +} + +QT_END_NAMESPACE + +#endif //QT_NO_CLIPBOARD diff --git a/src/gui/guikernel/qplatformclipboard_qpa.h b/src/gui/guikernel/qplatformclipboard_qpa.h new file mode 100644 index 0000000000..3381c062b8 --- /dev/null +++ b/src/gui/guikernel/qplatformclipboard_qpa.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMCLIPBOARD_QPA_H +#define QPLATFORMCLIPBOARD_QPA_H + +#include <qplatformdefs.h> + +#ifndef QT_NO_CLIPBOARD + +#include <QtGui/QClipboard> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QPlatformClipboard +{ +public: + virtual ~QPlatformClipboard(); + + virtual const QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard ) const; + virtual void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard); + virtual bool supportsMode(QClipboard::Mode mode) const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_CLIPBOARD + +#endif //QPLATFORMCLIPBOARD_QPA_H diff --git a/src/gui/guikernel/qplatformcursor_qpa.cpp b/src/gui/guikernel/qplatformcursor_qpa.cpp new file mode 100644 index 0000000000..2ea8332ecc --- /dev/null +++ b/src/gui/guikernel/qplatformcursor_qpa.cpp @@ -0,0 +1,660 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qplatformcursor_qpa.h" + +#include <QWidget> +#include <QPainter> +#include <QBitmap> +#include <QApplication> + +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QList <QWeakPointer<QPlatformCursor> > QPlatformCursorPrivate::instances; + +/*! + \class QGraphicsSystemCursor + + \brief The QGraphicsSystemCursor class provides information about + pointer device events (movement, buttons), and requests to change + the currently displayed cursor. + + Note that QGraphicsSystemCursor does not include any graphics for + display. An application that sets a QCursor may provide its own + graphics. + + \sa QGraphicsSystemCursorImage +*/ + +/*! + \fn virtual void QGraphicsSystemCursor::pointerEvent(const QMouseEvent & event) + + This method is called by Qt whenever a QMouseEvent is generated by the + underlying pointer input. \a event is a reference to the QMouseEvent in + question. A default do-nothing implementation is provided. + + \sa QApplicationPrivate::handleMouseEvent() +*/ + +/*! + \fn virtual void QGraphicsSystemCursor::changeCursor(QCursor * widgetCursor, QWidget * widget) + + \brief This method is called by Qt whenever the cursor graphic should be changed. + + Implementation of this method is mandatory for a subclass of QGraphicsSystemCursor. + + \a widgetCursor is a pointer to the QCursor that should be displayed. + + \a widget is a pointer to the widget currently displayed at QCursor::pos(). Note + that this may be 0 if the current position is not occupied by a displayed widget. + + \sa QApplicationPrivate::handleMouseEvent(), QCursor::pos() +*/ + +/*! + \fn QGraphicsSystemCursor::QGraphicsSystemCursor() + + \brief Constructs a QGraphicsSystemCursor +*/ +QPlatformCursor::QPlatformCursor(QPlatformScreen *scr ) + : screen(scr) +{ + QPlatformCursorPrivate::instances.append(this); +} + +// End of display and pointer event handling code +// Beginning of built-in cursor graphics +// from src/gui/embedded/QGraphicsSystemCursorImage_qws.cpp + +/*! + \class QGraphicsSystemCursorImage + + \brief The QGraphicsSystemCursorImage class provides a set of graphics + intended to be used as cursors. + + \sa QGraphicsSystemCursor +*/ + +static QPlatformCursorImage *systemCursorTable[Qt::LastCursor+1]; +static bool systemCursorTableInit = false; + +// 16 x 16 +static const uchar cur_arrow_bits[] = { + 0x07, 0x00, 0x39, 0x00, 0xc1, 0x01, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x08, + 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x88, 0x08, 0x48, 0x11, 0x28, 0x22, + 0x10, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x00 }; +static const uchar mcur_arrow_bits[] = { + 0x07, 0x00, 0x3f, 0x00, 0xff, 0x01, 0xfe, 0x0f, 0xfe, 0x1f, 0xfe, 0x0f, + 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x07, 0xf8, 0x0f, 0x78, 0x1f, 0x38, 0x3e, + 0x10, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00 }; + +static const unsigned char cur_up_arrow_bits[] = { + 0x80, 0x00, 0x40, 0x01, 0x40, 0x01, 0x20, 0x02, 0x20, 0x02, 0x10, 0x04, + 0x10, 0x04, 0x08, 0x08, 0x78, 0x0f, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, + 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0, 0x01}; +static const unsigned char mcur_up_arrow_bits[] = { + 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xe0, 0x03, 0xe0, 0x03, 0xf0, 0x07, + 0xf0, 0x07, 0xf8, 0x0f, 0xf8, 0x0f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01}; + +static const unsigned char cur_cross_bits[] = { + 0xc0, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, + 0x7f, 0x7f, 0x01, 0x40, 0x7f, 0x7f, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, + 0x40, 0x01, 0x40, 0x01, 0xc0, 0x01, 0x00, 0x00}; +static const unsigned char mcur_cross_bits[] = { + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00}; + +static const uchar cur_ibeam_bits[] = { + 0x00, 0x00, 0xe0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_ibeam_bits[] = { + 0xf0, 0x07, 0xf0, 0x07, 0xf0, 0x07, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xf0, 0x07, 0xf0, 0x07, 0xf0, 0x07, 0x00, 0x00 }; + +static const uchar cur_ver_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f, + 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00 }; +static const uchar mcur_ver_bits[] = { + 0x00, 0x00, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, + 0xfc, 0x7f, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xfc, 0x7f, 0xf8, 0x3f, + 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03 }; + +static const uchar cur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x30, 0x18, + 0x38, 0x38, 0xfc, 0x7f, 0xfc, 0x7f, 0x38, 0x38, 0x30, 0x18, 0x20, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x60, 0x0c, 0x70, 0x1c, 0x78, 0x3c, + 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0x78, 0x3c, + 0x70, 0x1c, 0x60, 0x0c, 0x40, 0x04, 0x00, 0x00 }; +static const uchar cur_bdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, + 0x00, 0x37, 0x88, 0x23, 0xd8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_bdiag_bits[] = { + 0x00, 0x00, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x7e, 0x04, 0x7f, + 0x8c, 0x7f, 0xdc, 0x77, 0xfc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, + 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 }; +static const uchar cur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, + 0xf8, 0x00, 0xd8, 0x01, 0x88, 0x23, 0x00, 0x37, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, + 0xfc, 0x41, 0xfc, 0x63, 0xdc, 0x77, 0x8c, 0x7f, 0x04, 0x7f, 0x00, 0x7e, + 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x7f, 0x00, 0x00 }; + +// 20 x 20 +static const uchar forbidden_bits[] = { + 0x00,0x00,0x00,0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xf0,0x00,0x38,0xc0,0x01, + 0x7c,0x80,0x03,0xec,0x00,0x03,0xce,0x01,0x07,0x86,0x03,0x06,0x06,0x07,0x06, + 0x06,0x0e,0x06,0x06,0x1c,0x06,0x0e,0x38,0x07,0x0c,0x70,0x03,0x1c,0xe0,0x03, + 0x38,0xc0,0x01,0xf0,0xe0,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00,0x00,0x00,0x00 }; + +static const uchar forbiddenm_bits[] = { + 0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xff,0x00,0xf8,0xff,0x01,0xfc,0xf0,0x03, + 0xfe,0xc0,0x07,0xfe,0x81,0x07,0xff,0x83,0x0f,0xcf,0x07,0x0f,0x8f,0x0f,0x0f, + 0x0f,0x1f,0x0f,0x0f,0x3e,0x0f,0x1f,0xfc,0x0f,0x1e,0xf8,0x07,0x3e,0xf0,0x07, + 0xfc,0xe0,0x03,0xf8,0xff,0x01,0xf0,0xff,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00}; + +// 32 x 32 +static const uchar wait_data_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0x04, 0x40, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x08, 0x20, 0x00, + 0x00, 0x08, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, + 0x00, 0x50, 0x15, 0x00, 0x00, 0xa0, 0x0a, 0x00, 0x00, 0x40, 0x05, 0x00, + 0x00, 0x80, 0x02, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x20, 0x08, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x21, 0x00, 0x00, 0x88, 0x22, 0x00, + 0x00, 0x48, 0x25, 0x00, 0x00, 0xa8, 0x2a, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0x04, 0x40, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar wait_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xc0, 0x07, 0x00, + 0x00, 0x80, 0x03, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x0f, 0x00, + 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uchar hsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03, + 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar hsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00, + 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07, + 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00, + 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar vsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar vsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar phand_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x7e, 0x04, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x70, 0x08, 0x00, 0x00, + 0x08, 0x08, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, 0x08, 0x22, 0x00, 0x00, + 0x30, 0x41, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x40, 0x12, 0x00, 0x00, + 0x80, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar phandm_bits[] = { + 0xfe, 0x01, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, + 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, + 0xfc, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, + 0xf8, 0xff, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, + 0xc0, 0x1f, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uchar size_all_data_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x80, 0x81, 0xc0, 0x00, + 0xc0, 0xff, 0xff, 0x01, 0x80, 0x81, 0xc0, 0x00, 0x00, 0x81, 0x40, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar size_all_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc2, 0x21, 0x00, + 0x00, 0xc3, 0x61, 0x00, 0x80, 0xc3, 0xe1, 0x00, 0xc0, 0xff, 0xff, 0x01, + 0xe0, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xff, 0x01, 0x80, 0xc3, 0xe1, 0x00, + 0x00, 0xc3, 0x61, 0x00, 0x00, 0xc2, 0x21, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uchar whatsthis_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x07, 0x00, + 0x09, 0x18, 0x0e, 0x00, 0x11, 0x1c, 0x0e, 0x00, 0x21, 0x1c, 0x0e, 0x00, + 0x41, 0x1c, 0x0e, 0x00, 0x81, 0x1c, 0x0e, 0x00, 0x01, 0x01, 0x07, 0x00, + 0x01, 0x82, 0x03, 0x00, 0xc1, 0xc7, 0x01, 0x00, 0x49, 0xc0, 0x01, 0x00, + 0x95, 0xc0, 0x01, 0x00, 0x93, 0xc0, 0x01, 0x00, 0x21, 0x01, 0x00, 0x00, + 0x20, 0xc1, 0x01, 0x00, 0x40, 0xc2, 0x01, 0x00, 0x40, 0x02, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static const uchar whatsthism_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0x00, 0x07, 0xf8, 0x0f, 0x00, + 0x0f, 0xfc, 0x1f, 0x00, 0x1f, 0x3e, 0x1f, 0x00, 0x3f, 0x3e, 0x1f, 0x00, + 0x7f, 0x3e, 0x1f, 0x00, 0xff, 0x3e, 0x1f, 0x00, 0xff, 0x9d, 0x0f, 0x00, + 0xff, 0xc3, 0x07, 0x00, 0xff, 0xe7, 0x03, 0x00, 0x7f, 0xe0, 0x03, 0x00, + 0xf7, 0xe0, 0x03, 0x00, 0xf3, 0xe0, 0x03, 0x00, 0xe1, 0xe1, 0x03, 0x00, + 0xe0, 0xe1, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + +static const uchar busy_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x41, 0xe0, 0xff, 0x00, 0x81, 0x20, 0x80, 0x00, 0x01, 0xe1, 0xff, 0x00, + 0x01, 0x42, 0x40, 0x00, 0xc1, 0x47, 0x40, 0x00, 0x49, 0x40, 0x55, 0x00, + 0x95, 0x80, 0x2a, 0x00, 0x93, 0x00, 0x15, 0x00, 0x21, 0x01, 0x0a, 0x00, + 0x20, 0x01, 0x11, 0x00, 0x40, 0x82, 0x20, 0x00, 0x40, 0x42, 0x44, 0x00, + 0x80, 0x41, 0x4a, 0x00, 0x00, 0x40, 0x55, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0x20, 0x80, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const uchar busym_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x7f, 0xe0, 0xff, 0x00, 0xff, 0xe0, 0xff, 0x00, 0xff, 0xe1, 0xff, 0x00, + 0xff, 0xc3, 0x7f, 0x00, 0xff, 0xc7, 0x7f, 0x00, 0x7f, 0xc0, 0x7f, 0x00, + 0xf7, 0x80, 0x3f, 0x00, 0xf3, 0x00, 0x1f, 0x00, 0xe1, 0x01, 0x0e, 0x00, + 0xe0, 0x01, 0x1f, 0x00, 0xc0, 0x83, 0x3f, 0x00, 0xc0, 0xc3, 0x7f, 0x00, + 0x80, 0xc1, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// 16 x 16 +static const uchar openhand_bits[] = { + 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92, + 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20, + 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00}; +static const uchar openhandm_bits[] = { + 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff, + 0xfe,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f, + 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00}; +static const uchar closedhand_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50, + 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10, + 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00}; +static const uchar closedhandm_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f, + 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f, + 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00}; + +void QPlatformCursorImage::createSystemCursor(int id) +{ + if (!systemCursorTableInit) { + for (int i = 0; i <= Qt::LastCursor; i++) + systemCursorTable[i] = 0; + systemCursorTableInit = true; + } + switch (id) { + // 16x16 cursors + case Qt::ArrowCursor: + systemCursorTable[Qt::ArrowCursor] = + new QPlatformCursorImage(cur_arrow_bits, mcur_arrow_bits, 16, 16, 0, 0); + break; + + case Qt::UpArrowCursor: + systemCursorTable[Qt::UpArrowCursor] = + new QPlatformCursorImage(cur_up_arrow_bits, mcur_up_arrow_bits, 16, 16, 7, 0); + break; + + case Qt::CrossCursor: + systemCursorTable[Qt::CrossCursor] = + new QPlatformCursorImage(cur_cross_bits, mcur_cross_bits, 16, 16, 7, 7); + break; + + case Qt::IBeamCursor: + systemCursorTable[Qt::IBeamCursor] = + new QPlatformCursorImage(cur_ibeam_bits, mcur_ibeam_bits, 16, 16, 7, 7); + break; + + case Qt::SizeVerCursor: + systemCursorTable[Qt::SizeVerCursor] = + new QPlatformCursorImage(cur_ver_bits, mcur_ver_bits, 16, 16, 7, 7); + break; + + case Qt::SizeHorCursor: + systemCursorTable[Qt::SizeHorCursor] = + new QPlatformCursorImage(cur_hor_bits, mcur_hor_bits, 16, 16, 7, 7); + break; + + case Qt::SizeBDiagCursor: + systemCursorTable[Qt::SizeBDiagCursor] = + new QPlatformCursorImage(cur_bdiag_bits, mcur_bdiag_bits, 16, 16, 7, 7); + break; + + case Qt::SizeFDiagCursor: + systemCursorTable[Qt::SizeFDiagCursor] = + new QPlatformCursorImage(cur_fdiag_bits, mcur_fdiag_bits, 16, 16, 7, 7); + break; + + case Qt::BlankCursor: + systemCursorTable[Qt::BlankCursor] = + new QPlatformCursorImage(0, 0, 0, 0, 0, 0); + break; + + // 20x20 cursors + case Qt::ForbiddenCursor: + systemCursorTable[Qt::ForbiddenCursor] = + new QPlatformCursorImage(forbidden_bits, forbiddenm_bits, 20, 20, 10, 10); + break; + + // 32x32 cursors + case Qt::WaitCursor: + systemCursorTable[Qt::WaitCursor] = + new QPlatformCursorImage(wait_data_bits, wait_mask_bits, 32, 32, 15, 15); + break; + + case Qt::SplitVCursor: + systemCursorTable[Qt::SplitVCursor] = + new QPlatformCursorImage(vsplit_bits, vsplitm_bits, 32, 32, 15, 15); + break; + + case Qt::SplitHCursor: + systemCursorTable[Qt::SplitHCursor] = + new QPlatformCursorImage(hsplit_bits, hsplitm_bits, 32, 32, 15, 15); + break; + + case Qt::SizeAllCursor: + systemCursorTable[Qt::SizeAllCursor] = + new QPlatformCursorImage(size_all_data_bits, size_all_mask_bits, 32, 32, 15, 15); + break; + + case Qt::PointingHandCursor: + systemCursorTable[Qt::PointingHandCursor] = + new QPlatformCursorImage(phand_bits, phandm_bits, 32, 32, 0, 0); + break; + + case Qt::WhatsThisCursor: + systemCursorTable[Qt::WhatsThisCursor] = + new QPlatformCursorImage(whatsthis_bits, whatsthism_bits, 32, 32, 0, 0); + break; + case Qt::BusyCursor: + systemCursorTable[Qt::BusyCursor] = + new QPlatformCursorImage(busy_bits, busym_bits, 32, 32, 0, 0); + break; + + case Qt::OpenHandCursor: + systemCursorTable[Qt::OpenHandCursor] = + new QPlatformCursorImage(openhand_bits, openhandm_bits, 16, 16, 8, 8); + break; + case Qt::ClosedHandCursor: + systemCursorTable[Qt::ClosedHandCursor] = + new QPlatformCursorImage(closedhand_bits, closedhandm_bits, 16, 16, 8, 8); + break; + default: + qWarning("Unknown system cursor %d", id); + } +} + +/*! + \fn void QGraphicsSystemCursorImage::set(Qt::CursorShape id) + + \brief Calling this method sets the cursor image to the specified shape + + \a id is one of the defined Qt::CursorShape values. + + If id is invalid, Qt::BitmapCursor, or unknown by the implementation, + Qt::ArrowCursor is used instead. +*/ + +void QPlatformCursorImage::set(Qt::CursorShape id) +{ + QPlatformCursorImage *cursor = 0; + if (id >= 0 && id <= Qt::LastCursor) { + if (!systemCursorTable[id]) + createSystemCursor(id); + cursor = systemCursorTable[id]; + } + + if (cursor == 0) { + if (!systemCursorTable[Qt::ArrowCursor]) + createSystemCursor(Qt::ArrowCursor); + cursor = systemCursorTable[Qt::ArrowCursor]; + } + cursorImage = cursor->cursorImage; + hot = cursor->hot; +} + +/*! + \fn void QGraphicsSystemCursorImage::set(const QImage * image, int hx, int hy) + + \brief Set the cursor image to the specified QImage, with the hotsport at (hx, hy) + + \a image A pointer to a QImage + + \a hx The x coordinate of the cursor's hotspot + + \a hy the y coordinate of the cursor's hotspot +*/ + +void QPlatformCursorImage::set(const QImage &image, int hx, int hy) +{ + hot.setX(hx); + hot.setY(hy); + cursorImage = image; +} + +/*! + \fn void QGraphicsSystemCursorImage::set(const uchar *data, const uchar *mask, int width, int height, int hx, int hy) + + \brief set the cursor image to the graphic represented by the combination of data, mask, + width, and height + + \a data The pixel data of the graphic + + \a mask Mask data for the graphic. pixels in data with a corresponding mask bit of 0 are not drawn + + \a width The width of the graphic in pixels + + \a height The height of the graphic in pixels + + \a hx The X hotspot of the cursor graphic + + \a hy The Y hotspot of the cursor graphic +*/ +void QPlatformCursorImage::set(const uchar *data, const uchar *mask, + int width, int height, int hx, int hy) +{ + hot.setX(hx); + hot.setY(hy); + + cursorImage = QImage(width,height, QImage::Format_Indexed8); + + if (!width || !height || !data || !mask || cursorImage.isNull()) + return; + + cursorImage.setNumColors(3); + cursorImage.setColor(0, 0xff000000); + cursorImage.setColor(1, 0xffffffff); + cursorImage.setColor(2, 0x00000000); + + int bytesPerLine = (width + 7) / 8; + int p = 0; + int d, m; + + int x = -1, w = 0; + + uchar *cursor_data = cursorImage.bits(); + int bpl = cursorImage.bytesPerLine(); + for (int i = 0; i < height; i++) + { + for (int j = 0; j < bytesPerLine; j++, data++, mask++) + { + for (int b = 0; b < 8 && j*8+b < width; b++) + { + d = *data & (1 << b); + m = *mask & (1 << b); + if (d && m) p = 0; + else if (!d && m) p = 1; + else p = 2; + cursor_data[j*8+b] = p; + + // calc region + if (x < 0 && m) + x = j*8+b; + else if (x >= 0 && !m) { + x = -1; + w = 0; + } + if (m) + w++; + } + } + if (x >= 0) { + x = -1; + w = 0; + } + cursor_data += bpl; + } + +} + +/*! + \fn QGraphicsSystemCursorImage::QGraphicsSystemCursorImage(const uchar *data, const uchar *mask, int width, int height, int hotX, int hotY) + + \brief set the cursor image to the graphic represented by the combination of data, mask, + width, and height + + \a data The pixel data of the graphic + + \a mask Mask data for the graphic. pixels in data with a corresponding mask bit of 0 are not drawn + + \a width The width of the graphic in pixels + + \a height The height of the graphic in pixels + + \a hotX The X hotspot of the cursor graphic + + \a hotY The Y hotspot of the cursor graphic + + \sa set +*/ + +/*! + \fn QImage *QGraphicsSystemCursorImage::image() + + \brief Return the cursor graphic as a pointer to a QImage +*/ + +/*! + \fn QPoint QGraphicsSystemCursorImage::hotspot() + + \brief Return the cursor's hotspot +*/ + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qplatformcursor_qpa.h b/src/gui/guikernel/qplatformcursor_qpa.h new file mode 100644 index 0000000000..18698b6457 --- /dev/null +++ b/src/gui/guikernel/qplatformcursor_qpa.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGRAPHICSSYSTEMCURSOR_H +#define QGRAPHICSSYSTEMCURSOR_H + +#include <QtCore/QList> +#include <QtGui/QImage> +#include <QtGui/QMouseEvent> +#include <QtCore/QWeakPointer> +#include <QtCore/QObject> +#include <QtGui/QPlatformScreen> +#include <QtGui/QCursor> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +// Cursor graphics management +class Q_GUI_EXPORT QPlatformCursorImage { +public: + QPlatformCursorImage(const uchar *data, const uchar *mask, int width, int height, int hotX, int hotY) + { set(data, mask, width, height, hotX, hotY); } + QImage * image() { return &cursorImage; } + QPoint hotspot() { return hot; } + void set(const uchar *data, const uchar *mask, int width, int height, int hotX, int hotY); + void set(const QImage &image, int hx, int hy); + void set(Qt::CursorShape); +private: + static void createSystemCursor(int id); + QImage cursorImage; + QPoint hot; +}; + +class QPlatformCursor; + +class QPlatformCursorPrivate { +public: + static QList<QWeakPointer<QPlatformCursor> > getInstances() { return instances; } + static QList<QWeakPointer<QPlatformCursor> > instances; +}; + +class Q_GUI_EXPORT QPlatformCursor : public QObject { +public: + QPlatformCursor(QPlatformScreen *); + + // input methods + virtual void pointerEvent(const QMouseEvent & event) { Q_UNUSED(event); } + virtual void changeCursor(QCursor * widgetCursor, QWindow * widget) = 0; + +protected: + QPlatformScreen* screen; // Where to request an update + +private: + Q_DECLARE_PRIVATE(QPlatformCursor); + friend void qt_qpa_set_cursor(QWidget * w, bool force); + friend class QApplicationPrivate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSSYSTEMCURSOR_H diff --git a/src/gui/guikernel/qplatformeventloopintegration_qpa.cpp b/src/gui/guikernel/qplatformeventloopintegration_qpa.cpp new file mode 100644 index 0000000000..0ed43eb4b5 --- /dev/null +++ b/src/gui/guikernel/qplatformeventloopintegration_qpa.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformeventloopintegration_qpa.h" + +#include <QtCore/QCoreApplication> + +#include <QtCore/QDebug> + +class QPlatformEventLoopIntegrationPrivate +{ +public: + QPlatformEventLoopIntegrationPrivate(); + qint64 nextTimerEvent; +}; + +QPlatformEventLoopIntegrationPrivate::QPlatformEventLoopIntegrationPrivate() + : nextTimerEvent(0) +{ +} + +QPlatformEventLoopIntegration::QPlatformEventLoopIntegration() + : d_ptr(new QPlatformEventLoopIntegrationPrivate) + +{ +} + +QPlatformEventLoopIntegration::~QPlatformEventLoopIntegration() +{ +} + +qint64 QPlatformEventLoopIntegration::nextTimerEvent() const +{ + Q_D(const QPlatformEventLoopIntegration); + return d->nextTimerEvent; +} + + +void QPlatformEventLoopIntegration::setNextTimerEvent(qint64 nextTimerEvent) +{ + Q_D(QPlatformEventLoopIntegration); + d->nextTimerEvent = nextTimerEvent; +} + +void QPlatformEventLoopIntegration::processEvents() +{ + QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); +} diff --git a/src/gui/guikernel/qplatformeventloopintegration_qpa.h b/src/gui/guikernel/qplatformeventloopintegration_qpa.h new file mode 100644 index 0000000000..87df7aefe4 --- /dev/null +++ b/src/gui/guikernel/qplatformeventloopintegration_qpa.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMEVENTLOOPINTEGRATION_QPA_H +#define QPLATFORMEVENTLOOPINTEGRATION_QPA_H + +#include <QtCore/qglobal.h> +#include <QtCore/QScopedPointer> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformEventLoopIntegrationPrivate; + +class Q_GUI_EXPORT QPlatformEventLoopIntegration +{ + Q_DECLARE_PRIVATE(QPlatformEventLoopIntegration); +public: + QPlatformEventLoopIntegration(); + virtual ~QPlatformEventLoopIntegration(); + + virtual void startEventLoop() = 0; + virtual void quitEventLoop() = 0; + virtual void qtNeedsToProcessEvents() = 0; + + qint64 nextTimerEvent() const; + void setNextTimerEvent(qint64 nextTimerEvent); + + static void processEvents(); +protected: + QScopedPointer<QPlatformEventLoopIntegrationPrivate> d_ptr; +private: + Q_DISABLE_COPY(QPlatformEventLoopIntegration); + friend class QEventDispatcherQPA; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMEVENTLOOPINTEGRATION_QPA_H diff --git a/src/gui/guikernel/qplatformglcontext_qpa.cpp b/src/gui/guikernel/qplatformglcontext_qpa.cpp new file mode 100644 index 0000000000..2177a01d9b --- /dev/null +++ b/src/gui/guikernel/qplatformglcontext_qpa.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformglcontext_qpa.h" + +/*! + \class QPlatformGLContext + \since 4.8 + \internal + \preliminary + \ingroup qpa + + \brief The QPlatformGLContext class provides an abstraction for native GL contexts. + + In QPA the way to support OpenGL or OpenVG or other technologies that requires a native GL + context is through the QPlatformGLContext wrapper. + + There is no factory function for QPlatformGLContexts, but rather only one accessor function. + The only place to retrieve a QPlatformGLContext from is through a QPlatformWindow. + + The context which is current for a specific thread can be collected by the currentContext() + function. This is how QPlatformGLContext also makes it possible to use the QtOpenGL module + withhout using QGLWidget. When using QGLContext::currentContext(), it will ask + QPlatformGLContext for the currentContext. Then a corresponding QGLContext will be returned, + which maps to the QPlatformGLContext. +*/ + +/*! \fn void swapBuffers() + Reimplement in subclass to native swap buffers calls +*/ + +/*! getProcAddress(const QString& procName) + Reimplement in subclass to native getProcAddr calls. + + Note: its convenient to use qPrintable(const QString &str) to get the const char * pointer +*/ + +/*! platformWindowFormat() const + QWidget has the function qplatformWindowFormat(). That function is for the application + programmer to request the format of the window and the context that he wants. + + Reimplement this function in a subclass to indicate what format the glContext actually has. +*/ diff --git a/src/gui/guikernel/qplatformglcontext_qpa.h b/src/gui/guikernel/qplatformglcontext_qpa.h new file mode 100644 index 0000000000..fbd43b2b48 --- /dev/null +++ b/src/gui/guikernel/qplatformglcontext_qpa.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORM_GL_CONTEXT_H +#define QPLATFORM_GL_CONTEXT_H + +#include <QtCore/qnamespace.h> +#include <QtGui/qwindowformat_qpa.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QPlatformGLContext +{ +public: + virtual ~QPlatformGLContext() {} + + virtual void makeCurrent() = 0; + virtual void doneCurrent() = 0; + virtual void swapBuffers() = 0; + virtual void *getProcAddress(const QString& procName) = 0; + + virtual QWindowFormat windowFormat() const = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif // QPLATFORM_GL_CONTEXT_H diff --git a/src/gui/guikernel/qplatformintegration_qpa.cpp b/src/gui/guikernel/qplatformintegration_qpa.cpp new file mode 100644 index 0000000000..8ff12ebf9c --- /dev/null +++ b/src/gui/guikernel/qplatformintegration_qpa.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformintegration_qpa.h" + +#include <QtGui/QPlatformFontDatabase> +#include <QtGui/QPlatformClipboard> + +QT_BEGIN_NAMESPACE + +QPixmap QPlatformIntegration::grabWindow(WId window, int x, int y, int width, int height) const +{ + Q_UNUSED(window); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(width); + Q_UNUSED(height); + return QPixmap(); +} + +/*! + Factory function for the eventloop integration interface. + + Default implementation returns 0, which causes the eventloop to run in a single thread mode. + + \sa QPlatformEventLoopIntegration +*/ +QPlatformEventLoopIntegration *QPlatformIntegration::createEventLoopIntegration() const +{ + return 0; +} + +/*! + Accessor for the platform integrations fontdatabase. + + Default implementation returns a default QPlatformFontDatabase. + + \sa QPlatformFontDatabase +*/ +QPlatformFontDatabase *QPlatformIntegration::fontDatabase() const +{ + static QPlatformFontDatabase *db = 0; + if (!db) { + db = new QPlatformFontDatabase; + } + return db; +} + +/*! + Accessor for the platform integrations clipboard. + + Default implementation returns a default QPlatformClipboard. + + \sa QPlatformClipboard + +*/ + +#ifndef QT_NO_CLIPBOARD + +QPlatformClipboard *QPlatformIntegration::clipboard() const +{ + static QPlatformClipboard *clipboard = 0; + if (!clipboard) { + clipboard = new QPlatformClipboard; + } + return clipboard; +} + +#endif + +QPlatformNativeInterface * QPlatformIntegration::nativeInterface() const +{ + return 0; +} + +/*! + \class QPlatformIntegration + \since 4.8 + \internal + \preliminary + \ingroup qpa + \brief The QPlatformIntegration class is the entry for WindowSystem specific functionality. + + QPlatformIntegration is the single entry point for windowsystem specific functionality when + using the QPA platform. It has factory functions for creating platform specific pixmaps and + windows. The class also controls the font subsystem. + + QPlatformIntegration is a singelton class which gets instansiated in the QApplication + constructor. The QPlatformIntegration instance do not have ownership of objects it creates in + functions where the name starts with create. However, functions which don't have a name + starting with create acts as assessors to member variables. + + It is not trivial to create or build a platform plugin outside of the Qt source tree. Therefor + the recommended approach for making new platform plugin is to copy an existing plugin inside + the QTSRCTREE/src/plugins/platform and develop the plugin inside the source tree. + + The minimal platformintegration is the smallest platform integration it is possible to make, + which makes it an ideal starting point for new plugins. For a slightly more advanced plugin, + consider reviewing the directfb plugin, or the testlite plugin. +*/ + +/*! + \fn QPixmapData *createPixmapData(QPixmapData::PixelType type) const + + Factory function for QPixmapData. PixelType can be either PixmapType or BitmapType. + \sa QPixmapData +*/ + +/*! + \fn QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId = 0) const + + Factory function for QPlatformWindow. The widget parameter is a pointer to the top level + widget(tlw) which the QPlatformWindow is suppose to be created for. The WId handle is actually + never used, but there for future reference. Its purpose is if it is going to be possible to + create QPlatformWindows on existing WId. + + All tlw has to have a QPlatformWindow, and it will be created when the QPlatformWindow is set + to be visible for the first time. If the tlw's window flags are changed, or if the tlw's + QPlatformWindowFormat is changed, then the tlw's QPlatformWindow is deleted and a new one is + created. + + \sa QPlatformWindow, QPlatformWindowFormat + \sa createWindowSurface(QWidget *widget, WId winId) const +*/ + +/*! + \fn QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const + + Factory function for QWindowSurface. The QWidget parameter is a pointer to the + top level widget(tlw) the window surface is created for. A QPlatformWindow is always created + before the QWindowSurface for tlw where the widget also requires a WindowSurface. It is + possible to create top level QWidgets without a QWindowSurface by specifying + QPlatformWindowFormat::setWindowSurface(false) for the tlw QPlatformWindowFormat. + + \sa QWindowSurface + \sa createPlatformWindow(QWidget *widget, WId winId = 0) const +*/ + +/*! + \fn void moveToScreen(QWidget *window, int screen) + + This function is called when a QWidget is displayed on screen, or the QWidget is to be + displayed on a new screen. The QWidget parameter is a pointer to the top level widget and + the int parameter is the index to the screen in QList<QPlatformScreen *> screens() const. + + Default implementation does nothing. + + \sa screens() const +*/ + +/*! + \fn QList<QPlatformScreen *> screens() const + + Accessor function to a list of all the screens on the current system. The screen with the + index == 0 is the default/main screen. +*/ + +/*! + \fn bool isVirtualDesktop() + + Returns if the current windowing system configuration defines all the screens to be one + desktop(virtual desktop), or if each screen is a desktop of its own. + + Default implementation returns false. +*/ + +/*! + \fn QPixmap grabWindow(WId window, int x, int y, int width, int height) const + + This function is called when Qt needs to be able to grab the content of a window. + + Returnes the content of the window specified with the WId handle within the boundaries of + QRect(x,y,width,height). +*/ + + +bool QPlatformIntegration::hasCapability(Capability cap) const +{ + Q_UNUSED(cap); + return false; +} + + + + + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qplatformintegration_qpa.h b/src/gui/guikernel/qplatformintegration_qpa.h new file mode 100644 index 0000000000..43080d297a --- /dev/null +++ b/src/gui/guikernel/qplatformintegration_qpa.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMINTEGRATION_H +#define QPLATFORMINTEGRATION_H + +#include <QtGui/qwindowdefs.h> +#include <QtGui/private/qwindowsurface_p.h> +#include <QtGui/private/qpixmapdata_p.h> +#include <QtGui/qplatformscreen_qpa.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformWindow; +class QWindow; +class QBlittable; +class QWidget; +class QPlatformEventLoopIntegration; +class QPlatformFontDatabase; +class QPlatformClipboard; +class QPlatformNativeInterface; + + +class Q_GUI_EXPORT QPlatformIntegration +{ +public: + enum Capability { + ThreadedPixmaps = 1, + OpenGL = 2 + }; + + virtual ~QPlatformIntegration() { } + + virtual bool hasCapability(Capability cap) const; + +// GraphicsSystem functions + virtual QPixmapData *createPixmapData(QPixmapData::PixelType type) const = 0; + virtual QPlatformWindow *createPlatformWindow(QWindow *window) const = 0; + virtual QWindowSurface *createWindowSurface(QWindow *window, WId winId) const = 0; + +// Window System functions + virtual QList<QPlatformScreen *> screens() const = 0; + virtual void moveToScreen(QWidget *window, int screen) {Q_UNUSED(window); Q_UNUSED(screen);} + virtual bool isVirtualDesktop() { return false; } + virtual QPixmap grabWindow(WId window, int x, int y, int width, int height) const; + +//Deeper window system integrations + virtual QPlatformFontDatabase *fontDatabase() const; +#ifndef QT_NO_CLIPBOARD + virtual QPlatformClipboard *clipboard() const; +#endif + +// Experimental in mainthread eventloop integration +// This should only be used if it is only possible to do window system event processing in +// the gui thread. All of the functions in QWindowSystemInterface are thread safe. + virtual QPlatformEventLoopIntegration *createEventLoopIntegration() const; + +// Access native handles. The window handle is already available from Wid; + virtual QPlatformNativeInterface *nativeInterface() const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMINTEGRATION_H diff --git a/src/gui/guikernel/qplatformintegrationfactory_qpa.cpp b/src/gui/guikernel/qplatformintegrationfactory_qpa.cpp new file mode 100644 index 0000000000..4135c9e86a --- /dev/null +++ b/src/gui/guikernel/qplatformintegrationfactory_qpa.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformintegrationfactory_qpa_p.h" +#include <QPlatformIntegrationPlugin> +#include "private/qfactoryloader_p.h" +#include "qmutex.h" + +#include "qapplication.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QPlatformIntegrationFactoryInterface_iid, QLatin1String("/platforms"), Qt::CaseInsensitive)) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, + (QPlatformIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) +#endif + +QPlatformIntegration *QPlatformIntegrationFactory::create(const QString& key, const QString &platformPluginPath) +{ + QPlatformIntegration *ret = 0; + QStringList paramList = key.split(QLatin1Char(':')); + QString platform = paramList.takeFirst().toLower(); + +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + // Try loading the plugin from platformPluginPath first: + if (!platformPluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(platformPluginPath); + if (QPlatformIntegrationFactoryInterface *factory = + qobject_cast<QPlatformIntegrationFactoryInterface*>(directLoader()->instance(platform))) + ret = factory->create(key, paramList); + + if (ret) + return ret; + } + if (QPlatformIntegrationFactoryInterface *factory = qobject_cast<QPlatformIntegrationFactoryInterface*>(loader()->instance(platform))) + ret = factory->create(platform, paramList); +#endif + + return ret; +} + +/*! + Returns the list of valid keys, i.e. the keys this factory can + create styles for. + + \sa create() +*/ +QStringList QPlatformIntegrationFactory::keys(const QString &platformPluginPath) +{ +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + QStringList list; + + if (!platformPluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(platformPluginPath); + foreach (const QString &key, directLoader()->keys()) { + list += key + QString(QLatin1String(" (from %1)")).arg(platformPluginPath); + } + } + + list += loader()->keys(); +#else + QStringList list; +#endif + return list; +} + +QT_END_NAMESPACE + diff --git a/src/gui/guikernel/qplatformintegrationfactory_qpa_p.h b/src/gui/guikernel/qplatformintegrationfactory_qpa_p.h new file mode 100644 index 0000000000..a6042a81e0 --- /dev/null +++ b/src/gui/guikernel/qplatformintegrationfactory_qpa_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMINTEGRATIONFACTORY_H +#define QPLATFORMINTEGRATIONFACTORY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstringlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformIntegration; + +class QPlatformIntegrationFactory +{ +public: + static QStringList keys(const QString &platformPluginPath = QString()); + static QPlatformIntegration *create(const QString &key, const QString &platformPluginPath = QString()); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMINTEGRATIONFACTORY_H + diff --git a/src/gui/guikernel/qplatformintegrationplugin_qpa.cpp b/src/gui/guikernel/qplatformintegrationplugin_qpa.cpp new file mode 100644 index 0000000000..62920b6992 --- /dev/null +++ b/src/gui/guikernel/qplatformintegrationplugin_qpa.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformintegrationplugin_qpa.h" + +QT_BEGIN_NAMESPACE + +QPlatformIntegrationPlugin::QPlatformIntegrationPlugin(QObject *parent) + : QObject(parent) +{ +} + +QPlatformIntegrationPlugin::~QPlatformIntegrationPlugin() +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qplatformintegrationplugin_qpa.h b/src/gui/guikernel/qplatformintegrationplugin_qpa.h new file mode 100644 index 0000000000..17bcba0e46 --- /dev/null +++ b/src/gui/guikernel/qplatformintegrationplugin_qpa.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMINTEGRATIONPLUGIN_H +#define QPLATFORMINTEGRATIONPLUGIN_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformIntegration; + +struct QPlatformIntegrationFactoryInterface : public QFactoryInterface +{ + virtual QPlatformIntegration *create(const QString &key, const QStringList ¶mList) = 0; +}; + +#define QPlatformIntegrationFactoryInterface_iid "com.nokia.Qt.QPlatformIntegrationFactoryInterface" + +Q_DECLARE_INTERFACE(QPlatformIntegrationFactoryInterface, QPlatformIntegrationFactoryInterface_iid) + +class Q_GUI_EXPORT QPlatformIntegrationPlugin : public QObject, public QPlatformIntegrationFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QPlatformIntegrationFactoryInterface:QFactoryInterface) +public: + explicit QPlatformIntegrationPlugin(QObject *parent = 0); + ~QPlatformIntegrationPlugin(); + + virtual QStringList keys() const = 0; + virtual QPlatformIntegration *create(const QString &key, const QStringList ¶mList) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMINTEGRATIONPLUGIN_H diff --git a/src/gui/guikernel/qplatformnativeinterface_qpa.cpp b/src/gui/guikernel/qplatformnativeinterface_qpa.cpp new file mode 100644 index 0000000000..f160ec2899 --- /dev/null +++ b/src/gui/guikernel/qplatformnativeinterface_qpa.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformnativeinterface_qpa.h" + +QT_BEGIN_NAMESPACE + +void *QPlatformNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window) +{ + Q_UNUSED(resource); + Q_UNUSED(window); + return 0; +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qplatformnativeinterface_qpa.h b/src/gui/guikernel/qplatformnativeinterface_qpa.h new file mode 100644 index 0000000000..ff3eacf2e0 --- /dev/null +++ b/src/gui/guikernel/qplatformnativeinterface_qpa.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMNATIVEINTERFACE_QPA_H +#define QPLATFORMNATIVEINTERFACE_QPA_H + +#include <QtGui/qwindowdefs.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWindow; + +class Q_GUI_EXPORT QPlatformNativeInterface +{ +public: + virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMNATIVEINTERFACE_QPA_H diff --git a/src/gui/guikernel/qplatformscreen_qpa.cpp b/src/gui/guikernel/qplatformscreen_qpa.cpp new file mode 100644 index 0000000000..8b686781ab --- /dev/null +++ b/src/gui/guikernel/qplatformscreen_qpa.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformscreen_qpa.h" +#include <QtGui/qapplication.h> +#include <QtGui/private/qapplication_p.h> +#include <QtGui/qdesktopwidget.h> +#include <QtGui/qplatformintegration_qpa.h> +#include <QtGui/qwidget.h> +#include <QtGui/private/qwidget_p.h> + +/*! + Return the given top level widget for a given position. + + Default implementation retrieves a list of all top level widgets and finds the first widget + which contains point \a pos +*/ +QWidget *QPlatformScreen::topLevelAt(const QPoint & pos) const +{ + QWidgetList list = QApplication::topLevelWidgets(); + for (int i = list.size()-1; i >= 0; --i) { + QWidget *w = list[i]; + //### mask is ignored + if (w != QApplication::desktop() && w->isVisible() && w->geometry().contains(pos)) + return w; + } + + return 0; +} + +/*! \fn physicalSize() const + Reimplement in subclass to return the physical size of the screen. This function is used by + QFont to convert point sizes to pixel sizes. + + Default implementation takes the pixel size of the screen, considers a dpi of 100 and returns + the calculated (and probably wrong) physical size +*/ +QSize QPlatformScreen::physicalSize() const +{ + static const int dpi = 100; + int width = geometry().width() / dpi * qreal(25.4) ; + int height = geometry().height() / dpi * qreal(25.4) ; + return QSize(width,height); +} + +Q_GUI_EXPORT extern QWidgetPrivate *qt_widget_private(QWidget *widget); +QPlatformScreen * QPlatformScreen::platformScreenForWidget(const QWidget *widget) +{ + int screenIndex = 0; + QWidget *window = widget->window(); + QWidgetPrivate *windowPrivate = qt_widget_private(window); + QTLWExtra * topData = windowPrivate->maybeTopData(); + if (topData) + screenIndex = topData->screenIndex; + QPlatformIntegration *integration = + QGuiApplicationPrivate::platformIntegration(); + return integration->screens()[screenIndex]; +} + +QPlatformScreen * QPlatformScreen::platformScreenForWindow(const QWindow *) +{ + return QGuiApplicationPrivate::platformIntegration()->screens().at(0); +} + +/*! + \class QPlatformScreen + \since 4.8 + \internal + \preliminary + \ingroup qpa + + \brief The QPlatformScreen class provides an abstraction for visual displays. + + Many window systems has support for retrieving information on the attached displays. To be able + to query the display QPA uses QPlatformScreen. Qt its self is most dependent on the + physicalSize() function, since this is the function it uses to calculate the dpi to use when + converting point sizes to pixels sizes. However, this is unfortunate on some systems, as the + native system fakes its dpi size. + + QPlatformScreen is also used by the public api QDesktopWidget for information about the desktop. + */ + +/*! \fn geometry() const + Reimplement in subclass to return the pixel geometry of the screen +*/ + +/*! \fn availableGeometry() const + Reimplement in subclass to return the pixel geometry of the available space + This normally is the desktop screen minus the task manager, global menubar etc. +*/ + +/*! \fn depth() const + Reimplement in subclass to return current depth of the screen +*/ + +/*! \fn format() const + Reimplement in subclass to return the image format which corresponds to the screen format +*/ + diff --git a/src/gui/guikernel/qplatformscreen_qpa.h b/src/gui/guikernel/qplatformscreen_qpa.h new file mode 100644 index 0000000000..cbaf5b9c47 --- /dev/null +++ b/src/gui/guikernel/qplatformscreen_qpa.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMSCREEN_H +#define QPLATFORMSCREEN_H + +#include <QtCore/qmetatype.h> +#include <QtCore/qnamespace.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qvariant.h> +#include <QtCore/qrect.h> +#include <QtCore/qobject.h> + +#include <QtGui/qcursor.h> +#include <QtGui/qimage.h> +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QPlatformScreen : public QObject +{ + Q_OBJECT +public: + virtual ~QPlatformScreen() { } + + virtual QRect geometry() const = 0; + virtual QRect availableGeometry() const {return geometry();} + virtual int depth() const = 0; + virtual QImage::Format format() const = 0; + virtual QSize physicalSize() const; + //jl: should setDirty be removed. + virtual void setDirty(const QRect &) {} + virtual QWidget *topLevelAt(const QPoint &point) const; + + //jl: should this function be in QPlatformIntegration + //jl: maybe screenForWidget is a better name? + static QPlatformScreen *platformScreenForWidget(const QWidget *widget); + + // temporary convenience + static QPlatformScreen *platformScreenForWindow(const QWindow *window); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMSCREEN_H diff --git a/src/gui/guikernel/qplatformwindow_qpa.cpp b/src/gui/guikernel/qplatformwindow_qpa.cpp new file mode 100644 index 0000000000..7ec5221840 --- /dev/null +++ b/src/gui/guikernel/qplatformwindow_qpa.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformwindow_qpa.h" + +#include <QtGui/qwindowsysteminterface_qpa.h> +#include <QtGui/qwidget.h> + +class QPlatformWindowPrivate +{ + QWindow *window; + QRect rect; + friend class QPlatformWindow; +}; + +/*! + Constructs a platform window with the given top level widget. +*/ + +QPlatformWindow::QPlatformWindow(QWindow *window) + : d_ptr(new QPlatformWindowPrivate) +{ + Q_D(QPlatformWindow); + d->window = window; +} + +/*! + Virtual destructor does not delete its top level widget. +*/ +QPlatformWindow::~QPlatformWindow() +{ +} + +/*! + Returnes the widget which belongs to the QPlatformWindow +*/ +QWindow *QPlatformWindow::window() const +{ + Q_D(const QPlatformWindow); + return d->window; +} + +/*! + This function is called by Qt whenever a window is moved or the window is resized. The resize + can happen programatically(from ie. user application) or by the window manager. This means that + there is no need to call this function specifically from the window manager callback, instead + call QWindowSystemInterface::handleGeometryChange(QWidget *w, const QRect &newRect); +*/ +void QPlatformWindow::setGeometry(const QRect &rect) +{ + Q_D(QPlatformWindow); + d->rect = rect; +} + +/*! + Returnes the current geometry of a window +*/ +QRect QPlatformWindow::geometry() const +{ + Q_D(const QPlatformWindow); + return d->rect; +} + +/*! + Reimplemented in subclasses to show the surface + if \a visible is \c true, and hide it if \a visible is \c false. +*/ +void QPlatformWindow::setVisible(bool visible) +{ + Q_UNUSED(visible); +} +/*! + Requests setting the window flags of this surface + to \a type. Returns the actual flags set. +*/ +Qt::WindowFlags QPlatformWindow::setWindowFlags(Qt::WindowFlags flags) +{ + return flags; +} + +/*! + Reimplement in subclasses to return a handle to the native window +*/ +WId QPlatformWindow::winId() const { return WId(0); } + +/*! + This function is called to enable native child widgets in QPA. It is common not to support this + feature in Window systems, but can be faked. When this function is called all geometry of this + platform window will be relative to the parent. +*/ +//jl: It would be useful to have a property on the platform window which indicated if the sub-class +// supported the setParent. If not, then geometry would be in screen coordinates. +void QPlatformWindow::setParent(const QPlatformWindow *parent) +{ + Q_UNUSED(parent); + qWarning("This plugin does not support setParent!"); +} + +/*! + Reimplement to set the window title to \a title +*/ +void QPlatformWindow::setWindowTitle(const QString &title) { Q_UNUSED(title); } + +/*! + Reimplement to be able to let Qt rais windows to the top of the desktop +*/ +void QPlatformWindow::raise() { qWarning("This plugin does not support raise()"); } + +/*! + Reimplement to be able to let Qt lower windows to the bottom of the desktop +*/ +void QPlatformWindow::lower() { qWarning("This plugin does not support lower()"); } + +/*! + Reimplement to be able to let Qt set the opacity level of a window +*/ +void QPlatformWindow::setOpacity(qreal level) +{ + Q_UNUSED(level); + qWarning("This plugin does not support setting window opacity"); +} + +/*! + Reimplement to let Qt be able to request activation/focus for a window + + Some window systems will probably not have callbacks for this functionality, + and then calling QWindowSystemInterface::handleWindowActivated(QWidget *w) + would be sufficient. + + If the window system has some event handling/callbacks then call + QWindowSystemInterface::handleWindowActivated(QWidget *w) when the window system + gives the notification. + + Default implementation calls QWindowSystem::handleWindowActivated(QWidget *w) +*/ +void QPlatformWindow::requestActivateWindow() +{ + QWindowSystemInterface::handleWindowActivated(window()); +} + +/*! + Reimplement to return the glContext associated with the window. +*/ +QPlatformGLContext *QPlatformWindow::glContext() const +{ + return 0; +} + +/*! + \class QPlatformWindow + \since 4.8 + \internal + \preliminary + \ingroup qpa + + \brief The QPlatformWindow class provides an abstraction for top-level windows. + + The QPlatformWindow abstraction is used by QWidget for all its top level widgets. It is being + created by calling the createPlatformWindow function in the loaded QPlatformIntegration + instance. + + QPlatformWindow is used to signal to the windowing system, how Qt persieves its frame. + However, it is not concerned with how Qt renders into the window it represents. + + Top level QWidgets(tlw) will always have a QPlatformWindow. However, it is not necessary for + all tlw to have a QWindowSurface. This is the case for QGLWidget. And could be the case for + widgets where some 3.party renders into it. + + The platform specific window handle can be retrieved by the winId function. + + QPlatformWindow is also the way QPA defines how native child windows should be supported + through the setParent function. + + The only way to retrieve a QPlatformGLContext in QPA is by calling the glContext() function + on QPlatformWindow. + + \sa QWindowSurface, QWidget +*/ diff --git a/src/gui/guikernel/qplatformwindow_qpa.h b/src/gui/guikernel/qplatformwindow_qpa.h new file mode 100644 index 0000000000..1730f3d85e --- /dev/null +++ b/src/gui/guikernel/qplatformwindow_qpa.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPLATFORMWINDOW_H +#define QPLATFORMWINDOW_H + + +#include <QtCore/qscopedpointer.h> +#include <QtCore/qrect.h> +#include <QtCore/qstring.h> +#include <QtGui/qwindowdefs.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformWindowPrivate; +class QWindow; +class QPlatformGLContext; + +class Q_GUI_EXPORT QPlatformWindow +{ + Q_DECLARE_PRIVATE(QPlatformWindow) +public: + QPlatformWindow(QWindow *window); + virtual ~QPlatformWindow(); + + QWindow *window() const; + virtual void setGeometry(const QRect &rect); + virtual QRect geometry() const; + + virtual void setVisible(bool visible); + virtual Qt::WindowFlags setWindowFlags(Qt::WindowFlags flags); + virtual WId winId() const; + virtual void setParent(const QPlatformWindow *window); + + virtual void setWindowTitle(const QString &title); + virtual void raise(); + virtual void lower(); + + virtual void setOpacity(qreal level); + virtual void requestActivateWindow(); + + virtual QPlatformGLContext *glContext() const; +protected: + QScopedPointer<QPlatformWindowPrivate> d_ptr; +private: + Q_DISABLE_COPY(QPlatformWindow) +}; + +QT_END_NAMESPACE + +QT_END_HEADER +#endif //QPLATFORMWINDOW_H diff --git a/src/gui/guikernel/qsessionmanager.h b/src/gui/guikernel/qsessionmanager.h new file mode 100644 index 0000000000..bd851b87e9 --- /dev/null +++ b/src/gui/guikernel/qsessionmanager.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSESSIONMANAGER_H +#define QSESSIONMANAGER_H + +#include <QtCore/qobject.h> +#include <QtGui/qwindowdefs.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> + +#ifndef QT_NO_SESSIONMANAGER + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QSessionManagerPrivate; + +class Q_GUI_EXPORT QSessionManager : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QSessionManager) + QSessionManager(QApplication *app, QString &id, QString &key); + ~QSessionManager(); +public: + QString sessionId() const; + QString sessionKey() const; +#if defined(Q_WS_X11) || defined(Q_WS_MAC) + void *handle() const; +#endif + + bool allowsInteraction(); + bool allowsErrorInteraction(); + void release(); + + void cancel(); + + enum RestartHint { + RestartIfRunning, + RestartAnyway, + RestartImmediately, + RestartNever + }; + void setRestartHint(RestartHint); + RestartHint restartHint() const; + + void setRestartCommand(const QStringList&); + QStringList restartCommand() const; + void setDiscardCommand(const QStringList&); + QStringList discardCommand() const; + + void setManagerProperty(const QString& name, const QString& value); + void setManagerProperty(const QString& name, const QStringList& value); + + bool isPhase2() const; + void requestPhase2(); + +private: + friend class QApplication; + friend class QApplicationPrivate; + friend class QBaseApplication; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_SESSIONMANAGER + +#endif // QSESSIONMANAGER_H diff --git a/src/gui/guikernel/qsessionmanager_qpa.cpp b/src/gui/guikernel/qsessionmanager_qpa.cpp new file mode 100644 index 0000000000..0730204720 --- /dev/null +++ b/src/gui/guikernel/qsessionmanager_qpa.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qsessionmanager.h> +#include <qapplication.h> + +#include <private/qobject_p.h> +#include <qapplication.h> + +#ifndef QT_NO_SESSIONMANAGER + +QT_BEGIN_NAMESPACE + +class QSessionManagerPrivate : public QObjectPrivate +{ +public: + QSessionManagerPrivate(QSessionManager *m, const QString &id, + const QString &key); + + QStringList restartCommand; + QStringList discardCommand; + const QString sessionId; + const QString sessionKey; + QSessionManager::RestartHint restartHint; +}; + +QSessionManagerPrivate::QSessionManagerPrivate(QSessionManager*, + const QString &id, + const QString &key) + : QObjectPrivate(), sessionId(id), sessionKey(key) +{ +} + +QSessionManager::QSessionManager(QApplication *app, QString &id, QString &key) + : QObject(*(new QSessionManagerPrivate(this, id, key)), app) +{ + Q_D(QSessionManager); + d->restartHint = RestartIfRunning; +} + +QSessionManager::~QSessionManager() +{ +} + +QString QSessionManager::sessionId() const +{ + Q_D(const QSessionManager); + return d->sessionId; +} + +QString QSessionManager::sessionKey() const +{ + Q_D(const QSessionManager); + return d->sessionKey; +} + + +bool QSessionManager::allowsInteraction() +{ + return false; +} + +bool QSessionManager::allowsErrorInteraction() +{ + return false; +} + +void QSessionManager::release() +{ +} + +void QSessionManager::cancel() +{ +} + +void QSessionManager::setRestartHint(QSessionManager::RestartHint hint) +{ + Q_D(QSessionManager); + d->restartHint = hint; +} + +QSessionManager::RestartHint QSessionManager::restartHint() const +{ + Q_D(const QSessionManager); + return d->restartHint; +} + +void QSessionManager::setRestartCommand(const QStringList &command) +{ + Q_D(QSessionManager); + d->restartCommand = command; +} + +QStringList QSessionManager::restartCommand() const +{ + Q_D(const QSessionManager); + return d->restartCommand; +} + +void QSessionManager::setDiscardCommand(const QStringList &command) +{ + Q_D(QSessionManager); + d->discardCommand = command; +} + +QStringList QSessionManager::discardCommand() const +{ + Q_D(const QSessionManager); + return d->discardCommand; +} + +void QSessionManager::setManagerProperty(const QString &name, + const QString &value) +{ + Q_UNUSED(name); + Q_UNUSED(value); +} + +void QSessionManager::setManagerProperty(const QString &name, + const QStringList &value) +{ + Q_UNUSED(name); + Q_UNUSED(value); +} + +bool QSessionManager::isPhase2() const +{ + return false; +} + +void QSessionManager::requestPhase2() +{ +} + +QT_END_NAMESPACE + +#endif // QT_NO_SESSIONMANAGER diff --git a/src/gui/guikernel/qshortcut.cpp b/src/gui/guikernel/qshortcut.cpp new file mode 100644 index 0000000000..978ef0c240 --- /dev/null +++ b/src/gui/guikernel/qshortcut.cpp @@ -0,0 +1,407 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qshortcut.h" +#include "private/qwidget_p.h" + +#ifndef QT_NO_SHORTCUT +#include <qevent.h> +#include <qwhatsthis.h> +#include <qmenu.h> +#include <qapplication.h> +#include <private/qapplication_p.h> +#include <private/qshortcutmap_p.h> + +QT_BEGIN_NAMESPACE + +#define QAPP_CHECK(functionName) \ + if (!qApp) { \ + qWarning("QShortcut: Initialize QApplication before calling '" functionName "'."); \ + return; \ + } + +/*! + \class QShortcut + \brief The QShortcut class is used to create keyboard shortcuts. + + \ingroup events + + + The QShortcut class provides a way of connecting keyboard + shortcuts to Qt's \l{signals and slots} mechanism, so that + objects can be informed when a shortcut is executed. The shortcut + can be set up to contain all the key presses necessary to + describe a keyboard shortcut, including the states of modifier + keys such as \gui Shift, \gui Ctrl, and \gui Alt. + + \target mnemonic + + On certain widgets, using '&' in front of a character will + automatically create a mnemonic (a shortcut) for that character, + e.g. "E&xit" will create the shortcut \gui Alt+X (use '&&' to + display an actual ampersand). The widget might consume and perform + an action on a given shortcut. On X11 the ampersand will not be + shown and the character will be underlined. On Windows, shortcuts + are normally not displayed until the user presses the \gui Alt + key, but this is a setting the user can change. On Mac, shortcuts + are disabled by default. Call qt_set_sequence_auto_mnemonic() to + enable them. However, because mnemonic shortcuts do not fit in + with Aqua's guidelines, Qt will not show the shortcut character + underlined. + + For applications that use menus, it may be more convenient to + use the convenience functions provided in the QMenu class to + assign keyboard shortcuts to menu items as they are created. + Alternatively, shortcuts may be associated with other types of + actions in the QAction class. + + The simplest way to create a shortcut for a particular widget is + to construct the shortcut with a key sequence. For example: + + \snippet doc/src/snippets/code/src_gui_kernel_qshortcut.cpp 0 + + When the user types the \l{QKeySequence}{key sequence} + for a given shortcut, the shortcut's activated() signal is + emitted. (In the case of ambiguity, the activatedAmbiguously() + signal is emitted.) A shortcut is "listened for" by Qt's event + loop when the shortcut's parent widget is receiving events. + + A shortcut's key sequence can be set with setKey() and retrieved + with key(). A shortcut can be enabled or disabled with + setEnabled(), and can have "What's This?" help text set with + setWhatsThis(). + + \sa QShortcutEvent, QKeySequence, QAction +*/ + +/*! + \fn QWidget *QShortcut::parentWidget() const + + Returns the shortcut's parent widget. +*/ + +/*! + \fn void QShortcut::activated() + + This signal is emitted when the user types the shortcut's key + sequence. + + \sa activatedAmbiguously() +*/ + +/*! + \fn void QShortcut::activatedAmbiguously() + + When a key sequence is being typed at the keyboard, it is said to + be ambiguous as long as it matches the start of more than one + shortcut. + + When a shortcut's key sequence is completed, + activatedAmbiguously() is emitted if the key sequence is still + ambiguous (i.e., it is the start of one or more other shortcuts). + The activated() signal is not emitted in this case. + + \sa activated() +*/ + +/* + \internal + Private data accessed through d-pointer. +*/ +class QShortcutPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QShortcut) +public: + QShortcutPrivate() : sc_context(Qt::WindowShortcut), sc_enabled(true), sc_autorepeat(true), sc_id(0) {} + QKeySequence sc_sequence; + Qt::ShortcutContext sc_context; + bool sc_enabled; + bool sc_autorepeat; + int sc_id; + QString sc_whatsthis; + void redoGrab(QShortcutMap &map); +}; + +void QShortcutPrivate::redoGrab(QShortcutMap &map) +{ + Q_Q(QShortcut); + if (!parent) { + qWarning("QShortcut: No widget parent defined"); + return; + } + + if (sc_id) + map.removeShortcut(sc_id, q); + if (sc_sequence.isEmpty()) + return; + sc_id = map.addShortcut(q, sc_sequence, sc_context); + if (!sc_enabled) + map.setShortcutEnabled(false, sc_id, q); + if (!sc_autorepeat) + map.setShortcutAutoRepeat(false, sc_id, q); +} + +/*! + Constructs a QShortcut object for the \a parent widget. Since no + shortcut key sequence is specified, the shortcut will not emit any + signals. + + \sa setKey() +*/ +QShortcut::QShortcut(QWidget *parent) + : QObject(*new QShortcutPrivate, parent) +{ + Q_ASSERT(parent != 0); +} + +/*! + Constructs a QShortcut object for the \a parent widget. The shortcut + operates on its parent, listening for \l{QShortcutEvent}s that + match the \a key sequence. Depending on the ambiguity of the + event, the shortcut will call the \a member function, or the \a + ambiguousMember function, if the key press was in the shortcut's + \a context. +*/ +QShortcut::QShortcut(const QKeySequence &key, QWidget *parent, + const char *member, const char *ambiguousMember, + Qt::ShortcutContext context) + : QObject(*new QShortcutPrivate, parent) +{ + QAPP_CHECK("QShortcut"); + + Q_D(QShortcut); + Q_ASSERT(parent != 0); + d->sc_context = context; + d->sc_sequence = key; + d->redoGrab(qApp->d_func()->shortcutMap); + if (member) + connect(this, SIGNAL(activated()), parent, member); + if (ambiguousMember) + connect(this, SIGNAL(activatedAmbiguously()), parent, ambiguousMember); +} + +/*! + Destroys the shortcut. +*/ +QShortcut::~QShortcut() +{ + Q_D(QShortcut); + if (qApp) + qApp->d_func()->shortcutMap.removeShortcut(d->sc_id, this); +} + +/*! + \property QShortcut::key + \brief the shortcut's key sequence + + This is a key sequence with an optional combination of Shift, Ctrl, + and Alt. The key sequence may be supplied in a number of ways: + + \snippet doc/src/snippets/code/src_gui_kernel_qshortcut.cpp 1 + + By default, this property contains an empty key sequence. +*/ +void QShortcut::setKey(const QKeySequence &key) +{ + Q_D(QShortcut); + if (d->sc_sequence == key) + return; + QAPP_CHECK("setKey"); + d->sc_sequence = key; + d->redoGrab(qApp->d_func()->shortcutMap); +} + +QKeySequence QShortcut::key() const +{ + Q_D(const QShortcut); + return d->sc_sequence; +} + +/*! + \property QShortcut::enabled + \brief whether the shortcut is enabled + + An enabled shortcut emits the activated() or activatedAmbiguously() + signal when a QShortcutEvent occurs that matches the shortcut's + key() sequence. + + If the application is in \c WhatsThis mode the shortcut will not emit + the signals, but will show the "What's This?" text instead. + + By default, this property is true. + + \sa whatsThis +*/ +void QShortcut::setEnabled(bool enable) +{ + Q_D(QShortcut); + if (d->sc_enabled == enable) + return; + QAPP_CHECK("setEnabled"); + d->sc_enabled = enable; + qApp->d_func()->shortcutMap.setShortcutEnabled(enable, d->sc_id, this); +} + +bool QShortcut::isEnabled() const +{ + Q_D(const QShortcut); + return d->sc_enabled; +} + +/*! + \property QShortcut::context + \brief the context in which the shortcut is valid + + A shortcut's context decides in which circumstances a shortcut is + allowed to be triggered. The normal context is Qt::WindowShortcut, + which allows the shortcut to trigger if the parent (the widget + containing the shortcut) is a subwidget of the active top-level + window. + + By default, this property is set to Qt::WindowShortcut. +*/ +void QShortcut::setContext(Qt::ShortcutContext context) +{ + Q_D(QShortcut); + if(d->sc_context == context) + return; + QAPP_CHECK("setContext"); + d->sc_context = context; + d->redoGrab(qApp->d_func()->shortcutMap); +} + +Qt::ShortcutContext QShortcut::context() +{ + Q_D(QShortcut); + return d->sc_context; +} + +/*! + \property QShortcut::whatsThis + \brief the shortcut's "What's This?" help text + + The text will be shown when the application is in "What's + This?" mode and the user types the shortcut key() sequence. + + To set "What's This?" help on a menu item (with or without a + shortcut key), set the help on the item's action. + + By default, this property contains an empty string. + + \sa QWhatsThis::inWhatsThisMode(), QAction::setWhatsThis() +*/ +void QShortcut::setWhatsThis(const QString &text) +{ + Q_D(QShortcut); + d->sc_whatsthis = text; +} + +QString QShortcut::whatsThis() const +{ + Q_D(const QShortcut); + return d->sc_whatsthis; +} + +/*! + \property QShortcut::autoRepeat + \brief whether the shortcut can auto repeat + \since 4.2 + + If true, the shortcut will auto repeat when the keyboard shortcut + combination is held down, provided that keyboard auto repeat is + enabled on the system. + The default value is true. +*/ +void QShortcut::setAutoRepeat(bool on) +{ + Q_D(QShortcut); + if (d->sc_autorepeat == on) + return; + QAPP_CHECK("setAutoRepeat"); + d->sc_autorepeat = on; + qApp->d_func()->shortcutMap.setShortcutAutoRepeat(on, d->sc_id, this); +} + +bool QShortcut::autoRepeat() const +{ + Q_D(const QShortcut); + return d->sc_autorepeat; +} + +/*! + Returns the shortcut's ID. + + \sa QShortcutEvent::shortcutId() +*/ +int QShortcut::id() const +{ + Q_D(const QShortcut); + return d->sc_id; +} + +/*! + \internal +*/ +bool QShortcut::event(QEvent *e) +{ + Q_D(QShortcut); + bool handled = false; + if (d->sc_enabled && e->type() == QEvent::Shortcut) { + QShortcutEvent *se = static_cast<QShortcutEvent *>(e); + if (se->shortcutId() == d->sc_id && se->key() == d->sc_sequence){ +#ifndef QT_NO_WHATSTHIS + if (QWhatsThis::inWhatsThisMode()) { + QWhatsThis::showText(QCursor::pos(), d->sc_whatsthis); + handled = true; + } else +#endif + if (se->isAmbiguous()) + emit activatedAmbiguously(); + else + emit activated(); + handled = true; + } + } + return handled; +} +#endif // QT_NO_SHORTCUT + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qshortcut.h b/src/gui/guikernel/qshortcut.h new file mode 100644 index 0000000000..f432d9ad97 --- /dev/null +++ b/src/gui/guikernel/qshortcut.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSHORTCUT_H +#define QSHORTCUT_H + +#include <QtGui/qwidget.h> +#include <QtGui/qkeysequence.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_SHORTCUT + +class QShortcutPrivate; +class Q_GUI_EXPORT QShortcut : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QShortcut) + Q_PROPERTY(QKeySequence key READ key WRITE setKey) + Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) + Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat) + Q_PROPERTY(Qt::ShortcutContext context READ context WRITE setContext) +public: + explicit QShortcut(QWidget *parent); + QShortcut(const QKeySequence& key, QWidget *parent, + const char *member = 0, const char *ambiguousMember = 0, + Qt::ShortcutContext context = Qt::WindowShortcut); + ~QShortcut(); + + void setKey(const QKeySequence& key); + QKeySequence key() const; + + void setEnabled(bool enable); + bool isEnabled() const; + + void setContext(Qt::ShortcutContext context); + Qt::ShortcutContext context(); + + void setWhatsThis(const QString &text); + QString whatsThis() const; + + void setAutoRepeat(bool on); + bool autoRepeat() const; + + int id() const; + + inline QWidget *parentWidget() const + { return static_cast<QWidget *>(QObject::parent()); } + +Q_SIGNALS: + void activated(); + void activatedAmbiguously(); + +protected: + bool event(QEvent *e); +}; + +#endif // QT_NO_SHORTCUT + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSHORTCUT_H diff --git a/src/gui/guikernel/qshortcutmap.cpp b/src/gui/guikernel/qshortcutmap.cpp new file mode 100644 index 0000000000..32b70ed758 --- /dev/null +++ b/src/gui/guikernel/qshortcutmap.cpp @@ -0,0 +1,897 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qshortcutmap_p.h" +#include "private/qobject_p.h" +#include "qkeysequence.h" +#include "qgraphicsscene.h" +#include "qgraphicsview.h" +#include "qdebug.h" +#include "qevent.h" +#include "qwidget.h" +#include "qapplication.h" +#include "qvector.h" +#include "qmenu.h" +#include "qmenubar.h" +#include "qshortcut.h" +#include "private/qapplication_p.h" +#include <private/qaction_p.h> +#include <private/qkeymapper_p.h> +#include <private/qwidget_p.h> + +#ifndef QT_NO_SHORTCUT + +QT_BEGIN_NAMESPACE + +// To enable verbose output uncomment below +//#define DEBUG_QSHORTCUTMAP + +/* \internal + Entry data for QShortcutMap + Contains: + Keysequence for entry + Pointer to parent owning the sequence +*/ +struct QShortcutEntry +{ + QShortcutEntry() + : keyseq(0), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(0) + {} + + QShortcutEntry(const QKeySequence &k) + : keyseq(k), context(Qt::WindowShortcut), enabled(false), autorepeat(1), id(0), owner(0) + {} + + QShortcutEntry(QObject *o, const QKeySequence &k, Qt::ShortcutContext c, int i) + : keyseq(k), context(c), enabled(true), autorepeat(1), id(i), owner(o) + {} + + QShortcutEntry(QObject *o, const QKeySequence &k, Qt::ShortcutContext c, int i, bool a) + : keyseq(k), context(c), enabled(true), autorepeat(a), id(i), owner(o) + {} + + bool operator<(const QShortcutEntry &f) const + { return keyseq < f.keyseq; } + + QKeySequence keyseq; + Qt::ShortcutContext context; + bool enabled : 1; + bool autorepeat : 1; + signed int id; + QObject *owner; +}; + +#if 0 //ndef QT_NO_DEBUG_STREAM +/*! \internal + QDebug operator<< for easy debug output of the shortcut entries. +*/ +static QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) { + if (!se) + return dbg << "QShortcutEntry(0x0)"; + dbg.nospace() + << "QShortcutEntry(" << se->keyseq + << "), id(" << se->id << "), enabled(" << se->enabled << "), autorepeat(" << se->autorepeat + << "), owner(" << se->owner << ')'; + return dbg.space(); +} +#endif // QT_NO_DEBUGSTREAM + +/* \internal + Private data for QShortcutMap +*/ +class QShortcutMapPrivate +{ + Q_DECLARE_PUBLIC(QShortcutMap) + +public: + QShortcutMapPrivate(QShortcutMap* parent) + : q_ptr(parent), currentId(0), ambigCount(0), currentState(QKeySequence::NoMatch) + { + identicals.reserve(10); + currentSequences.reserve(10); + } + QShortcutMap *q_ptr; // Private's parent + + QList<QShortcutEntry> sequences; // All sequences! + + int currentId; // Global shortcut ID number + int ambigCount; // Index of last enabled ambiguous dispatch + QKeySequence::SequenceMatch currentState; + QVector<QKeySequence> currentSequences; // Sequence for the current state + QVector<QKeySequence> newEntries; + QKeySequence prevSequence; // Sequence for the previous identical match + QVector<const QShortcutEntry*> identicals; // Last identical matches +}; + + +/*! \internal + QShortcutMap constructor. +*/ +QShortcutMap::QShortcutMap() + : d_ptr(new QShortcutMapPrivate(this)) +{ + resetState(); +} + +/*! \internal + QShortcutMap destructor. +*/ +QShortcutMap::~QShortcutMap() +{ +} + +/*! \internal + Adds a shortcut to the global map. + Returns the id of the newly added shortcut. +*/ +int QShortcutMap::addShortcut(QObject *owner, const QKeySequence &key, Qt::ShortcutContext context) +{ + Q_ASSERT_X(owner, "QShortcutMap::addShortcut", "All shortcuts need an owner"); + Q_ASSERT_X(!key.isEmpty(), "QShortcutMap::addShortcut", "Cannot add keyless shortcuts to map"); + Q_D(QShortcutMap); + + QShortcutEntry newEntry(owner, key, context, --(d->currentId), true); + QList<QShortcutEntry>::iterator it = qUpperBound(d->sequences.begin(), d->sequences.end(), newEntry); + d->sequences.insert(it, newEntry); // Insert sorted +#if defined(DEBUG_QSHORTCUTMAP) + qDebug().nospace() + << "QShortcutMap::addShortcut(" << owner << ", " + << key << ", " << context << ") = " << d->currentId; +#endif + return d->currentId; +} + +/*! \internal + Removes a shortcut from the global map. + If \a owner is 0, all entries in the map with the key sequence specified + is removed. If \a key is null, all sequences for \a owner is removed from + the map. If \a id is 0, any identical \a key sequences owned by \a owner + are removed. + Returns the number of sequences removed from the map. +*/ + +int QShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &key) +{ + Q_D(QShortcutMap); + int itemsRemoved = 0; + bool allOwners = (owner == 0); + bool allKeys = key.isEmpty(); + bool allIds = id == 0; + + // Special case, remove everything + if (allOwners && allKeys && id == 0) { + itemsRemoved = d->sequences.size(); + d->sequences.clear(); + return itemsRemoved; + } + + int i = d->sequences.size()-1; + while (i>=0) + { + const QShortcutEntry &entry = d->sequences.at(i); + int entryId = entry.id; + if ((allOwners || entry.owner == owner) + && (allIds || entry.id == id) + && (allKeys || entry.keyseq == key)) { + d->sequences.removeAt(i); + ++itemsRemoved; + } + if (id == entryId) + return itemsRemoved; + --i; + } +#if defined(DEBUG_QSHORTCUTMAP) + qDebug().nospace() + << "QShortcutMap::removeShortcut(" << id << ", " << owner << ", " + << key << ") = " << itemsRemoved; +#endif + return itemsRemoved; +} + +/*! \internal + Changes the enable state of a shortcut to \a enable. + If \a owner is 0, all entries in the map with the key sequence specified + is removed. If \a key is null, all sequences for \a owner is removed from + the map. If \a id is 0, any identical \a key sequences owned by \a owner + are changed. + Returns the number of sequences which are matched in the map. +*/ +int QShortcutMap::setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &key) +{ + Q_D(QShortcutMap); + int itemsChanged = 0; + bool allOwners = (owner == 0); + bool allKeys = key.isEmpty(); + bool allIds = id == 0; + + int i = d->sequences.size()-1; + while (i>=0) + { + QShortcutEntry entry = d->sequences.at(i); + if ((allOwners || entry.owner == owner) + && (allIds || entry.id == id) + && (allKeys || entry.keyseq == key)) { + d->sequences[i].enabled = enable; + ++itemsChanged; + } + if (id == entry.id) + return itemsChanged; + --i; + } +#if defined(DEBUG_QSHORTCUTMAP) + qDebug().nospace() + << "QShortcutMap::setShortcutEnabled(" << enable << ", " << id << ", " + << owner << ", " << key << ") = " << itemsChanged; +#endif + return itemsChanged; +} + +/*! \internal + Changes the auto repeat state of a shortcut to \a enable. + If \a owner is 0, all entries in the map with the key sequence specified + is removed. If \a key is null, all sequences for \a owner is removed from + the map. If \a id is 0, any identical \a key sequences owned by \a owner + are changed. + Returns the number of sequences which are matched in the map. +*/ +int QShortcutMap::setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &key) +{ + Q_D(QShortcutMap); + int itemsChanged = 0; + bool allOwners = (owner == 0); + bool allKeys = key.isEmpty(); + bool allIds = id == 0; + + int i = d->sequences.size()-1; + while (i>=0) + { + QShortcutEntry entry = d->sequences.at(i); + if ((allOwners || entry.owner == owner) + && (allIds || entry.id == id) + && (allKeys || entry.keyseq == key)) { + d->sequences[i].autorepeat = on; + ++itemsChanged; + } + if (id == entry.id) + return itemsChanged; + --i; + } +#if defined(DEBUG_QSHORTCUTMAP) + qDebug().nospace() + << "QShortcutMap::setShortcutAutoRepeat(" << on << ", " << id << ", " + << owner << ", " << key << ") = " << itemsChanged; +#endif + return itemsChanged; +} + +/*! \internal + Resets the state of the statemachine to NoMatch +*/ +void QShortcutMap::resetState() +{ + Q_D(QShortcutMap); + d->currentState = QKeySequence::NoMatch; + clearSequence(d->currentSequences); +} + +/*! \internal + Returns the current state of the statemachine +*/ +QKeySequence::SequenceMatch QShortcutMap::state() +{ + Q_D(QShortcutMap); + return d->currentState; +} + +/*! \internal + Uses ShortcutOverride event to see if any widgets want to override + the event. If not, uses nextState(QKeyEvent) to check for a grabbed + Shortcut, and dispatchEvent() is found an identical. + \sa nextState dispatchEvent +*/ +bool QShortcutMap::tryShortcutEvent(QObject *o, QKeyEvent *e) +{ + Q_D(QShortcutMap); + + bool wasAccepted = e->isAccepted(); + bool wasSpontaneous = e->spont; + if (d->currentState == QKeySequence::NoMatch) { + ushort orgType = e->t; + e->t = QEvent::ShortcutOverride; + e->ignore(); + QCoreApplication::sendEvent(o, e); + e->t = orgType; + e->spont = wasSpontaneous; + if (e->isAccepted()) { + if (!wasAccepted) + e->ignore(); + return false; + } + } + + QKeySequence::SequenceMatch result = nextState(e); + bool stateWasAccepted = e->isAccepted(); + if (wasAccepted) + e->accept(); + else + e->ignore(); + + int identicalMatches = d->identicals.count(); + + switch(result) { + case QKeySequence::NoMatch: + return stateWasAccepted; + case QKeySequence::ExactMatch: + resetState(); + dispatchEvent(e); + default: + break; + } + // If nextState is QKeySequence::ExactMatch && identicals.count == 0 + // we've only found disabled shortcuts + return identicalMatches > 0 || result == QKeySequence::PartialMatch; +} + +/*! \internal + Returns the next state of the statemachine + If return value is SequenceMatch::ExactMatch, then a call to matches() + will return a QObjects* list of all matching objects for the last matching + sequence. +*/ +QKeySequence::SequenceMatch QShortcutMap::nextState(QKeyEvent *e) +{ + Q_D(QShortcutMap); + // Modifiers can NOT be shortcuts... + if (e->key() >= Qt::Key_Shift && + e->key() <= Qt::Key_Alt) + return d->currentState; + + QKeySequence::SequenceMatch result = QKeySequence::NoMatch; + + // We start fresh each time.. + d->identicals.resize(0); + + result = find(e); + if (result == QKeySequence::NoMatch && e->modifiers() & Qt::ShiftModifier) { + // If Shift + Key_Backtab, also try Shift + Qt::Key_Tab + if (e->key() == Qt::Key_Backtab) { + QKeyEvent pe = QKeyEvent(e->type(), Qt::Key_Tab, e->modifiers(), e->text()); + result = find(&pe); + } + } + + // Should we eat this key press? + if (d->currentState == QKeySequence::PartialMatch + || (d->currentState == QKeySequence::ExactMatch && d->identicals.count())) + e->accept(); + // Does the new state require us to clean up? + if (result == QKeySequence::NoMatch) + clearSequence(d->currentSequences); + d->currentState = result; + +#if defined(DEBUG_QSHORTCUTMAP) + qDebug().nospace() << "QShortcutMap::nextState(" << e << ") = " << result; +#endif + return result; +} + + +/*! \internal + Determines if an enabled shortcut has a matcing key sequence. +*/ +bool QShortcutMap::hasShortcutForKeySequence(const QKeySequence &seq) const +{ + Q_D(const QShortcutMap); + QShortcutEntry entry(seq); // needed for searching + QList<QShortcutEntry>::ConstIterator itEnd = d->sequences.constEnd(); + QList<QShortcutEntry>::ConstIterator it = qLowerBound(d->sequences.constBegin(), itEnd, entry); + + for (;it != itEnd; ++it) { + if (matches(entry.keyseq, (*it).keyseq) == QKeySequence::ExactMatch && correctContext(*it) && (*it).enabled) { + return true; + } + } + + //end of the loop: we didn't find anything + return false; +} + +/*! \internal + Returns the next state of the statemachine, based + on the new key event \a e. + Matches are appended to the vector of identicals, + which can be access through matches(). + \sa matches +*/ +QKeySequence::SequenceMatch QShortcutMap::find(QKeyEvent *e) +{ + Q_D(QShortcutMap); + if (!d->sequences.count()) + return QKeySequence::NoMatch; + + createNewSequences(e, d->newEntries); +#if defined(DEBUG_QSHORTCUTMAP) + qDebug() << "Possible shortcut key sequences:" << d->newEntries; +#endif + + // Should never happen + if (d->newEntries == d->currentSequences) { + Q_ASSERT_X(e->key() != Qt::Key_unknown || e->text().length(), + "QShortcutMap::find", "New sequence to find identical to previous"); + return QKeySequence::NoMatch; + } + + // Looking for new identicals, scrap old + d->identicals.resize(0); + + bool partialFound = false; + bool identicalDisabledFound = false; + QVector<QKeySequence> okEntries; + int result = QKeySequence::NoMatch; + for (int i = d->newEntries.count()-1; i >= 0 ; --i) { + QShortcutEntry entry(d->newEntries.at(i)); // needed for searching + QList<QShortcutEntry>::ConstIterator itEnd = d->sequences.constEnd(); + QList<QShortcutEntry>::ConstIterator it = + qLowerBound(d->sequences.constBegin(), itEnd, entry); + + int oneKSResult = QKeySequence::NoMatch; + int tempRes = QKeySequence::NoMatch; + do { + if (it == itEnd) + break; + tempRes = matches(entry.keyseq, (*it).keyseq); + oneKSResult = qMax(oneKSResult, tempRes); + if (tempRes != QKeySequence::NoMatch && correctContext(*it)) { + if (tempRes == QKeySequence::ExactMatch) { + if ((*it).enabled) + d->identicals.append(&*it); + else + identicalDisabledFound = true; + } else if (tempRes == QKeySequence::PartialMatch) { + // We don't need partials, if we have identicals + if (d->identicals.size()) + break; + // We only care about enabled partials, so we don't consume + // key events when all partials are disabled! + partialFound |= (*it).enabled; + } + } + ++it; + // If we got a valid match on this run, there might still be more keys to check against, + // so we'll loop once more. If we get NoMatch, there's guaranteed no more possible + // matches in the shortcutmap. + } while (tempRes != QKeySequence::NoMatch); + + // If the type of match improves (ergo, NoMatch->Partial, or Partial->Exact), clear the + // previous list. If this match is equal or better than the last match, append to the list + if (oneKSResult > result) { + okEntries.clear(); +#if defined(DEBUG_QSHORTCUTMAP) + qDebug() << "Found better match (" << d->newEntries << "), clearing key sequence list"; +#endif + } + if (oneKSResult && oneKSResult >= result) { + okEntries << d->newEntries.at(i); +#if defined(DEBUG_QSHORTCUTMAP) + qDebug() << "Added ok key sequence" << d->newEntries; +#endif + } + } + + if (d->identicals.size()) { + result = QKeySequence::ExactMatch; + } else if (partialFound) { + result = QKeySequence::PartialMatch; + } else if (identicalDisabledFound) { + result = QKeySequence::ExactMatch; + } else { + clearSequence(d->currentSequences); + result = QKeySequence::NoMatch; + } + if (result != QKeySequence::NoMatch) + d->currentSequences = okEntries; +#if defined(DEBUG_QSHORTCUTMAP) + qDebug() << "Returning shortcut match == " << result; +#endif + return QKeySequence::SequenceMatch(result); +} + +/*! \internal + Clears \a seq to an empty QKeySequence. + Same as doing (the slower) + \snippet doc/src/snippets/code/src_gui_kernel_qshortcutmap.cpp 0 +*/ +void QShortcutMap::clearSequence(QVector<QKeySequence> &ksl) +{ + ksl.clear(); + d_func()->newEntries.clear(); +} + +/*! \internal + Alters \a seq to the new sequence state, based on the + current sequence state, and the new key event \a e. +*/ +void QShortcutMap::createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl) +{ + Q_D(QShortcutMap); + QList<int> possibleKeys = QKeyMapper::possibleKeys(e); + int pkTotal = possibleKeys.count(); + if (!pkTotal) + return; + + int ssActual = d->currentSequences.count(); + int ssTotal = qMax(1, ssActual); + // Resize to possible permutations of the current sequence(s). + ksl.resize(pkTotal * ssTotal); + + int index = ssActual ? d->currentSequences.at(0).count() : 0; + for (int pkNum = 0; pkNum < pkTotal; ++pkNum) { + for (int ssNum = 0; ssNum < ssTotal; ++ssNum) { + int i = (pkNum * ssTotal) + ssNum; + QKeySequence &curKsl = ksl[i]; + if (ssActual) { + const QKeySequence &curSeq = d->currentSequences.at(ssNum); + curKsl.setKey(curSeq[0], 0); + curKsl.setKey(curSeq[1], 1); + curKsl.setKey(curSeq[2], 2); + curKsl.setKey(curSeq[3], 3); + } else { + curKsl.setKey(0, 0); + curKsl.setKey(0, 1); + curKsl.setKey(0, 2); + curKsl.setKey(0, 3); + } + // Filtering keycode here with 0xdfffffff to ignore the Keypad modifier + curKsl.setKey(possibleKeys.at(pkNum) & 0xdfffffff, index); + } + } +} + +/*! \internal + Basically the same function as QKeySequence::matches(const QKeySequence &seq) const + only that is specially handles Key_hyphen as Key_Minus, as people mix these up all the time and + they conceptually the same. +*/ +QKeySequence::SequenceMatch QShortcutMap::matches(const QKeySequence &seq1, + const QKeySequence &seq2) const +{ + uint userN = seq1.count(), + seqN = seq2.count(); + + if (userN > seqN) + return QKeySequence::NoMatch; + + // If equal in length, we have a potential ExactMatch sequence, + // else we already know it can only be partial. + QKeySequence::SequenceMatch match = (userN == seqN + ? QKeySequence::ExactMatch + : QKeySequence::PartialMatch); + + for (uint i = 0; i < userN; ++i) { + int userKey = seq1[i], + sequenceKey = seq2[i]; + if ((userKey & Qt::Key_unknown) == Qt::Key_hyphen) + userKey = (userKey & Qt::KeyboardModifierMask) | Qt::Key_Minus; + if ((sequenceKey & Qt::Key_unknown) == Qt::Key_hyphen) + sequenceKey = (sequenceKey & Qt::KeyboardModifierMask) | Qt::Key_Minus; + if (userKey != sequenceKey) + return QKeySequence::NoMatch; + } + return match; +} + +/*! \internal + Returns true if the widget \a w is a logical sub window of the current + top-level widget. +*/ +bool QShortcutMap::correctContext(const QShortcutEntry &item) const { + Q_ASSERT_X(item.owner, "QShortcutMap", "Shortcut has no owner. Illegal map state!"); + + QWidget *active_window = QApplication::activeWindow(); + + // popups do not become the active window, + // so we fake it here to get the correct context + // for the shortcut system. + if (QApplication::activePopupWidget()) + active_window = QApplication::activePopupWidget(); + + if (!active_window) + return false; +#ifndef QT_NO_ACTION + if (QAction *a = qobject_cast<QAction *>(item.owner)) + return correctContext(item.context, a, active_window); +#endif +#ifndef QT_NO_GRAPHICSVIEW + if (QGraphicsWidget *gw = qobject_cast<QGraphicsWidget *>(item.owner)) + return correctGraphicsWidgetContext(item.context, gw, active_window); +#endif + QWidget *w = qobject_cast<QWidget *>(item.owner); + if (!w) { + QShortcut *s = qobject_cast<QShortcut *>(item.owner); + w = s->parentWidget(); + } + return correctWidgetContext(item.context, w, active_window); +} + +bool QShortcutMap::correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidget *active_window) const +{ + bool visible = w->isVisible(); +#ifdef Q_WS_MAC + if (!qApp->testAttribute(Qt::AA_DontUseNativeMenuBar) && qobject_cast<QMenuBar *>(w)) + visible = true; +#endif + + if (!visible || !w->isEnabled()) + return false; + + if (context == Qt::ApplicationShortcut) + return QApplicationPrivate::tryModalHelper(w, 0); // true, unless w is shadowed by a modal dialog + + if (context == Qt::WidgetShortcut) + return w == QApplication::focusWidget(); + + if (context == Qt::WidgetWithChildrenShortcut) { + const QWidget *tw = QApplication::focusWidget(); + while (tw && tw != w && (tw->windowType() == Qt::Widget || tw->windowType() == Qt::Popup)) + tw = tw->parentWidget(); + return tw == w; + } + + // Below is Qt::WindowShortcut context + QWidget *tlw = w->window(); +#ifndef QT_NO_GRAPHICSVIEW + if (QWExtra *topData = tlw->d_func()->extra) { + if (topData->proxyWidget) { + bool res = correctGraphicsWidgetContext(context, (QGraphicsWidget *)topData->proxyWidget, active_window); + return res; + } + } +#endif + + /* if a floating tool window is active, keep shortcuts on the + * parent working */ + if (active_window != tlw && active_window && active_window->windowType() == Qt::Tool && active_window->parentWidget()) { + active_window = active_window->parentWidget()->window(); + } + + if (active_window != tlw) + return false; + + /* if we live in a MDI subwindow, ignore the event if we are + not the active document window */ + const QWidget* sw = w; + while (sw && !(sw->windowType() == Qt::SubWindow) && !sw->isWindow()) + sw = sw->parentWidget(); + if (sw && (sw->windowType() == Qt::SubWindow)) { + QWidget *focus_widget = QApplication::focusWidget(); + while (focus_widget && focus_widget != sw) + focus_widget = focus_widget->parentWidget(); + return sw == focus_widget; + } + +#if defined(DEBUG_QSHORTCUTMAP) + qDebug().nospace() << "..true [Pass-through]"; +#endif + return true; +} + +#ifndef QT_NO_GRAPHICSVIEW +bool QShortcutMap::correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsWidget *w, QWidget *active_window) const +{ + bool visible = w->isVisible(); +#ifdef Q_WS_MAC + if (!qApp->testAttribute(Qt::AA_DontUseNativeMenuBar) && qobject_cast<QMenuBar *>(w)) + visible = true; +#endif + + if (!visible || !w->isEnabled() || !w->scene()) + return false; + + if (context == Qt::ApplicationShortcut) { + // Applicationwide shortcuts are always reachable unless their owner + // is shadowed by modality. In QGV there's no modality concept, but we + // must still check if all views are shadowed. + QList<QGraphicsView *> views = w->scene()->views(); + for (int i = 0; i < views.size(); ++i) { + if (QApplicationPrivate::tryModalHelper(views.at(i), 0)) + return true; + } + return false; + } + + if (context == Qt::WidgetShortcut) + return static_cast<QGraphicsItem *>(w) == w->scene()->focusItem(); + + if (context == Qt::WidgetWithChildrenShortcut) { + const QGraphicsItem *ti = w->scene()->focusItem(); + if (ti && ti->isWidget()) { + const QGraphicsWidget *tw = static_cast<const QGraphicsWidget *>(ti); + while (tw && tw != w && (tw->windowType() == Qt::Widget || tw->windowType() == Qt::Popup)) + tw = tw->parentWidget(); + return tw == w; + } + return false; + } + + // Below is Qt::WindowShortcut context + + // Find the active view (if any). + QList<QGraphicsView *> views = w->scene()->views(); + QGraphicsView *activeView = 0; + for (int i = 0; i < views.size(); ++i) { + QGraphicsView *view = views.at(i); + if (view->window() == active_window) { + activeView = view; + break; + } + } + if (!activeView) + return false; + + // The shortcut is reachable if owned by a windowless widget, or if the + // widget's window is the same as the focus item's window. + QGraphicsWidget *a = w->scene()->activeWindow(); + return !w->window() || a == w->window(); +} +#endif + +#ifndef QT_NO_ACTION +bool QShortcutMap::correctContext(Qt::ShortcutContext context, QAction *a, QWidget *active_window) const +{ + const QList<QWidget *> &widgets = a->d_func()->widgets; +#if defined(DEBUG_QSHORTCUTMAP) + if (widgets.isEmpty()) + qDebug() << a << "not connected to any widgets; won't trigger"; +#endif + for (int i = 0; i < widgets.size(); ++i) { + QWidget *w = widgets.at(i); +#ifndef QT_NO_MENU + if (QMenu *menu = qobject_cast<QMenu *>(w)) { + QAction *a = menu->menuAction(); + if (correctContext(context, a, active_window)) + return true; + } else +#endif + if (correctWidgetContext(context, w, active_window)) + return true; + } + +#ifndef QT_NO_GRAPHICSVIEW + const QList<QGraphicsWidget *> &graphicsWidgets = a->d_func()->graphicsWidgets; +#if defined(DEBUG_QSHORTCUTMAP) + if (graphicsWidgets.isEmpty()) + qDebug() << a << "not connected to any widgets; won't trigger"; +#endif + for (int i = 0; i < graphicsWidgets.size(); ++i) { + QGraphicsWidget *w = graphicsWidgets.at(i); + if (correctGraphicsWidgetContext(context, w, active_window)) + return true; + } +#endif + return false; +} +#endif // QT_NO_ACTION + +/*! \internal + Converts keyboard button states into modifier states +*/ +int QShortcutMap::translateModifiers(Qt::KeyboardModifiers modifiers) +{ + int result = 0; + if (modifiers & Qt::ShiftModifier) + result |= Qt::SHIFT; + if (modifiers & Qt::ControlModifier) + result |= Qt::CTRL; + if (modifiers & Qt::MetaModifier) + result |= Qt::META; + if (modifiers & Qt::AltModifier) + result |= Qt::ALT; + return result; +} + +/*! \internal + Returns the vector of QShortcutEntry's matching the last Identical state. +*/ +QVector<const QShortcutEntry*> QShortcutMap::matches() const +{ + Q_D(const QShortcutMap); + return d->identicals; +} + +/*! \internal + Dispatches QShortcutEvents to widgets who grabbed the matched key sequence. +*/ +void QShortcutMap::dispatchEvent(QKeyEvent *e) +{ + Q_D(QShortcutMap); + if (!d->identicals.size()) + return; + + const QKeySequence &curKey = d->identicals.at(0)->keyseq; + if (d->prevSequence != curKey) { + d->ambigCount = 0; + d->prevSequence = curKey; + } + // Find next + const QShortcutEntry *current = 0, *next = 0; + int i = 0, enabledShortcuts = 0; + while(i < d->identicals.size()) { + current = d->identicals.at(i); + if (current->enabled || !next){ + ++enabledShortcuts; + if (enabledShortcuts > d->ambigCount + 1) + break; + next = current; + } + ++i; + } + d->ambigCount = (d->identicals.size() == i ? 0 : d->ambigCount + 1); + // Don't trigger shortcut if we're autorepeating and the shortcut is + // grabbed with not accepting autorepeats. + if (!next || (e->isAutoRepeat() && !next->autorepeat)) + return; + // Dispatch next enabled +#if defined(DEBUG_QSHORTCUTMAP) + qDebug().nospace() + << "QShortcutMap::dispatchEvent(): Sending QShortcutEvent(\"" + << (QString)next->keyseq << "\", " << next->id << ", " + << (bool)(enabledShortcuts>1) << ") to object(" << next->owner << ')'; +#endif + QShortcutEvent se(next->keyseq, next->id, enabledShortcuts>1); + QApplication::sendEvent(const_cast<QObject *>(next->owner), &se); +} + +/* \internal + QShortcutMap dump function, only available when DEBUG_QSHORTCUTMAP is + defined. +*/ +#if defined(Dump_QShortcutMap) +void QShortcutMap::dumpMap() const +{ + Q_D(const QShortcutMap); + for (int i = 0; i < d->sequences.size(); ++i) + qDebug().nospace() << &(d->sequences.at(i)); +} +#endif + +QT_END_NAMESPACE + +#endif // QT_NO_SHORTCUT diff --git a/src/gui/guikernel/qshortcutmap_p.h b/src/gui/guikernel/qshortcutmap_p.h new file mode 100644 index 0000000000..bc530b00b4 --- /dev/null +++ b/src/gui/guikernel/qshortcutmap_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSHORTCUTMAP_P_H +#define QSHORTCUTMAP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtGui/qkeysequence.h" +#include "QtCore/qvector.h" +#include "QtCore/qscopedpointer.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SHORTCUT + +// To enable dump output uncomment below +//#define Dump_QShortcutMap + +class QKeyEvent; +struct QShortcutEntry; +class QShortcutMapPrivate; +class QGraphicsWidget; +class QWidget; +class QAction; +class QObject; + +class QShortcutMap +{ + Q_DECLARE_PRIVATE(QShortcutMap) +public: + QShortcutMap(); + ~QShortcutMap(); + + int addShortcut(QObject *owner, const QKeySequence &key, Qt::ShortcutContext context); + int removeShortcut(int id, QObject *owner, const QKeySequence &key = QKeySequence()); + int setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &key = QKeySequence()); + int setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &key = QKeySequence()); + + void resetState(); + QKeySequence::SequenceMatch nextState(QKeyEvent *e); + QKeySequence::SequenceMatch state(); + void dispatchEvent(QKeyEvent *e); + bool tryShortcutEvent(QObject *o, QKeyEvent *e); + +#ifdef Dump_QShortcutMap + void dumpMap() const; +#endif + + bool hasShortcutForKeySequence(const QKeySequence &seq) const; + + +private: + bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidget *active_window) const; +#ifndef QT_NO_GRAPHICSVIEW + bool correctGraphicsWidgetContext(Qt::ShortcutContext context, QGraphicsWidget *w, QWidget *active_window) const; +#endif +#ifndef QT_NO_ACTION + bool correctContext(Qt::ShortcutContext context,QAction *a, QWidget *active_window) const; +#endif + QScopedPointer<QShortcutMapPrivate> d_ptr; + + QKeySequence::SequenceMatch find(QKeyEvent *e); + QKeySequence::SequenceMatch matches(const QKeySequence &seq1, const QKeySequence &seq2) const; + QVector<const QShortcutEntry *> matches() const; + void createNewSequences(QKeyEvent *e, QVector<QKeySequence> &ksl); + void clearSequence(QVector<QKeySequence> &ksl); + bool correctContext(const QShortcutEntry &item) const; + int translateModifiers(Qt::KeyboardModifiers modifiers); +}; + +#endif // QT_NO_SHORTCUT + +QT_END_NAMESPACE + +#endif // QSHORTCUTMAP_P_H diff --git a/src/gui/guikernel/qwindow.cpp b/src/gui/guikernel/qwindow.cpp new file mode 100644 index 0000000000..7b57a949f0 --- /dev/null +++ b/src/gui/guikernel/qwindow.cpp @@ -0,0 +1,458 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindow.h" + +#include "qplatformwindow_qpa.h" +#include "qwindowformat_qpa.h" +#include "qplatformglcontext_qpa.h" +#include "qwindowcontext_qpa.h" + +#include "qwindow_p.h" +#include "qguiapplication_p.h" + +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +QWindow::QWindow(QWindow *parent) + : QObject(*new QWindowPrivate()) +{ + if (parent) { + setParent(parent); + } +} + +QWindow::~QWindow() +{ + destroy(); +} + +void QWindow::setVisible(bool visible) +{ + Q_D(QWindow); + if (!d->platformWindow) { + create(); + } + d->platformWindow->setVisible(visible); +} + +bool QWindow::visible() const +{ + Q_D(const QWindow); + + return d->visible; +} + +void QWindow::create() +{ + Q_D(QWindow); + if (!d->platformWindow) { + d->platformWindow = QGuiApplicationPrivate::platformIntegration()->createPlatformWindow(this); + d->windowFlags = d->platformWindow->setWindowFlags(d->windowFlags); + if (!d->windowTitle.isNull()) + d->platformWindow->setWindowTitle(d->windowTitle); + } + Q_ASSERT(d->platformWindow); + + QObjectList childObjects = children(); + for (int i = 0; i < childObjects.size(); i ++) { + QObject *object = childObjects.at(i); + if(object->isWindowType()) { + QWindow *window = static_cast<QWindow *>(object); + window->setParent(this); + } + } +} + +WId QWindow::winId() const +{ + Q_D(const QWindow); + if(!d->platformWindow) + const_cast<QWindow *>(this)->create(); + return d->platformWindow->winId(); +} + +/** + Sets the parent Window. This will lead to the windowing system managing the clip of the window, so it will be clipped to the parent window. + Setting parent to be 0(NULL) means map it as a top level window. If the parent window has grabbed its window system resources, then the current window will also grab its window system resources. + **/ + +void QWindow::setParent(QWindow *parent) +{ + Q_D(QWindow); + + if (d->parent == parent) + return; + + QObject::setParent(parent); + + if (parent) { + if (parent->d_func()->platformWindow) { + if(!d->platformWindow) { + create(); + } + d->platformWindow->setParent(parent->d_func()->platformWindow); + d->parent = parent; + } + } else { + d->parent = 0; + if (d->parentWindow) { + d->platformWindow->setParent(0); + } + } + +} + +void QWindow::setWindowFormat(const QWindowFormat &format) +{ + Q_D(QWindow); + d->requestedFormat = format; +} + +QWindowFormat QWindow::requestedWindowFormat() const +{ + Q_D(const QWindow); + return d->requestedFormat; +} + +QWindowFormat QWindow::actualWindowFormat() const +{ + return glContext()->handle()->windowFormat(); +} + +void QWindow::setSurfaceType(SurfaceType type) +{ + Q_D(QWindow); + d->surfaceType = type; +} + +QWindow::SurfaceType QWindow::surfaceType() const +{ + Q_D(const QWindow); + return d->surfaceType; +} + +void QWindow::setWindowFlags(Qt::WindowFlags flags) +{ + Q_D(QWindow); + if (d->platformWindow) + d->windowFlags = d->platformWindow->setWindowFlags(flags); + else + d->windowFlags = flags; +} + +Qt::WindowFlags QWindow::windowFlags() const +{ + Q_D(const QWindow); + return d->windowFlags; +} + +void QWindow::setWindowTitle(const QString &title) +{ + Q_D(QWindow); + d->windowTitle = title; + if (d->platformWindow) { + d->platformWindow->setWindowTitle(title); + } +} + +QString QWindow::windowTitle() const +{ + Q_D(const QWindow); + return d->windowTitle; +} + +void QWindow::raise() +{ + Q_D(QWindow); + if (d->platformWindow) { + d->platformWindow->raise(); + } +} + +void QWindow::lower() +{ + Q_D(QWindow); + if (d->platformWindow) { + d->platformWindow->lower(); + } +} + +void QWindow::setOpacity(qreal level) +{ + Q_D(QWindow); + if (d->platformWindow) { + d->platformWindow->setOpacity(level); + } +} + +void QWindow::requestActivateWindow() +{ + Q_D(QWindow); + if (d->platformWindow) { + d->platformWindow->requestActivateWindow(); + } +} + +Qt::WindowStates QWindow::windowState() const +{ + qDebug() << "unimplemented:" << __FILE__ << __LINE__; + return Qt::WindowNoState; +} + +void QWindow::setWindowState(Qt::WindowStates state) +{ + Q_UNUSED(state); + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +QSize QWindow::minimumSize() const +{ + qDebug() << "unimplemented:" << __FILE__ << __LINE__; + return QSize(); +} + +QSize QWindow::maximumSize() const +{ + qDebug() << "unimplemented:" << __FILE__ << __LINE__; + return QSize(); +} + +void QWindow::setMinimumSize(const QSize &size) const +{ + Q_UNUSED(size); + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +void QWindow::setMaximumSize(const QSize &size) const +{ + Q_UNUSED(size); + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +void QWindow::setGeometry(const QRect &rect) +{ + Q_D(QWindow); + d->geometry = rect; + if (d->platformWindow) { + d->platformWindow->setGeometry(rect); + } +} + +QRect QWindow::geometry() const +{ + Q_D(const QWindow); + return d->geometry; +} + +void QWindow::setWindowIcon(const QImage &icon) const +{ + Q_UNUSED(icon); + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +QWindowContext * QWindow::glContext() const +{ + Q_D(const QWindow); + if (d->platformWindow && !d->glContext) + const_cast<QWindowPrivate *>(d)->glContext = new QWindowContext(const_cast<QWindow *>(this)); + return d->glContext; +} + +void QWindow::setRequestFormat(const QWindowFormat &format) +{ + Q_D(QWindow); + d->requestedFormat = format; +} + +QWindowFormat QWindow::format() const +{ + return QWindowFormat(); +} + +void QWindow::destroy() +{ + Q_D(QWindow); + if (d->glContext) { + d->glContext->deleteQGLContext(); + } + delete d->glContext; + d->glContext = 0; + delete d->platformWindow; + d->platformWindow = 0; +} + +QPlatformWindow *QWindow::handle() const +{ + Q_D(const QWindow); + return d->platformWindow; +} + +QWindowSurface *QWindow::surface() const +{ + Q_D(const QWindow); + return d->surface; +} + +void QWindow::showMinimized() +{ + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +void QWindow::showMaximized() +{ + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +void QWindow::showFullScreen() +{ + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +void QWindow::showNormal() +{ + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +bool QWindow::close() +{ + //should we have close? + qDebug() << "unimplemented:" << __FILE__ << __LINE__; + return true; +} + +void QWindow::resizeEvent(QResizeEvent *) +{ +} + +void QWindow::showEvent(QShowEvent *) +{ + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +void QWindow::hideEvent(QHideEvent *) +{ + qDebug() << "unimplemented:" << __FILE__ << __LINE__; +} + +bool QWindow::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::MouseMove: + mouseMoveEvent(static_cast<QMouseEvent*>(event)); + break; + + case QEvent::MouseButtonPress: + mousePressEvent(static_cast<QMouseEvent*>(event)); + break; + + case QEvent::MouseButtonRelease: + mouseReleaseEvent(static_cast<QMouseEvent*>(event)); + break; + + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent(static_cast<QMouseEvent*>(event)); + break; + + case QEvent::Resize: + resizeEvent(static_cast<QResizeEvent*>(event)); + break; + + case QEvent::KeyPress: + keyPressEvent(static_cast<QKeyEvent *>(event)); + break; + + case QEvent::KeyRelease: + keyReleaseEvent(static_cast<QKeyEvent *>(event)); + break; + +#ifndef QT_NO_WHEELEVENT + case QEvent::Wheel: + wheelEvent(static_cast<QWheelEvent*>(event)); + break; +#endif + + case QEvent::Close: + destroy(); + break; + + default: + return QObject::event(event); + } + return true; +} + +void QWindow::keyPressEvent(QKeyEvent *) +{ +} + +void QWindow::keyReleaseEvent(QKeyEvent *) +{ +} + +void QWindow::inputMethodEvent(QInputMethodEvent *) +{ +} + +void QWindow::mousePressEvent(QMouseEvent *) +{ +} + +void QWindow::mouseReleaseEvent(QMouseEvent *) +{ +} + +void QWindow::mouseDoubleClickEvent(QMouseEvent *) +{ +} + +void QWindow::mouseMoveEvent(QMouseEvent *) +{ +} + +#ifndef QT_NO_WHEELEVENT +void QWindow::wheelEvent(QWheelEvent *) +{ +} +#endif //QT_NO_WHEELEVENT + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qwindow.h b/src/gui/guikernel/qwindow.h new file mode 100644 index 0000000000..f2fde2d532 --- /dev/null +++ b/src/gui/guikernel/qwindow.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOW_QPA_H +#define QWINDOW_QPA_H + +#include <QtCore/QObject> +#include <QtCore/QEvent> +#include <QtGui/qwindowformat_qpa.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWindowPrivate; + +class QResizeEvent; +class QShowEvent; +class QHideEvent; +class QKeyEvent; +class QInputMethodEvent; +class QMouseEvent; +#ifndef QT_NO_WHEELEVENT +class QWheelEvent; +#endif + +class QPlatformWindow; +class QWindowContext; +class QWindowSurface; + +class Q_GUI_EXPORT QWindow : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWindow) + + Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle) + +public: + enum SurfaceType { + RasterSurface, + OpenGLSurface + }; + + QWindow(QWindow *parent = 0); + virtual ~QWindow(); + + void setVisible(bool visible); + bool visible() const; + + void create(); + + WId winId() const; + + QWindow *parent() const; + void setParent(QWindow *parent); + + QWindow *topLevelWindow() const; + + void setWindowFormat(const QWindowFormat &format); + QWindowFormat requestedWindowFormat() const; + QWindowFormat actualWindowFormat() const; + + void setSurfaceType(SurfaceType type); + SurfaceType surfaceType() const; + + void setWindowFlags(Qt::WindowFlags flags); + Qt::WindowFlags windowFlags() const; + + QString windowTitle() const; + + void setOpacity(qreal level); + void requestActivateWindow(); + + Qt::WindowStates windowState() const; + void setWindowState(Qt::WindowStates state); + + QSize minimumSize() const; + QSize maximumSize() const; + + void setMinimumSize(const QSize &size) const; + void setMaximumSize(const QSize &size) const; + + void setGeometry(const QRect &rect); + QRect geometry() const; + + void setWindowIcon(const QImage &icon) const; + + QWindowContext *glContext() const; + + void setRequestFormat(const QWindowFormat &format); + QWindowFormat format() const; + + void destroy(); + + QPlatformWindow *handle() const; + QWindowSurface *surface() const; + +public Q_SLOTS: + inline void show() { setVisible(true); } + inline void hide() { setVisible(false); } + + void showMinimized(); + void showMaximized(); + void showFullScreen(); + void showNormal(); + + bool close(); + void raise(); + void lower(); + + void setWindowTitle(const QString &); + +Q_SIGNALS: + void backBufferReady(); + +protected: + virtual void resizeEvent(QResizeEvent *); + + virtual void showEvent(QShowEvent *); + virtual void hideEvent(QHideEvent *); + + virtual bool event(QEvent *); + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + virtual void inputMethodEvent(QInputMethodEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void mouseDoubleClickEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); +#ifndef QT_NO_WHEELEVENT + virtual void wheelEvent(QWheelEvent *); +#endif + +private: + Q_DISABLE_COPY(QWindow) + + friend class QGuiApplication; + friend class QGuiApplicationPrivate; + friend class QWindowSurface; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWINDOW_QPA_H diff --git a/src/gui/guikernel/qwindow_p.h b/src/gui/guikernel/qwindow_p.h new file mode 100644 index 0000000000..304ab3bc49 --- /dev/null +++ b/src/gui/guikernel/qwindow_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOW_QPA_P_H +#define QWINDOW_QPA_P_H + +#include <QtGui/qwindow.h> + +#include <QtCore/private/qobject_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWindowPrivate : public QObjectPrivate +{ +public: + QWindowPrivate() + : QObjectPrivate() + , windowFlags(Qt::Window) + , surfaceType(QWindow::RasterSurface) + , platformWindow(0) + , visible(false) + , glContext(0) + , surface(0) + { + isWindow = true; + } + + ~QWindowPrivate() + { + } + + Qt::WindowFlags windowFlags; + QWindow::SurfaceType surfaceType; + QWindow *parentWindow; + QPlatformWindow *platformWindow; + bool visible; + QWindowFormat requestedFormat; + QString windowTitle; + QRect geometry; + QWindowContext *glContext; + QWindowSurface *surface; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWINDOW_QPA_P_H diff --git a/src/gui/guikernel/qwindowcontext_qpa.cpp b/src/gui/guikernel/qwindowcontext_qpa.cpp new file mode 100644 index 0000000000..f121e846b2 --- /dev/null +++ b/src/gui/guikernel/qwindowcontext_qpa.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformglcontext_qpa.h" +#include "qwindowcontext_qpa.h" +#include "qwindow.h" + +#include <QtCore/QThreadStorage> +#include <QtCore/QThread> + +#include <QDebug> + +class QWindowThreadContext +{ +public: + ~QWindowThreadContext() { + if (context) + context->doneCurrent(); + } + QWindowContext *context; +}; + +static QThreadStorage<QWindowThreadContext *> qwindow_context_storage; + +class QWindowContextPrivate +{ +public: + QWindowContextPrivate() + :qGLContextHandle(0) + { + } + + virtual ~QWindowContextPrivate() + { + //do not delete the QGLContext handle here as it is deleted in + //QWidgetPrivate::deleteTLSysExtra() + } + void *qGLContextHandle; + void (*qGLContextDeleteFunction)(void *handle); + QPlatformGLContext *platformGLContext; + static QWindowContext *staticSharedContext; + + static void setCurrentContext(QWindowContext *context); +}; + +QWindowContext *QWindowContextPrivate::staticSharedContext = 0; + +void QWindowContextPrivate::setCurrentContext(QWindowContext *context) +{ + QWindowThreadContext *threadContext = qwindow_context_storage.localData(); + if (!threadContext) { + if (!QThread::currentThread()) { + qWarning("No QTLS available. currentContext wont work"); + return; + } + threadContext = new QWindowThreadContext; + qwindow_context_storage.setLocalData(threadContext); + } + threadContext->context = context; +} + +/*! + Returns the last context which called makeCurrent. This function is thread aware. +*/ +QWindowContext* QWindowContext::currentContext() +{ + QWindowThreadContext *threadContext = qwindow_context_storage.localData(); + if(threadContext) { + return threadContext->context; + } + return 0; +} + +QPlatformGLContext *QWindowContext::handle() const +{ + Q_D(const QWindowContext); + return d->platformGLContext; +} + +/*! + All subclasses needs to specify the platformWindow. It can be a null window. +*/ +QWindowContext::QWindowContext(QWindow *window) + :d_ptr(new QWindowContextPrivate()) +{ + Q_D(QWindowContext); + Q_ASSERT(window); + if (!window->handle()) + window->create(); + d->platformGLContext = window->handle()->glContext(); +} + +/*! + If this is the current context for the thread, doneCurrent is called +*/ +QWindowContext::~QWindowContext() +{ + if (QWindowContext::currentContext() == this) { + doneCurrent(); + } + +} + +/*! + Reimplement in subclass to do makeCurrent on native GL context +*/ +void QWindowContext::makeCurrent() +{ + Q_D(QWindowContext); + QWindowContextPrivate::setCurrentContext(this); + d->platformGLContext->makeCurrent(); +} + +/*! + Reimplement in subclass to release current context. + Typically this is calling makeCurrent with 0 "surface" +*/ +void QWindowContext::doneCurrent() +{ + Q_D(QWindowContext); + d->platformGLContext->doneCurrent(); + QWindowContextPrivate::setCurrentContext(0); +} + +void QWindowContext::swapBuffers() +{ + Q_D(QWindowContext); + d->platformGLContext->swapBuffers(); +} + +void (*QWindowContext::getProcAddress(const QByteArray &procName)) () +{ + Q_D(QWindowContext); + void *result = d->platformGLContext->getProcAddress(QString::fromAscii(procName.constData())); + return (void (*)())result; +} + +/* + internal: Needs to have a pointer to qGLContext. But since this is in QtGui we cant + have any type information. +*/ +void *QWindowContext::qGLContextHandle() const +{ + Q_D(const QWindowContext); + return d->qGLContextHandle; +} + +void QWindowContext::setQGLContextHandle(void *handle,void (*qGLContextDeleteFunction)(void *)) +{ + Q_D(QWindowContext); + d->qGLContextHandle = handle; + d->qGLContextDeleteFunction = qGLContextDeleteFunction; +} + +void QWindowContext::deleteQGLContext() +{ + Q_D(QWindowContext); + if (d->qGLContextDeleteFunction && d->qGLContextHandle) { + d->qGLContextDeleteFunction(d->qGLContextHandle); + d->qGLContextDeleteFunction = 0; + d->qGLContextHandle = 0; + } +} diff --git a/src/gui/guikernel/qwindowcontext_qpa.h b/src/gui/guikernel/qwindowcontext_qpa.h new file mode 100644 index 0000000000..c16666a7f4 --- /dev/null +++ b/src/gui/guikernel/qwindowcontext_qpa.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWCONTEXT_H +#define QWINDOWCONTEXT_H + +#include <QtCore/qnamespace.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWindowContextPrivate; +class QPlatformGLContext; + +class Q_GUI_EXPORT QWindowContext +{ +Q_DECLARE_PRIVATE(QWindowContext); +public: + ~QWindowContext(); + + void makeCurrent(); + void doneCurrent(); + void swapBuffers(); + void (*getProcAddress(const QByteArray &procName)) (); + + static QWindowContext *currentContext(); + + QPlatformGLContext *handle() const; + +private: + QWindowContext(QWindow *window); + + QScopedPointer<QWindowContextPrivate> d_ptr; + + //hack to make it work with QGLContext::CurrentContext + friend class QGLContext; + friend class QWidgetPrivate; + friend class QWindow; + void *qGLContextHandle() const; + void setQGLContextHandle(void *handle,void (*qGLContextDeleteFunction)(void *)); + void deleteQGLContext(); + Q_DISABLE_COPY(QWindowContext); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWINDOWCONTEXT_H diff --git a/src/gui/guikernel/qwindowdefs.h b/src/gui/guikernel/qwindowdefs.h new file mode 100644 index 0000000000..dd6d13a338 --- /dev/null +++ b/src/gui/guikernel/qwindowdefs.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWDEFS_H +#define QWINDOWDEFS_H + +#include <QtCore/qobjectdefs.h> +#include <QtCore/qnamespace.h> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +// Class forward definitions + +class QPaintDevice; +class QWidget; +class QDialog; +class QColor; +class QPalette; +class QCursor; +class QPoint; +class QSize; +class QRect; +class QPolygon; +class QPainter; +class QRegion; +class QFont; +class QFontMetrics; +class QFontInfo; +class QPen; +class QBrush; +class QMatrix; +class QPixmap; +class QBitmap; +class QMovie; +class QImage; +class QPicture; +class QPrinter; +class QTimer; +class QTime; +class QClipboard; +class QString; +class QByteArray; +class QApplication; + +template<typename T> class QList; +typedef QList<QWidget *> QWidgetList; + +QT_END_NAMESPACE +QT_END_HEADER + +// Window system dependent definitions + +#if defined(Q_WS_MAC) && !defined(Q_WS_QWS) + +#include <QtGui/qmacdefines_mac.h> + +#ifdef Q_WS_MAC32 +typedef int WId; +#else +typedef long WId; +#endif + +#endif // Q_WS_MAC + +#if defined(Q_WS_WIN) +#include <QtGui/qwindowdefs_win.h> +#endif // Q_WS_WIN + +#if defined(Q_WS_X11) + +typedef struct _XDisplay Display; +typedef union _XEvent XEvent; +typedef struct _XGC *GC; +typedef struct _XRegion *Region; +typedef unsigned long WId; + +#endif // Q_WS_X11 + +#if defined(Q_WS_QWS) + +typedef unsigned long WId; +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE +struct QWSEvent; +QT_END_NAMESPACE +QT_END_HEADER + +#endif // Q_WS_QWS + +#if defined(Q_WS_QPA) + +typedef unsigned long WId; + +#endif // Q_WS_QPA + +#if defined(Q_OS_SYMBIAN) +class CCoeControl; +typedef CCoeControl * WId; +#endif // Q_OS_SYMBIAN + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +template<class K, class V> class QHash; +typedef QHash<WId, QWidget *> QWidgetMapper; + +template<class V> class QSet; +typedef QSet<QWidget *> QWidgetSet; + +QT_END_NAMESPACE +QT_END_HEADER + +#if defined(QT_NEEDS_QMAIN) +#define main qMain +#endif + +// Global platform-independent types and functions + +#endif // QWINDOWDEFS_H diff --git a/src/gui/guikernel/qwindowdefs_win.h b/src/gui/guikernel/qwindowdefs_win.h new file mode 100644 index 0000000000..a4dd38410c --- /dev/null +++ b/src/gui/guikernel/qwindowdefs_win.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWDEFS_WIN_H +#define QWINDOWDEFS_WIN_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +QT_END_NAMESPACE + +#if !defined(Q_NOWINSTRICT) +#define Q_WINSTRICT +#endif + +#if defined(Q_WINSTRICT) + +#if !defined(STRICT) +#define STRICT +#endif +#undef NO_STRICT +#define Q_DECLARE_HANDLE(name) struct name##__; typedef struct name##__ *name + +#else + +#if !defined(NO_STRICT) +#define NO_STRICT +#endif +#undef STRICT +#define Q_DECLARE_HANDLE(name) typedef HANDLE name + +#endif + +#ifndef HINSTANCE +Q_DECLARE_HANDLE(HINSTANCE); +#endif +#ifndef HDC +Q_DECLARE_HANDLE(HDC); +#endif +#ifndef HWND +Q_DECLARE_HANDLE(HWND); +#endif +#ifndef HFONT +Q_DECLARE_HANDLE(HFONT); +#endif +#ifndef HPEN +Q_DECLARE_HANDLE(HPEN); +#endif +#ifndef HBRUSH +Q_DECLARE_HANDLE(HBRUSH); +#endif +#ifndef HBITMAP +Q_DECLARE_HANDLE(HBITMAP); +#endif +#ifndef HICON +Q_DECLARE_HANDLE(HICON); +#endif +#ifndef HCURSOR +typedef HICON HCURSOR; +#endif +#ifndef HPALETTE +Q_DECLARE_HANDLE(HPALETTE); +#endif +#ifndef HRGN +Q_DECLARE_HANDLE(HRGN); +#endif +#ifndef HMONITOR +Q_DECLARE_HANDLE(HMONITOR); +#endif +#ifndef HRESULT +typedef long HRESULT; +#endif + +typedef struct tagMSG MSG; +typedef HWND WId; + + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT HINSTANCE qWinAppInst(); +Q_CORE_EXPORT HINSTANCE qWinAppPrevInst(); +Q_CORE_EXPORT int qWinAppCmdShow(); +Q_GUI_EXPORT HDC qt_win_display_dc(); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QWINDOWDEFS_WIN_H diff --git a/src/gui/guikernel/qwindowformat_qpa.cpp b/src/gui/guikernel/qwindowformat_qpa.cpp new file mode 100644 index 0000000000..03ccba7b07 --- /dev/null +++ b/src/gui/guikernel/qwindowformat_qpa.cpp @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowformat_qpa.h" + +#include "qplatformglcontext_qpa.h" + +#include <QtCore/QDebug> + +class QWindowFormatPrivate +{ +public: + QWindowFormatPrivate() + : ref(1) + , opts(QWindowFormat::DoubleBuffer | QWindowFormat::WindowSurface) + , redBufferSize(-1) + , greenBufferSize(-1) + , blueBufferSize(-1) + , alphaBufferSize(-1) + , depthSize(-1) + , stencilSize(-1) + , swapBehavior(QWindowFormat::DefaultSwapBehavior) + , numSamples(-1) + , sharedContext(0) + { + } + + QWindowFormatPrivate(const QWindowFormatPrivate *other) + : ref(1), + opts(other->opts), + redBufferSize(other->redBufferSize), + greenBufferSize(other->greenBufferSize), + blueBufferSize(other->blueBufferSize), + alphaBufferSize(other->alphaBufferSize), + depthSize(other->depthSize), + stencilSize(other->stencilSize), + swapBehavior(other->swapBehavior), + numSamples(other->numSamples), + sharedContext(other->sharedContext) + { + } + QAtomicInt ref; + QWindowFormat::FormatOptions opts; + int redBufferSize; + int greenBufferSize; + int blueBufferSize; + int alphaBufferSize; + int depthSize; + int stencilSize; + QWindowFormat::SwapBehavior swapBehavior; + int numSamples; + QWindowContext *sharedContext; +}; + +QWindowFormat::QWindowFormat() +{ + d = new QWindowFormatPrivate; +} + +QWindowFormat::QWindowFormat(QWindowFormat::FormatOptions options) +{ + d = new QWindowFormatPrivate; + d->opts = options; +} + +/*! + \internal +*/ +void QWindowFormat::detach() +{ + if (d->ref != 1) { + QWindowFormatPrivate *newd = new QWindowFormatPrivate(d); + if (!d->ref.deref()) + delete d; + d = newd; + } +} + +/*! + Constructs a copy of \a other. +*/ + +QWindowFormat::QWindowFormat(const QWindowFormat &other) +{ + d = other.d; + d->ref.ref(); +} + +/*! + Assigns \a other to this object. +*/ + +QWindowFormat &QWindowFormat::operator=(const QWindowFormat &other) +{ + if (d != other.d) { + other.d->ref.ref(); + if (!d->ref.deref()) + delete d; + d = other.d; + } + return *this; +} + +/*! + Destroys the QWindowFormat. +*/ +QWindowFormat::~QWindowFormat() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + \fn bool QWindowFormat::stereo() const + + Returns true if stereo buffering is enabled; otherwise returns + false. Stereo buffering is disabled by default. + + \sa setStereo() +*/ + +/*! + If \a enable is true enables stereo buffering; otherwise disables + stereo buffering. + + Stereo buffering is disabled by default. + + Stereo buffering provides extra color buffers to generate left-eye + and right-eye images. + + \sa stereo() +*/ + +void QWindowFormat::setStereo(bool enable) +{ + if (enable) { + d->opts |= QWindowFormat::StereoBuffers; + } else { + d->opts &= ~QWindowFormat::StereoBuffers; + } +} + +/*! + Returns the number of samples per pixel when multisampling is + enabled. By default, the highest number of samples that is + available is used. + + \sa setSampleBuffers(), sampleBuffers(), setSamples() +*/ +int QWindowFormat::samples() const +{ + return d->numSamples; +} + +/*! + Set the preferred number of samples per pixel when multisampling + is enabled to \a numSamples. By default, the highest number of + samples available is used. + + \sa setSampleBuffers(), sampleBuffers(), samples() +*/ +void QWindowFormat::setSamples(int numSamples) +{ + detach(); + d->numSamples = numSamples; +} + + + +void QWindowFormat::setSharedContext(QWindowContext *context) +{ + d->sharedContext = context; +} + +QWindowContext *QWindowFormat::sharedContext() const +{ + return d->sharedContext; +} + +/*! + \fn bool QWindowFormat::hasWindowSurface() const + + Returns true if the corresponding widget has an instance of QWindowSurface. + + Otherwise returns false. + + WindowSurface is enabled by default. + + \sa setOverlay() +*/ + +void QWindowFormat::setWindowSurface(bool enable) +{ + if (enable) { + d->opts |= QWindowFormat::WindowSurface; + } else { + d->opts &= ~QWindowFormat::WindowSurface; + } +} + +/*! + Sets the format option to \a opt. + + \sa testOption() +*/ + +void QWindowFormat::setOption(QWindowFormat::FormatOptions opt) +{ + detach(); + d->opts |= opt; +} + +/*! + Returns true if format option \a opt is set; otherwise returns false. + + \sa setOption() +*/ + +bool QWindowFormat::testOption(QWindowFormat::FormatOptions opt) const +{ + return d->opts & opt; +} + +/*! + Set the minimum depth buffer size to \a size. + + \sa depthBufferSize(), setDepth(), depth() +*/ +void QWindowFormat::setDepthBufferSize(int size) +{ + detach(); + d->depthSize = size; +} + +/*! + Returns the depth buffer size. + + \sa depth(), setDepth(), setDepthBufferSize() +*/ +int QWindowFormat::depthBufferSize() const +{ + return d->depthSize; +} + +void QWindowFormat::setSwapBehavior(SwapBehavior behavior) +{ + d->swapBehavior = behavior; +} + +QWindowFormat::SwapBehavior QWindowFormat::swapBehavior() const +{ + return d->swapBehavior; +} + +bool QWindowFormat::hasAlpha() const +{ + return d->alphaBufferSize > 0; +} + +/*! + Set the preferred stencil buffer size to \a size. + + \sa stencilBufferSize(), setStencil(), stencil() +*/ +void QWindowFormat::setStencilBufferSize(int size) +{ + detach(); + d->stencilSize = size; +} + +/*! + Returns the stencil buffer size. + + \sa stencil(), setStencil(), setStencilBufferSize() +*/ +int QWindowFormat::stencilBufferSize() const +{ + return d->stencilSize; +} + +int QWindowFormat::redBufferSize() const +{ + return d->redBufferSize; +} + +int QWindowFormat::greenBufferSize() const +{ + return d->greenBufferSize; +} + +int QWindowFormat::blueBufferSize() const +{ + return d->blueBufferSize; +} + +int QWindowFormat::alphaBufferSize() const +{ + return d->alphaBufferSize; +} + +void QWindowFormat::setRedBufferSize(int size) +{ + d->redBufferSize = size; +} + +void QWindowFormat::setGreenBufferSize(int size) +{ + d->greenBufferSize = size; +} + +void QWindowFormat::setBlueBufferSize(int size) +{ + d->blueBufferSize = size; +} + +void QWindowFormat::setAlphaBufferSize(int size) +{ + d->alphaBufferSize = size; +} + +bool operator==(const QWindowFormat& a, const QWindowFormat& b) +{ + return (a.d == b.d) || ((int) a.d->opts == (int) b.d->opts + && a.d->stencilSize == b.d->stencilSize + && a.d->redBufferSize == b.d->redBufferSize + && a.d->greenBufferSize == b.d->greenBufferSize + && a.d->blueBufferSize == b.d->blueBufferSize + && a.d->alphaBufferSize == b.d->alphaBufferSize + && a.d->depthSize == b.d->depthSize + && a.d->numSamples == b.d->numSamples + && a.d->swapBehavior == b.d->swapBehavior + && a.d->sharedContext == b.d->sharedContext); +} + + +/*! + Returns false if all the options of the two QWindowFormat objects + \a a and \a b are equal; otherwise returns true. + + \relates QWindowFormat +*/ + +bool operator!=(const QWindowFormat& a, const QWindowFormat& b) +{ + return !(a == b); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QWindowFormat &f) +{ + const QWindowFormatPrivate * const d = f.d; + + dbg.nospace() << "QWindowFormat(" + << "options " << d->opts + << ", depthBufferSize " << d->depthSize + << ", redBufferSize " << d->redBufferSize + << ", greenBufferSize " << d->greenBufferSize + << ", blueBufferSize " << d->blueBufferSize + << ", alphaBufferSize " << d->alphaBufferSize + << ", stencilBufferSize " << d->stencilSize + << ", samples " << d->numSamples + << ", swapBehavior " << d->swapBehavior + << ", sharedContext " << d->sharedContext + << ')'; + + return dbg.space(); +} +#endif diff --git a/src/gui/guikernel/qwindowformat_qpa.h b/src/gui/guikernel/qwindowformat_qpa.h new file mode 100644 index 0000000000..9bc2ccdfaa --- /dev/null +++ b/src/gui/guikernel/qwindowformat_qpa.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPLATFORMWINDOWFORMAT_QPA_H +#define QPLATFORMWINDOWFORMAT_QPA_H + +#include <QtGui/QPlatformWindow> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWindowContext; +class QWindowFormatPrivate; + +class Q_GUI_EXPORT QWindowFormat +{ +public: + enum FormatOption { + StereoBuffers = 0x0001, + WindowSurface = 0x0002 + }; + Q_DECLARE_FLAGS(FormatOptions, FormatOption) + + enum SwapBehavior { + DefaultSwapBehavior, + SingleBuffer, + DoubleBuffer, + TripleBuffer + }; + + enum OpenGLContextProfile { + NoProfile, + CoreProfile, + CompatibilityProfile + }; + + QWindowFormat(); + QWindowFormat(FormatOptions options); + QWindowFormat(const QWindowFormat &other); + QWindowFormat &operator=(const QWindowFormat &other); + ~QWindowFormat(); + + void setDepthBufferSize(int size); + int depthBufferSize() const; + + void setStencilBufferSize(int size); + int stencilBufferSize() const; + + void setRedBufferSize(int size); + int redBufferSize() const; + void setGreenBufferSize(int size); + int greenBufferSize() const; + void setBlueBufferSize(int size); + int blueBufferSize() const; + void setAlphaBufferSize(int size); + int alphaBufferSize() const; + + void setSamples(int numSamples); + int samples() const; + + void setSwapBehavior(SwapBehavior behavior); + SwapBehavior swapBehavior() const; + + bool hasAlpha() const; + + void setProfile(OpenGLContextProfile profile); + OpenGLContextProfile profile() const; + + void setSharedContext(QWindowContext *context); + QWindowContext *sharedContext() const; + + bool stereo() const; + void setStereo(bool enable); + bool windowSurface() const; + void setWindowSurface(bool enable); + + void setOption(QWindowFormat::FormatOptions opt); + bool testOption(QWindowFormat::FormatOptions opt) const; + +private: + QWindowFormatPrivate *d; + + void detach(); + + friend Q_GUI_EXPORT bool operator==(const QWindowFormat&, const QWindowFormat&); + friend Q_GUI_EXPORT bool operator!=(const QWindowFormat&, const QWindowFormat&); +#ifndef QT_NO_DEBUG_STREAM + friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QWindowFormat &); +#endif +}; + +Q_GUI_EXPORT bool operator==(const QWindowFormat&, const QWindowFormat&); +Q_GUI_EXPORT bool operator!=(const QWindowFormat&, const QWindowFormat&); + +#ifndef QT_NO_DEBUG_STREAM +Q_OPENGL_EXPORT QDebug operator<<(QDebug, const QWindowFormat &); +#endif + +Q_DECLARE_OPERATORS_FOR_FLAGS(QWindowFormat::FormatOptions) + +inline bool QWindowFormat::stereo() const +{ + return testOption(QWindowFormat::StereoBuffers); +} + +inline bool QWindowFormat::windowSurface() const +{ + return testOption(QWindowFormat::WindowSurface); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QPLATFORMWINDOWFORMAT_QPA_H diff --git a/src/gui/guikernel/qwindowsysteminterface_qpa.cpp b/src/gui/guikernel/qwindowsysteminterface_qpa.cpp new file mode 100644 index 0000000000..5fd7d76003 --- /dev/null +++ b/src/gui/guikernel/qwindowsysteminterface_qpa.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwindowsysteminterface_qpa.h" +#include "qwindowsysteminterface_qpa_p.h" +#include "private/qapplication_p.h" +#include <QAbstractEventDispatcher> + +QT_BEGIN_NAMESPACE + + +QTime QWindowSystemInterfacePrivate::eventTime; + +//------------------------------------------------------------ +// +// Callback functions for plugins: +// + +QList<QWindowSystemInterfacePrivate::WindowSystemEvent *> QWindowSystemInterfacePrivate::windowSystemEventQueue; +QMutex QWindowSystemInterfacePrivate::queueMutex; + +extern QPointer<QWindow> qt_last_mouse_receiver; + + +void QWindowSystemInterface::handleEnterEvent(QWindow *tlw) +{ + if (tlw) { + QWindowSystemInterfacePrivate::EnterEvent *e = new QWindowSystemInterfacePrivate::EnterEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); + } +} + +void QWindowSystemInterface::handleLeaveEvent(QWindow *tlw) +{ + QWindowSystemInterfacePrivate::LeaveEvent *e = new QWindowSystemInterfacePrivate::LeaveEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleWindowActivated(QWindow *tlw) +{ + QWindowSystemInterfacePrivate::ActivatedWindowEvent *e = new QWindowSystemInterfacePrivate::ActivatedWindowEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleGeometryChange(QWindow *tlw, const QRect &newRect) +{ + QWindowSystemInterfacePrivate::GeometryChangeEvent *e = new QWindowSystemInterfacePrivate::GeometryChangeEvent(tlw,newRect); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + + +void QWindowSystemInterface::handleCloseEvent(QWindow *tlw) +{ + if (tlw) { + QWindowSystemInterfacePrivate::CloseEvent *e = + new QWindowSystemInterfacePrivate::CloseEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); + } +} + +/*! + +\a tlw == 0 means that \a ev is in global coords only + + +*/ +void QWindowSystemInterface::handleMouseEvent(QWindow *w, const QPoint & local, const QPoint & global, Qt::MouseButtons b) { + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleMouseEvent(w, time, local, global, b); +} + +void QWindowSystemInterface::handleMouseEvent(QWindow *tlw, ulong timestamp, const QPoint & local, const QPoint & global, Qt::MouseButtons b) +{ + QWindowSystemInterfacePrivate::MouseEvent * e = + new QWindowSystemInterfacePrivate::MouseEvent(tlw, timestamp, local, global, b); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleKeyEvent(QWindow *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) { + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleKeyEvent(w, time, t, k, mods, text, autorep, count); +} + +void QWindowSystemInterface::handleKeyEvent(QWindow *tlw, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) +{ + QWindowSystemInterfacePrivate::KeyEvent * e = + new QWindowSystemInterfacePrivate::KeyEvent(tlw, timestamp, t, k, mods, text, autorep, count); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleExtendedKeyEvent(QWindow *w, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text, bool autorep, + ushort count) +{ + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleExtendedKeyEvent(w, time, type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, + text, autorep, count); +} + +void QWindowSystemInterface::handleExtendedKeyEvent(QWindow *tlw, ulong timestamp, QEvent::Type type, int key, + Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text, bool autorep, + ushort count) +{ + QWindowSystemInterfacePrivate::KeyEvent * e = + new QWindowSystemInterfacePrivate::KeyEvent(tlw, timestamp, type, key, modifiers, + nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleWheelEvent(QWindow *w, const QPoint & local, const QPoint & global, int d, Qt::Orientation o) { + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleWheelEvent(w, time, local, global, d, o); +} + +void QWindowSystemInterface::handleWheelEvent(QWindow *tlw, ulong timestamp, const QPoint & local, const QPoint & global, int d, Qt::Orientation o) +{ + QWindowSystemInterfacePrivate::WheelEvent *e = + new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, d, o); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +int QWindowSystemInterfacePrivate::windowSystemEventsQueued() +{ + queueMutex.lock(); + int ret = windowSystemEventQueue.count(); + queueMutex.unlock(); + return ret; +} + +QWindowSystemInterfacePrivate::WindowSystemEvent * QWindowSystemInterfacePrivate::getWindowSystemEvent() +{ + queueMutex.lock(); + QWindowSystemInterfacePrivate::WindowSystemEvent *ret; + if (windowSystemEventQueue.isEmpty()) + ret = 0; + else + ret = windowSystemEventQueue.takeFirst(); + queueMutex.unlock(); + return ret; +} + +void QWindowSystemInterfacePrivate::queueWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *ev) +{ + queueMutex.lock(); + windowSystemEventQueue.append(ev); + queueMutex.unlock(); + + QAbstractEventDispatcher *dispatcher = QApplicationPrivate::qt_qpa_core_dispatcher(); + if (dispatcher) + dispatcher->wakeUp(); +} + +void QWindowSystemInterface::handleTouchEvent(QWindow *w, QEvent::Type type, QTouchEvent::DeviceType devType, const QList<struct TouchPoint> &points) { + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleTouchEvent(w, time, type, devType, points); +} + +void QWindowSystemInterface::handleTouchEvent(QWindow *tlw, ulong timestamp, QEvent::Type type, QTouchEvent::DeviceType devType, const QList<struct TouchPoint> &points) +{ + if (!points.size()) // Touch events must have at least one point + return; + + QList<QTouchEvent::TouchPoint> touchPoints; + Qt::TouchPointStates states; + QTouchEvent::TouchPoint p; + + QList<struct TouchPoint>::const_iterator point = points.constBegin(); + QList<struct TouchPoint>::const_iterator end = points.constEnd(); + while (point != end) { + p.setId(point->id); + p.setPressure(point->pressure); + states |= point->state; + Qt::TouchPointStates state = point->state; + if (point->isPrimary) { + state |= Qt::TouchPointPrimary; + } + p.setState(state); + p.setRect(point->area); + p.setScreenPos(point->area.center()); + p.setNormalizedPos(point->normalPosition); + + touchPoints.append(p); + ++point; + } + + QWindowSystemInterfacePrivate::TouchEvent *e = + new QWindowSystemInterfacePrivate::TouchEvent(tlw, timestamp, type, devType, touchPoints); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleScreenGeometryChange(int screenIndex) +{ + QWindowSystemInterfacePrivate::ScreenGeometryEvent *e = + new QWindowSystemInterfacePrivate::ScreenGeometryEvent(screenIndex); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleScreenAvailableGeometryChange(int screenIndex) +{ + QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent *e = + new QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent(screenIndex); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleScreenCountChange(int count) +{ + QWindowSystemInterfacePrivate::ScreenCountEvent *e = + new QWindowSystemInterfacePrivate::ScreenCountEvent(count); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +QT_END_NAMESPACE diff --git a/src/gui/guikernel/qwindowsysteminterface_qpa.h b/src/gui/guikernel/qwindowsysteminterface_qpa.h new file mode 100644 index 0000000000..bd8139933c --- /dev/null +++ b/src/gui/guikernel/qwindowsysteminterface_qpa.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QWINDOWSYSTEMINTERFACE_H +#define QWINDOWSYSTEMINTERFACE_H + +#include <QtCore/QTime> +#include <QtGui/qwindowdefs.h> +#include <QtCore/QEvent> +#include <QtGui/QWindow> +#include <QtCore/QWeakPointer> +#include <QtCore/QMutex> +#include <QtGui/QTouchEvent> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QWindowSystemInterface +{ +public: + static void handleMouseEvent(QWindow *w, const QPoint & local, const QPoint & global, Qt::MouseButtons b); + static void handleMouseEvent(QWindow *w, ulong timestamp, const QPoint & local, const QPoint & global, Qt::MouseButtons b); + + static void handleKeyEvent(QWindow *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); + static void handleKeyEvent(QWindow *w, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); + + static void handleExtendedKeyEvent(QWindow *w, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text = QString(), bool autorep = false, + ushort count = 1); + static void handleExtendedKeyEvent(QWindow *w, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text = QString(), bool autorep = false, + ushort count = 1); + + static void handleWheelEvent(QWindow *w, const QPoint & local, const QPoint & global, int d, Qt::Orientation o); + static void handleWheelEvent(QWindow *w, ulong timestamp, const QPoint & local, const QPoint & global, int d, Qt::Orientation o); + + struct TouchPoint { + int id; // for application use + bool isPrimary; // for application use + QPointF normalPosition; // touch device coordinates, (0 to 1, 0 to 1) + QRectF area; // the touched area, centered at position in screen coordinates + qreal pressure; // 0 to 1 + Qt::TouchPointState state; //Qt::TouchPoint{Pressed|Moved|Stationary|Released} + }; + + static void handleTouchEvent(QWindow *w, QEvent::Type type, QTouchEvent::DeviceType devType, const QList<struct TouchPoint> &points); + static void handleTouchEvent(QWindow *w, ulong timestamp, QEvent::Type type, QTouchEvent::DeviceType devType, const QList<struct TouchPoint> &points); + + static void handleGeometryChange(QWindow *w, const QRect &newRect); + static void handleCloseEvent(QWindow *w); + static void handleEnterEvent(QWindow *w); + static void handleLeaveEvent(QWindow *w); + static void handleWindowActivated(QWindow *w); + + // Changes to the screen + static void handleScreenGeometryChange(int screenIndex); + static void handleScreenAvailableGeometryChange(int screenIndex); + static void handleScreenCountChange(int count); +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // QWINDOWSYSTEMINTERFACE_H diff --git a/src/gui/guikernel/qwindowsysteminterface_qpa_p.h b/src/gui/guikernel/qwindowsysteminterface_qpa_p.h new file mode 100644 index 0000000000..30adecc405 --- /dev/null +++ b/src/gui/guikernel/qwindowsysteminterface_qpa_p.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QWINDOWSYSTEMINTERFACE_QPA_P_H +#define QWINDOWSYSTEMINTERFACE_QPA_P_H + +#include "qwindowsysteminterface_qpa.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QWindowSystemInterfacePrivate { +public: + enum EventType { + Close, + GeometryChange, + Enter, + Leave, + ActivatedWindow, + Mouse, + Wheel, + Key, + Touch, + ScreenGeometry, + ScreenAvailableGeometry, + ScreenCountChange + }; + + class WindowSystemEvent { + public: + WindowSystemEvent(EventType t) + : type(t) { } + EventType type; + }; + + class CloseEvent : public WindowSystemEvent { + public: + CloseEvent(QWindow *w) + : WindowSystemEvent(Close), window(w) { } + QWeakPointer<QWindow> window; + }; + + class GeometryChangeEvent : public WindowSystemEvent { + public: + GeometryChangeEvent(QWindow *tlw, const QRect &newGeometry) + : WindowSystemEvent(GeometryChange), tlw(tlw), newGeometry(newGeometry) + { } + QWeakPointer<QWindow> tlw; + QRect newGeometry; + }; + + class EnterEvent : public WindowSystemEvent { + public: + EnterEvent(QWindow *enter) + : WindowSystemEvent(Enter), enter(enter) + { } + QWeakPointer<QWindow> enter; + }; + + class LeaveEvent : public WindowSystemEvent { + public: + LeaveEvent(QWindow *leave) + : WindowSystemEvent(Leave), leave(leave) + { } + QWeakPointer<QWindow> leave; + }; + + class ActivatedWindowEvent : public WindowSystemEvent { + public: + ActivatedWindowEvent(QWindow *activatedWindow) + : WindowSystemEvent(ActivatedWindow), activated(activatedWindow) + { } + QWeakPointer<QWindow> activated; + }; + + class UserEvent : public WindowSystemEvent { + public: + UserEvent(QWindow * w, ulong time, EventType t) + : WindowSystemEvent(t), window(w), timestamp(time) { } + QWeakPointer<QWindow> window; + unsigned long timestamp; + }; + + class MouseEvent : public UserEvent { + public: + MouseEvent(QWindow * w, ulong time, const QPoint & local, const QPoint & global, Qt::MouseButtons b) + : UserEvent(w, time, Mouse), localPos(local), globalPos(global), buttons(b) { } + QPoint localPos; + QPoint globalPos; + Qt::MouseButtons buttons; + }; + + class WheelEvent : public UserEvent { + public: + WheelEvent(QWindow *w, ulong time, const QPoint & local, const QPoint & global, int d, Qt::Orientation o) + : UserEvent(w, time, Wheel), delta(d), localPos(local), globalPos(global), orient(o) { } + int delta; + QPoint localPos; + QPoint globalPos; + Qt::Orientation orient; + }; + + class KeyEvent : public UserEvent { + public: + KeyEvent(QWindow *w, ulong time, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1) + :UserEvent(w, time, Key), key(k), unicode(text), repeat(autorep), + repeatCount(count), modifiers(mods), keyType(t), + nativeScanCode(0), nativeVirtualKey(0), nativeModifiers(0) { } + KeyEvent(QWindow *w, ulong time, QEvent::Type t, int k, Qt::KeyboardModifiers mods, + quint32 nativeSC, quint32 nativeVK, quint32 nativeMods, + const QString & text = QString(), bool autorep = false, ushort count = 1) + :UserEvent(w, time, Key), key(k), unicode(text), repeat(autorep), + repeatCount(count), modifiers(mods), keyType(t), + nativeScanCode(nativeSC), nativeVirtualKey(nativeVK), nativeModifiers(nativeMods) { } + int key; + QString unicode; + bool repeat; + ushort repeatCount; + Qt::KeyboardModifiers modifiers; + QEvent::Type keyType; + quint32 nativeScanCode; + quint32 nativeVirtualKey; + quint32 nativeModifiers; + }; + + class TouchEvent : public UserEvent { + public: + TouchEvent(QWindow *w, ulong time, QEvent::Type t, QTouchEvent::DeviceType d, const QList<QTouchEvent::TouchPoint> &p) + :UserEvent(w, time, Touch), devType(d), points(p), touchType(t) { } + QTouchEvent::DeviceType devType; + QList<QTouchEvent::TouchPoint> points; + QEvent::Type touchType; + + }; + + class ScreenCountEvent : public WindowSystemEvent { + public: + ScreenCountEvent (int count) + : WindowSystemEvent(ScreenCountChange) , count(count) { } + int count; + }; + + class ScreenGeometryEvent : public WindowSystemEvent { + public: + ScreenGeometryEvent(int index) + : WindowSystemEvent(ScreenGeometry), index(index) { } + int index; + }; + + class ScreenAvailableGeometryEvent : public WindowSystemEvent { + public: + ScreenAvailableGeometryEvent(int index) + : WindowSystemEvent(ScreenAvailableGeometry), index(index) { } + int index; + }; + + static QList<WindowSystemEvent *> windowSystemEventQueue; + static QMutex queueMutex; + + static int windowSystemEventsQueued(); + static WindowSystemEvent * getWindowSystemEvent(); + static void queueWindowSystemEvent(WindowSystemEvent *ev); + + static QTime eventTime; +}; + +QT_END_HEADER +QT_END_NAMESPACE + +#endif // QWINDOWSYSTEMINTERFACE_QPA_P_H |