diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index da6e69eb36..bdd18500a3 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -583,9 +583,12 @@ static const char * xcb_atomnames = { "_NET_ACTIVE_WINDOW\0" // Property formats + "XA_STRING\0" "COMPOUND_TEXT\0" "TEXT\0" "UTF8_STRING\0" + "XA_PIXMAP\0" + "XA_BITMAP\0" // xdnd "XdndEnter\0" @@ -663,8 +666,32 @@ void QXcbConnection::initializeAllAtoms() { for (i = 0; i < QXcbAtom::NAtoms; ++i) cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]); - for (i = 0; i < QXcbAtom::NAtoms; ++i) - m_allAtoms[i] = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0)->atom; + for (i = 0; i < QXcbAtom::NAtoms; ++i) { + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0); + m_allAtoms[i] = reply->atom; + free(reply); + } +} + +xcb_atom_t QXcbConnection::internAtom(const char *name) +{ + if (!name || *name == 0) + return XCB_NONE; + + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xcb_connection(), false, strlen(name), name); + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookie, 0); + int atom = reply->atom; + free(reply); + return atom; +} + +QByteArray QXcbConnection::atomName(xcb_atom_t atom) +{ + xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name_unchecked(xcb_connection(), atom); + xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, 0); + QByteArray result(xcb_get_atom_name_name(reply)); + free(reply); + return result; } void QXcbConnection::sync() diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 23c704f6de..124b8512ea 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -164,9 +164,12 @@ namespace QXcbAtom { _NET_ACTIVE_WINDOW, // Property formats + XA_STRING, COMPOUND_TEXT, TEXT, UTF8_STRING, + XA_PIXMAP, + XA_BITMAP, // Xdnd XdndEnter, @@ -234,6 +237,8 @@ public: int primaryScreen() const { return m_primaryScreen; } xcb_atom_t atom(QXcbAtom::Atom atom); + xcb_atom_t internAtom(const char *name); + QByteArray atomName(xcb_atom_t atom); const char *displayName() const { return m_displayName.constData(); } diff --git a/src/plugins/platforms/xcb/qxcbmime.cpp b/src/plugins/platforms/xcb/qxcbmime.cpp new file mode 100644 index 0000000000..311a909447 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbmime.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** 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 plugins 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 "qxcbmime.h" + +#include +#include +#include + +#include + +#undef XA_STRING +#undef XA_PIXMAP +#undef XA_BITMAP + +QXcbMime::QXcbMime() + : QInternalMimeData() +{ } + +QXcbMime::~QXcbMime() +{} + + + +QString QXcbMime::mimeAtomToString(QXcbConnection *connection, xcb_atom_t a) +{ + if (a == XCB_NONE) + return 0; + + // special cases for string type + if (a == connection->atom(QXcbAtom::XA_STRING) + || a == connection->atom(QXcbAtom::UTF8_STRING) + || a == connection->atom(QXcbAtom::TEXT) + || a == connection->atom(QXcbAtom::COMPOUND_TEXT)) + return QLatin1String("text/plain"); + + // special case for images + if (a == connection->atom(QXcbAtom::XA_PIXMAP)) + return QLatin1String("image/ppm"); + + QByteArray atomName = connection->atomName(a); + + // special cases for uris + if (atomName == "text/x-moz-url") + atomName = "text/uri-list"; + + return QString::fromLatin1(atomName.constData()); +} + +bool QXcbMime::mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeData *mimeData, QByteArray *data, + xcb_atom_t *atomFormat, int *dataFormat) +{ + if (!data) + return false; + + bool ret = false; + *atomFormat = a; + *dataFormat = 8; + + if ((a == connection->atom(QXcbAtom::UTF8_STRING) + || a == connection->atom(QXcbAtom::XA_STRING) + || a == connection->atom(QXcbAtom::TEXT) + || a == connection->atom(QXcbAtom::COMPOUND_TEXT)) + && QInternalMimeData::hasFormatHelper(QLatin1String("text/plain"), mimeData)) { + if (a == connection->atom(QXcbAtom::UTF8_STRING)){ + *data = QInternalMimeData::renderDataHelper(QLatin1String("text/plain"), mimeData); + ret = true; + } else if (a == connection->atom(QXcbAtom::XA_STRING)) { + *data = QString::fromUtf8(QInternalMimeData::renderDataHelper( + QLatin1String("text/plain"), mimeData)).toLocal8Bit(); + ret = true; + } else if (a == connection->atom(QXcbAtom::TEXT) + || a == connection->atom(QXcbAtom::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 == connection->atom(QXcbAtom::COMPOUND_TEXT)) + ? XCompoundTextStyle : XStdICCTextStyle; + XTextProperty textprop; + if (list[0] != NULL + && XmbTextListToTextProperty(DISPLAY_FROM_XCB(connection), list, 1, style, &textprop) == Success) { + *atomFormat = textprop.encoding; + *dataFormat = textprop.format; + *data = QByteArray((const char *) textprop.value, textprop.nitems * textprop.format / 8); + ret = true; + + XFree(textprop.value); + } + } + return ret; + } + + QString atomName = mimeAtomToString(connection, a); + if (QInternalMimeData::hasFormatHelper(atomName, mimeData)) { + *data = QInternalMimeData::renderDataHelper(atomName, mimeData); + if (atomName == QLatin1String("application/x-color")) + *dataFormat = 16; + ret = true; + } 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(mozUri.utf16()), mozUri.length() * 2); + ret = true; + } else if ((a == connection->atom(QXcbAtom::XA_PIXMAP) || a == connection->atom(QXcbAtom::XA_BITMAP)) && mimeData->hasImage()) { + ret = true; + } + return ret; +} + +QList QXcbMime::mimeAtomsForFormat(QXcbConnection *connection, const QString &format) +{ + QList atoms; + atoms.append(connection->internAtom(format.toLatin1())); + + // special cases for strings + if (format == QLatin1String("text/plain")) { + atoms.append(connection->atom(QXcbAtom::UTF8_STRING)); + atoms.append(connection->atom(QXcbAtom::XA_STRING)); + atoms.append(connection->atom(QXcbAtom::TEXT)); + atoms.append(connection->atom(QXcbAtom::COMPOUND_TEXT)); + } + + // special cases for uris + if (format == QLatin1String("text/uri-list")) + atoms.append(connection->internAtom("text/x-moz-url")); + + //special cases for images + if (format == QLatin1String("image/ppm")) + atoms.append(connection->atom(QXcbAtom::XA_PIXMAP)); + if (format == QLatin1String("image/pbm")) + atoms.append(connection->atom(QXcbAtom::XA_BITMAP)); + + return atoms; +} + +QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &data, const QString &format, + QVariant::Type requestedType, const QByteArray &encoding) +{ + QString atomName = mimeAtomToString(connection, 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 == connection->atom(QXcbAtom::UTF8_STRING)) + return QString::fromUtf8(data); + if (a == connection->atom(QXcbAtom::XA_STRING)) + return QString::fromLatin1(data); + if (a == connection->atom(QXcbAtom::TEXT) + || a == connection->atom(QXcbAtom::COMPOUND_TEXT)) + // #### might be wrong for COMPOUND_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 + // 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(); + } + } + +#if 0 // ### + // special case for images + if (format == QLatin1String("image/ppm")) { + if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) { + Pixmap xpm = *((Pixmap*)data.data()); + if (!xpm) + return QByteArray(); + Window root; + int x; + int y; + uint width; + uint height; + uint border_width; + uint depth; + + XGetGeometry(display, xpm, &root, &x, &y, &width, &height, &border_width, &depth); + XImage *ximg = XGetImage(display,xpm,x,y,width,height,AllPlanes,depth==1 ? XYPixmap : ZPixmap); + QImage qimg = QXlibStatic::qimageFromXImage(ximg); + XDestroyImage(ximg); + + QImageWriter imageWriter; + imageWriter.setFormat("PPMRAW"); + QBuffer buf; + buf.open(QIODevice::WriteOnly); + imageWriter.setDevice(&buf); + imageWriter.write(qimg); + return buf.buffer(); + } + } +#endif + return QVariant(); +} + +xcb_atom_t QXcbMime::mimeAtomForFormat(QXcbConnection *connection, const QString &format, QVariant::Type requestedType, + const QList<xcb_atom_t> &atoms, QByteArray *requestedEncoding) +{ + requestedEncoding->clear(); + + // find matches for string types + if (format == QLatin1String("text/plain")) { + if (atoms.contains(connection->atom(QXcbAtom::UTF8_STRING))) + return connection->atom(QXcbAtom::UTF8_STRING); + if (atoms.contains(connection->atom(QXcbAtom::COMPOUND_TEXT))) + return connection->atom(QXcbAtom::COMPOUND_TEXT); + if (atoms.contains(connection->atom(QXcbAtom::TEXT))) + return connection->atom(QXcbAtom::TEXT); + if (atoms.contains(connection->atom(QXcbAtom::XA_STRING))) + return connection->atom(QXcbAtom::XA_STRING); + } + + // find matches for uri types + if (format == QLatin1String("text/uri-list")) { + xcb_atom_t a = connection->internAtom(format.toLatin1()); + if (a && atoms.contains(a)) + return a; + a = connection->internAtom("text/x-moz-url"); + if (a && atoms.contains(a)) + return a; + } + + // find match for image + if (format == QLatin1String("image/ppm")) { + if (atoms.contains(connection->atom(QXcbAtom::XA_PIXMAP))) + return connection->atom(QXcbAtom::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")); + + xcb_atom_t a = connection->internAtom(formatWithCharset.toLatin1()); + if (a && atoms.contains(a)) { + *requestedEncoding = "utf-8"; + return a; + } + } + + xcb_atom_t a = connection->internAtom(format.toLatin1()); + if (a && atoms.contains(a)) + return a; + + return 0; +} diff --git a/src/plugins/platforms/xcb/qxcbmime.h b/src/plugins/platforms/xcb/qxcbmime.h new file mode 100644 index 0000000000..4ab38ed803 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbmime.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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 plugins 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 QXCBMIME_H +#define QXCBMIME_H + +#include <private/qdnd_p.h> + +#include <QtGui/QClipboard> + +#include "qxcbintegration.h" +#include "qxcbconnection.h" + +class QXcbMime : public QInternalMimeData { + Q_OBJECT +public: + QXcbMime(); + ~QXcbMime(); + + static QList<xcb_atom_t> mimeAtomsForFormat(QXcbConnection *connection, const QString &format); + static QString mimeAtomToString(QXcbConnection *connection, xcb_atom_t a); + static bool mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeData *mimeData, QByteArray *data, + xcb_atom_t *atomFormat, int *dataFormat); + static QVariant mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &data, const QString &format, + QVariant::Type requestedType, const QByteArray &encoding); + static xcb_atom_t mimeAtomForFormat(QXcbConnection *connection, const QString &format, QVariant::Type requestedType, + const QList<xcb_atom_t> &atoms, QByteArray *requestedEncoding); +}; + +#endif // QXCBMIME_H diff --git a/src/plugins/platforms/xcb/xcb.pro b/src/plugins/platforms/xcb/xcb.pro index c6bf17f0ef..6dd69c0643 100644 --- a/src/plugins/platforms/xcb/xcb.pro +++ b/src/plugins/platforms/xcb/xcb.pro @@ -9,6 +9,7 @@ SOURCES = \ qxcbconnection.cpp \ qxcbintegration.cpp \ qxcbkeyboard.cpp \ + qxcbmime.cpp \ qxcbscreen.cpp \ qxcbwindow.cpp \ qxcbwindowsurface.cpp \ @@ -19,6 +20,7 @@ HEADERS = \ qxcbconnection.h \ qxcbintegration.h \ qxcbkeyboard.h \ + qxcbmime.h \ qxcbobject.h \ qxcbscreen.h \ qxcbwindow.h \