diff options
390 files changed, 28230 insertions, 4594 deletions
@@ -601,7 +601,7 @@ CFG_GFX_ON="linuxfb multiscreen" CFG_GFX_PLUGIN_AVAILABLE= CFG_GFX_PLUGIN= CFG_GFX_OFF= -CFG_KBD_AVAILABLE="tty usb sl5000 yopy vr41xx qvfb" +CFG_KBD_AVAILABLE="tty linuxinput sl5000 yopy vr41xx qvfb" CFG_KBD_ON="tty" #default, see QMakeVar above CFG_MOUSE_AVAILABLE="pc bus linuxtp yopy vr41xx tslib qvfb" CFG_MOUSE_ON="pc linuxtp" #default, see QMakeVar above diff --git a/demos/arthurplugin/plugin.cpp b/demos/arthurplugin/plugin.cpp index e2bf54e..c26aae7 100644 --- a/demos/arthurplugin/plugin.cpp +++ b/demos/arthurplugin/plugin.cpp @@ -55,7 +55,26 @@ QT_FORWARD_DECLARE_CLASS(QDesignerFormEditorInterface) -static inline QString customWidgetDomXml(const QString &className) +// Specify "text" to be a singleline property (no richtext) +static inline QString textSingleLinePropertyDeclaration(const QString &className) +{ + QString rc = QLatin1String( + "<customwidgets>\n" + " <customwidget>\n" + " <class>"); + rc += className; + rc += QLatin1String("</class>\n" + " <propertyspecifications>\n" + " <stringpropertyspecification name=\"text\" type=\"singleline\"/>\n" + " </propertyspecifications>\n" + " </customwidget>\n" + "</customwidgets>\n"); + return rc; +} + +// Plain XML for a custom widget +static inline QString customWidgetDomXml(const QString &className, + const QString &customSection = QString()) { QString rc = QLatin1String("<ui language=\"c++\"><widget class=\""); rc += className; @@ -63,7 +82,9 @@ static inline QString customWidgetDomXml(const QString &className) QString objectName = className; objectName[0] = objectName.at(0).toLower(); rc += objectName; - rc += QLatin1String("\"/></ui>"); + rc += QLatin1String("\"/>"); + rc += customSection; + rc += QLatin1String("</ui>"); return rc; } @@ -80,7 +101,7 @@ class DemoPlugin : public QDesignerCustomWidgetInterface Q_INTERFACES(QDesignerCustomWidgetInterface) protected: - DemoPlugin(const QString &className); + explicit DemoPlugin(const QString &className, const QString &customSection = QString()); public: QString name() const { return m_className; } @@ -105,9 +126,9 @@ private: bool m_initialized; }; -DemoPlugin::DemoPlugin(const QString &className) : +DemoPlugin::DemoPlugin(const QString &className, const QString &customSection) : m_className(className), - m_domXml(customWidgetDomXml(className)), + m_domXml(customWidgetDomXml(className, customSection)), m_initialized(false) { } @@ -117,8 +138,8 @@ class DeformPlugin : public QObject, public DemoPlugin Q_OBJECT public: - DeformPlugin(QObject *parent = 0) : QObject(parent), DemoPlugin(QLatin1String("PathDeformRendererEx")) { } - QString includeFile() const { return "deform.h"; } + explicit DeformPlugin(QObject *parent = 0); + QString includeFile() const { return QLatin1String("deform.h"); } QWidget *createWidget(QWidget *parent) { @@ -126,12 +147,19 @@ public: deform->setRadius(70); deform->setAnimated(false); deform->setFontSize(20); - deform->setText("Arthur Widgets Demo"); + deform->setText(QLatin1String("Arthur Widgets Demo")); return deform; } }; +DeformPlugin::DeformPlugin(QObject *parent) : + QObject(parent), + DemoPlugin(QLatin1String("PathDeformRendererEx"), + textSingleLinePropertyDeclaration(QLatin1String("PathDeformRendererEx"))) +{ +} + class XFormRendererEx : public XFormView { Q_OBJECT @@ -144,24 +172,30 @@ class XFormPlugin : public QObject, public DemoPlugin { Q_OBJECT public: - XFormPlugin(QObject *parent = 0) : QObject(parent), DemoPlugin(QLatin1String("XFormRendererEx")) { } - QString includeFile() const { return "xform.h"; } + explicit XFormPlugin(QObject *parent = 0); + QString includeFile() const { return QLatin1String("xform.h"); } QWidget *createWidget(QWidget *parent) { XFormRendererEx *xform = new XFormRendererEx(parent); - xform->setText("Qt - Hello World!!"); - xform->setPixmap(QPixmap(":/trolltech/arthurplugin/bg1.jpg")); + xform->setText(QLatin1String("Qt - Hello World!!")); + xform->setPixmap(QPixmap(QLatin1String(":/trolltech/arthurplugin/bg1.jpg"))); return xform; } }; +XFormPlugin::XFormPlugin(QObject *parent) : + QObject(parent), + DemoPlugin(QLatin1String("XFormRendererEx"), + textSingleLinePropertyDeclaration(QLatin1String("XFormRendererEx"))) +{ +} class GradientEditorPlugin : public QObject, public DemoPlugin { Q_OBJECT public: - GradientEditorPlugin(QObject *parent = 0) : QObject(parent), DemoPlugin(QLatin1String("GradientEditor")) { } + explicit GradientEditorPlugin(QObject *parent = 0) : QObject(parent), DemoPlugin(QLatin1String("GradientEditor")) { } QString includeFile() const { return "gradients.h"; } QWidget *createWidget(QWidget *parent) @@ -184,7 +218,7 @@ class GradientRendererPlugin : public QObject, public DemoPlugin Q_OBJECT public: GradientRendererPlugin(QObject *parent = 0) : QObject(parent), DemoPlugin(QLatin1String("GradientRendererEx")) { } - QString includeFile() const { return "gradients.h"; } + QString includeFile() const { return QLatin1String("gradients.h"); } QWidget *createWidget(QWidget *parent) { @@ -198,7 +232,7 @@ class PathStrokeRendererEx : public PathStrokeRenderer { Q_OBJECT public: - PathStrokeRendererEx(QWidget *p) : PathStrokeRenderer(p) { } + explicit PathStrokeRendererEx(QWidget *p) : PathStrokeRenderer(p) { } QSize sizeHint() const { return QSize(300, 200); } }; @@ -206,8 +240,8 @@ class StrokeRenderPlugin : public QObject, public DemoPlugin { Q_OBJECT public: - StrokeRenderPlugin(QObject *parent = 0) : QObject(parent), DemoPlugin(QLatin1String("PathStrokeRendererEx")) { } - QString includeFile() const { return "pathstroke.h"; } + explicit StrokeRenderPlugin(QObject *parent = 0) : QObject(parent), DemoPlugin(QLatin1String("PathStrokeRendererEx")) { } + QString includeFile() const { return QLatin1String("pathstroke.h"); } QWidget *createWidget(QWidget *parent) { @@ -221,8 +255,8 @@ class CompositionModePlugin : public QObject, public DemoPlugin { Q_OBJECT public: - CompositionModePlugin(QObject *parent = 0) : QObject(parent), DemoPlugin(QLatin1String("CompositionRenderer")) { } - QString includeFile() const { return "composition.h"; } + explicit CompositionModePlugin(QObject *parent = 0) : QObject(parent), DemoPlugin(QLatin1String("CompositionRenderer")) { } + QString includeFile() const { return QLatin1String("composition.h"); } QWidget *createWidget(QWidget *parent) { @@ -239,7 +273,7 @@ class ArthurPlugins : public QObject, public QDesignerCustomWidgetCollectionInte Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) public: - ArthurPlugins(QObject *parent = 0); + explicit ArthurPlugins(QObject *parent = 0); QList<QDesignerCustomWidgetInterface*> customWidgets() const { return m_plugins; } private: diff --git a/doc/src/designer-manual.qdoc b/doc/src/designer-manual.qdoc index e10a5be..97713b1 100644 --- a/doc/src/designer-manual.qdoc +++ b/doc/src/designer-manual.qdoc @@ -2407,12 +2407,106 @@ pixmap property in the property editor. is used to hide widgets that should not be explicitly created by the user, but are required by other widgets. - If you would like to use a container widget that is not a subclass of the - containers provided in \QD, but the container is still based on the notion - of \e{Current Page}, you need to provide a container extension and - tell \QD which method to use to add the pages. This can be done using the - \c{<addpagemethod>} XML tag. + + A complete custom widget specification looks like: + + \code +<ui language="c++"> displayname="MyWidget"> + <widget class="widgets::MyWidget" name="mywidget"/> + <customwidgets> + <customwidget> + <class>widgets::MyWidget</class> + <addpagemethod>addPage</addpagemethod> + <propertyspecifications> + <stringpropertyspecification name="fileName" notr="true" type="singleline" + <stringpropertyspecification name="text" type="richtext" + </propertyspecifications> + </customwidget> + </customwidgets> +</ui> + \endcode + + Attributes of the \c{<ui>} tag: + \table + \header + \o Attribute + \o Presence + \o Values + \o Comment + \row + \o \c{language} + \o optional + \o "c++", "jambi" + \o This attribute specifies the language the custom widget is intended for. + It is mainly there to prevent C++-plugins from appearing in Qt Jambi. + \row + \o \c{displayname} + \o optional + \o Class name + \o The value of the attribute appears in the Widget box and can be used to + strip away namespaces. + \endtable + + The \c{<addpagemethod>} tag tells \QD and \l uic which method should be used to + add pages to a container widget. This applies to container widgets that require + calling a particular method to add a child rather than adding the child by passing + the parent. In particular, this is relevant for containers that are not a + a subclass of the containers provided in \QD, but are based on the notion + of \e{Current Page}. In addition, you need to provide a container extension + for them. + + The \c{<propertyspecifications>} element can contain a list of property meta information. + Currently, properties of type string are supported. For these properties, the + \c{<stringpropertyspecification>} tag can be used. This tag has the following attributes: + + + \table + \header + \o Attribute + \o Presence + \o Values + \o Comment + \row + \o \c{name} + \o required + \o Name of the property + \row + \o \c{type} + \o required + \o See below table + \o The value of the attribute determines how the property editor will handle them. + \row + \o \c{notr} + \o optional + \o "true", "false" + \o If the attribute is "true", the value is not meant to be translated. + \endtable + Values of the \c{type} attribute of the string property: + + \table + \header + \o Value + \o Type + \row + \o \c{"richtext"} + \o Rich text. + \row + \o \c{"multiline"} + \o Multi-line plain text. + \row + \o \c{"singleline"} + \o Single-line plain text. + \row + \o \c{"stylesheet"} + \o A CSS-style sheet. + \row + \o \c{"objectname"} + \o An object name (restricted set of valid characters). + \row + \o \c{"url"} + \o URL, file name. + \endtable \section1 Plugin Requirements diff --git a/doc/src/examples/basicgraphicslayouts.qdoc b/doc/src/examples/basicgraphicslayouts.qdoc index 92571af..9696fb6 100644 --- a/doc/src/examples/basicgraphicslayouts.qdoc +++ b/doc/src/examples/basicgraphicslayouts.qdoc @@ -45,6 +45,7 @@ The Basic Graphics Layouts example shows how to use the layout classes in QGraphicsView: QGraphicsLinearLayout and QGraphicsGridLayout. + In addition to that it shows how to write your own custom layout item. \image basicgraphicslayouts-example.png Screenshot of the Basic Layouts Example @@ -115,26 +116,24 @@ \section1 LayoutItem Class Definition - The \c LayoutItem class is a subclass of QGraphicsWidget. It has a - constructor, a destructor, and a reimplementation of the - {QGraphicsItem::paint()}{paint()} function. + The \c LayoutItem class is a subclass of QGraphicsLayoutItem and + QGraphicsItem. It has a constructor, a destructor, and some required + reimplementations. + Since it inherits QGraphicsLayoutItem it must reimplement + {QGraphicsLayoutItem::setGeometry()}{setGeometry()} and + {QGraphicsLayoutItem::sizeHint()}{sizeHint()}. + In addition to that it inherits QGraphicsItem, so it must reimplement + {QGraphicsItem::boundingRect()}{boundingRect()} and + {QGraphicsItem::paint()}{paint()}. \snippet examples/graphicsview/basicgraphicslayouts/layoutitem.h 0 - The \c LayoutItem class also has a private instance of QPixmap, \c pix. - - \note We subclass QGraphicsWidget so that \c LayoutItem objects can - be automatically plugged into a layout, as QGraphicsWidget is a - specialization of QGraphicsLayoutItem. + The \c LayoutItem class also has a private instance of QPixmap, \c m_pix. \section1 LayoutItem Class Implementation - In \c{LayoutItem}'s constructor, \c pix is instantiated and the - \c{QT_original_R.png} image is loaded into it. We set the size of - \c LayoutItem to be slightly larger than the size of the pixmap as we - require some space around it for borders that we will paint later. - Alternatively, you could scale the pixmap to prevent the item from - becoming smaller than the pixmap. + In \c{LayoutItem}'s constructor, \c m_pix is instantiated and the + \c{block.png} image is loaded into it. \snippet examples/graphicsview/basicgraphicslayouts/layoutitem.cpp 0 @@ -148,4 +147,32 @@ \snippet examples/graphicsview/basicgraphicslayouts/layoutitem.cpp 2 + The reimplementation of {QGraphicsItem::boundingRect()}{boundingRect()} + will set the top left corner at (0,0), and the size of it will be + the size of the layout items + {QGraphicsLayoutItem::geometry()}{geometry()}. This is the area that + we paint within. + + \snippet examples/graphicsview/basicgraphicslayouts/layoutitem.cpp 3 + + + The reimplementation of {QGraphicsLayoutItem::setGeometry()}{setGeometry()} + simply calls its baseclass implementation. However, since this will change + the boundingRect we must also call + {QGraphicsItem::prepareGeometryChange()}{prepareGeometryChange()}. + Finally, we move the item according to \c geom.topLeft(). + + \snippet examples/graphicsview/basicgraphicslayouts/layoutitem.cpp 4 + + + Since we don't want the size of the item to be smaller than the pixmap, we + must make sure that we return a size hint that is larger than \c m_pix. + We also add some extra space around for borders that we will paint later. + Alternatively, you could scale the pixmap to prevent the item from + becoming smaller than the pixmap. + The preferred size is the same as the minimum size hint, while we set + maximum to be a large value + + \snippet examples/graphicsview/basicgraphicslayouts/layoutitem.cpp 5 + */
\ No newline at end of file diff --git a/doc/src/qset.qdoc b/doc/src/qset.qdoc index 7fbf97a..9795123 100644 --- a/doc/src/qset.qdoc +++ b/doc/src/qset.qdoc @@ -324,6 +324,16 @@ \sa insert(), remove(), find() */ +/*! + \fn bool QSet::contains(const QSet<T> &other) const + \since 4.6 + + Returns true if the set contains all items from the \a other set; + otherwise returns false. + + \sa insert(), remove(), find() +*/ + /*! \fn QSet::const_iterator QSet::begin() const Returns a const \l{STL-style iterator} positioned at the first diff --git a/doc/src/snippets/code/doc_src_stylesheet.qdoc b/doc/src/snippets/code/doc_src_stylesheet.qdoc index 60622d3..a62148f 100644 --- a/doc/src/snippets/code/doc_src_stylesheet.qdoc +++ b/doc/src/snippets/code/doc_src_stylesheet.qdoc @@ -1538,6 +1538,11 @@ QSplitter::handle:horizontal { QSplitter::handle:vertical { height: 2px; } + +QSplitter::handle:pressed { + url(images/splitter_pressed.png); +} + //! [142] diff --git a/doc/src/snippets/code/src_gui_image_qpixmap.cpp b/doc/src/snippets/code/src_gui_image_qpixmap.cpp index f86eeae..822b466 100644 --- a/doc/src/snippets/code/src_gui_image_qpixmap.cpp +++ b/doc/src/snippets/code/src_gui_image_qpixmap.cpp @@ -10,3 +10,9 @@ static const char * const start_xpm[]={ QPixmap myPixmap; myPixmap->setMask(myPixmap->createHeuristicMask()); //! [1] + +//! [2] +QPixmap pixmap("background.png"); +QRegion exposed; +pixmap.scroll(10, 10, pixmap.rect(), &exposed); +//! [2] diff --git a/doc/src/snippets/qprocess-environment/main.cpp b/doc/src/snippets/qprocess-environment/main.cpp index bce3578..148518b 100644 --- a/doc/src/snippets/qprocess-environment/main.cpp +++ b/doc/src/snippets/qprocess-environment/main.cpp @@ -43,6 +43,7 @@ void startProcess() { + { //! [0] QProcess process; QStringList env = QProcess::systemEnvironment(); @@ -51,6 +52,18 @@ env.replaceInStrings(QRegExp("^PATH=(.*)", Qt::CaseInsensitive), "PATH=\\1;C:\\B process.setEnvironment(env); process.start("myapp"); //! [0] + } + + { +//! [1] +QProcess process; +QHash<QString, QString> env = QProcess::systemEnvironmentHash(); +env.insert("TMPDIR", "C:\\MyApp\\temp"); // Add an environment variable +env["PATH"] += ";C:\\Bin"; +process.setEnvironment(env); +process.start("myapp"); +//! [0] + } } int main(int argc, char *argv[]) diff --git a/examples/designer/taskmenuextension/tictactoeplugin.cpp b/examples/designer/taskmenuextension/tictactoeplugin.cpp index 1333090..0a2d13b 100644 --- a/examples/designer/taskmenuextension/tictactoeplugin.cpp +++ b/examples/designer/taskmenuextension/tictactoeplugin.cpp @@ -124,6 +124,14 @@ QString TicTacToePlugin::domXml() const return QLatin1String("\ <ui language=\"c++\">\ <widget class=\"TicTacToe\" name=\"ticTacToe\"/>\ + <customwidgets>\ + <customwidget>\ + <class>TicTacToe</class>\ + <propertyspecifications>\ + <stringpropertyspecification name=\"state\" notr=\"true\" type=\"singleline\"/>\ + </propertyspecifications>\ + </customwidget>\ + </customwidgets>\ </ui>"); } diff --git a/examples/draganddrop/dropsite/dropsitewindow.cpp b/examples/draganddrop/dropsite/dropsitewindow.cpp index 6e7055b..627baaa 100644 --- a/examples/draganddrop/dropsite/dropsitewindow.cpp +++ b/examples/draganddrop/dropsite/dropsitewindow.cpp @@ -119,10 +119,8 @@ void DropSiteWindow::updateFormatsTable(const QMimeData *mimeData) text = mimeData->html().simplified(); } else if (format == "text/uri-list") { QList<QUrl> urlList = mimeData->urls(); - for (int i = 0; i < urlList.size() && i < 32; ++i) { - QString url = urlList.at(i).path(); - text.append(url + " "); - } + for (int i = 0; i < urlList.size() && i < 32; ++i) + text.append(urlList[i].toString() + " "); } else { QByteArray data = mimeData->data(format); for (int i = 0; i < data.size() && i < 32; ++i) { diff --git a/examples/graphicsview/basicgraphicslayouts/layoutitem.cpp b/examples/graphicsview/basicgraphicslayouts/layoutitem.cpp index 8216b6e..7311b65 100644 --- a/examples/graphicsview/basicgraphicslayouts/layoutitem.cpp +++ b/examples/graphicsview/basicgraphicslayouts/layoutitem.cpp @@ -43,19 +43,18 @@ //! [0] LayoutItem::LayoutItem(QGraphicsItem *parent/* = 0*/) - : QGraphicsWidget(parent) + : QGraphicsLayoutItem(), QGraphicsItem(parent) { - pix = new QPixmap(QLatin1String(":/images/block.png")); - // Do not allow a size smaller than the pixmap with two frames around it. - setMinimumSize(pix->size() + QSize(12, 12)); + m_pix = new QPixmap(QLatin1String(":/images/block.png")); + setGraphicsItem(this); } //! [0] LayoutItem::~LayoutItem() { - delete pix; + delete m_pix; } - + //! [1] void LayoutItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget /*= 0*/) @@ -64,8 +63,8 @@ void LayoutItem::paint(QPainter *painter, Q_UNUSED(option); QRectF frame(QPointF(0,0), geometry().size()); - qreal w = pix->width(); - qreal h = pix->height(); + qreal w = m_pix->width(); + qreal h = m_pix->height(); QGradientStops stops; //! [1] @@ -94,6 +93,39 @@ void LayoutItem::paint(QPainter *painter, gradient.setStops(stops); painter->setBrush(QBrush(gradient)); painter->drawRoundedRect(innerFrame, 10.0, 10.0); - painter->drawPixmap(pixpos, *pix); + painter->drawPixmap(pixpos, *m_pix); } //! [2] + +//! [3] +QRectF LayoutItem::boundingRect() const +{ + return QRectF(QPointF(0,0), geometry().size()); +} +//! [3] + +//! [4] +void LayoutItem::setGeometry(const QRectF &geom) +{ + prepareGeometryChange(); + QGraphicsLayoutItem::setGeometry(geom); + setPos(geom.topLeft()); +} +//! [4] + +//! [5] +QSizeF LayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + switch (which) { + case Qt::MinimumSize: + case Qt::PreferredSize: + // Do not allow a size smaller than the pixmap with two frames around it. + return m_pix->size() + QSize(12, 12); + case Qt::MaximumSize: + return QSizeF(1000,1000); + default: + break; + } + return constraint; +} +//! [5] diff --git a/examples/graphicsview/basicgraphicslayouts/layoutitem.h b/examples/graphicsview/basicgraphicslayouts/layoutitem.h index cbda636..8ec9038 100644 --- a/examples/graphicsview/basicgraphicslayouts/layoutitem.h +++ b/examples/graphicsview/basicgraphicslayouts/layoutitem.h @@ -44,18 +44,22 @@ #include <QtGui> //! [0] -class LayoutItem : public QGraphicsWidget +class LayoutItem : public QGraphicsLayoutItem, public QGraphicsItem { - Q_OBJECT - public: LayoutItem(QGraphicsItem *parent = 0); ~LayoutItem(); + // Inherited from QGraphicsLayoutItem + void setGeometry(const QRectF &geom); + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + + // Inherited from QGraphicsItem + QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); private: - QPixmap *pix; + QPixmap *m_pix; }; //! [0] diff --git a/examples/graphicsview/flowlayout/flowlayout.cpp b/examples/graphicsview/flowlayout/flowlayout.cpp new file mode 100644 index 0000000..d1676bc --- /dev/null +++ b/examples/graphicsview/flowlayout/flowlayout.cpp @@ -0,0 +1,169 @@ +#include "flowlayout.h" +#include <QtGui/qwidget.h> +#include <QtCore/qmath.h> + +FlowLayout::FlowLayout() +{ + m_spacing[0] = 6; + m_spacing[1] = 6; + QSizePolicy sp = sizePolicy(); + sp.setHeightForWidth(true); + setSizePolicy(sp); +} + +void FlowLayout::insertItem(int index, QGraphicsLayoutItem *item) +{ + item->setParentLayoutItem(this); + if (uint(index) > uint(m_items.count())) + index = m_items.count(); + m_items.insert(index, item); + invalidate(); +} + +int FlowLayout::count() const +{ + return m_items.count(); +} + +QGraphicsLayoutItem *FlowLayout::itemAt(int index) const +{ + return m_items.value(index); +} + +void FlowLayout::removeAt(int index) +{ + m_items.removeAt(index); +} + +qreal FlowLayout::spacing(Qt::Orientation o) const +{ + return m_spacing[int(o) - 1]; +} + +void FlowLayout::setSpacing(Qt::Orientations o, qreal spacing) +{ + if (o & Qt::Horizontal) + m_spacing[0] = spacing; + if (o & Qt::Vertical) + m_spacing[1] = spacing; +} + +void FlowLayout::setGeometry(const QRectF &geom) +{ + QGraphicsLayout::setGeometry(geom); + doLayout(geom, true); +} + +qreal FlowLayout::doLayout(const QRectF &geom, bool applyNewGeometry) +{ + QPointF tl = geom.topLeft(); + qreal maxw = geom.width(); + + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + maxw = maxw - left - right; + qreal x = 0; + qreal y = 0; + qreal maxRowHeight = 0; + QSizeF pref; + for (int i = 0; i < m_items.count(); ++i) { + QGraphicsLayoutItem *item = m_items.at(i); + pref = item->effectiveSizeHint(Qt::PreferredSize); + maxRowHeight = qMax(maxRowHeight, pref.height()); + + qreal next_x; + next_x = x + pref.width(); + if (next_x > maxw) { + if (x == 0) { + pref.setWidth(maxw); + } else { + x = 0; + next_x = pref.width(); + } + y += maxRowHeight + spacing(Qt::Vertical); + maxRowHeight = 0; + } + + if (applyNewGeometry) + item->setGeometry(QRectF(QPointF(left + x, top + y), pref)); + x = next_x + spacing(Qt::Horizontal); + } + maxRowHeight = qMax(maxRowHeight, pref.height()); + return top + y + maxRowHeight + bottom; +} + +QSizeF FlowLayout::minSize(const QSizeF &constraint) const +{ + QSizeF size(0, 0); + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + if (constraint.width() > 0) { // height for width + FlowLayout *that = const_cast<FlowLayout *>(this); + qreal height = that->doLayout(QRectF(QPointF(0,0), constraint), false); + size = QSizeF(constraint.width(), height); + } else { + QGraphicsLayoutItem *item; + foreach (item, m_items) + size = size.expandedTo(item->effectiveSizeHint(Qt::MinimumSize)); + size += QSize(left + right, top + bottom); + } + return size; +} + +QSizeF FlowLayout::prefSize() const +{ + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + + QGraphicsLayoutItem *item; + qreal maxh = 0; + qreal totalWidth = 0; + foreach (item, m_items) { + if (totalWidth > 0) + totalWidth += spacing(Qt::Horizontal); + QSizeF pref = item->effectiveSizeHint(Qt::PreferredSize); + totalWidth += pref.width(); + maxh = qMax(maxh, pref.height()); + } + maxh += spacing(Qt::Vertical); + + const qreal goldenAspectRatio = 1.61803399; + qreal w = qSqrt(totalWidth * maxh * goldenAspectRatio) + left + right; + + return minSize(QSizeF(w, -1)); +} + +QSizeF FlowLayout::maxSize() const +{ + QGraphicsLayoutItem *item; + qreal totalWidth = 0; + qreal totalHeight = 0; + foreach (item, m_items) { + if (totalWidth > 0) + totalWidth += spacing(Qt::Horizontal); + if (totalHeight > 0) + totalHeight += spacing(Qt::Vertical); + QSizeF pref = item->effectiveSizeHint(Qt::PreferredSize); + totalWidth += pref.width(); + totalHeight += pref.height(); + } + + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + return QSizeF(left + totalWidth + right, top + totalHeight + bottom); +} + +QSizeF FlowLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + switch (which) { + case Qt::PreferredSize: + return prefSize(); + case Qt::MinimumSize: + return minSize(constraint); + case Qt::MaximumSize: + return maxSize(); + default: + break; + } + return constraint; +} diff --git a/examples/graphicsview/flowlayout/flowlayout.h b/examples/graphicsview/flowlayout/flowlayout.h new file mode 100644 index 0000000..ab3d33b --- /dev/null +++ b/examples/graphicsview/flowlayout/flowlayout.h @@ -0,0 +1,36 @@ +#include <QtGui/qgraphicslayout.h> + +class FlowLayout : public QGraphicsLayout +{ +public: + FlowLayout(); + inline void addItem(QGraphicsLayoutItem *item); + void insertItem(int index, QGraphicsLayoutItem *item); + void setSpacing(Qt::Orientations o, qreal spacing); + qreal spacing(Qt::Orientation o) const; + + // inherited functions + void setGeometry(const QRectF &geom); + + int count() const; + QGraphicsLayoutItem *itemAt(int index) const; + void removeAt(int index); + +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + +private: + qreal doLayout(const QRectF &geom, bool applyNewGeometry); + QSizeF minSize(const QSizeF &constraint) const; + QSizeF prefSize() const; + QSizeF maxSize() const; + + QList<QGraphicsLayoutItem*> m_items; + qreal m_spacing[2]; +}; + + +inline void FlowLayout::addItem(QGraphicsLayoutItem *item) +{ + insertItem(-1, item); +} diff --git a/examples/graphicsview/flowlayout/flowlayout.pro b/examples/graphicsview/flowlayout/flowlayout.pro new file mode 100644 index 0000000..c029d6c --- /dev/null +++ b/examples/graphicsview/flowlayout/flowlayout.pro @@ -0,0 +1,12 @@ +###################################################################### +# Automatically generated by qmake (2.01a) ma 30. mar 12:46:15 2009 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += flowlayout.h window.h +SOURCES += flowlayout.cpp main.cpp window.cpp diff --git a/examples/graphicsview/flowlayout/main.cpp b/examples/graphicsview/flowlayout/main.cpp new file mode 100644 index 0000000..a7af2d4 --- /dev/null +++ b/examples/graphicsview/flowlayout/main.cpp @@ -0,0 +1,14 @@ +#include <QtGui> +#include "window.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QGraphicsScene scene; + QGraphicsView *view = new QGraphicsView(&scene); + Window *w = new Window; + scene.addItem(w); + view->show(); + return app.exec(); +}
\ No newline at end of file diff --git a/examples/graphicsview/flowlayout/window.cpp b/examples/graphicsview/flowlayout/window.cpp new file mode 100644 index 0000000..b632bc9 --- /dev/null +++ b/examples/graphicsview/flowlayout/window.cpp @@ -0,0 +1,20 @@ +#include <QtGui/qgraphicsproxywidget.h> +#include <QtGui/qlabel.h> +#include "flowlayout.h" +#include "window.h" + +Window::Window() +: QGraphicsWidget(0, Qt::Window) +{ + FlowLayout *lay = new FlowLayout; + QString sentence(QLatin1String("I am not bothered by the fact that I am unknown. I am bothered when I do not know others.")); + QStringList words = sentence.split(QLatin1Char(' '), QString::SkipEmptyParts); + for (int i = 0; i < words.count(); ++i) { + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(this); + QLabel *label = new QLabel(words.at(i)); + label->setFrameStyle(QFrame::Box | QFrame::Plain); + proxy->setWidget(label); + lay->addItem(proxy); + } + setLayout(lay); +}
\ No newline at end of file diff --git a/examples/graphicsview/flowlayout/window.h b/examples/graphicsview/flowlayout/window.h new file mode 100644 index 0000000..50dac92 --- /dev/null +++ b/examples/graphicsview/flowlayout/window.h @@ -0,0 +1,7 @@ +#include <QtGui/qgraphicswidget.h> + +class Window : public QGraphicsWidget { + Q_OBJECT +public: + Window(); +};
\ No newline at end of file diff --git a/mkspecs/features/qttest_p4.prf b/mkspecs/features/qttest_p4.prf index 3e1c918..e9d79b0 100644 --- a/mkspecs/features/qttest_p4.prf +++ b/mkspecs/features/qttest_p4.prf @@ -1,80 +1,7 @@ isEmpty(TEMPLATE):TEMPLATE=app -CONFIG += qt warn_on console depend_includepath +CONFIG += qt warn_on console depend_includepath -wince*:{ -LIBS += corelibc.lib ole32.lib oleaut32.lib uuid.lib commctrl.lib coredll.lib winsock.lib -CONFIG(debug, debug|release) { - LIBS += $$[QT_INSTALL_LIBS]/qtmaind.lib -} else { - LIBS += $$[QT_INSTALL_LIBS]/qtmain.lib -} -} - -# find testlib's prl file and extract the librarie's name -QTEST_LIB = -mac:exists($$[QT_INSTALL_LIBS]/QtTest.framework):QTEST_LIB = QtTest.framework -CONFIG(debug, debug|release) { - isEmpty(QTEST_LIB) { - win32 { - QTEST_LIB = $$fromfile($$[QT_INSTALL_LIBS]/QtTest$${QT_LIBINFIX}d.prl, QMAKE_PRL_TARGET)4.dll - } - mac { - QTEST_LIB = $$fromfile($$[QT_INSTALL_LIBS]/libQtTest$${QT_LIBINFIX}_debug.prl, QMAKE_PRL_TARGET) - } - unix:!mac { - QTEST_LIB = $$fromfile($$[QT_INSTALL_LIBS]/libQtTest$${QT_LIBINFIX}.prl, QMAKE_PRL_TARGET) - } - } - OBJECTS_DIR = tmp/debug - MOC_DIR = tmp/debug -} else { - isEmpty(QTEST_LIB) { - win32 { - QTEST_LIB = $$fromfile($$[QT_INSTALL_LIBS]/QtTest$${QT_LIBINFIX}.prl, QMAKE_PRL_TARGET)4.dll - } else { - QTEST_LIB = $$fromfile($$[QT_INSTALL_LIBS]/libQtTest$${QT_LIBINFIX}.prl, QMAKE_PRL_TARGET) - } - } - OBJECTS_DIR = tmp/release - MOC_DIR = tmp/release -} -isEmpty(QTEST_LIB) { - warning("Cannot find testlib's prl file, run qmake in $QTDIR/src/testlib") - CONFIG += embed_testlib -} else { - !exists($$[QT_INSTALL_LIBS]/$$QTEST_LIB):CONFIG += embed_testlib -} - -# make sure the test is build according to your Qt configuration -contains(QT_CONFIG, embedded):CONFIG += embedded - -embed_testlib { - QTESTDIR = $${QT_SOURCE_TREE} - isEmpty(QTESTDIR):QTESTDIR = $$(QTSRCDIR) - isEmpty(QTESTDIR):QTESTDIR = $$(QTDIR) - isEmpty(QTESTDIR):warning("Cannot find QTestLib sources, set QTDIR or QTSRCDIR") - QTESTDIR = $${QTESTDIR}/src/testlib - HEADERS += $${QTESTDIR}/qtesteventloop.h - SOURCES += $${QTESTDIR}/qasciikey.cpp \ - $${QTESTDIR}/qabstracttestlogger.cpp \ - $${QTESTDIR}/qsignaldumper.cpp \ - $${QTESTDIR}/qtestdata.cpp \ - $${QTESTDIR}/qtestresult.cpp \ - $${QTESTDIR}/qxmltestlogger.cpp \ - $${QTESTDIR}/qplaintestlogger.cpp \ - $${QTESTDIR}/qtestcase.cpp \ - $${QTESTDIR}/qbenchmark.cpp \ - $${QTESTDIR}/qbenchmarkevent.cpp \ - $${QTESTDIR}/qbenchmarkmeasurement.cpp \ - $${QTESTDIR}/qbenchmarkvalgrind.cpp \ - $${QTESTDIR}/qtestlog.cpp \ - $${QTESTDIR}/qtesttable.cpp - DEFINES += QTEST_EMBED QTEST_LIGHT -} else { - CONFIG += qtestlib -} - -embedded:QMAKE_CXXFLAGS+=-fno-rtti +qtAddLibrary(QtTest) # prefix test binary with tst_ !contains(TARGET, ^tst_.*):TARGET = $$join(TARGET,,"tst_") diff --git a/qmake/generators/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp index 1f02fb5..4970dc8 100644 --- a/qmake/generators/unix/unixmake.cpp +++ b/qmake/generators/unix/unixmake.cpp @@ -763,7 +763,7 @@ UnixMakefileGenerator::defaultInstall(const QString &t) if(!project->isEmpty("QMAKE_RANLIB")) ret += QString("\n\t$(RANLIB) \"") + dst_targ + "\""; } else if(!project->isActiveConfig("debug") && !project->isActiveConfig("nostrip") && !project->isEmpty("QMAKE_STRIP")) { - ret += "\n\t-" + var("QMAKE_STRIP"); + ret += "\n\t-$(STRIP)"; if(project->first("TEMPLATE") == "lib" && !project->isEmpty("QMAKE_STRIPFLAGS_LIB")) ret += " " + var("QMAKE_STRIPFLAGS_LIB"); else if(project->first("TEMPLATE") == "app" && !project->isEmpty("QMAKE_STRIPFLAGS_APP")) diff --git a/qmake/generators/unix/unixmake2.cpp b/qmake/generators/unix/unixmake2.cpp index 585ab55..9787075 100644 --- a/qmake/generators/unix/unixmake2.cpp +++ b/qmake/generators/unix/unixmake2.cpp @@ -163,6 +163,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) t << "SED = " << var("QMAKE_STREAM_EDITOR") << endl; t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl; t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl; + t << "STRIP = " << var("QMAKE_STRIP") << endl; t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl; t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl; t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl; diff --git a/src/3rdparty/phonon/qt7/mediaobject.h b/src/3rdparty/phonon/qt7/mediaobject.h index 27949ec..ae623a9 100644 --- a/src/3rdparty/phonon/qt7/mediaobject.h +++ b/src/3rdparty/phonon/qt7/mediaobject.h @@ -25,6 +25,10 @@ #include "medianode.h" +#if QT_ALLOW_QUICKTIME + #include <QuickTime/QuickTime.h> +#endif + QT_BEGIN_NAMESPACE namespace Phonon @@ -92,6 +96,10 @@ namespace QT7 int videoOutputCount(); +#if QT_ALLOW_QUICKTIME + void displayLinkEvent(); +#endif + signals: void stateChanged(Phonon::State,Phonon::State); void tick(qint64); @@ -120,6 +128,14 @@ namespace QT7 MediaObjectAudioNode *m_mediaObjectAudioNode; QuickTimeMetaData *m_metaData; +#if QT_ALLOW_QUICKTIME + CVDisplayLinkRef m_displayLink; + QMutex m_displayLinkMutex; + bool m_pendingDisplayLinkEvent; + void startDisplayLink(); + void stopDisplayLink(); +#endif + qint32 m_tickInterval; qint32 m_transitionTime; quint32 m_prefinishMark; @@ -127,7 +143,8 @@ namespace QT7 float m_percentageLoaded; int m_tickTimer; - int m_bufferTimer; + int m_videoTimer; + int m_audioTimer; int m_rapidTimer; bool m_waitNextSwap; @@ -141,8 +158,7 @@ namespace QT7 void pause_internal(); void play_internal(); void setupAudioSystem(); - void updateTimer(int &timer, int interval); - void bufferAudioVideo(); + void restartAudioVideoTimers(); void updateRapidly(); void updateCrossFade(); void updateAudioBuffers(); diff --git a/src/3rdparty/phonon/qt7/mediaobject.mm b/src/3rdparty/phonon/qt7/mediaobject.mm index 002c337..6886a3c 100644 --- a/src/3rdparty/phonon/qt7/mediaobject.mm +++ b/src/3rdparty/phonon/qt7/mediaobject.mm @@ -63,15 +63,24 @@ MediaObject::MediaObject(QObject *parent) : MediaNode(AudioSource | VideoSource, m_errorType = Phonon::NoError; m_tickTimer = 0; - m_bufferTimer = 0; + m_videoTimer = 0; + m_audioTimer = 0; m_rapidTimer = 0; +#if QT_ALLOW_QUICKTIME + m_displayLink = 0; + m_pendingDisplayLinkEvent = false; +#endif + checkForError(); } MediaObject::~MediaObject() -{ - // m_mediaObjectAudioNode is owned by super class. +{ + // m_mediaObjectAudioNode is owned by super class. +#if QT_ALLOW_QUICKTIME + stopDisplayLink(); +#endif m_audioPlayer->unsetVideoPlayer(); m_nextAudioPlayer->unsetVideoPlayer(); delete m_videoPlayer; @@ -88,7 +97,7 @@ bool MediaObject::setState(Phonon::State state) emit stateChanged(m_state, prevState); if (m_state != state){ // End-application did something - // upon receiving the signal. + // upon receiving the signal. return false; } } @@ -214,28 +223,28 @@ void MediaObject::setSource(const MediaSource &source) IMPLEMENTED; PhononAutoReleasePool pool; setState(Phonon::LoadingState); - + // Save current state for event/signal handling below: bool prevHasVideo = m_videoPlayer->hasVideo(); qint64 prevTotalTime = totalTime(); m_waitNextSwap = false; - + // Cancel cross-fade if any: m_nextVideoPlayer->pause(); m_nextAudioPlayer->pause(); m_mediaObjectAudioNode->cancelCrossFade(); - + // Set new source: m_audioPlayer->unsetVideoPlayer(); m_videoPlayer->setMediaSource(source); m_audioPlayer->setVideoPlayer(m_videoPlayer); - m_metaData->setVideo(m_videoPlayer); + m_metaData->setVideo(m_videoPlayer); - m_audioGraph->updateStreamSpecifications(); + m_audioGraph->updateStreamSpecifications(); m_nextAudioPlayer->unsetVideoPlayer(); m_nextVideoPlayer->unsetVideo(); m_currentTime = 0; - + // Emit/notify information about the new source: QRect videoRect = m_videoPlayer->videoRect(); MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); @@ -296,7 +305,7 @@ void MediaObject::swapCurrentWithNext(qint32 transitionTime) m_waitNextSwap = false; m_currentTime = 0; - + // Emit/notify information about the new source: QRect videoRect = m_videoPlayer->videoRect(); MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); @@ -306,7 +315,7 @@ void MediaObject::swapCurrentWithNext(qint32 transitionTime) emit metaDataChanged(m_metaData->metaData()); if (prevHasVideo != m_videoPlayer->hasVideo()) - emit hasVideoChanged(m_videoPlayer->hasVideo()); + emit hasVideoChanged(m_videoPlayer->hasVideo()); if (prevTotalTime != totalTime()) emit totalTimeChanged(totalTime()); if (checkForError()) @@ -327,28 +336,107 @@ void MediaObject::swapCurrentWithNext(qint32 transitionTime) } } -void MediaObject::updateTimer(int &timer, int interval) +#if QT_ALLOW_QUICKTIME +static CVReturn displayLinkCallback(CVDisplayLinkRef /*displayLink*/, + const CVTimeStamp */*inNow*/, + const CVTimeStamp */*inOutputTime*/, + CVOptionFlags /*flagsIn*/, + CVOptionFlags */*flagsOut*/, + void *userData) +{ + MediaObject *mediaObject = static_cast<MediaObject *>(userData); + mediaObject->displayLinkEvent(); + return kCVReturnSuccess; +} + +void MediaObject::displayLinkEvent() +{ + // This function is called from a + // thread != gui thread. So we post the event. + // But we need to make sure that we don't post faster + // than the event loop can eat: + m_displayLinkMutex.lock(); + bool pending = m_pendingDisplayLinkEvent; + m_pendingDisplayLinkEvent = true; + m_displayLinkMutex.unlock(); + + if (!pending) + qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); +} + +void MediaObject::startDisplayLink() { - if (timer) - killTimer(timer); - timer = 0; - if (interval >= 0) - timer = startTimer(interval); + if (m_displayLink) + return; + OSStatus err = CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink); + if (err != noErr) + goto fail; + err = CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay); + if (err != noErr) + goto fail; + err = CVDisplayLinkSetOutputCallback(m_displayLink, displayLinkCallback, this); + if (err != noErr) + goto fail; + err = CVDisplayLinkStart(m_displayLink); + if (err != noErr) + goto fail; + return; +fail: + stopDisplayLink(); +} + +void MediaObject::stopDisplayLink() +{ + if (!m_displayLink) + return; + CVDisplayLinkStop(m_displayLink); + CFRelease(m_displayLink); + m_displayLink = 0; +} +#endif + +void MediaObject::restartAudioVideoTimers() +{ + if (m_videoTimer) + killTimer(m_videoTimer); + if (m_audioTimer) + killTimer(m_audioTimer); + +#if QT_ALLOW_QUICKTIME + // We prefer to use a display link as timer if available, since + // it is more steady, and results in better and smoother frame drawing: + startDisplayLink(); + if (!m_displayLink){ + float fps = m_videoPlayer->staticFps(); + long videoUpdateFrequency = fps ? long(1000.0f / fps) : 0.001; + m_videoTimer = startTimer(videoUpdateFrequency); + } +#else + float fps = m_videoPlayer->staticFps(); + long videoUpdateFrequency = fps ? long(1000.0f / fps) : 0.001; + m_videoTimer = startTimer(videoUpdateFrequency); +#endif + + long audioUpdateFrequency = m_audioPlayer->regularTaskFrequency(); + m_audioTimer = startTimer(audioUpdateFrequency); + updateVideoFrames(); + updateAudioBuffers(); } void MediaObject::play_internal() { // Play main audio/video: m_videoPlayer->play(); - m_audioPlayer->play(); + m_audioPlayer->play(); updateLipSynch(0); // Play old audio/video to finish cross-fade: if (m_nextVideoPlayer->currentTime() > 0){ m_nextVideoPlayer->play(); m_nextAudioPlayer->play(); } - bufferAudioVideo(); - updateTimer(m_rapidTimer, 100); + restartAudioVideoTimers(); + if (!m_rapidTimer) + m_rapidTimer = startTimer(100); } void MediaObject::pause_internal() @@ -358,9 +446,15 @@ void MediaObject::pause_internal() m_nextAudioPlayer->pause(); m_videoPlayer->pause(); m_nextVideoPlayer->pause(); - updateTimer(m_rapidTimer, -1); - updateTimer(m_bufferTimer, -1); - + killTimer(m_rapidTimer); + killTimer(m_videoTimer); + killTimer(m_audioTimer); + m_rapidTimer = 0; + m_videoTimer = 0; + m_audioTimer = 0; +#if QT_ALLOW_QUICKTIME + stopDisplayLink(); +#endif if (m_waitNextSwap) m_swapTimeLeft = m_swapTime.msecsTo(QTime::currentTime()); } @@ -382,7 +476,7 @@ void MediaObject::play() if (!m_videoPlayer->canPlayMedia()) return; if (!setState(Phonon::PlayingState)) - return; + return; if (m_audioSystem == AS_Graph){ m_audioGraph->start(); m_mediaObjectAudioNode->setMute(true); @@ -435,7 +529,7 @@ void MediaObject::seek(qint64 milliseconds) IMPLEMENTED; if (m_state == Phonon::ErrorState) return; - + // Stop cross-fade if any: m_nextVideoPlayer->unsetVideo(); m_nextAudioPlayer->unsetVideoPlayer(); @@ -446,7 +540,7 @@ void MediaObject::seek(qint64 milliseconds) m_videoPlayer->seek(milliseconds); m_audioPlayer->seek(m_videoPlayer->currentTime()); m_mediaObjectAudioNode->setMute(false); - + // Update time and cancel pending swap: if (m_currentTime < m_videoPlayer->duration()) m_waitNextSwap = false; @@ -699,7 +793,7 @@ void MediaObject::updateCrossFade() m_nextVideoPlayer->unsetVideo(); m_nextAudioPlayer->unsetVideoPlayer(); } - } + } } void MediaObject::updateBufferStatus() @@ -728,7 +822,7 @@ void MediaObject::updateVideoFrames() // Draw next frame if awailable: if (m_videoPlayer->videoFrameChanged()){ updateLipSynch(50); - VideoFrame frame(m_videoPlayer); + VideoFrame frame(m_videoPlayer); if (m_nextVideoPlayer->isPlaying() && m_nextVideoPlayer->hasVideo() && isCrossFading()){ @@ -736,9 +830,9 @@ void MediaObject::updateVideoFrames() frame.setBackgroundFrame(bgFrame); frame.setBaseOpacity(m_mediaObjectAudioNode->m_volume1); } - + // Send the frame through the graph: - updateVideo(frame); + updateVideo(frame); checkForError(); } } @@ -749,7 +843,7 @@ void MediaObject::updateLipSynch(int allowedOffset) return; if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty()) return; - + if (m_videoPlayer->hasVideo()){ qint64 diff = m_audioPlayer->currentTime() - m_videoPlayer->currentTime(); if (-allowedOffset > diff || diff > allowedOffset) @@ -763,16 +857,6 @@ void MediaObject::updateLipSynch(int allowedOffset) } } -void MediaObject::bufferAudioVideo() -{ - long nextVideoUpdate = m_videoPlayer->hasVideo() ? 30 : INT_MAX; - long nextAudioUpdate = m_audioPlayer->regularTaskFrequency(); - updateAudioBuffers(); - updateVideoFrames(); - if (m_state == Phonon::PlayingState) - updateTimer(m_bufferTimer, qMin(nextVideoUpdate, nextAudioUpdate)); -} - void MediaObject::updateRapidly() { updateCurrentTime(); @@ -797,8 +881,8 @@ void MediaObject::mediaNodeEvent(const MediaNodeEvent *event) synchAudioVideo(); checkForError(); m_mediaObjectAudioNode->setMute(false); - if (m_state == Phonon::PlayingState) - bufferAudioVideo(); + if (m_state == Phonon::PlayingState) + restartAudioVideoTimers(); break; case MediaNodeEvent::AudioGraphCannotPlay: case MediaNodeEvent::AudioGraphInitialized: @@ -809,7 +893,7 @@ void MediaObject::mediaNodeEvent(const MediaNodeEvent *event) checkForError(); m_mediaObjectAudioNode->setMute(false); } - break; + break; default: break; } @@ -818,16 +902,25 @@ void MediaObject::mediaNodeEvent(const MediaNodeEvent *event) bool MediaObject::event(QEvent *event) { switch (event->type()){ - case QEvent::Timer: { - QTimerEvent *timerEvent = static_cast<QTimerEvent *>(event); - if (timerEvent->timerId() == m_rapidTimer) +#if QT_ALLOW_QUICKTIME + case QEvent::User:{ + m_displayLinkMutex.lock(); + m_pendingDisplayLinkEvent = false; + m_displayLinkMutex.unlock(); + updateVideoFrames(); + break; } +#endif + case QEvent::Timer:{ + int timerId = static_cast<QTimerEvent *>(event)->timerId(); + if (timerId == m_rapidTimer) updateRapidly(); - else if (timerEvent->timerId() == m_tickTimer) + else if (timerId == m_tickTimer) emit tick(currentTime()); - else if (timerEvent->timerId() == m_bufferTimer) - bufferAudioVideo(); - } - break; + else if (timerId == m_videoTimer) + updateVideoFrames(); + else if (timerId == m_audioTimer) + updateAudioBuffers(); + break; } default: break; } diff --git a/src/3rdparty/phonon/qt7/quicktimevideoplayer.h b/src/3rdparty/phonon/qt7/quicktimevideoplayer.h index b80570a..bf4f216 100644 --- a/src/3rdparty/phonon/qt7/quicktimevideoplayer.h +++ b/src/3rdparty/phonon/qt7/quicktimevideoplayer.h @@ -67,11 +67,13 @@ namespace QT7 GLuint currentFrameAsGLTexture(); void *currentFrameAsCIImage(); QImage currentFrameAsQImage(); + void releaseImageCache(); QRect videoRect() const; quint64 duration() const; quint64 currentTime() const; long timeScale() const; + float staticFps(); QString currentTimeString(); void setColors(qreal brightness = 0, qreal contrast = 1, qreal hue = 0, qreal saturation = 1); @@ -117,6 +119,9 @@ namespace QT7 State m_state; QGLPixelBuffer *m_QImagePixelBuffer; + CVOpenGLTextureRef m_cachedCVTextureRef; + QImage m_cachedQImage; + bool m_playbackRateSat; bool m_isDrmProtected; bool m_isDrmAuthorized; @@ -126,8 +131,10 @@ namespace QT7 float m_masterVolume; float m_relativeVolume; float m_playbackRate; + float m_staticFps; quint64 m_currentTime; MediaSource m_mediaSource; + void *m_primaryRenderingCIImage; qreal m_brightness; qreal m_contrast; @@ -154,6 +161,7 @@ namespace QT7 void setError(NSError *error); bool errorOccured(); void readProtection(); + void calculateStaticFps(); void checkIfVideoAwailable(); bool movieNotLoaded(); void waitStatePlayable(); diff --git a/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm b/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm index 3f76132..de7c6ed 100644 --- a/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm +++ b/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm @@ -61,12 +61,14 @@ QuickTimeVideoPlayer::QuickTimeVideoPlayer() : QObject(0) m_mute = false; m_audioEnabled = false; m_hasVideo = false; + m_staticFps = 0; m_playbackRateSat = false; m_isDrmProtected = false; m_isDrmAuthorized = true; m_primaryRenderingTarget = 0; m_primaryRenderingCIImage = 0; m_QImagePixelBuffer = 0; + m_cachedCVTextureRef = 0; #ifdef QUICKTIME_C_API_AVAILABLE OSStatus err = EnterMovies(); @@ -77,6 +79,7 @@ QuickTimeVideoPlayer::QuickTimeVideoPlayer() : QObject(0) QuickTimeVideoPlayer::~QuickTimeVideoPlayer() { + PhononAutoReleasePool pool; unsetVideo(); [(NSObject*)m_primaryRenderingTarget release]; m_primaryRenderingTarget = 0; @@ -86,6 +89,15 @@ QuickTimeVideoPlayer::~QuickTimeVideoPlayer() #endif } +void QuickTimeVideoPlayer::releaseImageCache() +{ + if (m_cachedCVTextureRef){ + CVOpenGLTextureRelease(m_cachedCVTextureRef); + m_cachedCVTextureRef = 0; + } + m_cachedQImage = QImage(); +} + void QuickTimeVideoPlayer::createVisualContext() { #ifdef QUICKTIME_C_API_AVAILABLE @@ -125,7 +137,10 @@ bool QuickTimeVideoPlayer::videoFrameChanged() return false; QTVisualContextTask(m_visualContext); - return QTVisualContextIsNewImageAvailable(m_visualContext, 0); + bool changed = QTVisualContextIsNewImageAvailable(m_visualContext, 0); + if (changed) + releaseImageCache(); + return changed; #elif defined(QT_MAC_USE_COCOA) return true; @@ -140,10 +155,11 @@ CVOpenGLTextureRef QuickTimeVideoPlayer::currentFrameAsCVTexture() #ifdef QUICKTIME_C_API_AVAILABLE if (!m_visualContext) return 0; - CVOpenGLTextureRef texture = 0; - OSStatus err = QTVisualContextCopyImageForTime(m_visualContext, 0, 0, &texture); - BACKEND_ASSERT3(err == noErr, "Could not copy image for time in QuickTime player", FATAL_ERROR, 0) - return texture; + if (!m_cachedCVTextureRef){ + OSStatus err = QTVisualContextCopyImageForTime(m_visualContext, 0, 0, &m_cachedCVTextureRef); + BACKEND_ASSERT3(err == noErr, "Could not copy image for time in QuickTime player", FATAL_ERROR, 0) + } + return m_cachedCVTextureRef; #else return 0; @@ -152,6 +168,9 @@ CVOpenGLTextureRef QuickTimeVideoPlayer::currentFrameAsCVTexture() QImage QuickTimeVideoPlayer::currentFrameAsQImage() { + if (!m_cachedQImage.isNull()) + return m_cachedQImage; + #ifdef QUICKTIME_C_API_AVAILABLE QGLContext *prevContext = const_cast<QGLContext *>(QGLContext::currentContext()); CVOpenGLTextureRef texture = currentFrameAsCVTexture(); @@ -181,12 +200,11 @@ QImage QuickTimeVideoPlayer::currentFrameAsQImage() glVertex2i(-1, -1); glEnd(); - QImage image = m_QImagePixelBuffer->toImage(); - CVOpenGLTextureRelease(texture); + m_cachedQImage = m_QImagePixelBuffer->toImage(); // Because of QuickTime, m_QImagePixelBuffer->doneCurrent() will fail. // So we store, and restore, the context our selves: prevContext->makeCurrent(); - return image; + return m_cachedQImage; #else CIImage *img = (CIImage *)currentFrameAsCIImage(); if (!img) @@ -195,10 +213,10 @@ QImage QuickTimeVideoPlayer::currentFrameAsQImage() NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithCIImage:img]; CGRect bounds = [img extent]; QImage qImg([bitmap bitmapData], bounds.size.width, bounds.size.height, QImage::Format_ARGB32); - QImage swapped = qImg.rgbSwapped(); + m_cachedQImage = qImg.rgbSwapped(); [bitmap release]; [img release]; - return swapped; + return m_cachedQImage; #endif } @@ -250,8 +268,7 @@ void *QuickTimeVideoPlayer::currentFrameAsCIImage() #ifdef QUICKTIME_C_API_AVAILABLE CVOpenGLTextureRef cvImg = currentFrameAsCVTexture(); CIImage *img = [[CIImage alloc] initWithCVImageBuffer:cvImg]; - CVOpenGLTextureRelease(cvImg); - return img; + return img; #else return 0; #endif @@ -273,7 +290,7 @@ GLuint QuickTimeVideoPlayer::currentFrameAsGLTexture() int samplesPerPixel = [bitmap samplesPerPixel]; if (![bitmap isPlanar] && (samplesPerPixel == 3 || samplesPerPixel == 4)){ - glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, + glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8, [bitmap pixelsWide], [bitmap pixelsHigh], 0, samplesPerPixel == 4 ? GL_RGBA : GL_RGB, @@ -302,7 +319,7 @@ void QuickTimeVideoPlayer::setVolume(float masterVolume, float relativeVolume) m_masterVolume = masterVolume; m_relativeVolume = relativeVolume; if (!m_QTMovie || !m_audioEnabled || m_mute) - return; + return; [m_QTMovie setVolume:(m_masterVolume * m_relativeVolume)]; } @@ -313,7 +330,7 @@ void QuickTimeVideoPlayer::setMute(bool mute) return; // Work-around bug that happends if you set/unset mute - // before movie is playing, and audio is not played + // before movie is playing, and audio is not played // through graph. Then audio is delayed. [m_QTMovie setMuted:mute]; [m_QTMovie setVolume:(mute ? 0 : m_masterVolume * m_relativeVolume)]; @@ -326,7 +343,7 @@ void QuickTimeVideoPlayer::enableAudio(bool enable) return; // Work-around bug that happends if you set/unset mute - // before movie is playing, and audio is not played + // before movie is playing, and audio is not played // through graph. Then audio is delayed. [m_QTMovie setMuted:(!enable || m_mute)]; [m_QTMovie setVolume:((!enable || m_mute) ? 0 : m_masterVolume * m_relativeVolume)]; @@ -345,7 +362,7 @@ bool QuickTimeVideoPlayer::setAudioDevice(int id) #ifdef QUICKTIME_C_API_AVAILABLE // The following code will not work for some media codecs that // typically mingle audio/video frames (e.g mpeg). - CFStringRef idString = PhononCFString::toCFStringRef(AudioDevice::deviceUID(id)); + CFStringRef idString = PhononCFString::toCFStringRef(AudioDevice::deviceUID(id)); QTAudioContextRef context; QTAudioContextCreateForAudioDevice(kCFAllocatorDefault, idString, 0, &context); OSStatus err = SetMovieAudioContext([m_QTMovie quickTimeMovie], context); @@ -369,11 +386,16 @@ void QuickTimeVideoPlayer::setColors(qreal brightness, qreal contrast, qreal hue contrast += 1; saturation += 1; + if (m_brightness == brightness + && m_contrast == contrast + && m_hue == hue + && m_saturation == saturation) + return; + m_brightness = brightness; m_contrast = contrast; m_hue = hue; m_saturation = saturation; - #ifdef QUICKTIME_C_API_AVAILABLE Float32 value; value = brightness; @@ -385,6 +407,7 @@ void QuickTimeVideoPlayer::setColors(qreal brightness, qreal contrast, qreal hue value = saturation; SetMovieVisualSaturation([m_QTMovie quickTimeMovie], value, 0); #endif + releaseImageCache(); } QRect QuickTimeVideoPlayer::videoRect() const @@ -410,11 +433,14 @@ void QuickTimeVideoPlayer::unsetVideo() m_state = NoMedia; m_isDrmProtected = false; m_isDrmAuthorized = true; + m_hasVideo = false; + m_staticFps = 0; m_mediaSource = MediaSource(); [(CIImage *)m_primaryRenderingCIImage release]; m_primaryRenderingCIImage = 0; delete m_QImagePixelBuffer; m_QImagePixelBuffer = 0; + releaseImageCache(); } QuickTimeVideoPlayer::State QuickTimeVideoPlayer::state() const @@ -557,6 +583,7 @@ void QuickTimeVideoPlayer::setMediaSource(const MediaSource &mediaSource) if (!m_playbackRateSat) m_playbackRate = prefferedPlaybackRate(); checkIfVideoAwailable(); + calculateStaticFps(); enableAudio(m_audioEnabled); setMute(m_mute); setVolume(m_masterVolume, m_relativeVolume); @@ -720,6 +747,44 @@ long QuickTimeVideoPlayer::timeScale() const return [[m_QTMovie attributeForKey:@"QTMovieTimeScaleAttribute"] longValue]; } +float QuickTimeVideoPlayer::staticFps() +{ + return m_staticFps; +} + +void QuickTimeVideoPlayer::calculateStaticFps() +{ + if (!m_hasVideo){ + m_staticFps = 0; + return; + } + +#ifdef QT_ALLOW_QUICKTIME + Boolean isMpeg = false; + Track videoTrack = GetMovieIndTrackType([m_QTMovie quickTimeMovie], 1, + FOUR_CHAR_CODE('vfrr'), // 'vfrr' means: has frame rate + movieTrackCharacteristic | movieTrackEnabledOnly); + Media media = GetTrackMedia(videoTrack); + MediaHandler mediaH = GetMediaHandler(media); + MediaHasCharacteristic(mediaH, FOUR_CHAR_CODE('mpeg'), &isMpeg); + + if (isMpeg){ + MHInfoEncodedFrameRateRecord frameRate; + Size frameRateSize = sizeof(frameRate); + MediaGetPublicInfo(mediaH, kMHInfoEncodedFrameRate, &frameRate, &frameRateSize); + m_staticFps = float(Fix2X(frameRate.encodedFrameRate)); + } else { + Media media = GetTrackMedia(videoTrack); + long sampleCount = GetMediaSampleCount(media); + TimeValue64 duration = GetMediaDisplayDuration(media); + TimeValue64 timeScale = GetMediaTimeScale(media); + m_staticFps = float((double)sampleCount * (double)timeScale / (double)duration); + } +#else + m_staticFps = 30.0f; +#endif +} + QString QuickTimeVideoPlayer::timeToString(quint64 ms) { int sec = ms/1000; diff --git a/src/3rdparty/phonon/qt7/videoframe.mm b/src/3rdparty/phonon/qt7/videoframe.mm index 92a3cd5..7b67b5e 100644 --- a/src/3rdparty/phonon/qt7/videoframe.mm +++ b/src/3rdparty/phonon/qt7/videoframe.mm @@ -20,6 +20,8 @@ #import <QuartzCore/CIFilter.h> #import <QuartzCore/CIContext.h> +//#define CACHE_CV_TEXTURE + QT_BEGIN_NAMESPACE namespace Phonon @@ -70,7 +72,9 @@ namespace QT7 void VideoFrame::copyMembers(const VideoFrame& frame) { +#ifdef CACHE_CV_TEXTURE m_cachedCVTextureRef = frame.m_cachedCVTextureRef; +#endif m_cachedCIImage = frame.m_cachedCIImage; m_cachedQImage = frame.m_cachedQImage; m_cachedNSBitmap = frame.m_cachedNSBitmap; @@ -105,11 +109,20 @@ namespace QT7 CVOpenGLTextureRef VideoFrame::cachedCVTexture() const { +#ifdef CACHE_CV_TEXTURE if (!m_cachedCVTextureRef && m_videoPlayer){ m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = m_videoPlayer->currentFrameAsCVTexture(); + CVOpenGLTextureRetain((const_cast<VideoFrame *>(this))->m_cachedCVTextureRef); } return m_cachedCVTextureRef; +#else + if (m_videoPlayer){ + m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); + return m_videoPlayer->currentFrameAsCVTexture(); + } + return 0; +#endif } void *VideoFrame::cachedCIImage() const @@ -329,10 +342,12 @@ namespace QT7 void VideoFrame::invalidateImage() const { +#ifdef CACHE_CV_TEXTURE if (m_cachedCVTextureRef){ CVOpenGLTextureRelease(m_cachedCVTextureRef); (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = 0; } +#endif if (m_cachedCIImage){ [(CIImage *) m_cachedCIImage release]; (const_cast<VideoFrame *>(this))->m_cachedCIImage = 0; @@ -346,8 +361,10 @@ namespace QT7 void VideoFrame::retain() const { +#ifdef CACHE_CV_TEXTURE if (m_cachedCVTextureRef) CVOpenGLTextureRetain(m_cachedCVTextureRef); +#endif if (m_cachedCIImage) [(CIImage *) m_cachedCIImage retain]; if (m_backgroundFrame) @@ -358,8 +375,12 @@ namespace QT7 void VideoFrame::release() const { - if (m_cachedCVTextureRef) +#ifdef CACHE_CV_TEXTURE + if (m_cachedCVTextureRef){ CVOpenGLTextureRelease(m_cachedCVTextureRef); + (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = 0; + } +#endif if (m_cachedCIImage) [(CIImage *) m_cachedCIImage release]; if (m_backgroundFrame) @@ -368,7 +389,6 @@ namespace QT7 [m_cachedNSBitmap release]; (const_cast<VideoFrame *>(this))->m_backgroundFrame = 0; - (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = 0; (const_cast<VideoFrame *>(this))->m_cachedCIImage = 0; (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = 0; } diff --git a/src/3rdparty/webkit/WebCore/dom/XMLTokenizerQt.cpp b/src/3rdparty/webkit/WebCore/dom/XMLTokenizerQt.cpp index 30926e1..2050a70 100644 --- a/src/3rdparty/webkit/WebCore/dom/XMLTokenizerQt.cpp +++ b/src/3rdparty/webkit/WebCore/dom/XMLTokenizerQt.cpp @@ -622,7 +622,7 @@ void XMLTokenizer::parseProcessingInstruction() #if ENABLE(XSLT) m_sawXSLTransform = !m_sawFirstElement && pi->isXSL(); - if (m_sawXSLTransform && !m_doc->transformSourceDocument())) + if (m_sawXSLTransform && !m_doc->transformSourceDocument()) stopParsing(); #endif } diff --git a/src/activeqt/container/qaxbase.cpp b/src/activeqt/container/qaxbase.cpp index 1ec704a..44c3e9e 100644 --- a/src/activeqt/container/qaxbase.cpp +++ b/src/activeqt/container/qaxbase.cpp @@ -1660,11 +1660,15 @@ private: QMap<QByteArray, Property> property_list; void addProperty(const QByteArray &type, const QByteArray &name, uint flags) { + QByteArray propertyType(type); + if (propertyType.endsWith("&")) + propertyType.chop(1); + Property &prop = property_list[name]; - if (!type.isEmpty() && type != "HRESULT") { - prop.type = replaceType(type); - if (prop.type != type) - prop.realType = type; + if (!propertyType.isEmpty() && propertyType != "HRESULT") { + prop.type = replaceType(propertyType); + if (prop.type != propertyType) + prop.realType = propertyType; } if (flags & Writable) flags |= Stored; diff --git a/src/corelib/global/qfeatures.h b/src/corelib/global/qfeatures.h index 6d55f7c..c172f5f 100644 --- a/src/corelib/global/qfeatures.h +++ b/src/corelib/global/qfeatures.h @@ -120,6 +120,9 @@ // QMovie //#define QT_NO_MOVIE +// QNetworkInterface +//#define QT_NO_NETWORKINTERFACE + // QNetworkProxy //#define QT_NO_NETWORKPROXY @@ -198,12 +201,12 @@ // Qt Prerendered Font Format //#define QT_NO_QWS_QPF -// Raster Paint Engine callback functions -//#define QT_NO_RASTERCALLBACKS - // Qt Prerendered Font Format 2 //#define QT_NO_QWS_QPF2 +// Raster Paint Engine callback functions +//#define QT_NO_RASTERCALLBACKS + // Resize Handler //#define QT_NO_RESIZEHANDLER @@ -273,9 +276,6 @@ // HtmlParser //#define QT_NO_TEXTHTMLPARSER -// OdfWriter -//#define QT_NO_TEXTODFWRITER - // QTextStream //#define QT_NO_TEXTSTREAM @@ -316,6 +316,11 @@ #define QT_NO_BUTTONGROUP #endif +// QClipboard +#if !defined(QT_NO_CLIPBOARD) && (defined(QT_NO_QWS_PROPERTIES)) +#define QT_NO_CLIPBOARD +#endif + // Codecs #if !defined(QT_NO_CODECS) && (defined(QT_NO_TEXTCODEC)) #define QT_NO_CODECS @@ -356,6 +361,11 @@ #define QT_NO_MENU #endif +// QNetworkDiskCache +#if !defined(QT_NO_NETWORKDISKCACHE) && (defined(QT_NO_TEMPORARYFILE)) +#define QT_NO_NETWORKDISKCACHE +#endif + // Phonon::SeekSlider #if !defined(QT_NO_PHONON_SEEKSLIDER) && (defined(QT_NO_SLIDER)) #define QT_NO_PHONON_SEEKSLIDER @@ -481,11 +491,6 @@ #define QT_NO_XMLSTREAMWRITER #endif -// Odf Writer -#if !defined(QT_NO_TEXTODFWRITER) && (defined(QT_NO_XMLSTREAMWRITER)) -#define QT_NO_TEXTODFWRITER -#endif - // Context menu #if !defined(QT_NO_CONTEXTMENU) && (defined(QT_NO_MENU)) #define QT_NO_CONTEXTMENU @@ -516,6 +521,11 @@ #define QT_NO_SCROLLAREA #endif +// OdfWriter +#if !defined(QT_NO_TEXTODFWRITER) && (defined(QT_NO_XMLSTREAMWRITER)) +#define QT_NO_TEXTODFWRITER +#endif + // QToolButton #if !defined(QT_NO_TOOLBUTTON) && (defined(QT_NO_ICON) || defined(QT_NO_ACTION)) #define QT_NO_TOOLBUTTON @@ -636,11 +646,6 @@ #define QT_NO_WHATSTHIS #endif -// QClipboard -#if !defined(QT_NO_CLIPBOARD) && (defined(QT_NO_QWS_PROPERTIES)) -#define QT_NO_CLIPBOARD -#endif - // Common UNIX Printing System #if !defined(QT_NO_CUPS) && (defined(QT_NO_PRINTER) || defined(QT_NO_LIBRARY)) #define QT_NO_CUPS diff --git a/src/corelib/global/qfeatures.txt b/src/corelib/global/qfeatures.txt index 40ccb15..5d63e46 100644 --- a/src/corelib/global/qfeatures.txt +++ b/src/corelib/global/qfeatures.txt @@ -1085,6 +1085,13 @@ Requires: Name: QNetworkInterface SeeAlso: ??? +Feature: NETWORKDISKCACHE +Description: Supports a disk cache for network resources +Section: Networking +Requires: TEMPORARYFILE +Name: QNetworkDiskCache +SeeAlso: ??? + # Utilities Feature: COMPLETER diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 8324d05..ce98ec4 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -2959,7 +2959,12 @@ bool QInternal::callFunction(InternalFunction func, void **args) \relates <QtGlobal> \since 4.4 \threadsafe - \overload + + Compares the floating point value \a p1 and \a p2 and + returns \c true if they are considered equal, otherwise \c false. + + The two numbers are compared in a relative way, where the + exactness is stronger the smaller the numbers are. */ /*! diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 81af295..672862a 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -44,11 +44,11 @@ #include <stddef.h> -#define QT_VERSION_STR "4.5.1" +#define QT_VERSION_STR "4.6.0" /* QT_VERSION is (major << 16) + (minor << 8) + patch. */ -#define QT_VERSION 0x040501 +#define QT_VERSION 0x040600 /* can be used like #if (QT_VERSION >= QT_VERSION_CHECK(4, 4, 0)) */ @@ -1837,7 +1837,7 @@ enum { /* TYPEINFO flags */ #define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \ template <> \ -class QTypeInfo<TYPE> \ +class QTypeInfo<TYPE > \ { \ public: \ enum { \ diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h index 4c8295e..ec5780c 100644 --- a/src/corelib/io/qdatastream.h +++ b/src/corelib/io/qdatastream.h @@ -83,10 +83,11 @@ public: Qt_4_2 = 8, Qt_4_3 = 9, Qt_4_4 = 10, - Qt_4_5 = 11 -#if QT_VERSION >= 0x040600 + Qt_4_5 = 11, + Qt_4_6 = Qt_4_5 +#if QT_VERSION >= 0x040700 #error Add the datastream version for this Qt version - , Qt_4_6 = Qt_4_5 + Qt_4_7 = Qt_4_6 #endif }; diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index 0d88b06..8a0a3f5 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -935,6 +935,8 @@ bool QFSFileEngine::setSize(qint64 size) Q_D(QFSFileEngine); if (d->fd != -1) return !QT_FTRUNCATE(d->fd, size); + if (d->fh) + return !QT_FTRUNCATE(QT_FILENO(d->fh), size); return !QT_TRUNCATE(d->nativeFilePath.constData(), size); } diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index c739054..b6c4eb1 100644 --- a/src/corelib/io/qiodevice.cpp +++ b/src/corelib/io/qiodevice.cpp @@ -48,6 +48,10 @@ #include "qstringlist.h" #include <limits.h> +#ifdef QIODEVICE_DEBUG +# include <ctype.h> +#endif + QT_BEGIN_NAMESPACE #ifdef QIODEVICE_DEBUG @@ -362,7 +366,7 @@ QIODevice::QIODevice() { #if defined QIODEVICE_DEBUG QFile *file = qobject_cast<QFile *>(this); - printf("%p QIODevice::QIODevice(\"%s\") %s\n", this, className(), + printf("%p QIODevice::QIODevice(\"%s\") %s\n", this, metaObject()->className(), qPrintable(file ? file->fileName() : QString())); #endif } @@ -375,7 +379,7 @@ QIODevice::QIODevice(QObject *parent) : QObject(*new QIODevicePrivate, parent) { #if defined QIODEVICE_DEBUG - printf("%p QIODevice::QIODevice(%p \"%s\")\n", this, parent, className()); + printf("%p QIODevice::QIODevice(%p \"%s\")\n", this, parent, metaObject()->className()); #endif } diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index a128482..30f4291 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -101,6 +101,29 @@ QT_END_NAMESPACE QT_BEGIN_NAMESPACE +static QHash<QString, QString> environmentHashFromList(const QStringList &environment) +{ + QHash<QString, QString> result; + QStringList::ConstIterator it = environment.constBegin(), + end = environment.constEnd(); + for ( ; it != end; ++it) { + int equals = it->indexOf(QLatin1Char('=')); + + QString name = *it; + QString value; + if (equals != -1) { + name.truncate(equals); +#ifdef Q_OS_WIN + name = name.toUpper(); +#endif + value = it->mid(equals + 1); + } + result.insert(name, value); + } + + return result; +} + void QProcessPrivate::Channel::clear() { switch (type) { @@ -416,6 +439,7 @@ QProcessPrivate::QProcessPrivate() sequenceNumber = 0; exitCode = 0; exitStatus = QProcess::NormalExit; + environment = 0; startupSocketNotifier = 0; deathNotifier = 0; notifier = 0; @@ -442,6 +466,7 @@ QProcessPrivate::QProcessPrivate() */ QProcessPrivate::~QProcessPrivate() { + delete environment; if (stdinChannel.process) stdinChannel.process->stdoutChannel.clear(); if (stdoutChannel.process) @@ -1191,29 +1216,83 @@ QProcess::ProcessState QProcess::state() const \snippet doc/src/snippets/qprocess-environment/main.cpp 0 - \sa environment(), systemEnvironment() + \sa environment(), systemEnvironment(), setEnvironmentHash() */ void QProcess::setEnvironment(const QStringList &environment) { - Q_D(QProcess); - d->environment = environment; + setEnvironmentHash(environmentHashFromList(environment)); } /*! Returns the environment that QProcess will use when starting a process, or an empty QStringList if no environment has been set - using setEnvironment(). If no environment has been set, the - environment of the calling process will be used. + using setEnvironment() or setEnvironmentHash(). If no environment + has been set, the environment of the calling process will be used. \note The environment settings are ignored on Windows CE, as there is no concept of an environment. - \sa setEnvironment(), systemEnvironment() + \sa environmentHash(), setEnvironment(), systemEnvironment() */ QStringList QProcess::environment() const { Q_D(const QProcess); - return d->environment; + + QStringList result; + if (!d->environment) + return result; + + QHash<QString, QString>::ConstIterator it = d->environment->constBegin(), + end = d->environment->constEnd(); + for ( ; it != end; ++it) { + QString data = it.key(); + data.reserve(data.length() + it.value().length() + 1); + data.append(QLatin1Char('=')); + data.append(it.value()); + result << data; + } + return result; +} + +/*! + \since 4.5 + Sets the environment that QProcess will use when starting a process to the + \a environment hash map. + + For example, the following code adds the \c{C:\\BIN} directory to the list of + executable paths (\c{PATHS}) on Windows and sets \c{TMPDIR}: + + \snippet doc/src/snippets/qprocess-environment/main.cpp 1 + + \sa environment(), systemEnvironmentHash(), setEnvironment() +*/ +void QProcess::setEnvironmentHash(const QHash<QString, QString> &environment) +{ + Q_D(QProcess); + if (!d->environment) + d->environment = new QHash<QString, QString>(environment); + else + *d->environment = environment; +} + +/*! + \since 4.5 + Returns the environment that QProcess will use when starting a + process, or an empty QHash if no environment has been set using + setEnvironment() or setEnvironmentHash(). If no environment has + been set, the environment of the calling process will be used. + + \note The environment settings are ignored on Windows CE, + as there is no concept of an environment. + + \sa setEnvironmentHash(), setEnvironment(), systemEnvironmentHash() +*/ +QHash<QString, QString> QProcess::environmentHash() const +{ + Q_D(const QProcess); + if (d->environment) + return *d->environment; + return QHash<QString, QString>(); } /*! @@ -1812,7 +1891,7 @@ QT_END_INCLUDE_NAMESPACE \snippet doc/src/snippets/code/src_corelib_io_qprocess.cpp 8 - \sa environment(), setEnvironment() + \sa systemEnvironmentHash(), environment(), setEnvironment() */ QStringList QProcess::systemEnvironment() { @@ -1825,6 +1904,18 @@ QStringList QProcess::systemEnvironment() } /*! + \since 4.5 + + Returns the environment of the calling process as a QHash. + + \sa systemEnvironment(), environmentHash(), setEnvironmentHash() +*/ +QHash<QString, QString> QProcess::systemEnvironmentHash() +{ + return environmentHashFromList(systemEnvironment()); +} + +/*! \typedef Q_PID \relates QProcess diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h index 54a96c7..bf9a5a6 100644 --- a/src/corelib/io/qprocess.h +++ b/src/corelib/io/qprocess.h @@ -53,6 +53,8 @@ QT_MODULE(Core) #ifndef QT_NO_PROCESS +template <class Key, class T> class QHash; + #if (!defined(Q_OS_WIN32) && !defined(Q_OS_WINCE)) || defined(qdoc) typedef qint64 Q_PID; #else @@ -121,6 +123,8 @@ public: void setEnvironment(const QStringList &environment); QStringList environment() const; + void setEnvironmentHash(const QHash<QString, QString> &environment); + QHash<QString, QString> environmentHash() const; QProcess::ProcessError error() const; QProcess::ProcessState state() const; @@ -156,6 +160,7 @@ public: static bool startDetached(const QString &program); static QStringList systemEnvironment(); + static QHash<QString, QString> systemEnvironmentHash(); public Q_SLOTS: void terminate(); diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h index 33059dd..f67fd2d 100644 --- a/src/corelib/io/qprocess_p.h +++ b/src/corelib/io/qprocess_p.h @@ -161,7 +161,7 @@ public: QString program; QStringList arguments; - QStringList environment; + QHash<QString, QString> *environment; QRingBuffer outputReadBuffer; QRingBuffer errorReadBuffer; diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 37173c8..5537c9d 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -531,8 +531,12 @@ bool QProcessPrivate::createChannel(Channel &channel) } } -static char **_q_dupEnvironment(const QStringList &environment, int *envc) +static char **_q_dupEnvironment(const QHash<QString, QString> *environment, int *envc) { + *envc = 0; + if (!environment) + return 0; // use the default environment + // if LD_LIBRARY_PATH exists in the current environment, but // not in the environment list passed by the programmer, then // copy it over. @@ -541,27 +545,29 @@ static char **_q_dupEnvironment(const QStringList &environment, int *envc) #else static const char libraryPath[] = "LD_LIBRARY_PATH"; #endif - const QString libraryPathString = QLatin1String(libraryPath); - QStringList env = environment; - QStringList matches = env.filter( - QRegExp(QLatin1Char('^') + libraryPathString + QLatin1Char('='))); - const QString envLibraryPath = QString::fromLocal8Bit(::getenv(libraryPath)); - if (matches.isEmpty() && !envLibraryPath.isEmpty()) { - QString entry = libraryPathString; - entry += QLatin1Char('='); - entry += envLibraryPath; - env << libraryPathString + QLatin1Char('=') + envLibraryPath; - } - - char **envp = new char *[env.count() + 1]; - envp[env.count()] = 0; - - for (int j = 0; j < env.count(); ++j) { - QString item = env.at(j); - envp[j] = ::strdup(item.toLocal8Bit().constData()); + const QByteArray envLibraryPath = qgetenv(libraryPath); + bool needToAddLibraryPath = !envLibraryPath.isEmpty() && + !environment->contains(QLatin1String(libraryPath)); + + char **envp = new char *[environment->count() + 2]; + envp[environment->count()] = 0; + envp[environment->count() + 1] = 0; + + QHash<QString, QString>::ConstIterator it = environment->constBegin(); + const QHash<QString, QString>::ConstIterator end = environment->constEnd(); + for ( ; it != end; ++it) { + QByteArray key = it.key().toLocal8Bit(); + QByteArray value = it.value().toLocal8Bit(); + key.reserve(key.length() + 1 + value.length()); + key.append('='); + key.append(value); + + envp[(*envc)++] = ::strdup(key.constData()); } - *envc = env.count(); + if (needToAddLibraryPath) + envp[(*envc)++] = ::strdup(QByteArray(libraryPath) + '=' + + envLibraryPath); return envp; } @@ -800,7 +806,7 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv q->setupChildProcess(); // execute the process - if (environment.isEmpty()) { + if (!envp) { qt_native_execvp(argv[0], argv); } else { if (path) { diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index 0e36760..5d862e5 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -304,44 +304,69 @@ static QString qt_create_commandline(const QString &program, const QStringList & return args; } -static QByteArray qt_create_environment(const QStringList &environment) +static QByteArray qt_create_environment(const QHash<QString, QString> *environment) { QByteArray envlist; - if (!environment.isEmpty()) { - QStringList envStrings = environment; - int pos = 0; - // add PATH if necessary (for DLL loading) - if (envStrings.filter(QRegExp(QLatin1String("^PATH="),Qt::CaseInsensitive)).isEmpty()) { + if (environment) { + QHash<QString, QString> copy = *environment; + + // add PATH if necessary (for DLL loading) + if (!copy.contains(QLatin1String("PATH"))) { QByteArray path = qgetenv("PATH"); if (!path.isEmpty()) - envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path))); + copy.insert(QLatin1String("PATH"), QString::fromLocal8Bit(path)); } + // add systemroot if needed - if (envStrings.filter(QRegExp(QLatin1String("^SystemRoot="),Qt::CaseInsensitive)).isEmpty()) { - QByteArray systemRoot = qgetenv("SystemRoot"); + if (!copy.contains(QLatin1String("SYSTEMROOT"))) { + QByteArray systemRoot = qgetenv("SYSTEMROOT"); if (!systemRoot.isEmpty()) - envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot))); + copy.insert(QLatin1String("SYSTEMROOT"), QString::fromLocal8Bit(systemRoot)); } + + int pos = 0; + QHash<QString, QString>::ConstIterator it = copy.constBegin(), + end = copy.constEnd(); #ifdef UNICODE if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { - for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); ++it) { - QString tmp = *it; - uint tmpSize = sizeof(TCHAR) * (tmp.length()+1); + static const TCHAR equal = L'='; + static const TCHAR nul = L'\0'; + + for ( ; it != end; ++it) { + uint tmpSize = sizeof(TCHAR) * (it.key().length() + it.value().length() + 2); + // ignore empty strings + if (tmpSize == sizeof(TCHAR)*2) + continue; envlist.resize(envlist.size() + tmpSize); - memcpy(envlist.data()+pos, tmp.utf16(), tmpSize); + + tmpSize = it.key().length() * sizeof(TCHAR); + memcpy(envlist.data()+pos, it.key().utf16(), tmpSize); + pos += tmpSize; + + memcpy(envlist.data()+pos, &equal, sizeof(TCHAR)); + pos += sizeof(TCHAR); + + tmpSize = it.value().length() * sizeof(TCHAR); + memcpy(envlist.data()+pos, it.value().utf16(), tmpSize); pos += tmpSize; - } - // add the 2 terminating 0 (actually 4, just to be on the safe side) - envlist.resize( envlist.size()+4 ); - envlist[pos++] = 0; - envlist[pos++] = 0; - envlist[pos++] = 0; - envlist[pos++] = 0; + + memcpy(envlist.data()+pos, &nul, sizeof(TCHAR)); + pos += sizeof(TCHAR); + } + // add the 2 terminating 0 (actually 4, just to be on the safe side) + envlist.resize( envlist.size()+4 ); + envlist[pos++] = 0; + envlist[pos++] = 0; + envlist[pos++] = 0; + envlist[pos++] = 0; } else #endif // UNICODE { - for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); it++) { - QByteArray tmp = (*it).toLocal8Bit(); + for ( ; it != end; it++) { + QByteArray tmp = it.key().toLocal8Bit(); + tmp.append('='); + tmp.append(it.value().toLocal8Bit()); + uint tmpSize = tmp.length() + 1; envlist.resize(envlist.size() + tmpSize); memcpy(envlist.data()+pos, tmp.data(), tmpSize); @@ -418,7 +443,7 @@ void QProcessPrivate::startProcess() }; success = CreateProcessW(0, (WCHAR*)args.utf16(), 0, 0, TRUE, dwCreationFlags, - environment.isEmpty() ? 0 : envlist.data(), + environment ? envlist.data() : 0, workingDirectory.isEmpty() ? 0 : (WCHAR*)QDir::toNativeSeparators(workingDirectory).utf16(), &startupInfo, pid); @@ -437,7 +462,7 @@ void QProcessPrivate::startProcess() }; success = CreateProcessA(0, args.toLocal8Bit().data(), - 0, 0, TRUE, dwCreationFlags, environment.isEmpty() ? 0 : envlist.data(), + 0, 0, TRUE, dwCreationFlags, environment ? envlist.data() : 0, workingDirectory.isEmpty() ? 0 : QDir::toNativeSeparators(workingDirectory).toLocal8Bit().data(), &startupInfo, pid); diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 7f87897..2fb6335 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -297,6 +297,12 @@ void QAdoptedThread::run() priority parameter. If the thread is already running, this function does nothing. + The effect of the \a priority parameter is dependent on the + operating system's scheduling policy. In particular, the \a priority + will be ignored on systems that do not support thread priorities + (such as on Linux, see http://linux.die.net/man/2/sched_setscheduler + for more details). + \sa run(), terminate() */ @@ -590,6 +596,12 @@ void QThread::cleanup() The \a priority argument can be any value in the \c QThread::Priority enum except for \c InheritPriorty. + The effect of the \a priority parameter is dependent on the + operating system's scheduling policy. In particular, the \a priority + will be ignored on systems that do not support thread priorities + (such as on Linux, see http://linux.die.net/man/2/sched_setscheduler + for more details). + \sa Priority priority() start() */ diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h index 4b19adc..993ce48 100644 --- a/src/corelib/tools/qset.h +++ b/src/corelib/tools/qset.h @@ -85,6 +85,8 @@ public: inline bool contains(const T &value) const { return q_hash.contains(value); } + bool contains(const QSet<T> &set) const; + class const_iterator; class iterator @@ -274,6 +276,18 @@ Q_INLINE_TEMPLATE QSet<T> &QSet<T>::subtract(const QSet<T> &other) return *this; } +template <class T> +Q_INLINE_TEMPLATE bool QSet<T>::contains(const QSet<T> &other) const +{ + typename QSet<T>::const_iterator i = other.constBegin(); + while (i != other.constEnd()) { + if (!contains(*i)) + return false; + ++i; + } + return true; +} + template <typename T> Q_OUTOFLINE_TEMPLATE QList<T> QSet<T>::toList() const { diff --git a/src/corelib/xml/make-parser.sh b/src/corelib/xml/make-parser.sh index 0c2ba12..0e2cbe1 100755 --- a/src/corelib/xml/make-parser.sh +++ b/src/corelib/xml/make-parser.sh @@ -6,10 +6,8 @@ mkdir -p $me/out for f in $me/out/*.h; do n=$(basename $f) - p4 open $n cp $f $n done -p4 revert -a ... -p4 diff -dub ... +git diff . diff --git a/src/corelib/xml/qxmlstream.g b/src/corelib/xml/qxmlstream.g index d5e64a6..0cc744e 100644 --- a/src/corelib/xml/qxmlstream.g +++ b/src/corelib/xml/qxmlstream.g @@ -1533,8 +1533,8 @@ attribute ::= qname space_opt EQ space_opt attribute_value; QStringRef namespacePrefix = symString(attribute.key); QStringRef namespaceUri = symString(attribute.value); attributeStack.pop(); - if ((namespacePrefix == QLatin1String("xml") - ^ namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace")) + if (((namespacePrefix == QLatin1String("xml")) + ^ (namespaceUri == QLatin1String("http://www.w3.org/XML/1998/namespace"))) || namespaceUri == QLatin1String("http://www.w3.org/2000/xmlns/") || namespaceUri.isEmpty() || namespacePrefix == QLatin1String("xmlns")) diff --git a/src/corelib/xml/qxmlstream_p.h b/src/corelib/xml/qxmlstream_p.h index 201f0c1..cf1d228 100644 --- a/src/corelib/xml/qxmlstream_p.h +++ b/src/corelib/xml/qxmlstream_p.h @@ -1,3 +1,4 @@ +// This file was generated by qlalr - DO NOT EDIT! /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). diff --git a/src/gui/embedded/embedded.pri b/src/gui/embedded/embedded.pri index 95c4132..4a9aa3f 100644 --- a/src/gui/embedded/embedded.pri +++ b/src/gui/embedded/embedded.pri @@ -89,6 +89,8 @@ embedded { HEADERS += embedded/qscreendriverplugin_qws.h \ embedded/qscreendriverfactory_qws.h \ embedded/qkbd_qws.h \ + embedded/qkbd_qws_p.h \ + embedded/qkbd_defaultmap_qws_p.h \ embedded/qkbddriverplugin_qws.h \ embedded/qkbddriverfactory_qws.h \ embedded/qmouse_qws.h \ @@ -152,17 +154,11 @@ embedded { contains( kbd-drivers, tty ) { HEADERS +=embedded/qkbdtty_qws.h SOURCES +=embedded/qkbdtty_qws.cpp - !contains( kbd-drivers, pc101 ) { - kbd-drivers += pc101 - } } - contains( kbd-drivers, usb ) { - HEADERS +=embedded/qkbdusb_qws.h - SOURCES +=embedded/qkbdusb_qws.cpp - !contains( kbd-drivers, pc101 ) { - kbd-drivers += pc101 - } + contains( kbd-drivers, linuxinput ) { + HEADERS +=embedded/qkbdlinuxinput_qws.h + SOURCES +=embedded/qkbdlinuxinput_qws.cpp } contains( kbd-drivers, um ) { @@ -170,11 +166,6 @@ embedded { SOURCES +=embedded/qkbdum_qws.cpp } - contains( kbd-drivers, pc101 ) { - HEADERS +=embedded/qkbdpc101_qws.h - SOURCES +=embedded/qkbdpc101_qws.cpp - } - contains( kbd-drivers, yopy ) { HEADERS +=embedded/qkbdyopy_qws.h SOURCES +=embedded/qkbdyopy_qws.cpp diff --git a/src/gui/embedded/qkbd_defaultmap_qws_p.h b/src/gui/embedded/qkbd_defaultmap_qws_p.h new file mode 100644 index 0000000..34ce69e --- /dev/null +++ b/src/gui/embedded/qkbd_defaultmap_qws_p.h @@ -0,0 +1,744 @@ +#ifndef QWSKEYBOARDHANDLER_DEFAULTMAP_H +#define QWSKEYBOARDHANDLER_DEFAULTMAP_H + +const QWSKeyboard::Mapping QWSKbPrivate::s_keymap_default[] = { + { 1, 0xffff, 0x01000000, 0x00, 0x00, 0x0000 }, + { 2, 0x0031, 0x00000031, 0x00, 0x00, 0x0000 }, + { 2, 0x0021, 0x00000021, 0x01, 0x00, 0x0000 }, + { 3, 0x0032, 0x00000032, 0x00, 0x00, 0x0000 }, + { 3, 0x0040, 0x00000040, 0x01, 0x00, 0x0000 }, + { 3, 0x0040, 0x00000040, 0x02, 0x00, 0x0000 }, + { 4, 0x0033, 0x00000033, 0x00, 0x00, 0x0000 }, + { 4, 0x0023, 0x00000023, 0x01, 0x00, 0x0000 }, + { 4, 0xffff, 0x01000000, 0x04, 0x00, 0x0000 }, + { 5, 0x0034, 0x00000034, 0x00, 0x00, 0x0000 }, + { 5, 0x0024, 0x00000024, 0x01, 0x00, 0x0000 }, + { 5, 0x0024, 0x00000024, 0x02, 0x00, 0x0000 }, + { 5, 0x005c, 0x0400005c, 0x04, 0x00, 0x0000 }, + { 6, 0x0035, 0x00000035, 0x00, 0x00, 0x0000 }, + { 6, 0x0025, 0x00000025, 0x01, 0x00, 0x0000 }, + { 6, 0x005d, 0x0400005d, 0x04, 0x00, 0x0000 }, + { 7, 0x0036, 0x00000036, 0x00, 0x00, 0x0000 }, + { 7, 0x005e, 0x0000005e, 0x01, 0x00, 0x0000 }, + { 7, 0x005e, 0x01001252, 0x02, 0x01, 0x0000 }, + { 7, 0x005e, 0x0400005e, 0x04, 0x00, 0x0000 }, + { 8, 0x0037, 0x00000037, 0x00, 0x00, 0x0000 }, + { 8, 0x0026, 0x00000026, 0x01, 0x00, 0x0000 }, + { 8, 0x007b, 0x0000007b, 0x02, 0x00, 0x0000 }, + { 8, 0x005f, 0x0400005f, 0x04, 0x00, 0x0000 }, + { 9, 0x0038, 0x00000038, 0x00, 0x00, 0x0000 }, + { 9, 0x002a, 0x0000002a, 0x01, 0x00, 0x0000 }, + { 9, 0x005b, 0x0000005b, 0x02, 0x00, 0x0000 }, + { 9, 0xffff, 0x01000003, 0x04, 0x00, 0x0000 }, + { 10, 0x0039, 0x00000039, 0x00, 0x00, 0x0000 }, + { 10, 0x0028, 0x00000028, 0x01, 0x00, 0x0000 }, + { 10, 0x005d, 0x0000005d, 0x02, 0x00, 0x0000 }, + { 11, 0x0030, 0x00000030, 0x00, 0x00, 0x0000 }, + { 11, 0x0029, 0x00000029, 0x01, 0x00, 0x0000 }, + { 11, 0x007d, 0x0000007d, 0x02, 0x00, 0x0000 }, + { 12, 0x002d, 0x0000002d, 0x00, 0x00, 0x0000 }, + { 12, 0x005f, 0x0000005f, 0x01, 0x00, 0x0000 }, + { 12, 0x005c, 0x0000005c, 0x02, 0x00, 0x0000 }, + { 12, 0x005f, 0x0400005f, 0x04, 0x00, 0x0000 }, + { 12, 0x005f, 0x0400005f, 0x05, 0x00, 0x0000 }, + { 13, 0x003d, 0x0000003d, 0x00, 0x00, 0x0000 }, + { 13, 0x002b, 0x0000002b, 0x01, 0x00, 0x0000 }, + { 14, 0xffff, 0x01000003, 0x00, 0x00, 0x0000 }, + { 14, 0xffff, 0x01000000, 0x0c, 0x08, 0x0300 }, + { 15, 0xffff, 0x01000001, 0x00, 0x00, 0x0000 }, + { 16, 0x0071, 0x00000051, 0x00, 0x00, 0x0000 }, + { 16, 0x0051, 0x00000051, 0x01, 0x00, 0x0000 }, + { 16, 0x0071, 0x00000051, 0x02, 0x00, 0x0000 }, + { 16, 0x0051, 0x00000051, 0x03, 0x00, 0x0000 }, + { 16, 0x0071, 0x04000051, 0x04, 0x00, 0x0000 }, + { 16, 0x0071, 0x04000051, 0x05, 0x00, 0x0000 }, + { 16, 0x0071, 0x04000051, 0x06, 0x00, 0x0000 }, + { 16, 0x0071, 0x04000051, 0x07, 0x00, 0x0000 }, + { 16, 0x0071, 0x08000051, 0x08, 0x00, 0x0000 }, + { 16, 0x0071, 0x08000051, 0x09, 0x00, 0x0000 }, + { 16, 0x0071, 0x08000051, 0x0a, 0x00, 0x0000 }, + { 16, 0x0071, 0x08000051, 0x0b, 0x00, 0x0000 }, + { 16, 0x0071, 0x0c000051, 0x0c, 0x00, 0x0000 }, + { 16, 0x0071, 0x0c000051, 0x0d, 0x00, 0x0000 }, + { 16, 0x0071, 0x0c000051, 0x0e, 0x00, 0x0000 }, + { 16, 0x0071, 0x0c000051, 0x0f, 0x00, 0x0000 }, + { 17, 0x0077, 0x00000057, 0x00, 0x00, 0x0000 }, + { 17, 0x0057, 0x00000057, 0x01, 0x00, 0x0000 }, + { 17, 0x0077, 0x00000057, 0x02, 0x00, 0x0000 }, + { 17, 0x0057, 0x00000057, 0x03, 0x00, 0x0000 }, + { 17, 0x0077, 0x04000057, 0x04, 0x00, 0x0000 }, + { 17, 0x0077, 0x04000057, 0x05, 0x00, 0x0000 }, + { 17, 0x0077, 0x04000057, 0x06, 0x00, 0x0000 }, + { 17, 0x0077, 0x04000057, 0x07, 0x00, 0x0000 }, + { 17, 0x0077, 0x08000057, 0x08, 0x00, 0x0000 }, + { 17, 0x0077, 0x08000057, 0x09, 0x00, 0x0000 }, + { 17, 0x0077, 0x08000057, 0x0a, 0x00, 0x0000 }, + { 17, 0x0077, 0x08000057, 0x0b, 0x00, 0x0000 }, + { 17, 0x0077, 0x0c000057, 0x0c, 0x00, 0x0000 }, + { 17, 0x0077, 0x0c000057, 0x0d, 0x00, 0x0000 }, + { 17, 0x0077, 0x0c000057, 0x0e, 0x00, 0x0000 }, + { 17, 0x0077, 0x0c000057, 0x0f, 0x00, 0x0000 }, + { 18, 0x0065, 0x00000045, 0x00, 0x00, 0x0000 }, + { 18, 0x0045, 0x00000045, 0x01, 0x00, 0x0000 }, + { 18, 0x0065, 0x00000045, 0x02, 0x00, 0x0000 }, + { 18, 0x0045, 0x00000045, 0x03, 0x00, 0x0000 }, + { 18, 0x0065, 0x04000045, 0x04, 0x00, 0x0000 }, + { 18, 0x0065, 0x04000045, 0x05, 0x00, 0x0000 }, + { 18, 0x0065, 0x04000045, 0x06, 0x00, 0x0000 }, + { 18, 0x0065, 0x04000045, 0x07, 0x00, 0x0000 }, + { 18, 0x0065, 0x08000045, 0x08, 0x00, 0x0000 }, + { 18, 0x0065, 0x08000045, 0x09, 0x00, 0x0000 }, + { 18, 0x0065, 0x08000045, 0x0a, 0x00, 0x0000 }, + { 18, 0x0065, 0x08000045, 0x0b, 0x00, 0x0000 }, + { 18, 0x0065, 0x0c000045, 0x0c, 0x00, 0x0000 }, + { 18, 0x0065, 0x0c000045, 0x0d, 0x00, 0x0000 }, + { 18, 0x0065, 0x0c000045, 0x0e, 0x00, 0x0000 }, + { 18, 0x0065, 0x0c000045, 0x0f, 0x00, 0x0000 }, + { 19, 0x0072, 0x00000052, 0x00, 0x00, 0x0000 }, + { 19, 0x0052, 0x00000052, 0x01, 0x00, 0x0000 }, + { 19, 0x0072, 0x00000052, 0x02, 0x00, 0x0000 }, + { 19, 0x0052, 0x00000052, 0x03, 0x00, 0x0000 }, + { 19, 0x0072, 0x04000052, 0x04, 0x00, 0x0000 }, + { 19, 0x0072, 0x04000052, 0x05, 0x00, 0x0000 }, + { 19, 0x0072, 0x04000052, 0x06, 0x00, 0x0000 }, + { 19, 0x0072, 0x04000052, 0x07, 0x00, 0x0000 }, + { 19, 0x0072, 0x08000052, 0x08, 0x00, 0x0000 }, + { 19, 0x0072, 0x08000052, 0x09, 0x00, 0x0000 }, + { 19, 0x0072, 0x08000052, 0x0a, 0x00, 0x0000 }, + { 19, 0x0072, 0x08000052, 0x0b, 0x00, 0x0000 }, + { 19, 0x0072, 0x0c000052, 0x0c, 0x00, 0x0000 }, + { 19, 0x0072, 0x0c000052, 0x0d, 0x00, 0x0000 }, + { 19, 0x0072, 0x0c000052, 0x0e, 0x00, 0x0000 }, + { 19, 0x0072, 0x0c000052, 0x0f, 0x00, 0x0000 }, + { 20, 0x0074, 0x00000054, 0x00, 0x00, 0x0000 }, + { 20, 0x0054, 0x00000054, 0x01, 0x00, 0x0000 }, + { 20, 0x0074, 0x00000054, 0x02, 0x00, 0x0000 }, + { 20, 0x0054, 0x00000054, 0x03, 0x00, 0x0000 }, + { 20, 0x0074, 0x04000054, 0x04, 0x00, 0x0000 }, + { 20, 0x0074, 0x04000054, 0x05, 0x00, 0x0000 }, + { 20, 0x0074, 0x04000054, 0x06, 0x00, 0x0000 }, + { 20, 0x0074, 0x04000054, 0x07, 0x00, 0x0000 }, + { 20, 0x0074, 0x08000054, 0x08, 0x00, 0x0000 }, + { 20, 0x0074, 0x08000054, 0x09, 0x00, 0x0000 }, + { 20, 0x0074, 0x08000054, 0x0a, 0x00, 0x0000 }, + { 20, 0x0074, 0x08000054, 0x0b, 0x00, 0x0000 }, + { 20, 0x0074, 0x0c000054, 0x0c, 0x00, 0x0000 }, + { 20, 0x0074, 0x0c000054, 0x0d, 0x00, 0x0000 }, + { 20, 0x0074, 0x0c000054, 0x0e, 0x00, 0x0000 }, + { 20, 0x0074, 0x0c000054, 0x0f, 0x00, 0x0000 }, + { 21, 0x0079, 0x00000059, 0x00, 0x00, 0x0000 }, + { 21, 0x0059, 0x00000059, 0x01, 0x00, 0x0000 }, + { 21, 0x0079, 0x00000059, 0x02, 0x00, 0x0000 }, + { 21, 0x0059, 0x00000059, 0x03, 0x00, 0x0000 }, + { 21, 0x0079, 0x04000059, 0x04, 0x00, 0x0000 }, + { 21, 0x0079, 0x04000059, 0x05, 0x00, 0x0000 }, + { 21, 0x0079, 0x04000059, 0x06, 0x00, 0x0000 }, + { 21, 0x0079, 0x04000059, 0x07, 0x00, 0x0000 }, + { 21, 0x0079, 0x08000059, 0x08, 0x00, 0x0000 }, + { 21, 0x0079, 0x08000059, 0x09, 0x00, 0x0000 }, + { 21, 0x0079, 0x08000059, 0x0a, 0x00, 0x0000 }, + { 21, 0x0079, 0x08000059, 0x0b, 0x00, 0x0000 }, + { 21, 0x0079, 0x0c000059, 0x0c, 0x00, 0x0000 }, + { 21, 0x0079, 0x0c000059, 0x0d, 0x00, 0x0000 }, + { 21, 0x0079, 0x0c000059, 0x0e, 0x00, 0x0000 }, + { 21, 0x0079, 0x0c000059, 0x0f, 0x00, 0x0000 }, + { 22, 0x0075, 0x00000055, 0x00, 0x00, 0x0000 }, + { 22, 0x0055, 0x00000055, 0x01, 0x00, 0x0000 }, + { 22, 0x0075, 0x00000055, 0x02, 0x00, 0x0000 }, + { 22, 0x0055, 0x00000055, 0x03, 0x00, 0x0000 }, + { 22, 0x0075, 0x04000055, 0x04, 0x00, 0x0000 }, + { 22, 0x0075, 0x04000055, 0x05, 0x00, 0x0000 }, + { 22, 0x0075, 0x04000055, 0x06, 0x00, 0x0000 }, + { 22, 0x0075, 0x04000055, 0x07, 0x00, 0x0000 }, + { 22, 0x0075, 0x08000055, 0x08, 0x00, 0x0000 }, + { 22, 0x0075, 0x08000055, 0x09, 0x00, 0x0000 }, + { 22, 0x0075, 0x08000055, 0x0a, 0x00, 0x0000 }, + { 22, 0x0075, 0x08000055, 0x0b, 0x00, 0x0000 }, + { 22, 0x0075, 0x0c000055, 0x0c, 0x00, 0x0000 }, + { 22, 0x0075, 0x0c000055, 0x0d, 0x00, 0x0000 }, + { 22, 0x0075, 0x0c000055, 0x0e, 0x00, 0x0000 }, + { 22, 0x0075, 0x0c000055, 0x0f, 0x00, 0x0000 }, + { 23, 0x0069, 0x00000049, 0x00, 0x00, 0x0000 }, + { 23, 0x0049, 0x00000049, 0x01, 0x00, 0x0000 }, + { 23, 0x0069, 0x00000049, 0x02, 0x00, 0x0000 }, + { 23, 0x0049, 0x00000049, 0x03, 0x00, 0x0000 }, + { 23, 0x0069, 0x04000049, 0x04, 0x00, 0x0000 }, + { 23, 0x0069, 0x04000049, 0x05, 0x00, 0x0000 }, + { 23, 0x0069, 0x04000049, 0x06, 0x00, 0x0000 }, + { 23, 0x0069, 0x04000049, 0x07, 0x00, 0x0000 }, + { 23, 0x0069, 0x08000049, 0x08, 0x00, 0x0000 }, + { 23, 0x0069, 0x08000049, 0x09, 0x00, 0x0000 }, + { 23, 0x0069, 0x08000049, 0x0a, 0x00, 0x0000 }, + { 23, 0x0069, 0x08000049, 0x0b, 0x00, 0x0000 }, + { 23, 0x0069, 0x0c000049, 0x0c, 0x00, 0x0000 }, + { 23, 0x0069, 0x0c000049, 0x0d, 0x00, 0x0000 }, + { 23, 0x0069, 0x0c000049, 0x0e, 0x00, 0x0000 }, + { 23, 0x0069, 0x0c000049, 0x0f, 0x00, 0x0000 }, + { 24, 0x006f, 0x0000004f, 0x00, 0x00, 0x0000 }, + { 24, 0x004f, 0x0000004f, 0x01, 0x00, 0x0000 }, + { 24, 0x006f, 0x0000004f, 0x02, 0x00, 0x0000 }, + { 24, 0x004f, 0x0000004f, 0x03, 0x00, 0x0000 }, + { 24, 0x006f, 0x0400004f, 0x04, 0x00, 0x0000 }, + { 24, 0x006f, 0x0400004f, 0x05, 0x00, 0x0000 }, + { 24, 0x006f, 0x0400004f, 0x06, 0x00, 0x0000 }, + { 24, 0x006f, 0x0400004f, 0x07, 0x00, 0x0000 }, + { 24, 0x006f, 0x0800004f, 0x08, 0x00, 0x0000 }, + { 24, 0x006f, 0x0800004f, 0x09, 0x00, 0x0000 }, + { 24, 0x006f, 0x0800004f, 0x0a, 0x00, 0x0000 }, + { 24, 0x006f, 0x0800004f, 0x0b, 0x00, 0x0000 }, + { 24, 0x006f, 0x0c00004f, 0x0c, 0x00, 0x0000 }, + { 24, 0x006f, 0x0c00004f, 0x0d, 0x00, 0x0000 }, + { 24, 0x006f, 0x0c00004f, 0x0e, 0x00, 0x0000 }, + { 24, 0x006f, 0x0c00004f, 0x0f, 0x00, 0x0000 }, + { 25, 0x0070, 0x00000050, 0x00, 0x00, 0x0000 }, + { 25, 0x0050, 0x00000050, 0x01, 0x00, 0x0000 }, + { 25, 0x0070, 0x00000050, 0x02, 0x00, 0x0000 }, + { 25, 0x0050, 0x00000050, 0x03, 0x00, 0x0000 }, + { 25, 0x0070, 0x04000050, 0x04, 0x00, 0x0000 }, + { 25, 0x0070, 0x04000050, 0x05, 0x00, 0x0000 }, + { 25, 0x0070, 0x04000050, 0x06, 0x00, 0x0000 }, + { 25, 0x0070, 0x04000050, 0x07, 0x00, 0x0000 }, + { 25, 0x0070, 0x08000050, 0x08, 0x00, 0x0000 }, + { 25, 0x0070, 0x08000050, 0x09, 0x00, 0x0000 }, + { 25, 0x0070, 0x08000050, 0x0a, 0x00, 0x0000 }, + { 25, 0x0070, 0x08000050, 0x0b, 0x00, 0x0000 }, + { 25, 0x0070, 0x0c000050, 0x0c, 0x00, 0x0000 }, + { 25, 0x0070, 0x0c000050, 0x0d, 0x00, 0x0000 }, + { 25, 0x0070, 0x0c000050, 0x0e, 0x00, 0x0000 }, + { 25, 0x0070, 0x0c000050, 0x0f, 0x00, 0x0000 }, + { 26, 0x005b, 0x0000005b, 0x00, 0x00, 0x0000 }, + { 26, 0x007b, 0x0000007b, 0x01, 0x00, 0x0000 }, + { 26, 0xffff, 0x01000000, 0x04, 0x00, 0x0000 }, + { 27, 0x005d, 0x0000005d, 0x00, 0x00, 0x0000 }, + { 27, 0x007d, 0x0000007d, 0x01, 0x00, 0x0000 }, + { 27, 0x007e, 0x0000007e, 0x02, 0x00, 0x0000 }, + { 27, 0x005d, 0x0400005d, 0x04, 0x00, 0x0000 }, + { 28, 0xffff, 0x01000004, 0x00, 0x00, 0x0000 }, + { 28, 0x006d, 0x0c00004d, 0x08, 0x00, 0x0000 }, + { 29, 0xffff, 0x01000021, 0x00, 0x04, 0x0004 }, + { 30, 0x0061, 0x00000041, 0x00, 0x00, 0x0000 }, + { 30, 0x0041, 0x00000041, 0x01, 0x00, 0x0000 }, + { 30, 0x0061, 0x00000041, 0x02, 0x00, 0x0000 }, + { 30, 0x0041, 0x00000041, 0x03, 0x00, 0x0000 }, + { 30, 0x0061, 0x04000041, 0x04, 0x00, 0x0000 }, + { 30, 0x0061, 0x04000041, 0x05, 0x00, 0x0000 }, + { 30, 0x0061, 0x04000041, 0x06, 0x00, 0x0000 }, + { 30, 0x0061, 0x04000041, 0x07, 0x00, 0x0000 }, + { 30, 0x0061, 0x08000041, 0x08, 0x00, 0x0000 }, + { 30, 0x0061, 0x08000041, 0x09, 0x00, 0x0000 }, + { 30, 0x0061, 0x08000041, 0x0a, 0x00, 0x0000 }, + { 30, 0x0061, 0x08000041, 0x0b, 0x00, 0x0000 }, + { 30, 0x0061, 0x0c000041, 0x0c, 0x00, 0x0000 }, + { 30, 0x0061, 0x0c000041, 0x0d, 0x00, 0x0000 }, + { 30, 0x0061, 0x0c000041, 0x0e, 0x00, 0x0000 }, + { 30, 0x0061, 0x0c000041, 0x0f, 0x00, 0x0000 }, + { 31, 0x0073, 0x00000053, 0x00, 0x00, 0x0000 }, + { 31, 0x0053, 0x00000053, 0x01, 0x00, 0x0000 }, + { 31, 0x0073, 0x00000053, 0x02, 0x00, 0x0000 }, + { 31, 0x0053, 0x00000053, 0x03, 0x00, 0x0000 }, + { 31, 0x0073, 0x04000053, 0x04, 0x00, 0x0000 }, + { 31, 0x0073, 0x04000053, 0x05, 0x00, 0x0000 }, + { 31, 0x0073, 0x04000053, 0x06, 0x00, 0x0000 }, + { 31, 0x0073, 0x04000053, 0x07, 0x00, 0x0000 }, + { 31, 0x0073, 0x08000053, 0x08, 0x00, 0x0000 }, + { 31, 0x0073, 0x08000053, 0x09, 0x00, 0x0000 }, + { 31, 0x0073, 0x08000053, 0x0a, 0x00, 0x0000 }, + { 31, 0x0073, 0x08000053, 0x0b, 0x00, 0x0000 }, + { 31, 0x0073, 0x0c000053, 0x0c, 0x00, 0x0000 }, + { 31, 0x0073, 0x0c000053, 0x0d, 0x00, 0x0000 }, + { 31, 0x0073, 0x0c000053, 0x0e, 0x00, 0x0000 }, + { 31, 0x0073, 0x0c000053, 0x0f, 0x00, 0x0000 }, + { 32, 0x0064, 0x00000044, 0x00, 0x00, 0x0000 }, + { 32, 0x0044, 0x00000044, 0x01, 0x00, 0x0000 }, + { 32, 0x0064, 0x00000044, 0x02, 0x00, 0x0000 }, + { 32, 0x0044, 0x00000044, 0x03, 0x00, 0x0000 }, + { 32, 0x0064, 0x04000044, 0x04, 0x00, 0x0000 }, + { 32, 0x0064, 0x04000044, 0x05, 0x00, 0x0000 }, + { 32, 0x0064, 0x04000044, 0x06, 0x00, 0x0000 }, + { 32, 0x0064, 0x04000044, 0x07, 0x00, 0x0000 }, + { 32, 0x0064, 0x08000044, 0x08, 0x00, 0x0000 }, + { 32, 0x0064, 0x08000044, 0x09, 0x00, 0x0000 }, + { 32, 0x0064, 0x08000044, 0x0a, 0x00, 0x0000 }, + { 32, 0x0064, 0x08000044, 0x0b, 0x00, 0x0000 }, + { 32, 0x0064, 0x0c000044, 0x0c, 0x00, 0x0000 }, + { 32, 0x0064, 0x0c000044, 0x0d, 0x00, 0x0000 }, + { 32, 0x0064, 0x0c000044, 0x0e, 0x00, 0x0000 }, + { 32, 0x0064, 0x0c000044, 0x0f, 0x00, 0x0000 }, + { 33, 0x0066, 0x00000046, 0x00, 0x00, 0x0000 }, + { 33, 0x0046, 0x00000046, 0x01, 0x00, 0x0000 }, + { 33, 0x0066, 0x00000046, 0x02, 0x00, 0x0000 }, + { 33, 0x0046, 0x00000046, 0x03, 0x00, 0x0000 }, + { 33, 0x0066, 0x04000046, 0x04, 0x00, 0x0000 }, + { 33, 0x0066, 0x04000046, 0x05, 0x00, 0x0000 }, + { 33, 0x0066, 0x04000046, 0x06, 0x00, 0x0000 }, + { 33, 0x0066, 0x04000046, 0x07, 0x00, 0x0000 }, + { 33, 0x0066, 0x08000046, 0x08, 0x00, 0x0000 }, + { 33, 0x0066, 0x08000046, 0x09, 0x00, 0x0000 }, + { 33, 0x0066, 0x08000046, 0x0a, 0x00, 0x0000 }, + { 33, 0x0066, 0x08000046, 0x0b, 0x00, 0x0000 }, + { 33, 0x0066, 0x0c000046, 0x0c, 0x00, 0x0000 }, + { 33, 0x0066, 0x0c000046, 0x0d, 0x00, 0x0000 }, + { 33, 0x0066, 0x0c000046, 0x0e, 0x00, 0x0000 }, + { 33, 0x0066, 0x0c000046, 0x0f, 0x00, 0x0000 }, + { 34, 0x0067, 0x00000047, 0x00, 0x00, 0x0000 }, + { 34, 0x0047, 0x00000047, 0x01, 0x00, 0x0000 }, + { 34, 0x0067, 0x00000047, 0x02, 0x00, 0x0000 }, + { 34, 0x0047, 0x00000047, 0x03, 0x00, 0x0000 }, + { 34, 0x0067, 0x04000047, 0x04, 0x00, 0x0000 }, + { 34, 0x0067, 0x04000047, 0x05, 0x00, 0x0000 }, + { 34, 0x0067, 0x04000047, 0x06, 0x00, 0x0000 }, + { 34, 0x0067, 0x04000047, 0x07, 0x00, 0x0000 }, + { 34, 0x0067, 0x08000047, 0x08, 0x00, 0x0000 }, + { 34, 0x0067, 0x08000047, 0x09, 0x00, 0x0000 }, + { 34, 0x0067, 0x08000047, 0x0a, 0x00, 0x0000 }, + { 34, 0x0067, 0x08000047, 0x0b, 0x00, 0x0000 }, + { 34, 0x0067, 0x0c000047, 0x0c, 0x00, 0x0000 }, + { 34, 0x0067, 0x0c000047, 0x0d, 0x00, 0x0000 }, + { 34, 0x0067, 0x0c000047, 0x0e, 0x00, 0x0000 }, + { 34, 0x0067, 0x0c000047, 0x0f, 0x00, 0x0000 }, + { 35, 0x0068, 0x00000048, 0x00, 0x00, 0x0000 }, + { 35, 0x0048, 0x00000048, 0x01, 0x00, 0x0000 }, + { 35, 0x0068, 0x00000048, 0x02, 0x00, 0x0000 }, + { 35, 0x0048, 0x00000048, 0x03, 0x00, 0x0000 }, + { 35, 0x0068, 0x04000048, 0x04, 0x00, 0x0000 }, + { 35, 0x0068, 0x04000048, 0x05, 0x00, 0x0000 }, + { 35, 0x0068, 0x04000048, 0x06, 0x00, 0x0000 }, + { 35, 0x0068, 0x04000048, 0x07, 0x00, 0x0000 }, + { 35, 0x0068, 0x08000048, 0x08, 0x00, 0x0000 }, + { 35, 0x0068, 0x08000048, 0x09, 0x00, 0x0000 }, + { 35, 0x0068, 0x08000048, 0x0a, 0x00, 0x0000 }, + { 35, 0x0068, 0x08000048, 0x0b, 0x00, 0x0000 }, + { 35, 0x0068, 0x0c000048, 0x0c, 0x00, 0x0000 }, + { 35, 0x0068, 0x0c000048, 0x0d, 0x00, 0x0000 }, + { 35, 0x0068, 0x0c000048, 0x0e, 0x00, 0x0000 }, + { 35, 0x0068, 0x0c000048, 0x0f, 0x00, 0x0000 }, + { 36, 0x006a, 0x0000004a, 0x00, 0x00, 0x0000 }, + { 36, 0x004a, 0x0000004a, 0x01, 0x00, 0x0000 }, + { 36, 0x006a, 0x0000004a, 0x02, 0x00, 0x0000 }, + { 36, 0x004a, 0x0000004a, 0x03, 0x00, 0x0000 }, + { 36, 0x006a, 0x0400004a, 0x04, 0x00, 0x0000 }, + { 36, 0x006a, 0x0400004a, 0x05, 0x00, 0x0000 }, + { 36, 0x006a, 0x0400004a, 0x06, 0x00, 0x0000 }, + { 36, 0x006a, 0x0400004a, 0x07, 0x00, 0x0000 }, + { 36, 0x006a, 0x0800004a, 0x08, 0x00, 0x0000 }, + { 36, 0x006a, 0x0800004a, 0x09, 0x00, 0x0000 }, + { 36, 0x006a, 0x0800004a, 0x0a, 0x00, 0x0000 }, + { 36, 0x006a, 0x0800004a, 0x0b, 0x00, 0x0000 }, + { 36, 0x006a, 0x0c00004a, 0x0c, 0x00, 0x0000 }, + { 36, 0x006a, 0x0c00004a, 0x0d, 0x00, 0x0000 }, + { 36, 0x006a, 0x0c00004a, 0x0e, 0x00, 0x0000 }, + { 36, 0x006a, 0x0c00004a, 0x0f, 0x00, 0x0000 }, + { 37, 0x006b, 0x0000004b, 0x00, 0x00, 0x0000 }, + { 37, 0x004b, 0x0000004b, 0x01, 0x00, 0x0000 }, + { 37, 0x006b, 0x0000004b, 0x02, 0x00, 0x0000 }, + { 37, 0x004b, 0x0000004b, 0x03, 0x00, 0x0000 }, + { 37, 0x006b, 0x0400004b, 0x04, 0x00, 0x0000 }, + { 37, 0x006b, 0x0400004b, 0x05, 0x00, 0x0000 }, + { 37, 0x006b, 0x0400004b, 0x06, 0x00, 0x0000 }, + { 37, 0x006b, 0x0400004b, 0x07, 0x00, 0x0000 }, + { 37, 0x006b, 0x0800004b, 0x08, 0x00, 0x0000 }, + { 37, 0x006b, 0x0800004b, 0x09, 0x00, 0x0000 }, + { 37, 0x006b, 0x0800004b, 0x0a, 0x00, 0x0000 }, + { 37, 0x006b, 0x0800004b, 0x0b, 0x00, 0x0000 }, + { 37, 0x006b, 0x0c00004b, 0x0c, 0x00, 0x0000 }, + { 37, 0x006b, 0x0c00004b, 0x0d, 0x00, 0x0000 }, + { 37, 0x006b, 0x0c00004b, 0x0e, 0x00, 0x0000 }, + { 37, 0x006b, 0x0c00004b, 0x0f, 0x00, 0x0000 }, + { 38, 0x006c, 0x0000004c, 0x00, 0x00, 0x0000 }, + { 38, 0x004c, 0x0000004c, 0x01, 0x00, 0x0000 }, + { 38, 0x006c, 0x0000004c, 0x02, 0x00, 0x0000 }, + { 38, 0x004c, 0x0000004c, 0x03, 0x00, 0x0000 }, + { 38, 0x006c, 0x0400004c, 0x04, 0x00, 0x0000 }, + { 38, 0x006c, 0x0400004c, 0x05, 0x00, 0x0000 }, + { 38, 0x006c, 0x0400004c, 0x06, 0x00, 0x0000 }, + { 38, 0x006c, 0x0400004c, 0x07, 0x00, 0x0000 }, + { 38, 0x006c, 0x0800004c, 0x08, 0x00, 0x0000 }, + { 38, 0x006c, 0x0800004c, 0x09, 0x00, 0x0000 }, + { 38, 0x006c, 0x0800004c, 0x0a, 0x00, 0x0000 }, + { 38, 0x006c, 0x0800004c, 0x0b, 0x00, 0x0000 }, + { 38, 0x006c, 0x0c00004c, 0x0c, 0x00, 0x0000 }, + { 38, 0x006c, 0x0c00004c, 0x0d, 0x00, 0x0000 }, + { 38, 0x006c, 0x0c00004c, 0x0e, 0x00, 0x0000 }, + { 38, 0x006c, 0x0c00004c, 0x0f, 0x00, 0x0000 }, + { 39, 0x003b, 0x0000003b, 0x00, 0x00, 0x0000 }, + { 39, 0x003a, 0x0000003a, 0x01, 0x00, 0x0000 }, + { 40, 0x0027, 0x00000027, 0x00, 0x00, 0x0000 }, + { 40, 0x0022, 0x00000022, 0x01, 0x00, 0x0000 }, + { 40, 0x0027, 0x01001251, 0x02, 0x01, 0x0000 }, + { 40, 0x0022, 0x01001257, 0x03, 0x01, 0x0000 }, + { 40, 0x0067, 0x04000047, 0x04, 0x00, 0x0000 }, + { 41, 0x0060, 0x00000060, 0x00, 0x00, 0x0000 }, + { 41, 0x007e, 0x0000007e, 0x01, 0x00, 0x0000 }, + { 41, 0x0060, 0x01001250, 0x02, 0x01, 0x0000 }, + { 41, 0x007e, 0x01001253, 0x03, 0x01, 0x0000 }, + { 42, 0xffff, 0x01000020, 0x00, 0x04, 0x0001 }, + { 43, 0x005c, 0x0000005c, 0x00, 0x00, 0x0000 }, + { 43, 0x007c, 0x0000007c, 0x01, 0x00, 0x0000 }, + { 43, 0x005c, 0x0400005c, 0x04, 0x00, 0x0000 }, + { 44, 0x007a, 0x0000005a, 0x00, 0x00, 0x0000 }, + { 44, 0x005a, 0x0000005a, 0x01, 0x00, 0x0000 }, + { 44, 0x007a, 0x0000005a, 0x02, 0x00, 0x0000 }, + { 44, 0x005a, 0x0000005a, 0x03, 0x00, 0x0000 }, + { 44, 0x007a, 0x0400005a, 0x04, 0x00, 0x0000 }, + { 44, 0x007a, 0x0400005a, 0x05, 0x00, 0x0000 }, + { 44, 0x007a, 0x0400005a, 0x06, 0x00, 0x0000 }, + { 44, 0x007a, 0x0400005a, 0x07, 0x00, 0x0000 }, + { 44, 0x007a, 0x0800005a, 0x08, 0x00, 0x0000 }, + { 44, 0x007a, 0x0800005a, 0x09, 0x00, 0x0000 }, + { 44, 0x007a, 0x0800005a, 0x0a, 0x00, 0x0000 }, + { 44, 0x007a, 0x0800005a, 0x0b, 0x00, 0x0000 }, + { 44, 0x007a, 0x0c00005a, 0x0c, 0x00, 0x0000 }, + { 44, 0x007a, 0x0c00005a, 0x0d, 0x00, 0x0000 }, + { 44, 0x007a, 0x0c00005a, 0x0e, 0x00, 0x0000 }, + { 44, 0x007a, 0x0c00005a, 0x0f, 0x00, 0x0000 }, + { 45, 0x0078, 0x00000058, 0x00, 0x00, 0x0000 }, + { 45, 0x0058, 0x00000058, 0x01, 0x00, 0x0000 }, + { 45, 0x0078, 0x00000058, 0x02, 0x00, 0x0000 }, + { 45, 0x0058, 0x00000058, 0x03, 0x00, 0x0000 }, + { 45, 0x0078, 0x04000058, 0x04, 0x00, 0x0000 }, + { 45, 0x0078, 0x04000058, 0x05, 0x00, 0x0000 }, + { 45, 0x0078, 0x04000058, 0x06, 0x00, 0x0000 }, + { 45, 0x0078, 0x04000058, 0x07, 0x00, 0x0000 }, + { 45, 0x0078, 0x08000058, 0x08, 0x00, 0x0000 }, + { 45, 0x0078, 0x08000058, 0x09, 0x00, 0x0000 }, + { 45, 0x0078, 0x08000058, 0x0a, 0x00, 0x0000 }, + { 45, 0x0078, 0x08000058, 0x0b, 0x00, 0x0000 }, + { 45, 0x0078, 0x0c000058, 0x0c, 0x00, 0x0000 }, + { 45, 0x0078, 0x0c000058, 0x0d, 0x00, 0x0000 }, + { 45, 0x0078, 0x0c000058, 0x0e, 0x00, 0x0000 }, + { 45, 0x0078, 0x0c000058, 0x0f, 0x00, 0x0000 }, + { 46, 0x0063, 0x00000043, 0x00, 0x00, 0x0000 }, + { 46, 0x0043, 0x00000043, 0x01, 0x00, 0x0000 }, + { 46, 0x0063, 0x00000043, 0x02, 0x00, 0x0000 }, + { 46, 0x0043, 0x00000043, 0x03, 0x00, 0x0000 }, + { 46, 0x0063, 0x04000043, 0x04, 0x00, 0x0000 }, + { 46, 0x0063, 0x04000043, 0x05, 0x00, 0x0000 }, + { 46, 0x0063, 0x04000043, 0x06, 0x00, 0x0000 }, + { 46, 0x0063, 0x04000043, 0x07, 0x00, 0x0000 }, + { 46, 0x0063, 0x08000043, 0x08, 0x00, 0x0000 }, + { 46, 0x0063, 0x08000043, 0x09, 0x00, 0x0000 }, + { 46, 0x0063, 0x08000043, 0x0a, 0x00, 0x0000 }, + { 46, 0x0063, 0x08000043, 0x0b, 0x00, 0x0000 }, + { 46, 0x0063, 0x0c000043, 0x0c, 0x00, 0x0000 }, + { 46, 0x0063, 0x0c000043, 0x0d, 0x00, 0x0000 }, + { 46, 0x0063, 0x0c000043, 0x0e, 0x00, 0x0000 }, + { 46, 0x0063, 0x0c000043, 0x0f, 0x00, 0x0000 }, + { 47, 0x0076, 0x00000056, 0x00, 0x00, 0x0000 }, + { 47, 0x0056, 0x00000056, 0x01, 0x00, 0x0000 }, + { 47, 0x0076, 0x00000056, 0x02, 0x00, 0x0000 }, + { 47, 0x0056, 0x00000056, 0x03, 0x00, 0x0000 }, + { 47, 0x0076, 0x04000056, 0x04, 0x00, 0x0000 }, + { 47, 0x0076, 0x04000056, 0x05, 0x00, 0x0000 }, + { 47, 0x0076, 0x04000056, 0x06, 0x00, 0x0000 }, + { 47, 0x0076, 0x04000056, 0x07, 0x00, 0x0000 }, + { 47, 0x0076, 0x08000056, 0x08, 0x00, 0x0000 }, + { 47, 0x0076, 0x08000056, 0x09, 0x00, 0x0000 }, + { 47, 0x0076, 0x08000056, 0x0a, 0x00, 0x0000 }, + { 47, 0x0076, 0x08000056, 0x0b, 0x00, 0x0000 }, + { 47, 0x0076, 0x0c000056, 0x0c, 0x00, 0x0000 }, + { 47, 0x0076, 0x0c000056, 0x0d, 0x00, 0x0000 }, + { 47, 0x0076, 0x0c000056, 0x0e, 0x00, 0x0000 }, + { 47, 0x0076, 0x0c000056, 0x0f, 0x00, 0x0000 }, + { 48, 0x0062, 0x00000042, 0x00, 0x00, 0x0000 }, + { 48, 0x0042, 0x00000042, 0x01, 0x00, 0x0000 }, + { 48, 0x0062, 0x00000042, 0x02, 0x00, 0x0000 }, + { 48, 0x0042, 0x00000042, 0x03, 0x00, 0x0000 }, + { 48, 0x0062, 0x04000042, 0x04, 0x00, 0x0000 }, + { 48, 0x0062, 0x04000042, 0x05, 0x00, 0x0000 }, + { 48, 0x0062, 0x04000042, 0x06, 0x00, 0x0000 }, + { 48, 0x0062, 0x04000042, 0x07, 0x00, 0x0000 }, + { 48, 0x0062, 0x08000042, 0x08, 0x00, 0x0000 }, + { 48, 0x0062, 0x08000042, 0x09, 0x00, 0x0000 }, + { 48, 0x0062, 0x08000042, 0x0a, 0x00, 0x0000 }, + { 48, 0x0062, 0x08000042, 0x0b, 0x00, 0x0000 }, + { 48, 0x0062, 0x0c000042, 0x0c, 0x00, 0x0000 }, + { 48, 0x0062, 0x0c000042, 0x0d, 0x00, 0x0000 }, + { 48, 0x0062, 0x0c000042, 0x0e, 0x00, 0x0000 }, + { 48, 0x0062, 0x0c000042, 0x0f, 0x00, 0x0000 }, + { 49, 0x006e, 0x0000004e, 0x00, 0x00, 0x0000 }, + { 49, 0x004e, 0x0000004e, 0x01, 0x00, 0x0000 }, + { 49, 0x006e, 0x0000004e, 0x02, 0x00, 0x0000 }, + { 49, 0x004e, 0x0000004e, 0x03, 0x00, 0x0000 }, + { 49, 0x006e, 0x0400004e, 0x04, 0x00, 0x0000 }, + { 49, 0x006e, 0x0400004e, 0x05, 0x00, 0x0000 }, + { 49, 0x006e, 0x0400004e, 0x06, 0x00, 0x0000 }, + { 49, 0x006e, 0x0400004e, 0x07, 0x00, 0x0000 }, + { 49, 0x006e, 0x0800004e, 0x08, 0x00, 0x0000 }, + { 49, 0x006e, 0x0800004e, 0x09, 0x00, 0x0000 }, + { 49, 0x006e, 0x0800004e, 0x0a, 0x00, 0x0000 }, + { 49, 0x006e, 0x0800004e, 0x0b, 0x00, 0x0000 }, + { 49, 0x006e, 0x0c00004e, 0x0c, 0x00, 0x0000 }, + { 49, 0x006e, 0x0c00004e, 0x0d, 0x00, 0x0000 }, + { 49, 0x006e, 0x0c00004e, 0x0e, 0x00, 0x0000 }, + { 49, 0x006e, 0x0c00004e, 0x0f, 0x00, 0x0000 }, + { 50, 0x006d, 0x0000004d, 0x00, 0x00, 0x0000 }, + { 50, 0x004d, 0x0000004d, 0x01, 0x00, 0x0000 }, + { 50, 0x006d, 0x0000004d, 0x02, 0x00, 0x0000 }, + { 50, 0x004d, 0x0000004d, 0x03, 0x00, 0x0000 }, + { 50, 0x006d, 0x0400004d, 0x04, 0x00, 0x0000 }, + { 50, 0x006d, 0x0400004d, 0x05, 0x00, 0x0000 }, + { 50, 0x006d, 0x0400004d, 0x06, 0x00, 0x0000 }, + { 50, 0x006d, 0x0400004d, 0x07, 0x00, 0x0000 }, + { 50, 0x006d, 0x0800004d, 0x08, 0x00, 0x0000 }, + { 50, 0x006d, 0x0800004d, 0x09, 0x00, 0x0000 }, + { 50, 0x006d, 0x0800004d, 0x0a, 0x00, 0x0000 }, + { 50, 0x006d, 0x0800004d, 0x0b, 0x00, 0x0000 }, + { 50, 0x006d, 0x0c00004d, 0x0c, 0x00, 0x0000 }, + { 50, 0x006d, 0x0c00004d, 0x0d, 0x00, 0x0000 }, + { 50, 0x006d, 0x0c00004d, 0x0e, 0x00, 0x0000 }, + { 50, 0x006d, 0x0c00004d, 0x0f, 0x00, 0x0000 }, + { 51, 0x002c, 0x0000002c, 0x00, 0x00, 0x0000 }, + { 51, 0x003c, 0x0000003c, 0x01, 0x00, 0x0000 }, + { 51, 0x002c, 0x0100125b, 0x02, 0x01, 0x0000 }, + { 52, 0x002e, 0x0000002e, 0x00, 0x00, 0x0000 }, + { 52, 0x003e, 0x0000003e, 0x01, 0x00, 0x0000 }, + { 52, 0xffff, 0x01001120, 0x02, 0x00, 0x0000 }, + { 53, 0x002f, 0x0000002f, 0x00, 0x00, 0x0000 }, + { 53, 0x003f, 0x0000003f, 0x01, 0x00, 0x0000 }, + { 53, 0xffff, 0x01000003, 0x04, 0x00, 0x0000 }, + { 54, 0xffff, 0x01000020, 0x00, 0x04, 0x0001 }, + { 55, 0x002a, 0x2000002a, 0x00, 0x00, 0x0000 }, + { 56, 0xffff, 0x01000023, 0x00, 0x04, 0x0008 }, + { 57, 0x0020, 0x00000020, 0x00, 0x00, 0x0000 }, + { 58, 0xffff, 0x01000024, 0x00, 0x00, 0x0000 }, + { 59, 0xffff, 0x01000030, 0x00, 0x00, 0x0000 }, + { 59, 0xffff, 0x0100003c, 0x01, 0x00, 0x0000 }, + { 59, 0xffff, 0x01000048, 0x04, 0x00, 0x0000 }, + { 59, 0xffff, 0x01000000, 0x0c, 0x08, 0x0100 }, + { 60, 0xffff, 0x01000031, 0x00, 0x00, 0x0000 }, + { 60, 0xffff, 0x0100003d, 0x01, 0x00, 0x0000 }, + { 60, 0xffff, 0x01000049, 0x04, 0x00, 0x0000 }, + { 60, 0xffff, 0x01000000, 0x0c, 0x08, 0x0101 }, + { 61, 0xffff, 0x01000032, 0x00, 0x00, 0x0000 }, + { 61, 0xffff, 0x0100003e, 0x01, 0x00, 0x0000 }, + { 61, 0xffff, 0x0100004a, 0x04, 0x00, 0x0000 }, + { 61, 0xffff, 0x01000000, 0x0c, 0x08, 0x0102 }, + { 62, 0xffff, 0x01000033, 0x00, 0x00, 0x0000 }, + { 62, 0xffff, 0x0100003f, 0x01, 0x00, 0x0000 }, + { 62, 0xffff, 0x0100004b, 0x04, 0x00, 0x0000 }, + { 62, 0xffff, 0x01000000, 0x0c, 0x08, 0x0103 }, + { 63, 0xffff, 0x01000034, 0x00, 0x00, 0x0000 }, + { 63, 0xffff, 0x01000040, 0x01, 0x00, 0x0000 }, + { 63, 0xffff, 0x0100004c, 0x04, 0x00, 0x0000 }, + { 63, 0xffff, 0x01000000, 0x0c, 0x08, 0x0104 }, + { 64, 0xffff, 0x01000035, 0x00, 0x00, 0x0000 }, + { 64, 0xffff, 0x01000041, 0x01, 0x00, 0x0000 }, + { 64, 0xffff, 0x0100004d, 0x04, 0x00, 0x0000 }, + { 64, 0xffff, 0x01000000, 0x0c, 0x08, 0x0105 }, + { 65, 0xffff, 0x01000036, 0x00, 0x00, 0x0000 }, + { 65, 0xffff, 0x01000042, 0x01, 0x00, 0x0000 }, + { 65, 0xffff, 0x0100004e, 0x04, 0x00, 0x0000 }, + { 65, 0xffff, 0x01000000, 0x0c, 0x08, 0x0106 }, + { 66, 0xffff, 0x01000037, 0x00, 0x00, 0x0000 }, + { 66, 0xffff, 0x01000043, 0x01, 0x00, 0x0000 }, + { 66, 0xffff, 0x0100004f, 0x04, 0x00, 0x0000 }, + { 66, 0xffff, 0x01000000, 0x0c, 0x08, 0x0107 }, + { 67, 0xffff, 0x01000038, 0x00, 0x00, 0x0000 }, + { 67, 0xffff, 0x01000044, 0x01, 0x00, 0x0000 }, + { 67, 0xffff, 0x01000050, 0x04, 0x00, 0x0000 }, + { 67, 0xffff, 0x01000000, 0x0c, 0x08, 0x0108 }, + { 68, 0xffff, 0x01000039, 0x00, 0x00, 0x0000 }, + { 68, 0xffff, 0x01000045, 0x01, 0x00, 0x0000 }, + { 68, 0xffff, 0x01000051, 0x04, 0x00, 0x0000 }, + { 68, 0xffff, 0x01000000, 0x0c, 0x08, 0x0109 }, + { 69, 0xffff, 0x01000025, 0x00, 0x00, 0x0000 }, + { 70, 0xffff, 0x01000026, 0x00, 0x00, 0x0000 }, + { 70, 0xffff, 0x01000026, 0x08, 0x00, 0x0000 }, + { 71, 0x0037, 0x20000037, 0x00, 0x00, 0x0000 }, + { 72, 0x0038, 0x20000038, 0x00, 0x00, 0x0000 }, + { 73, 0x0039, 0x20000039, 0x00, 0x00, 0x0000 }, + { 74, 0x002d, 0x2000002d, 0x00, 0x00, 0x0000 }, + { 75, 0x0034, 0x20000034, 0x00, 0x00, 0x0000 }, + { 76, 0x0035, 0x20000035, 0x00, 0x00, 0x0000 }, + { 77, 0x0036, 0x20000036, 0x00, 0x00, 0x0000 }, + { 78, 0x002b, 0x2000002b, 0x00, 0x00, 0x0000 }, + { 79, 0x0031, 0x20000031, 0x00, 0x00, 0x0000 }, + { 80, 0x0032, 0x20000032, 0x00, 0x00, 0x0000 }, + { 81, 0x0033, 0x20000033, 0x00, 0x00, 0x0000 }, + { 82, 0x0030, 0x20000030, 0x00, 0x00, 0x0000 }, + { 83, 0x002e, 0x2000002e, 0x00, 0x00, 0x0000 }, + { 83, 0xffff, 0x01000000, 0x06, 0x08, 0x0200 }, + { 83, 0xffff, 0x01000000, 0x0c, 0x08, 0x0200 }, + { 86, 0x003c, 0x0000003c, 0x00, 0x00, 0x0000 }, + { 86, 0x003e, 0x0000003e, 0x01, 0x00, 0x0000 }, + { 86, 0x007c, 0x0000007c, 0x02, 0x00, 0x0000 }, + { 87, 0xffff, 0x0100003a, 0x00, 0x00, 0x0000 }, + { 87, 0xffff, 0x01000046, 0x01, 0x00, 0x0000 }, + { 87, 0xffff, 0x01000052, 0x04, 0x00, 0x0000 }, + { 87, 0xffff, 0x01000000, 0x0c, 0x08, 0x010a }, + { 88, 0xffff, 0x0100003b, 0x00, 0x00, 0x0000 }, + { 88, 0xffff, 0x01000047, 0x01, 0x00, 0x0000 }, + { 88, 0xffff, 0x01000000, 0x0c, 0x08, 0x010b }, + { 96, 0xffff, 0x21000005, 0x00, 0x00, 0x0000 }, + { 97, 0xffff, 0x01000021, 0x00, 0x04, 0x0004 }, + { 98, 0x002f, 0x2000002f, 0x00, 0x00, 0x0000 }, + { 99, 0x005c, 0x0400005c, 0x00, 0x00, 0x0000 }, + { 100, 0xffff, 0x01001103, 0x00, 0x04, 0x0002 }, + { 102, 0xffff, 0x01000010, 0x00, 0x00, 0x0000 }, + { 103, 0xffff, 0x01000013, 0x00, 0x00, 0x0000 }, + { 104, 0xffff, 0x01000016, 0x00, 0x00, 0x0000 }, + { 105, 0xffff, 0x01000012, 0x00, 0x00, 0x0000 }, + { 105, 0xffff, 0x01000000, 0x0c, 0x08, 0x0180 }, + { 106, 0xffff, 0x01000014, 0x00, 0x00, 0x0000 }, + { 106, 0xffff, 0x01000000, 0x0c, 0x08, 0x0181 }, + { 107, 0xffff, 0x01000011, 0x00, 0x00, 0x0000 }, + { 108, 0xffff, 0x01000015, 0x00, 0x00, 0x0000 }, + { 109, 0xffff, 0x01000017, 0x00, 0x00, 0x0000 }, + { 110, 0xffff, 0x01000006, 0x00, 0x00, 0x0000 }, + { 111, 0xffff, 0x01000007, 0x00, 0x00, 0x0000 }, + { 111, 0xffff, 0x01000000, 0x06, 0x08, 0x0200 }, + { 111, 0xffff, 0x01000000, 0x0c, 0x08, 0x0200 }, + { 119, 0xffff, 0x01000008, 0x00, 0x00, 0x0000 }, +}; + +const QWSKeyboard::Composing QWSKbPrivate::s_keycompose_default[] = { + { 0x0060, 0x0041, 0x00c0 }, + { 0x0060, 0x0061, 0x00e0 }, + { 0x0027, 0x0041, 0x00c1 }, + { 0x0027, 0x0061, 0x00e1 }, + { 0x005e, 0x0041, 0x00c2 }, + { 0x005e, 0x0061, 0x00e2 }, + { 0x007e, 0x0041, 0x00c3 }, + { 0x007e, 0x0061, 0x00e3 }, + { 0x0022, 0x0041, 0x00c4 }, + { 0x0022, 0x0061, 0x00e4 }, + { 0x002d, 0x0061, 0x00aa }, + { 0x002d, 0x0041, 0x00aa }, + { 0x004f, 0x0041, 0x00c5 }, + { 0x006f, 0x0061, 0x00e5 }, + { 0x0030, 0x0041, 0x00c5 }, + { 0x0030, 0x0061, 0x00e5 }, + { 0x0041, 0x0041, 0x00c5 }, + { 0x0061, 0x0061, 0x00e5 }, + { 0x00b0, 0x0041, 0x00c5 }, + { 0x00b0, 0x0061, 0x00e5 }, + { 0x0041, 0x0045, 0x00c6 }, + { 0x0061, 0x0065, 0x00e6 }, + { 0x002c, 0x0043, 0x00c7 }, + { 0x002c, 0x0063, 0x00e7 }, + { 0x005e, 0x0043, 0x00c7 }, + { 0x005e, 0x0063, 0x00e7 }, + { 0x0060, 0x0045, 0x00c8 }, + { 0x0060, 0x0065, 0x00e8 }, + { 0x0027, 0x0045, 0x00c9 }, + { 0x0027, 0x0065, 0x00e9 }, + { 0x005e, 0x0045, 0x00ca }, + { 0x005e, 0x0065, 0x00ea }, + { 0x0022, 0x0045, 0x00cb }, + { 0x0022, 0x0065, 0x00eb }, + { 0x0060, 0x0049, 0x00cc }, + { 0x0060, 0x0069, 0x00ec }, + { 0x0027, 0x0049, 0x00cd }, + { 0x0027, 0x0069, 0x00ed }, + { 0x005e, 0x0049, 0x00ce }, + { 0x005e, 0x0069, 0x00ee }, + { 0x0022, 0x0049, 0x00cf }, + { 0x0022, 0x0069, 0x00ef }, + { 0x002d, 0x0044, 0x00d0 }, + { 0x002d, 0x0064, 0x00f0 }, + { 0x005e, 0x0044, 0x00d0 }, + { 0x005e, 0x0064, 0x00f0 }, + { 0x007e, 0x004e, 0x00d1 }, + { 0x007e, 0x006e, 0x00f1 }, + { 0x005e, 0x004e, 0x00d1 }, + { 0x005e, 0x006e, 0x00f1 }, + { 0x0060, 0x004f, 0x00d2 }, + { 0x0060, 0x006f, 0x00f2 }, + { 0x0027, 0x004f, 0x00d3 }, + { 0x0027, 0x006f, 0x00f3 }, + { 0x005e, 0x004f, 0x00d4 }, + { 0x005e, 0x006f, 0x00f4 }, + { 0x007e, 0x004f, 0x00d5 }, + { 0x007e, 0x006f, 0x00f5 }, + { 0x0022, 0x004f, 0x00d6 }, + { 0x0022, 0x006f, 0x00f6 }, + { 0x002f, 0x004f, 0x00d8 }, + { 0x002f, 0x006f, 0x00f8 }, + { 0x002d, 0x006f, 0x00ba }, + { 0x002d, 0x004f, 0x00ba }, + { 0x0060, 0x0055, 0x00d9 }, + { 0x0060, 0x0075, 0x00f9 }, + { 0x0027, 0x0055, 0x00da }, + { 0x0027, 0x0075, 0x00fa }, + { 0x005e, 0x0055, 0x00db }, + { 0x005e, 0x0075, 0x00fb }, + { 0x0022, 0x0055, 0x00dc }, + { 0x0022, 0x0075, 0x00fc }, + { 0x0027, 0x0059, 0x00dd }, + { 0x0027, 0x0079, 0x00fd }, + { 0x0054, 0x0048, 0x00de }, + { 0x0074, 0x0068, 0x00fe }, + { 0x0073, 0x0073, 0x00df }, + { 0x0022, 0x0079, 0x00ff }, + { 0x0073, 0x007a, 0x00df }, + { 0x006e, 0x006e, 0x00f1 }, + { 0x006e, 0x0068, 0x00f1 }, + { 0x004e, 0x0059, 0x00d1 }, + { 0x004e, 0x004e, 0x00d1 }, + { 0x004e, 0x0048, 0x00d1 }, + { 0x004e, 0x0079, 0x00d1 }, + { 0x004e, 0x006e, 0x00d1 }, + { 0x004e, 0x0068, 0x00d1 }, + { 0x002d, 0x004c, 0x00a3 }, + { 0x003c, 0x003c, 0x00ab }, + { 0x003e, 0x003e, 0x00bb }, + { 0x003f, 0x003f, 0x00bf }, + { 0x005e, 0x003f, 0x00bf }, + { 0x0021, 0x0021, 0x00a1 }, + { 0x005e, 0x0021, 0x00a1 }, + { 0x005e, 0x0031, 0x00b9 }, + { 0x005e, 0x0032, 0x00b2 }, + { 0x005e, 0x0033, 0x00b3 }, + { 0x002b, 0x002d, 0x00b1 }, + { 0x0063, 0x003d, 0x00a2 }, + { 0x0063, 0x002f, 0x00a2 }, + { 0x002f, 0x0063, 0x00a2 }, + { 0x002d, 0x0063, 0x00a2 }, + { 0x002d, 0x0043, 0x00a2 }, + { 0x004c, 0x003d, 0x00a3 }, + { 0x002d, 0x004c, 0x00a3 }, + { 0x002d, 0x006c, 0x00a3 }, + { 0x005e, 0x002a, 0x00d7 }, + { 0x005e, 0x0078, 0x00d7 }, + { 0x0078, 0x0078, 0x00d7 }, + { 0x005e, 0x002e, 0x00b7 }, + { 0x002e, 0x002e, 0x00b7 }, + { 0x005e, 0x002f, 0x00f7 }, + { 0x005e, 0x003a, 0x00f7 }, + { 0x002d, 0x003a, 0x00f7 }, + { 0x003a, 0x002d, 0x00f7 }, + { 0x0059, 0x003d, 0x00a5 }, + { 0x002d, 0x0059, 0x00a5 }, + { 0x002d, 0x006c, 0x00a5 }, + { 0x0028, 0x0063, 0x00a9 }, + { 0x0022, 0x0063, 0x00a9 }, + { 0x002d, 0x0061, 0x00aa }, + { 0x002d, 0x0041, 0x00aa }, + { 0x002d, 0x006f, 0x00ba }, + { 0x002d, 0x004f, 0x00ba }, + { 0x0028, 0x0072, 0x00ae }, + { 0x0022, 0x0072, 0x00ae }, + { 0x006d, 0x0075, 0x00b5 }, + { 0x0031, 0x0034, 0x0152 }, + { 0x0031, 0x0032, 0x0153 }, + { 0x0033, 0x0034, 0x0178 }, + { 0x0065, 0x003d, 0x20ac }, + { 0x002d, 0x0065, 0x20ac }, + { 0x002d, 0x0045, 0x20ac }, + { 0x0076, 0x0053, 0x0160 }, + { 0x005e, 0x0053, 0x0160 }, + { 0x0076, 0x0073, 0x0161 }, + { 0x005e, 0x0073, 0x0161 }, + { 0x0076, 0x005a, 0x017d }, + { 0x005e, 0x005a, 0x017d }, + { 0x0076, 0x007a, 0x017e }, + { 0x005e, 0x007a, 0x017e }, + { 0x004f, 0x0045, 0x0152 }, + { 0x004f, 0x0065, 0x0152 }, + { 0x006f, 0x0065, 0x0153 }, + { 0x0022, 0x0059, 0x0178 }, + { 0x0069, 0x006a, 0x00ff }, + { 0x0049, 0x004a, 0x0178 }, +}; + +#endif diff --git a/src/gui/embedded/qkbd_qws.cpp b/src/gui/embedded/qkbd_qws.cpp index 8cf87db..dc92a01 100644 --- a/src/gui/embedded/qkbd_qws.cpp +++ b/src/gui/embedded/qkbd_qws.cpp @@ -40,56 +40,217 @@ ****************************************************************************/ #include "qkbd_qws.h" +#include "qkbd_qws_p.h" #ifndef QT_NO_QWS_KEYBOARD +#include <QFile> +#include <QDataStream> +#include <QStringList> + #include "qwindowsystem_qws.h" #include "qscreen_qws.h" #include "qtimer.h" #include <stdlib.h> +//#define QT_DEBUG_KEYMAP + + QT_BEGIN_NAMESPACE class QWSKbPrivate : public QObject { Q_OBJECT public: - QWSKbPrivate(QWSKeyboardHandler *h) { - handler = h; - arTimer = new QTimer(this); - arTimer->setSingleShot(true); - connect(arTimer, SIGNAL(timeout()), SLOT(autoRepeat())); - repeatdelay = 400; - repeatperiod = 80; + QWSKbPrivate(QWSKeyboardHandler *h, const QString &device) + : m_handler(h), m_modifiers(0), m_composing(0), m_dead_unicode(0xffff), + m_no_zap(false), m_do_compose(false), + m_keymap(0), m_keymap_size(0), m_keycompose(0), m_keycompose_size(0) + { + m_ar_timer = new QTimer(this); + m_ar_timer->setSingleShot(true); + connect(m_ar_timer, SIGNAL(timeout()), SLOT(autoRepeat())); + m_ar_delay = 400; + m_ar_period = 80; + + memset(m_locks, 0, sizeof(m_locks)); + + QString keymap; + QStringList args = device.split(QLatin1Char(':')); + foreach (const QString &arg, args) { + if (arg.startsWith(QLatin1String("keymap="))) + keymap = arg.mid(7); + else if (arg == QLatin1String("disable-zap")) + m_no_zap = true; + else if (arg == QLatin1String("enable-compose")) + m_do_compose = true; + else if (arg.startsWith(QLatin1String("repeat-delay="))) + m_ar_delay = arg.mid(13).toInt(); + else if (arg.startsWith(QLatin1String("repeat-rate="))) + m_ar_period = arg.mid(12).toInt(); + } + + if (keymap.isEmpty() || !loadKeymap(keymap)) + unloadKeymap(); } - void beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod) { - unicode = uni; - keycode = code; - modifier = mod; - arTimer->start(repeatdelay); + ~QWSKbPrivate() + { + unloadKeymap(); } - void endAutoRepeat() { - arTimer->stop(); + + void beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod) + { + m_ar_unicode = uni; + m_ar_keycode = code; + m_ar_modifier = mod; + m_ar_timer->start(m_ar_delay); } + void endAutoRepeat() + { + m_ar_timer->stop(); + } + + static Qt::KeyboardModifiers toQtModifiers(quint8 mod) + { + Qt::KeyboardModifiers qtmod = Qt::NoModifier; + + if (mod & (QWSKeyboard::ModShift | QWSKeyboard::ModShiftL | QWSKeyboard::ModShiftR)) + qtmod |= Qt::ShiftModifier; + if (mod & (QWSKeyboard::ModControl | QWSKeyboard::ModCtrlL | QWSKeyboard::ModCtrlR)) + qtmod |= Qt::ControlModifier; + if (mod & QWSKeyboard::ModAlt) + qtmod |= Qt::AltModifier; + + return qtmod; + } + + void unloadKeymap(); + bool loadKeymap(const QString &file); + private slots: - void autoRepeat() { - handler->processKeyEvent(unicode, keycode, modifier, false, true); - handler->processKeyEvent(unicode, keycode, modifier, true, true); - arTimer->start(repeatperiod); + void autoRepeat() + { + m_handler->processKeyEvent(m_ar_unicode, m_ar_keycode, m_ar_modifier, false, true); + m_handler->processKeyEvent(m_ar_unicode, m_ar_keycode, m_ar_modifier, true, true); + m_ar_timer->start(m_ar_period); } private: - QWSKeyboardHandler *handler; - int unicode; - int keycode; - Qt::KeyboardModifiers modifier; - int repeatdelay; - int repeatperiod; - QTimer *arTimer; + QWSKeyboardHandler *m_handler; + + // auto repeat simulation + int m_ar_unicode; + int m_ar_keycode; + Qt::KeyboardModifiers m_ar_modifier; + int m_ar_delay; + int m_ar_period; + QTimer *m_ar_timer; + + // keymap handling + quint8 m_modifiers; + quint8 m_locks[3]; + int m_composing; + quint16 m_dead_unicode; + + bool m_no_zap; + bool m_do_compose; + + const QWSKeyboard::Mapping *m_keymap; + int m_keymap_size; + const QWSKeyboard::Composing *m_keycompose; + int m_keycompose_size; + + static const QWSKeyboard::Mapping s_keymap_default[]; + static const QWSKeyboard::Composing s_keycompose_default[]; + + friend class QWSKeyboardHandler; }; +// simple builtin US keymap +#include "qkbd_defaultmap_qws_p.h" + +// the unloadKeymap() function needs to be AFTER the defaultmap include, +// since the sizeof(s_keymap_default) wouldn't work otherwise. + +void QWSKbPrivate::unloadKeymap() +{ + if (m_keymap && m_keymap != s_keymap_default) + delete [] m_keymap; + if (m_keycompose && m_keycompose != s_keycompose_default) + delete [] m_keycompose; + + m_keymap = s_keymap_default; + m_keymap_size = sizeof(s_keymap_default) / sizeof(s_keymap_default[0]); + m_keycompose = s_keycompose_default; + m_keycompose_size = sizeof(s_keycompose_default) / sizeof(s_keycompose_default[0]); + + // reset state, so we could switch keymaps at runtime + m_modifiers = 0; + memset(m_locks, 0, sizeof(m_locks)); + m_composing = 0; + m_dead_unicode = 0xffff; +} + +bool QWSKbPrivate::loadKeymap(const QString &file) +{ + QFile f(file); + + if (!f.open(QIODevice::ReadOnly)) { + qWarning("Could not open keymap file '%s'", qPrintable(file)); + return false; + } + + // .qmap files have a very simple structure: + // quint32 magic (QWSKeyboard::FileMagic) + // quint32 version (1) + // quint32 keymap_size (# of struct QWSKeyboard::Mappings) + // quint32 keycompose_size (# of struct QWSKeyboard::Composings) + // all QWSKeyboard::Mappings via QDataStream::operator(<<|>>) + // all QWSKeyboard::Composings via QDataStream::operator(<<|>>) + + quint32 qmap_magic, qmap_version, qmap_keymap_size, qmap_keycompose_size; + + QDataStream ds(&f); + + ds >> qmap_magic >> qmap_version >> qmap_keymap_size >> qmap_keycompose_size; + + if (ds.status() != QDataStream::Ok || qmap_magic != QWSKeyboard::FileMagic || qmap_version != 1 || qmap_keymap_size == 0) { + qWarning("'%s' is ot a valid.qmap keymap file.", qPrintable(file)); + return false; + } + + QWSKeyboard::Mapping *qmap_keymap = new QWSKeyboard::Mapping[qmap_keymap_size]; + QWSKeyboard::Composing *qmap_keycompose = qmap_keycompose_size ? new QWSKeyboard::Composing[qmap_keycompose_size] : 0; + + for (quint32 i = 0; i < qmap_keymap_size; ++i) + ds >> qmap_keymap[i]; + for (quint32 i = 0; i < qmap_keycompose_size; ++i) + ds >> qmap_keycompose[i]; + + if (ds.status() != QDataStream::Ok) { + delete [] qmap_keymap; + delete [] qmap_keycompose; + + qWarning("Keymap file '%s' can not be loaded.", qPrintable(file)); + return false; + } + + // unload currently active and clear state + unloadKeymap(); + + m_keymap = qmap_keymap; + m_keymap_size = qmap_keymap_size; + m_keycompose = qmap_keycompose; + m_keycompose_size = qmap_keycompose_size; + + m_do_compose = true; + + return true; +} + + /*! \class QWSKeyboardHandler \ingroup qws @@ -132,18 +293,29 @@ private: /*! - Constructs a keyboard driver. + Constructs a keyboard driver. The \a device argument is passed by the + QWS_KEYBOARD environment variable. Call the QWSServer::setKeyboardHandler() function to make the newly created keyboard driver, the primary driver. Note that the primary driver is controlled by the system, i.e., the system will delete it upon exit. */ +QWSKeyboardHandler::QWSKeyboardHandler(const QString &device) +{ + d = new QWSKbPrivate(this, device); +} + +/*! + \overload +*/ QWSKeyboardHandler::QWSKeyboardHandler() { - d = new QWSKbPrivate(this); + d = new QWSKbPrivate(this, QString()); } + + /*! Destroys this keyboard driver. @@ -172,7 +344,10 @@ QWSKeyboardHandler::~QWSKeyboardHandler() event is caused by an auto-repeat mechanism and not an actual key press. - \sa beginAutoRepeat(), endAutoRepeat(), transformDirKey() + Note that this function does not handle key mapping. Please use + processKeycode() if you need that functionality. + + \sa processKeycode(), beginAutoRepeat(), endAutoRepeat(), transformDirKey() */ void QWSKeyboardHandler::processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, bool isPress, bool autoRepeat) @@ -241,6 +416,256 @@ void QWSKeyboardHandler::endAutoRepeat() d->endAutoRepeat(); } +/*! + \enum QWSKeyboardHandler::KeycodeAction + + This enum describes the various special actions that actual + QWSKeyboardHandler implementations have to take care of. + + \value None No further action required. + + \value CapsLockOn Set the state of the Caps lock LED to on. + \value CapsLockOff Set the state of the Caps lock LED to off. + \value NumLockOn Set the state of the Num lock LED to on. + \value NumLockOff Set the state of the Num lock LED to off. + \value ScrollLockOn Set the state of the Scroll lock LED to on. + \value ScrollLockOff Set the state of the Scroll lock LED to off. + + \value PreviousConsole Switch to the previous virtual console (by + default Ctrl+Alt+Left on Linux). + \value NextConsole Switch to the next virtual console (by default + Ctrl+Alt+Right on Linux). + \value SwitchConsoleFirst Switch to the first virtual console (0). + \value SwitchConsoleLast Switch to the last virtual console (255). + \value SwitchConsoleMask If the KeyAction value is between SwitchConsoleFirst + and SwitchConsoleLast, you can use this mask to get + the specific virtual console number to switch to. + + \value Reboot Reboot the machine - this is ignored in both the TTY and + LinuxInput handlers though (by default Ctrl+Alt+Del on Linux). + + \sa processKeycode() +*/ + +/*! + \fn QWSKeyboardHandler::KeycodeAction QWSKeyboardHandler::processKeycode(quint16 keycode, bool isPress, bool autoRepeat) + + Maps \a keycode according to a keymap and sends that key event to the + \l{Qt for Embedded Linux} server application. + + Please see the QWS_KEYBOARD documentation for a description on how to + create and use keymap files. + + The key event is identified by its \a keycode value and the \a isPress + and \a autoRepeat parameters. + + The \a keycode parameter is \bold NOT the Qt keycode value as defined by + the Qt::Key enum. This functions expects a standard Linux 16 bit kernel + keycode as it is used in the Linux Input Event sub-system. This + \a keycode is transformed to a Qt::Key code by using either a + compiled-in US keyboard layout or by dynamically loading a keymap at + startup which can be specified via the QWS_KEYBOARD environment + variable. + + The \a isPress parameter is true if the event is a key press event and + \a autoRepeat is true if the event is caused by an auto-repeat mechanism + and not an actual key press. + + The return value indicates if the actual QWSKeyboardHandler + implementation needs to take care of a special action, like console + switching or LED handling. + + Standard Linux console keymaps can be found at the + \l {http://lct.sourceforege.net}{LCT project} + + If standard Linux console keymaps are used, \a keycode must be one of the + standardized values defined in \c /usr/include/linux/input.h + + \sa processKeyEvent(), KeycodeAction +*/ + +QWSKeyboardHandler::KeycodeAction QWSKeyboardHandler::processKeycode(quint16 keycode, bool pressed, bool autorepeat) +{ + KeycodeAction result = None; + bool first_press = pressed && !autorepeat; + + const QWSKeyboard::Mapping *map_plain = 0; + const QWSKeyboard::Mapping *map_withmod = 0; + + // get a specific and plain mapping for the keycode and the current modifiers + for (int i = 0; i < d->m_keymap_size && !(map_plain && map_withmod); ++i) { + const QWSKeyboard::Mapping *m = d->m_keymap + i; + if (m->keycode == keycode) { + if (m->modifiers == 0) + map_plain = m; + + quint8 testmods = d->m_modifiers; + if (d->m_locks[0] /*CapsLock*/ && (m->flags & QWSKeyboard::IsLetter)) + testmods ^= QWSKeyboard::ModShift; + if (m->modifiers == testmods) + map_withmod = m; + } + } + +#ifdef QT_DEBUG_KEYMAP + qWarning("Processing key event: keycode=%3d, modifiers=%02x pressed=%d, autorepeat=%d | plain=%d, withmod=%d, size=%d", \ + keycode, d->m_modifiers, pressed ? 1 : 0, autorepeat ? 1 : 0, \ + map_plain ? map_plain - d->m_keymap : -1, \ + map_withmod ? map_withmod - d->m_keymap : -1, \ + d->m_keymap_size); +#endif + + const QWSKeyboard::Mapping *it = map_withmod ? map_withmod : map_plain; + + if (!it) { +#ifdef QT_DEBUG_KEYMAP + // we couldn't even find a plain mapping + qWarning("Could not find a suitable mapping for keycode: %3d, modifiers: %02x", keycode, d->m_modifiers); +#endif + return result; + } + + bool skip = false; + quint16 unicode = it->unicode; + quint32 qtcode = it->qtcode; + + if ((it->flags & QWSKeyboard::IsModifier) && it->special) { + // this is a modifier, i.e. Shift, Alt, ... + if (pressed) + d->m_modifiers |= quint8(it->special); + else + d->m_modifiers &= ~quint8(it->special); + } else if (qtcode >= Qt::Key_CapsLock && qtcode <= Qt::Key_ScrollLock) { + // (Caps|Num|Scroll)Lock + if (first_press) { + quint8 &lock = d->m_locks[qtcode - Qt::Key_CapsLock]; + lock ^= 1; + + switch (qtcode) { + case Qt::Key_CapsLock : result = lock ? CapsLockOn : CapsLockOff; break; + case Qt::Key_NumLock : result = lock ? NumLockOn : NumLockOff; break; + case Qt::Key_ScrollLock: result = lock ? ScrollLockOn : ScrollLockOff; break; + default : break; + } + } + } else if ((it->flags & QWSKeyboard::IsSystem) && it->special && first_press) { + switch (it->special) { + case QWSKeyboard::SystemReboot: + result = Reboot; + break; + + case QWSKeyboard::SystemZap: + if (!d->m_no_zap) + qApp->quit(); + break; + + case QWSKeyboard::SystemConsolePrevious: + result = PreviousConsole; + break; + + case QWSKeyboard::SystemConsoleNext: + result = NextConsole; + break; + + default: + if (it->special >= QWSKeyboard::SystemConsoleFirst && + it->special <= QWSKeyboard::SystemConsoleLast) { + result = KeycodeAction(SwitchConsoleFirst + ((it->special & QWSKeyboard::SystemConsoleMask) & SwitchConsoleMask)); + } + break; + } + + skip = true; // no need to tell QWS about it + } else if ((qtcode == Qt::Key_Multi_key) && d->m_do_compose) { + // the Compose key was pressed + if (first_press) + d->m_composing = 2; + skip = true; + } else if ((it->flags & QWSKeyboard::IsDead) && d->m_do_compose) { + // a Dead key was pressed + if (first_press && d->m_composing == 1 && d->m_dead_unicode == unicode) { // twice + d->m_composing = 0; + qtcode = Qt::Key_unknown; // otherwise it would be Qt::Key_Dead... + } else if (first_press && unicode != 0xffff) { + d->m_dead_unicode = unicode; + d->m_composing = 1; + skip = true; + } else { + skip = true; + } + } + + if (!skip) { + // a normal key was pressed + const int modmask = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier; + + // we couldn't find a specific mapping for the current modifiers, + // or that mapping didn't have special modifiers: + // so just report the plain mapping with additional modifiers. + if ((it == map_plain && it != map_withmod) || + (map_withmod && !(map_withmod->qtcode & modmask))) { + qtcode |= QWSKbPrivate::toQtModifiers(d->m_modifiers); + } + + if (d->m_composing == 2 && first_press && !(it->flags & QWSKeyboard::IsModifier)) { + // the last key press was the Compose key + if (unicode != 0xffff) { + int idx = 0; + // check if this code is in the compose table at all + for ( ; idx < d->m_keycompose_size; ++idx) { + if (d->m_keycompose[idx].first == unicode) + break; + } + if (idx < d->m_keycompose_size) { + // found it -> simulate a Dead key press + d->m_dead_unicode = unicode; + unicode = 0xffff; + d->m_composing = 1; + skip = true; + } else { + d->m_composing = 0; + } + } else { + d->m_composing = 0; + } + } else if (d->m_composing == 1 && first_press && !(it->flags & QWSKeyboard::IsModifier)) { + // the last key press was a Dead key + bool valid = false; + if (unicode != 0xffff) { + int idx = 0; + // check if this code is in the compose table at all + for ( ; idx < d->m_keycompose_size; ++idx) { + if (d->m_keycompose[idx].first == d->m_dead_unicode && d->m_keycompose[idx].second == unicode) + break; + } + if (idx < d->m_keycompose_size) { + quint16 composed = d->m_keycompose[idx].result; + if (composed != 0xffff) { + unicode = composed; + qtcode = Qt::Key_unknown; + valid = true; + } + } + } + if (!valid) { + unicode = d->m_dead_unicode; + qtcode = Qt::Key_unknown; + } + d->m_composing = 0; + } + + if (!skip) { +#ifdef QT_DEBUG_KEYMAP + qWarning("Processing: uni=%04x, qt=%08x, qtmod=%08x", unicode, qtcode & ~modmask, (qtcode & modmask)); +#endif + + // send the result to the QWS server + processKeyEvent(unicode, qtcode & ~modmask, Qt::KeyboardModifiers(qtcode & modmask), pressed, autorepeat); + } + } + return result; +} + QT_END_NAMESPACE #include "qkbd_qws.moc" diff --git a/src/gui/embedded/qkbd_qws.h b/src/gui/embedded/qkbd_qws.h index 8809f0a..171adc0 100644 --- a/src/gui/embedded/qkbd_qws.h +++ b/src/gui/embedded/qkbd_qws.h @@ -58,11 +58,33 @@ class Q_GUI_EXPORT QWSKeyboardHandler { public: QWSKeyboardHandler(); + QWSKeyboardHandler(const QString &device); virtual ~QWSKeyboardHandler(); virtual void processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, bool isPress, bool autoRepeat); + enum KeycodeAction { + None = 0, + + CapsLockOff = 0x01000000, + CapsLockOn = 0x01000001, + NumLockOff = 0x02000000, + NumLockOn = 0x02000001, + ScrollLockOff = 0x03000000, + ScrollLockOn = 0x03000001, + + Reboot = 0x04000000, + + PreviousConsole = 0x05000000, + NextConsole = 0x05000001, + SwitchConsoleFirst = 0x06000000, + SwitchConsoleLast = 0x0600007f, + SwitchConsoleMask = 0x0000007f, + }; + + KeycodeAction processKeycode(quint16 keycode, bool pressed, bool autorepeat); + protected: int transformDirKey(int key); void beginAutoRepeat(int uni, int code, Qt::KeyboardModifiers mod); diff --git a/src/gui/embedded/qkbd_qws_p.h b/src/gui/embedded/qkbd_qws_p.h new file mode 100644 index 0000000..f7dcdaf --- /dev/null +++ b/src/gui/embedded/qkbd_qws_p.h @@ -0,0 +1,77 @@ +#ifndef QWSKEYBOARD_P_H +#define QWSKEYBOARD_P_H + +#include <QDataStream> + +namespace QWSKeyboard { + const quint32 FileMagic = 0x514d4150; // 'QMAP' + + struct Mapping { + quint16 keycode; + quint16 unicode; + quint32 qtcode; + quint8 modifiers; + quint8 flags; + quint16 special; + + }; + + enum Flags { + IsDead = 0x01, + IsLetter = 0x02, + IsModifier = 0x04, + IsSystem = 0x08, + }; + + enum System { + SystemConsoleFirst = 0x0100, + SystemConsoleMask = 0x007f, + SystemConsoleLast = 0x017f, + SystemConsolePrevious = 0x0180, + SystemConsoleNext = 0x0181, + SystemReboot = 0x0200, + SystemZap = 0x0300, + }; + + struct Composing { + quint16 first; + quint16 second; + quint16 result; + }; + + enum Modifiers { + ModPlain = 0x00, + ModShift = 0x01, + ModAltGr = 0x02, + ModControl = 0x04, + ModAlt = 0x08, + ModShiftL = 0x10, + ModShiftR = 0x20, + ModCtrlL = 0x40, + ModCtrlR = 0x80, + // ModCapsShift = 0x100, // not supported! + }; +}; + +inline QDataStream &operator>>(QDataStream &ds, QWSKeyboard::Mapping &m) +{ + return ds >> m.keycode >> m.unicode >> m.qtcode >> m.modifiers >> m.flags >> m.special; +} + +inline QDataStream &operator<<(QDataStream &ds, const QWSKeyboard::Mapping &m) +{ + return ds << m.keycode << m.unicode << m.qtcode << m.modifiers << m.flags << m.special; +} + +inline QDataStream &operator>>(QDataStream &ds, QWSKeyboard::Composing &c) +{ + return ds >> c.first >> c.second >> c.result; +} + +inline QDataStream &operator<<(QDataStream &ds, const QWSKeyboard::Composing &c) +{ + return ds << c.first << c.second << c.result; +} + + +#endif // QWSKEYBOARD_H diff --git a/src/gui/embedded/qkbddriverfactory_qws.cpp b/src/gui/embedded/qkbddriverfactory_qws.cpp index 1ade652..dbfac5c 100644 --- a/src/gui/embedded/qkbddriverfactory_qws.cpp +++ b/src/gui/embedded/qkbddriverfactory_qws.cpp @@ -45,7 +45,7 @@ #include "qapplication.h" #include "qkbdtty_qws.h" -#include "qkbdusb_qws.h" +#include "qkbdlinuxinput_qws.h" #include "qkbdum_qws.h" #include "qkbdsl5000_qws.h" #include "qkbdvfb_qws.h" @@ -121,9 +121,11 @@ QWSKeyboardHandler *QKbdDriverFactory::create(const QString& key, const QString& if (driver == QLatin1String("tty") || driver.isEmpty()) return new QWSTtyKeyboardHandler(device); # endif -# ifndef QT_NO_QWS_KBD_USB - if (driver == QLatin1String("usb")) - return new QWSUsbKeyboardHandler(device); +# ifndef QT_NO_QWS_KBD_LINUXINPUT + if (driver == QLatin1String("linuxinput") || \ + driver == QLatin1String("usb") || \ + driver == QLatin1String("linuxis")) + return new QWSLinuxInputKeyboardHandler(device); # endif # ifndef QT_NO_QWS_KBD_UM if (driver == QLatin1String("um") || driver == QLatin1String("qvfbkeyboard")) @@ -168,8 +170,8 @@ QStringList QKbdDriverFactory::keys() #ifndef QT_NO_QWS_KBD_TTY list << QLatin1String("TTY"); #endif -#ifndef QT_NO_QWS_KBD_USB - list << QLatin1String("USB"); +#ifndef QT_NO_QWS_KBD_LINUXINPUT + list << QLatin1String("LinuxInput"); #endif #ifndef QT_NO_QWS_KBD_UM list << QLatin1String("UM"); diff --git a/src/gui/embedded/qkbdlinuxinput_qws.cpp b/src/gui/embedded/qkbdlinuxinput_qws.cpp new file mode 100644 index 0000000..d04b09f --- /dev/null +++ b/src/gui/embedded/qkbdlinuxinput_qws.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qkbdlinuxinput_qws.h" + +#ifndef QT_NO_QWS_KEYBOARD + +#include <QSocketNotifier> +#include <QStringList> + +#include <qplatformdefs.h> + +#include <errno.h> +#include <termios.h> + +#include <linux/kd.h> +#include <linux/input.h> + +QT_BEGIN_NAMESPACE + + +class QWSLinuxInputKbPrivate : public QObject +{ + Q_OBJECT +public: + QWSLinuxInputKbPrivate(QWSLinuxInputKeyboardHandler *, const QString &); + ~QWSLinuxInputKbPrivate(); + +private: + void switchLed(int, bool); + +private Q_SLOTS: + void readKeycode(); + +private: + QWSLinuxInputKeyboardHandler *m_handler; + int m_fd; + int m_tty_fd; + struct termios m_tty_attr; +}; + +QWSLinuxInputKeyboardHandler::QWSLinuxInputKeyboardHandler(const QString &device) + : QWSKeyboardHandler(device) +{ + d = new QWSLinuxInputKbPrivate(this, device); +} + +QWSLinuxInputKeyboardHandler::~QWSLinuxInputKeyboardHandler() +{ + delete d; +} + +bool QWSLinuxInputKeyboardHandler::filterInputEvent(quint16 &, qint32 &) +{ + return false; +} + +QWSLinuxInputKbPrivate::QWSLinuxInputKbPrivate(QWSLinuxInputKeyboardHandler *h, const QString &device) + : m_handler(h), m_fd(-1), m_tty_fd(-1) + +{ + setObjectName(QLatin1String("LinuxInputSubsystem Keyboard Handler")); + + QString dev = QLatin1String("/dev/input/event1"); + int repeat_delay = -1; + int repeat_rate = -1; + + QStringList args = device.split(QLatin1Char(':')); + foreach (const QString &arg, args) { + if (arg.startsWith(QLatin1String("repeat_delay="))) + repeat_delay = arg.mid(13).toInt(); + else if (arg.startsWith(QLatin1String("repeat_rate="))) + repeat_rate = arg.mid(12).toInt(); + else if (arg.startsWith(QLatin1String("/dev/"))) + dev = arg; + } + + m_fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDWR, 0); + if (m_fd >= 0) { + if (repeat_delay > 0 && repeat_rate > 0) { + int kbdrep[2] = { repeat_delay, repeat_rate }; + ::ioctl(m_fd, EVIOCSREP, kbdrep); + } + + QSocketNotifier *notifier; + notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)), this, SLOT(readKeycode())); + + // play nice in case we are started from a shell (e.g. for debugging) + m_tty_fd = isatty(0) ? 0 : -1; + + if (m_tty_fd >= 0) { + // save tty config for restore. + tcgetattr(m_tty_fd, &m_tty_attr); + + struct ::termios termdata; + tcgetattr(m_tty_fd, &termdata); + + // setting this tranlation mode is also needed in INPUT mode to prevent + // the shell from also interpreting codes, if the process has a tty + // attached: e.g. Ctrl+C wouldn't copy, but kill the application. + ::ioctl(m_tty_fd, KDSKBMODE, K_MEDIUMRAW); + + // set the tty layer to pass-through + termdata.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP); + termdata.c_oflag = 0; + termdata.c_cflag = CREAD | CS8; + termdata.c_lflag = 0; + termdata.c_cc[VTIME]=0; + termdata.c_cc[VMIN]=1; + cfsetispeed(&termdata, 9600); + cfsetospeed(&termdata, 9600); + tcsetattr(m_tty_fd, TCSANOW, &termdata); + } + } else { + qWarning("Cannot open input device '%s': %s", qPrintable(dev), strerror(errno)); + return; + } +} + +QWSLinuxInputKbPrivate::~QWSLinuxInputKbPrivate() +{ + if (m_tty_fd >= 0) { + ::ioctl(m_tty_fd, KDSKBMODE, K_XLATE); + tcsetattr(m_tty_fd, TCSANOW, &m_tty_attr); + } + if (m_fd >= 0) + QT_CLOSE(m_fd); +} + +void QWSLinuxInputKbPrivate::switchLed(int led, bool state) +{ + struct ::input_event led_ie; + ::gettimeofday(&led_ie.time, 0); + led_ie.type = EV_LED; + led_ie.code = led; + led_ie.value = state; + + QT_WRITE(m_fd, &led_ie, sizeof(led_ie)); +} + +void QWSLinuxInputKbPrivate::readKeycode() +{ + struct ::input_event buffer[32]; + int n = 0; + + forever { + n = QT_READ(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n); + + if (n == 0) { + qWarning("Got EOF from the input device."); + return; + } else if (n < 0 && (errno != EINTR && errno != EAGAIN)) { + qWarning("Could not read from input device: %s", strerror(errno)); + return; + } else if (n % sizeof(buffer[0]) == 0) { + break; + } + } + + n /= sizeof(buffer[0]); + + for (int i = 0; i < n; ++i) { + if (buffer[i].type != EV_KEY) + continue; + + quint16 code = buffer[i].code; + qint32 value = buffer[i].value; + + if (m_handler->filterInputEvent(code, value)) + continue; + + QWSKeyboardHandler::KeycodeAction ka; + ka = m_handler->processKeycode(code, value != 0, value == 2); + + switch (ka) { + case QWSKeyboardHandler::CapsLockOn: + case QWSKeyboardHandler::CapsLockOff: + switchLed(LED_CAPSL, ka == QWSKeyboardHandler::CapsLockOn); + break; + + case QWSKeyboardHandler::NumLockOn: + case QWSKeyboardHandler::NumLockOff: + switchLed(LED_NUML, ka == QWSKeyboardHandler::NumLockOn); + break; + + case QWSKeyboardHandler::ScrollLockOn: + case QWSKeyboardHandler::ScrollLockOff: + switchLed(LED_SCROLLL, ka == QWSKeyboardHandler::ScrollLockOn); + break; + + default: + // ignore console switching and reboot + break; + } + } +} + +QT_END_NAMESPACE + +#include "qkbdlinuxinput_qws.moc" + +#endif // QT_NO_QWS_KEYBOARD diff --git a/src/gui/embedded/qkbdusb_qws.h b/src/gui/embedded/qkbdlinuxinput_qws.h index 81d0103..5fa8214 100644 --- a/src/gui/embedded/qkbdusb_qws.h +++ b/src/gui/embedded/qkbdlinuxinput_qws.h @@ -39,10 +39,10 @@ ** ****************************************************************************/ -#ifndef QKBDUSB_QWS_H -#define QKBDUSB_QWS_H +#ifndef QKBDLINUXINPUT_QWS_H +#define QKBDLINUXINPUT_QWS_H -#include <QtGui/qkbdpc101_qws.h> +#include <QtGui/qkbd_qws.h> QT_BEGIN_HEADER @@ -52,21 +52,23 @@ QT_MODULE(Gui) #ifndef QT_NO_QWS_KEYBOARD -#ifndef QT_NO_QWS_KBD_USB +#ifndef QT_NO_QWS_KBD_LINUXINPUT -class QWSUsbKbPrivate; +class QWSLinuxInputKbPrivate; -class QWSUsbKeyboardHandler : public QWSPC101KeyboardHandler +class QWSLinuxInputKeyboardHandler : public QWSKeyboardHandler { public: - QWSUsbKeyboardHandler(const QString&); - virtual ~QWSUsbKeyboardHandler(); + QWSLinuxInputKeyboardHandler(const QString&); + virtual ~QWSLinuxInputKeyboardHandler(); + + virtual bool filterInputEvent(quint16 &input_code, qint32 &input_value); private: - QWSUsbKbPrivate *d; + QWSLinuxInputKbPrivate *d; }; -#endif // QT_NO_QWS_KBD_USB +#endif // QT_NO_QWS_KBD_LINUXINPUT #endif // QT_NO_QWS_KEYBOARD @@ -74,4 +76,4 @@ QT_END_NAMESPACE QT_END_HEADER -#endif // QKBDUSB_QWS_H +#endif // QKBDLINUXINPUT_QWS_H diff --git a/src/gui/embedded/qkbdsl5000_qws.cpp b/src/gui/embedded/qkbdsl5000_qws.cpp index bc412b6..1e8095e 100644 --- a/src/gui/embedded/qkbdsl5000_qws.cpp +++ b/src/gui/embedded/qkbdsl5000_qws.cpp @@ -195,6 +195,13 @@ static const int keyMSize = sizeof(sl5000KeyMap)/sizeof(QWSKeyMap)-1; QWSSL5000KeyboardHandler::QWSSL5000KeyboardHandler(const QString &device) : QWSTtyKeyboardHandler(device) { + shift = false; + alt = false; + ctrl = false; + extended = 0; + prevuni = 0; + prevkey = 0; + caps = false; meta = false; fn = false; numLock = false; @@ -220,7 +227,7 @@ const QWSKeyMap *QWSSL5000KeyboardHandler::keyMap() const return sl5000KeyMap; } -void QWSSL5000KeyboardHandler::doKey(uchar code) +bool QWSSL5000KeyboardHandler::filterKeycode(char &code) { int keyCode = Qt::Key_unknown; bool release = false; @@ -239,19 +246,19 @@ void QWSSL5000KeyboardHandler::doKey(uchar code) else if (code == 0x52) { unicode='Z'-'@'; scan=Qt::Key_Z; } // Undo if (scan) { processKeyEvent(unicode, scan, Qt::ControlModifier, !release, false); - return; + return true; } } if (code < keyMSize) { - keyCode = keyMap()[code].key_code; + keyCode = keyMap()[int(code)].key_code; } bool repeatable = true; if (release && (keyCode == Qt::Key_F34 || keyCode == Qt::Key_F35)) - return; // no release for power and light keys - if (keyCode >= Qt::Key_F1 && keyCode <= Qt::Key_F35 + return true; // no release for power and light keys + if ((keyCode >= Qt::Key_F1 && keyCode <= Qt::Key_F35) || keyCode == Qt::Key_Escape || keyCode == Qt::Key_Home || keyCode == Qt::Key_Shift || keyCode == Qt::Key_Meta) repeatable = false; @@ -318,11 +325,11 @@ void QWSSL5000KeyboardHandler::doKey(uchar code) keyCode = Qt::Key_QuoteLeft; unicode = '`'; } else if (bCtrl) - unicode = keyMap()[code].ctrl_unicode ? keyMap()[code].ctrl_unicode : 0xffff; + unicode = keyMap()[int(code)].ctrl_unicode ? keyMap()[int(code)].ctrl_unicode : 0xffff; else if (bCaps) - unicode = keyMap()[code].shift_unicode ? keyMap()[code].shift_unicode : 0xffff; + unicode = keyMap()[int(code)].shift_unicode ? keyMap()[int(code)].shift_unicode : 0xffff; else - unicode = keyMap()[code].unicode ? keyMap()[code].unicode : 0xffff; + unicode = keyMap()[int(code)].unicode ? keyMap()[int(code)].unicode : 0xffff; } modifiers = 0; @@ -349,6 +356,8 @@ void QWSSL5000KeyboardHandler::doKey(uchar code) beginAutoRepeat(prevuni, prevkey, modifiers); else endAutoRepeat(); + + return true; } QT_END_NAMESPACE diff --git a/src/gui/embedded/qkbdsl5000_qws.h b/src/gui/embedded/qkbdsl5000_qws.h index 514d602..a96e038 100644 --- a/src/gui/embedded/qkbdsl5000_qws.h +++ b/src/gui/embedded/qkbdsl5000_qws.h @@ -52,7 +52,13 @@ QT_MODULE(Gui) #ifndef QT_NO_QWS_KBD_SL5000 -class QWSSL5000KbPrivate; +struct QWSKeyMap { + uint key_code; + ushort unicode; + ushort shift_unicode; + ushort ctrl_unicode; +}; + class QWSSL5000KeyboardHandler : public QWSTtyKeyboardHandler { @@ -60,14 +66,21 @@ public: explicit QWSSL5000KeyboardHandler(const QString&); virtual ~QWSSL5000KeyboardHandler(); - virtual void doKey(uchar scancode); + bool filterKeycode(char &keycode); virtual const QWSKeyMap *keyMap() const; private: + bool shift; + bool alt; + bool ctrl; + bool caps; + uint extended:2; + Qt::KeyboardModifiers modifiers; + int prevuni; + int prevkey; bool meta; bool fn; bool numLock; - QWSSL5000KbPrivate *d; }; #endif // QT_NO_QWS_KBD_SL5000 diff --git a/src/gui/embedded/qkbdtty_qws.cpp b/src/gui/embedded/qkbdtty_qws.cpp index b588e55..37ed510 100644 --- a/src/gui/embedded/qkbdtty_qws.cpp +++ b/src/gui/embedded/qkbdtty_qws.cpp @@ -43,64 +43,57 @@ #if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_TTY) -#include "qscreen_qws.h" - -#include "qwindowsystem_qws.h" -#include "qapplication.h" -#include "qsocketnotifier.h" -#include "qnamespace.h" -#include "qtimer.h" -#include <private/qwssignalhandler_p.h> -#include <private/qwindowsurface_qws_p.h> - -#include <unistd.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> -#include <signal.h> -#include <termios.h> +#include <QSocketNotifier> +#include <QStringList> -#include <qeventloop.h> +#include <qplatformdefs.h> -#ifdef Q_OS_LINUX -#include <sys/kd.h> -#include <sys/vt.h> -#endif +#include <errno.h> +#include <termios.h> -QT_BEGIN_NAMESPACE +#if defined Q_OS_LINUX +# include <linux/kd.h> +# include <linux/vt.h> //TODO: move vt handling somewhere else (QLinuxFbScreen?) -#define VTACQSIG SIGUSR1 -#define VTRELSIG SIGUSR2 +# include "qscreen_qws.h" +# include "qwindowsystem_qws.h" +# include "qapplication.h" +# include "private/qwindowsurface_qws_p.h" +# include "private/qwssignalhandler_p.h" -static int vtQws = 0; -static int kbdFD = -1; +# define VTACQSIG SIGUSR1 +# define VTRELSIG SIGUSR2 +#endif -//=========================================================================== -// -// Tty keyboard -// +QT_BEGIN_NAMESPACE class QWSTtyKbPrivate : public QObject { Q_OBJECT public: - QWSTtyKbPrivate(QWSPC101KeyboardHandler *, const QString &device); + QWSTtyKbPrivate(QWSTtyKeyboardHandler *handler, const QString &device); ~QWSTtyKbPrivate(); -private slots: - void readKeyboardData(); - void handleTtySwitch(int); +private: + void switchLed(char, bool); + void switchConsole(int vt); + +private Q_SLOTS: + void readKeycode(); + void handleConsoleSwitch(int sig); private: - QWSPC101KeyboardHandler *handler; - struct termios origTermData; + QWSTtyKeyboardHandler *m_handler; + int m_tty_fd; + struct termios m_tty_attr; + char m_last_keycode; + int m_vt_qws; }; + QWSTtyKeyboardHandler::QWSTtyKeyboardHandler(const QString &device) - : QWSPC101KeyboardHandler(device) + : QWSKeyboardHandler(device) { d = new QWSTtyKbPrivate(this, device); } @@ -110,59 +103,63 @@ QWSTtyKeyboardHandler::~QWSTtyKeyboardHandler() delete d; } -void QWSTtyKeyboardHandler::processKeyEvent(int unicode, int keycode, - Qt::KeyboardModifiers modifiers, bool isPress, - bool autoRepeat) +bool QWSTtyKeyboardHandler::filterKeycode(char &) { -#if defined(Q_OS_LINUX) - // Virtual console switching - int term = 0; - bool ctrl = modifiers & Qt::ControlModifier; - bool alt = modifiers & Qt::AltModifier; - if (ctrl && alt && keycode >= Qt::Key_F1 && keycode <= Qt::Key_F10) - term = keycode - Qt::Key_F1 + 1; - else if (ctrl && alt && keycode == Qt::Key_Left) - term = qMax(vtQws - 1, 1); - else if (ctrl && alt && keycode == Qt::Key_Right) - term = qMin(vtQws + 1, 10); - if (term && isPress) { - ioctl(kbdFD, VT_ACTIVATE, term); - return; - } -#endif - - QWSPC101KeyboardHandler::processKeyEvent(unicode, keycode, modifiers, - isPress, autoRepeat); + return false; } - -QWSTtyKbPrivate::QWSTtyKbPrivate(QWSPC101KeyboardHandler *h, const QString &device) : handler(h) +QWSTtyKbPrivate::QWSTtyKbPrivate(QWSTtyKeyboardHandler *h, const QString &device) + : m_handler(h), m_tty_fd(-1), m_last_keycode(0), m_vt_qws(0) { - kbdFD = ::open(device.isEmpty()?"/dev/tty0":device.toLatin1().constData(), O_RDWR|O_NDELAY, 0); + setObjectName(QLatin1String("TTY Keyboard Handler")); #ifndef QT_NO_QWS_SIGNALHANDLER QWSSignalHandler::instance()->addObject(this); #endif - if (kbdFD >= 0) { + QString dev = QLatin1String("/dev/tty0"); + int repeat_delay = -1; + int repeat_rate = -1; + + QStringList args = device.split(QLatin1Char(':')); + foreach (const QString &arg, args) { + if (arg.startsWith(QLatin1String("repeat_delay="))) + repeat_delay = arg.mid(13).toInt(); + else if (arg.startsWith(QLatin1String("repeat_rate="))) + repeat_rate = arg.mid(12).toInt(); + else if (arg.startsWith(QLatin1String("/dev/"))) + dev = arg; + } + + m_tty_fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDWR, 0); + if (m_tty_fd >= 0) { + if (repeat_delay > 0 && repeat_rate > 0) { +#if defined(Q_OS_LINUX) + struct ::kbd_repeat kbdrep = { repeat_delay, repeat_rate }; + ::ioctl(m_tty_fd, KDKBDREP, &kbdrep); +#endif + } + QSocketNotifier *notifier; - notifier = new QSocketNotifier(kbdFD, QSocketNotifier::Read, this); - connect(notifier, SIGNAL(activated(int)),this, - SLOT(readKeyboardData())); + notifier = new QSocketNotifier(m_tty_fd, QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)), this, SLOT(readKeycode())); - // save for restore. - tcgetattr(kbdFD, &origTermData); + // save tty config for restore. + tcgetattr(m_tty_fd, &m_tty_attr); - struct termios termdata; - tcgetattr(kbdFD, &termdata); + struct ::termios termdata; + tcgetattr(m_tty_fd, &termdata); #if defined(Q_OS_LINUX) -# ifdef QT_QWS_USE_KEYCODES - ioctl(kbdFD, KDSKBMODE, K_MEDIUMRAW); -# else - ioctl(kbdFD, KDSKBMODE, K_RAW); -# endif + // PLEASE NOTE: + // The tty keycode interface can only report keycodes 0x01 .. 0x7f + // KEY_MAX is however defined to 0x1ff. In practice this is sufficient + // for a PC style keyboard though. + // we don't support K_RAW anymore - if you need, you habe to add a + // scan- to keycode converter. + ::ioctl(m_tty_fd, KDSKBMODE, K_MEDIUMRAW); #endif + // set the tty layer to pass-through termdata.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP); termdata.c_oflag = 0; termdata.c_cflag = CREAD | CS8; @@ -171,50 +168,146 @@ QWSTtyKbPrivate::QWSTtyKbPrivate(QWSPC101KeyboardHandler *h, const QString &devi termdata.c_cc[VMIN]=1; cfsetispeed(&termdata, 9600); cfsetospeed(&termdata, 9600); - tcsetattr(kbdFD, TCSANOW, &termdata); + tcsetattr(m_tty_fd, TCSANOW, &termdata); #if defined(Q_OS_LINUX) - - connect(QApplication::instance(), SIGNAL(unixSignal(int)), this, SLOT(handleTtySwitch(int))); + // VT switching is handled via unix signals + connect(QApplication::instance(), SIGNAL(unixSignal(int)), this, SLOT(handleConsoleSwitch(int))); QApplication::instance()->watchUnixSignal(VTACQSIG, true); QApplication::instance()->watchUnixSignal(VTRELSIG, true); - struct vt_mode vtMode; - ioctl(kbdFD, VT_GETMODE, &vtMode); + struct ::vt_mode vtMode; + if (::ioctl(m_tty_fd, VT_GETMODE, &vtMode) == 0) { + vtMode.mode = VT_PROCESS; + vtMode.relsig = VTRELSIG; + vtMode.acqsig = VTACQSIG; - // let us control VT switching - vtMode.mode = VT_PROCESS; - vtMode.relsig = VTRELSIG; - vtMode.acqsig = VTACQSIG; - ioctl(kbdFD, VT_SETMODE, &vtMode); + if (::ioctl(m_tty_fd, VT_SETMODE, &vtMode) == 0) { + struct ::vt_stat vtStat; + ::memset(&vtStat, 0, sizeof(vtStat)); - struct vt_stat vtStat; - ioctl(kbdFD, VT_GETSTATE, &vtStat); - vtQws = vtStat.v_active; + if (::ioctl(m_tty_fd, VT_GETSTATE, &vtStat) == 0 ) { + m_vt_qws = vtStat.v_active; + } + } + } + + if (!m_vt_qws) + qWarning("Could not initialize virtual console switching"); #endif } else { - qCritical("Cannot open keyboard: %s", strerror(errno)); + qWarning("Cannot open input device '%s': %s", qPrintable(dev), strerror(errno)); + return; } } QWSTtyKbPrivate::~QWSTtyKbPrivate() { - if (kbdFD >= 0) { + if (m_tty_fd >= 0) { #if defined(Q_OS_LINUX) - ioctl(kbdFD, KDSKBMODE, K_XLATE); + ::ioctl(m_tty_fd, KDSKBMODE, K_XLATE); #endif - tcsetattr(kbdFD, TCSANOW, &origTermData); - ::close(kbdFD); - kbdFD = -1; + tcsetattr(m_tty_fd, TCSANOW, &m_tty_attr); } } -void QWSTtyKbPrivate::handleTtySwitch(int sig) + + +void QWSTtyKbPrivate::switchLed(char led, bool state) { #if defined(Q_OS_LINUX) + char ledstate; + + ::ioctl(m_tty_fd, KDGETLED, &ledstate); + if (state) + ledstate |= led; + else + ledstate &= ~led; + ::ioctl(m_tty_fd, KDSETLED, ledstate); +#endif +} + +void QWSTtyKbPrivate::readKeycode() +{ + char buffer[32]; + int n = 0; + + forever { + n = QT_READ(m_tty_fd, buffer + n, 32 - n); + + if (n == 0) { + qWarning("Got EOF from the input device."); + return; + } else if (n < 0 && (errno != EINTR && errno != EAGAIN)) { + qWarning("Could not read from input device: %s", strerror(errno)); + return; + } else { + break; + } + } + + for (int i = 0; i < n; ++i) { + if (m_handler->filterKeycode(buffer[i])) + continue; + + QWSKeyboardHandler::KeycodeAction ka; + ka = m_handler->processKeycode(buffer[i] & 0x7f, (buffer[i] & 0x80) == 0x00, buffer[i] == m_last_keycode); + m_last_keycode = buffer[i]; + +qWarning("Special: %08x", ka); + switch (ka) { + case QWSKeyboardHandler::CapsLockOn: + case QWSKeyboardHandler::CapsLockOff: + switchLed(LED_CAP, ka == QWSKeyboardHandler::CapsLockOn); + break; + + case QWSKeyboardHandler::NumLockOn: + case QWSKeyboardHandler::NumLockOff: + switchLed(LED_NUM, ka == QWSKeyboardHandler::NumLockOn); + break; + + case QWSKeyboardHandler::ScrollLockOn: + case QWSKeyboardHandler::ScrollLockOff: + switchLed(LED_SCR, ka == QWSKeyboardHandler::ScrollLockOn); + break; + + case QWSKeyboardHandler::PreviousConsole: + switchConsole(qBound(1, m_vt_qws - 1, 10)); + break; + + case QWSKeyboardHandler::NextConsole: + switchConsole(qBound(1, m_vt_qws + 1, 10)); + break; + + default: + if (ka >= QWSKeyboardHandler::SwitchConsoleFirst && + ka <= QWSKeyboardHandler::SwitchConsoleLast) { + switchConsole(1 + (ka & QWSKeyboardHandler::SwitchConsoleMask)); + } + //ignore reboot + break; + } + } +} + + +void QWSTtyKbPrivate::switchConsole(int vt) +{ +#if defined(Q_OS_LINUX) + if (m_vt_qws && vt && (m_tty_fd >= 0 )) + ::ioctl(m_tty_fd, VT_ACTIVATE, vt); +#endif +} + +void QWSTtyKbPrivate::handleConsoleSwitch(int sig) +{ +#if defined(Q_OS_LINUX) + // received a notification from the kernel that the current VT is + // changing: either enable or disable QWS painting accordingly. + if (sig == VTACQSIG) { - if (ioctl(kbdFD, VT_RELDISP, VT_ACKACQ) == 0) { + if (::ioctl(m_tty_fd, VT_RELDISP, VT_ACKACQ) == 0) { qwsServer->enablePainting(true); qt_screen->restore(); qwsServer->resumeMouse(); @@ -236,9 +329,9 @@ void QWSTtyKbPrivate::handleTtySwitch(int sig) } if (!allWindowsHidden) { - ioctl(kbdFD, VT_RELDISP, 0); // abort console switch + ::ioctl(m_tty_fd, VT_RELDISP, 0); // abort console switch qwsServer->enablePainting(true); - } else if (ioctl(kbdFD, VT_RELDISP, 1) == 0) { + } else if (::ioctl(m_tty_fd, VT_RELDISP, 1) == 0) { qt_screen->save(); qwsServer->suspendMouse(); } else { @@ -248,14 +341,6 @@ void QWSTtyKbPrivate::handleTtySwitch(int sig) #endif } -void QWSTtyKbPrivate::readKeyboardData() -{ - unsigned char buf[81]; - int n = read(kbdFD, buf, 80); - for (int loop = 0; loop < n; loop++) - handler->doKey(buf[loop]); -} - QT_END_NAMESPACE #include "qkbdtty_qws.moc" diff --git a/src/gui/embedded/qkbdtty_qws.h b/src/gui/embedded/qkbdtty_qws.h index 4f93d6c..fc3eaa1 100644 --- a/src/gui/embedded/qkbdtty_qws.h +++ b/src/gui/embedded/qkbdtty_qws.h @@ -42,7 +42,7 @@ #ifndef QKBDTTY_QWS_H #define QKBDTTY_QWS_H -#include <QtGui/qkbdpc101_qws.h> +#include <QtGui/qkbd_qws.h> QT_BEGIN_HEADER @@ -56,15 +56,13 @@ QT_MODULE(Gui) class QWSTtyKbPrivate; -class QWSTtyKeyboardHandler : public QWSPC101KeyboardHandler +class QWSTtyKeyboardHandler : public QWSKeyboardHandler { public: explicit QWSTtyKeyboardHandler(const QString&); virtual ~QWSTtyKeyboardHandler(); -protected: - virtual void processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, - bool isPress, bool autoRepeat); + virtual bool filterKeycode(char &code); private: QWSTtyKbPrivate *d; diff --git a/src/gui/embedded/qkbdum_qws.cpp b/src/gui/embedded/qkbdum_qws.cpp index d525c66..fa05d6c 100644 --- a/src/gui/embedded/qkbdum_qws.cpp +++ b/src/gui/embedded/qkbdum_qws.cpp @@ -54,6 +54,7 @@ #include <qstring.h> #include <qwindowsystem_qws.h> #include <qsocketnotifier.h> +#include "qplatformdefs.h" QT_BEGIN_NAMESPACE @@ -81,13 +82,13 @@ QWSUmKeyboardHandlerPrivate::QWSUmKeyboardHandlerPrivate(const QString &device) { kbdBuffer = new unsigned char [kbdBufferLen]; - if ((kbdFD = open((const char *)device.toLocal8Bit(), O_RDONLY | O_NDELAY)) < 0) { + if ((kbdFD = QT_OPEN((const char *)device.toLocal8Bit(), O_RDONLY | O_NDELAY, 0)) < 0) { qDebug("Cannot open %s (%s)", (const char *)device.toLocal8Bit(), strerror(errno)); } else { // Clear pending input char buf[2]; - while (read(kbdFD, buf, 1) > 0) { } + while (QT_READ(kbdFD, buf, 1) > 0) { } notifier = new QSocketNotifier(kbdFD, QSocketNotifier::Read, this); connect(notifier, SIGNAL(activated(int)),this, SLOT(readKeyboardData())); @@ -97,7 +98,7 @@ QWSUmKeyboardHandlerPrivate::QWSUmKeyboardHandlerPrivate(const QString &device) QWSUmKeyboardHandlerPrivate::~QWSUmKeyboardHandlerPrivate() { if (kbdFD >= 0) - close(kbdFD); + QT_CLOSE(kbdFD); delete [] kbdBuffer; } @@ -106,7 +107,7 @@ void QWSUmKeyboardHandlerPrivate::readKeyboardData() { int n; do { - n = read(kbdFD, kbdBuffer+kbdIdx, kbdBufferLen - kbdIdx); + n = QT_READ(kbdFD, kbdBuffer+kbdIdx, kbdBufferLen - kbdIdx); if (n > 0) kbdIdx += n; } while (n > 0); diff --git a/src/gui/embedded/qkbdusb_qws.cpp b/src/gui/embedded/qkbdusb_qws.cpp deleted file mode 100644 index e35ac55..0000000 --- a/src/gui/embedded/qkbdusb_qws.cpp +++ /dev/null @@ -1,401 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (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 either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** 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.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qkbdusb_qws.h" - -#ifndef QT_NO_QWS_KEYBOARD - -#include "qscreen_qws.h" - -#include "qwindowsystem_qws.h" -#include "qapplication.h" -#include "qsocketnotifier.h" -#include "qnamespace.h" -#include "qtimer.h" - -#include <unistd.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> -#include <signal.h> - -#include <linux/input.h> - -#ifdef Q_OS_LINUX -#include <sys/kd.h> -#include <sys/vt.h> -#endif - -QT_BEGIN_NAMESPACE - -/* USB driver */ - - -class QWSUsbKbPrivate : public QObject -{ - Q_OBJECT -public: - QWSUsbKbPrivate(QWSPC101KeyboardHandler *, const QString &); - ~QWSUsbKbPrivate(); - -private slots: - void readKeyboardData(); - -private: - QWSPC101KeyboardHandler *handler; - int fd; -#ifdef QT_QWS_ZYLONITE - bool shift; -#endif -}; - -QWSUsbKeyboardHandler::QWSUsbKeyboardHandler(const QString &device) - : QWSPC101KeyboardHandler(device) -{ - d = new QWSUsbKbPrivate(this, device); -} - -QWSUsbKeyboardHandler::~QWSUsbKeyboardHandler() -{ - delete d; -} - -QWSUsbKbPrivate::QWSUsbKbPrivate(QWSPC101KeyboardHandler *h, const QString &device) : handler(h) -{ -#ifdef QT_QWS_ZYLONITE - shift = FALSE; -#endif - fd = ::open(device.isEmpty()?"/dev/input/event1":device.toLocal8Bit(),O_RDONLY, 0); - if (fd >= 0) { - QSocketNotifier *notifier; - notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); - connect(notifier, SIGNAL(activated(int)),this, - SLOT(readKeyboardData())); - } -} - -QWSUsbKbPrivate::~QWSUsbKbPrivate() -{ - ::close(fd); -} - -void QWSUsbKbPrivate::readKeyboardData() -{ - input_event event; - if (read(fd, &event, sizeof(input_event)) != sizeof(input_event)) - return; - - if (event.type != EV_KEY) - return; - -#ifdef QT_QWS_ZYLONITE - qDebug("keypressed: code=%03d (%s)\n",event.code,((event.value)!=0) ? "Down":"Up"); - int modifiers=0; - int unicode=0xffff; - int key_code=0; - - switch(event.code) - { - case 0xA2: - key_code = ((!shift) ? Qt::Key_0 : Qt::Key_Plus ); - unicode = ((!shift) ? 0x30 : 0x2B ); - break; - case 0x70: - key_code = ((!shift) ? Qt::Key_1 : Qt::Key_At ); - unicode = ((!shift) ? 0x31 : 0x40 ); - break; - case 0x72: - key_code = ((!shift) ? Qt::Key_2 : Qt::Key_Ampersand ); - unicode = ((!shift) ? 0x32 : 0x26 ); - break; - case 0x74: - key_code = ((!shift) ? Qt::Key_3 : Qt::Key_At ); - unicode = ((!shift) ? 0x33 : 0x3F ); - break; - case 0x80: - key_code = ((!shift) ? Qt::Key_4 : Qt::Key_Minus ); - unicode = ((!shift) ? 0x34 : 0x2D ); - break; - case 0x82: - key_code = ((!shift) ? Qt::Key_5 : Qt::Key_Apostrophe); - unicode = ((!shift) ? 0x35 : 0x27 ); - break; - case 0x84: - key_code = ((!shift) ? Qt::Key_6 : Qt::Key_Slash ); - unicode = ((!shift) ? 0x36 : 0x5C ); - break; - case 0x90: - key_code = ((!shift) ? Qt::Key_7 : Qt::Key_Colon ); - unicode = ((!shift) ? 0x37 : 0x3A ); - break; - case 0x92: - key_code = ((!shift) ? Qt::Key_8 : Qt::Key_Semicolon ); - unicode = ((!shift) ? 0x38 : 0x3B ); - break; - case 0x94: - key_code = ((!shift) ? Qt::Key_9 : Qt::Key_QuoteDbl ); - unicode = ((!shift) ? 0x39 : 0x22 ); - break; - case 0x0: - key_code = Qt::Key_A; - unicode = ((!shift) ? 0x61 : 0x41 ); - break; - case 0x10: - key_code = Qt::Key_B; - unicode = ((!shift) ? 0x62 : 0x42 ); - break; - case 0x20: - key_code = Qt::Key_C; - unicode = ((!shift) ? 0x63 : 0x43 ); - break; - case 0x30: - key_code = Qt::Key_D; - unicode = ((!shift) ? 0x64 : 0x44 ); - break; - case 0x40: - key_code = Qt::Key_E; - unicode = ((!shift) ? 0x65 : 0x45 ); - break; - case 0x50: - key_code = Qt::Key_F; - unicode = ((!shift) ? 0x66 : 0x46 ); - break; - case 0x01: - key_code = Qt::Key_G; - unicode = ((!shift) ? 0x67 : 0x47 ); - break; - case 0x11: - key_code = Qt::Key_H; - unicode = ((!shift) ? 0x68 : 0x48 ); - break; - case 0x21: - key_code = Qt::Key_I; - unicode = ((!shift) ? 0x69 : 0x49 ); - break; - case 0x31: - key_code = Qt::Key_J; - unicode = ((!shift) ? 0x6A : 0x4A ); - break; - case 0x41: - key_code = Qt::Key_K; - unicode = ((!shift) ? 0x6B : 0x4B ); - break; - case 0x51: - key_code = Qt::Key_L; - unicode = ((!shift) ? 0x6C : 0x4C ); - break; - case 0x02: - key_code = Qt::Key_M; - unicode = ((!shift) ? 0x6D : 0x4D ); - break; - case 0x12: - key_code = Qt::Key_N; - unicode = ((!shift) ? 0x6E : 0x4E ); - break; - case 0x22: - key_code = Qt::Key_O; - unicode = ((!shift) ? 0x6F : 0x4F ); - break; - case 0x32: - key_code = Qt::Key_P; - unicode = ((!shift) ? 0x70 : 0x50 ); - break; - case 0x42: - key_code = Qt::Key_Q; - unicode = ((!shift) ? 0x71 : 0x51 ); - break; - case 0x52: - key_code = Qt::Key_R; - unicode = ((!shift) ? 0x72 : 0x52 ); - break; - case 0x03: - key_code = Qt::Key_S; - unicode = ((!shift) ? 0x73 : 0x53 ); - break; - case 0x13: - key_code = Qt::Key_T; - unicode = ((!shift) ? 0x74 : 0x54 ); - break; - case 0x23: - key_code = Qt::Key_U; - unicode = ((!shift) ? 0x75 : 0x55 ); - break; - case 0x33: - key_code = Qt::Key_V; - unicode = ((!shift) ? 0x76 : 0x56 ); - break; - case 0x43: - key_code = Qt::Key_W; - unicode = ((!shift) ? 0x77 : 0x57 ); - break; - case 0x53: - key_code = Qt::Key_X; - unicode = ((!shift) ? 0x78 : 0x58 ); - break; - case 0x24: - key_code = Qt::Key_Y; - unicode = ((!shift) ? 0x79 : 0x59 ); - break; - case 0x34: - key_code = Qt::Key_Z; - unicode = ((!shift) ? 0x7A : 0x5A ); - break; - case 0xA4: - key_code = ((!shift) ? Qt::Key_NumberSign : Qt::Key_Period); - unicode = ((!shift) ? 0x23 : 0x2E ); - break; - case 0xA0: - key_code = ((!shift) ? Qt::Key_Asterisk : Qt::Key_NumberSign ); - unicode = ((!shift) ? 0x2A : 0x2C ); - break; - case 0x25: - key_code = Qt::Key_Space; - unicode = 0x20; - break; - case 0x06: - key_code = Qt::Key_Up; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x16: - key_code = Qt::Key_Down; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x26: - key_code = Qt::Key_Left; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x36: - key_code = Qt::Key_Right; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x46: - key_code = Qt::Key_Select; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x61: - key_code = Qt::Key_No; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x60: - key_code = Qt::Key_Call; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x55: - key_code = Qt::Key_Hangup; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x62: - key_code = Qt::Key_Context1; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x63: - key_code = Qt::Key_No; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x05: - key_code = Qt::Key_Home; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x15: - key_code = Qt::Key_Shift; - unicode = 0xffff; modifiers |= Qt::ShiftModifier; - if(event.value==0) break; - if(shift) { - shift = FALSE; - qWarning("Caps Off!"); - } else { - shift = TRUE; - qWarning("Caps On!"); - } - break; - case 0x1C: - key_code = ((!shift) ? Qt::Key_Back : Qt::Key_Enter ); - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x19: - key_code = Qt::Key_Context2; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x1A: - key_code = Qt::Key_Context3; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - case 0x1B: - key_code = Qt::Key_Context4; - unicode = 0xffff; modifiers |= Qt::KeypadModifier; - break; - } - if(shift) modifiers |= Qt::ShiftModifier; - handler->processKeyEvent(unicode, key_code, (Qt::KeyboardModifiers)modifiers, event.value!=0, false); -#else - - int key=event.code; -#ifndef QT_QWS_USE_KEYCODES - // Handle SOME keys, otherwise it's useless. - - if(key==103) { - handler->processKeyEvent(0, Qt::Key_Up, 0, event.value!=0, false); - } else if(key==106) { - handler->processKeyEvent(0, Qt::Key_Right, 0, event.value!=0, false ); - } else if(key==108) { - handler->processKeyEvent(0, Qt::Key_Down, 0, event.value!=0, false); - } else if(key==105) { - handler->processKeyEvent(0, Qt::Key_Left, 0, event.value!=0, false); - } else - -#endif - - { - if(event.value == 0) { - key=key | 0x80; - } - handler->doKey(key); - } -#endif -} - -QT_END_NAMESPACE - -#include "qkbdusb_qws.moc" - -#endif // QT_NO_QWS_KEYBOARD diff --git a/src/gui/embedded/qscreen_qws.cpp b/src/gui/embedded/qscreen_qws.cpp index 6741f2c..ff48403 100644 --- a/src/gui/embedded/qscreen_qws.cpp +++ b/src/gui/embedded/qscreen_qws.cpp @@ -198,10 +198,12 @@ void QScreenCursor::set(const QImage &image, int hotx, int hoty) */ void QScreenCursor::move(int x, int y) { - const QRegion r = boundingRect(); + QRegion r = boundingRect(); pos = QPoint(x,y); - if (enable && !hwaccel) - qt_screen->exposeRegion(r | boundingRect(), 0); + if (enable && !hwaccel) { + r |= boundingRect(); + qt_screen->exposeRegion(r, 0); + } } diff --git a/src/gui/embedded/qwscommand_qws.cpp b/src/gui/embedded/qwscommand_qws.cpp index 88e33a3..b0fd78b 100644 --- a/src/gui/embedded/qwscommand_qws.cpp +++ b/src/gui/embedded/qwscommand_qws.cpp @@ -43,7 +43,6 @@ #include "qtransportauth_qws.h" #include "qtransportauth_qws_p.h" -#include <sys/uio.h> #include <unistd.h> // #define QWSCOMMAND_DEBUG 1 // Uncomment to debug client/server communication diff --git a/src/gui/embedded/qwssignalhandler.cpp b/src/gui/embedded/qwssignalhandler.cpp index 0946fb6..e85bd36 100644 --- a/src/gui/embedded/qwssignalhandler.cpp +++ b/src/gui/embedded/qwssignalhandler.cpp @@ -44,8 +44,10 @@ #ifndef QT_NO_QWS_SIGNALHANDLER #include <sys/types.h> -#include <sys/ipc.h> -#include <sys/sem.h> +#ifndef QT_NO_QWS_MULTIPROCESS +# include <sys/ipc.h> +# include <sys/sem.h> +#endif #include <signal.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 83a7e67..c5f5889 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -522,6 +522,11 @@ #include <private/qtextdocumentlayout_p.h> #include <private/qtextengine_p.h> +#ifdef Q_WS_X11 +#include <private/qt_x11_p.h> +#include <private/qpixmap_x11_p.h> +#endif + #include <math.h> QT_BEGIN_NAMESPACE @@ -562,29 +567,6 @@ Q_GLOBAL_STATIC(QGraphicsItemCustomDataStore, qt_dataStore) /*! \internal - Removes the first instance of \a child from \a children. This is a - heuristic approach that assumes that it's common to remove items from the - start or end of the list. -*/ -static void qt_graphicsitem_removeChild(QGraphicsItem *child, QList<QGraphicsItem *> *children) -{ - const int n = children->size(); - for (int i = 0; i < (n + 1) / 2; ++i) { - if (children->at(i) == child) { - children->removeAt(i); - return; - } - int j = n - i - 1; - if (children->at(j) == child) { - children->removeAt(j); - return; - } - } -} - -/*! - \internal - Returns a QPainterPath of \a path when stroked with the \a pen. Ignoring dash pattern. */ @@ -783,6 +765,132 @@ QVariant QGraphicsItemPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query /*! \internal + If \a deleting is true, then this item is being deleted, and \a parent is + null. Make sure not to trigger any pure virtual function calls (e.g., + prepareGeometryChange). +*/ +void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool deleting) +{ + Q_Q(QGraphicsItem); + if (newParent == q) { + qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this); + return; + } + if (newParent == parent) + return; + + QVariant variant; + qVariantSetValue<QGraphicsItem *>(variant, newParent); + newParent = qVariantValue<QGraphicsItem *>(q->itemChange(QGraphicsItem::ItemParentChange, variant)); + if (newParent == parent) + return; + + if (QGraphicsWidget *w = isWidget ? static_cast<QGraphicsWidget *>(q) : q->parentWidget()) { + // Update the child focus chain; when reparenting a widget that has a + // focus child, ensure that that focus child clears its focus child + // chain from our parents before it's reparented. + if (QGraphicsWidget *focusChild = w->focusWidget()) + focusChild->clearFocus(); + } + + // We anticipate geometry changes. If the item is deleted, it will be + // removed from the index at a later stage, and the whole scene will be + // updated. + if (!deleting) + q_ptr->prepareGeometryChange(); + + if (parent) { + // Remove from current parent + parent->d_ptr->removeChild(q); + qVariantSetValue<QGraphicsItem *>(variant, q); + parent->itemChange(QGraphicsItem::ItemChildRemovedChange, variant); + } + + // Update toplevelitem list. If this item is being deleted, its parent + // will be 0 but we don't want to register/unregister it in the TLI list. + if (scene && !deleting) { + if (parent && !newParent) { + scene->d_func()->registerTopLevelItem(q); + } else if (!parent && newParent) { + scene->d_func()->unregisterTopLevelItem(q); + } + } + + if ((parent = newParent)) { + bool implicitUpdate = false; + if (parent->d_func()->scene && parent->d_func()->scene != scene) { + // Move this item to its new parent's scene + parent->d_func()->scene->addItem(q); + implicitUpdate = true; + } else if (!parent->d_func()->scene && scene) { + // Remove this item from its former scene + scene->removeItem(q); + } + + parent->d_ptr->addChild(q); + qVariantSetValue<QGraphicsItem *>(variant, q); + parent->itemChange(QGraphicsItem::ItemChildAddedChange, variant); + if (!implicitUpdate) + updateHelper(); + + // Inherit ancestor flags from the new parent. + updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); + updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape); + updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations); + + // Update item visible / enabled. + if (parent->isVisible() != visible) { + if (!parent->isVisible() || !explicitlyHidden) + setVisibleHelper(parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate); + } + if (parent->isEnabled() != enabled) { + if (!parent->isEnabled() || !explicitlyDisabled) + setEnabledHelper(parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate); + } + + } else { + // Inherit ancestor flags from the new parent. + updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); + updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape); + updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations); + + if (!deleting) { + // Update item visible / enabled. + if (!visible && !explicitlyHidden) + setVisibleHelper(true, /* explicit = */ false); + if (!enabled && !explicitlyDisabled) + setEnabledHelper(true, /* explicit = */ false); + + // If the item is being deleted, the whole scene will be updated. + updateHelper(); + } + } + + if (scene) { + // Invalidate any sort caching; arrival of a new item means we need to + // resort. + scene->d_func()->invalidateSortCache(); + } + + // Resolve opacity. + if (parent) + resolveEffectiveOpacity(parent->effectiveOpacity()); + else + resolveEffectiveOpacity(1.0); + + // Resolve depth. + resolveDepth(parent ? parent->d_ptr->depth : -1); + + // Invalidate transform cache. + invalidateSceneTransformCache(); + + // Deliver post-change notification + q->itemChange(QGraphicsItem::ItemParentHasChanged, qVariantFromValue<QGraphicsItem *>(parent)); +} + +/*! + \internal + Empty all cached pixmaps from the pixmap cache. */ void QGraphicsItemCache::purge() @@ -855,24 +963,17 @@ QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent, */ QGraphicsItem::~QGraphicsItem() { + if (d_ptr->scene && !d_ptr->parent) + d_ptr->scene->d_func()->unregisterTopLevelItem(this); + clearFocus(); - d_ptr->removeExtraItemCache(); - QVariant variant; - foreach (QGraphicsItem *child, d_ptr->children) { - if (QGraphicsItem *parent = child->parentItem()) { - qVariantSetValue<QGraphicsItem *>(variant, child); - parent->itemChange(ItemChildRemovedChange, variant); - } - delete child; - } - d_ptr->children.clear(); + d_ptr->removeExtraItemCache(); + QList<QGraphicsItem *> oldChildren = d_ptr->children; + qDeleteAll(oldChildren); + Q_ASSERT(d_ptr->children.isEmpty()); - if (QGraphicsItem *parent = parentItem()) { - qVariantSetValue<QGraphicsItem *>(variant, this); - parent->itemChange(ItemChildRemovedChange, variant); - qt_graphicsitem_removeChild(this, &parent->d_func()->children); - } + d_ptr->setParentItemHelper(0, /* deleting = */ true); if (d_ptr->scene) d_ptr->scene->d_func()->_q_removeItemLater(this); @@ -1017,103 +1118,7 @@ QGraphicsWidget *QGraphicsItem::window() const */ void QGraphicsItem::setParentItem(QGraphicsItem *parent) { - if (parent == this) { - qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this); - return; - } - if (parent == d_ptr->parent) - return; - QVariant variant; - qVariantSetValue<QGraphicsItem *>(variant, parent); - parent = qVariantValue<QGraphicsItem *>(itemChange(ItemParentChange, variant)); - if (parent == d_ptr->parent) - return; - - if (QGraphicsWidget *w = d_ptr->isWidget ? static_cast<QGraphicsWidget *>(this) : parentWidget()) { - // Update the child focus chain; when reparenting a widget that has a - // focus child, ensure that that focus child clears its focus child - // chain from our parents before it's reparented. - if (QGraphicsWidget *focusChild = w->focusWidget()) - focusChild->clearFocus(); - } - - // We anticipate geometry changes - prepareGeometryChange(); - - if (d_ptr->parent) { - // Remove from current parent - qt_graphicsitem_removeChild(this, &d_ptr->parent->d_func()->children); - qVariantSetValue<QGraphicsItem *>(variant, this); - d_ptr->parent->itemChange(ItemChildRemovedChange, variant); - } - - if ((d_ptr->parent = parent)) { - bool implicitUpdate = false; - if (parent->d_func()->scene && parent->d_func()->scene != d_ptr->scene) { - // Move this item to its new parent's scene - parent->d_func()->scene->addItem(this); - implicitUpdate = true; - } else if (!parent->d_func()->scene && d_ptr->scene) { - // Remove this item from its former scene - d_ptr->scene->removeItem(this); - } - - d_ptr->parent->d_func()->children << this; - qVariantSetValue<QGraphicsItem *>(variant, this); - d_ptr->parent->itemChange(ItemChildAddedChange, variant); - if (!implicitUpdate) - d_ptr->updateHelper(); - - // Inherit ancestor flags from the new parent. - d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); - d_ptr->updateAncestorFlag(ItemClipsChildrenToShape); - d_ptr->updateAncestorFlag(ItemIgnoresTransformations); - - // Update item visible / enabled. - if (d_ptr->parent->isVisible() != d_ptr->visible) { - if (!d_ptr->parent->isVisible() || !d_ptr->explicitlyHidden) - d_ptr->setVisibleHelper(d_ptr->parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate); - } - if (d_ptr->parent->isEnabled() != d_ptr->enabled) { - if (!d_ptr->parent->isEnabled() || !d_ptr->explicitlyDisabled) - d_ptr->setEnabledHelper(d_ptr->parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate); - } - - } else { - // Inherit ancestor flags from the new parent. - d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); - d_ptr->updateAncestorFlag(ItemClipsChildrenToShape); - d_ptr->updateAncestorFlag(ItemIgnoresTransformations); - - // Update item visible / enabled. - if (!d_ptr->visible && !d_ptr->explicitlyHidden) - d_ptr->setVisibleHelper(true, /* explicit = */ false); - if (!d_ptr->enabled && !d_ptr->explicitlyDisabled) - d_ptr->setEnabledHelper(true, /* explicit = */ false); - - d_ptr->updateHelper(); - } - - if (d_ptr->scene) { - // Invalidate any sort caching; arrival of a new item means we need to - // resort. - d_ptr->scene->d_func()->invalidateSortCache(); - } - - // Resolve opacity. - if (QGraphicsItem *p = d_ptr->parent) - d_ptr->resolveEffectiveOpacity(p->effectiveOpacity()); - else - d_ptr->resolveEffectiveOpacity(1.0); - - // Resolve depth. - d_ptr->resolveDepth(parent ? parent->d_ptr->depth : -1); - - // Invalidate transform cache. - d_ptr->invalidateSceneTransformCache(); - - // Deliver post-change notification - itemChange(QGraphicsItem::ItemParentHasChanged, qVariantFromValue<QGraphicsItem *>(parent)); + d_ptr->setParentItemHelper(parent, /* deleting = */ false); } /*! @@ -2930,11 +2935,11 @@ qreal QGraphicsItem::zValue() const /*! Sets the Z-value, or the elevation, of the item, to \a z. The elevation decides the stacking order of sibling (neighboring) items. An item of high - Z-value will be drawn on top of an item with a lower Z-value if they - share the same parent item. In addition, children of an item will always be drawn - on top of the parent, regardless of the child's Z-value. Sibling items - that share the same Z-value will be drawn in an undefined order, although - the order will stay the same for as long as the items live. + Z-value will be drawn on top of an item with a lower Z-value if they share + the same parent item. In addition, children of an item will always be + drawn on top of the parent, regardless of the child's Z-value. Sibling + items that share the same Z-value will be drawn in order of insertion; the + last inserted child is stacked above previous children. \img graphicsview-zorder.png @@ -2961,7 +2966,7 @@ void QGraphicsItem::setZValue(qreal z) qreal newZ = qreal(itemChange(ItemZValueChange, double(z)).toDouble()); if (newZ == d_ptr->z) return; - d_ptr->z = z; + d_ptr->z = newZ; d_ptr->fullUpdateHelper(); if (d_ptr->scene) { @@ -3589,15 +3594,27 @@ void QGraphicsItem::setBoundingRegionGranularity(qreal granularity) only case where the item's background should be marked as dirty even when the item isn't visible. */ -void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force) +void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force, bool updateCache) { // No scene, or if the scene is updating everything, means we have nothing // to do. The only exception is if the scene tracks the growing scene rect. - if (dirty) + QGraphicsItemCache *cache = maybeExtraItemCache(); + if (dirty && (!updateCache || !cache || cache->allExposed)) return; + if (!scene || (scene && scene->d_func()->updateAll && scene->d_func()->hasSceneRect)) return; - if (scene && (visible || force)) { + + if (updateCache && QGraphicsItem::CacheMode(cacheMode) != QGraphicsItem::NoCache) { + if (rect.isNull()) { + cache->allExposed = true; + cache->exposed.clear(); + } else { + cache->exposed.append(rect); + } + } + + if (!dirty && scene && (visible || force)) { if (rect.isNull()) dirty = 1; scene->itemUpdated(q_ptr, rect); @@ -3706,6 +3723,41 @@ void QGraphicsItemPrivate::invalidateSceneTransformCache() children.at(i)->d_ptr->invalidateSceneTransformCache(); } +/*! + \internal +*/ +void QGraphicsItemPrivate::addChild(QGraphicsItem *child) +{ + child->d_ptr->siblingIndex = children.size(); + children.append(child); +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::removeChild(QGraphicsItem *child) +{ + int idx = child->d_ptr->siblingIndex; + int size = children.size(); + for (int i = idx; i < size - 1; ++i) { + QGraphicsItem *p = children[i + 1]; + children[i] = p; + p->d_ptr->siblingIndex = i; + } + children.removeLast(); +} + +/*! + \internal +*/ +QGraphicsItemCache *QGraphicsItemPrivate::maybeExtraItemCache() const +{ + return (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); +} + +/*! + \internal +*/ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const { QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); @@ -3717,6 +3769,9 @@ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const return c; } +/*! + \internal +*/ void QGraphicsItemPrivate::removeExtraItemCache() { QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); @@ -3757,22 +3812,48 @@ bool QGraphicsItemPrivate::isProxyWidget() const */ void QGraphicsItem::update(const QRectF &rect) { - if (d_ptr->dirty) - return; - if (d_ptr->scene && isVisible()) { - if (CacheMode(d_ptr->cacheMode) != NoCache) { - QGraphicsItemCache *cache = d_ptr->extraItemCache(); - if (rect.isNull()) { - cache->allExposed = true; - cache->exposed.clear(); - } else { - cache->exposed.append(rect); - } - } - d_ptr->updateHelper(rect); - } + d_ptr->updateHelper(rect, /* force = */ false, /* updateCache = */ true); } +/*! + \internal + + Scrolls \a rect in \a pix by \a dx, \a dy. + + ### This can be done much more efficiently by using XCopyArea on X11 with + the same dst and src, and through moving pixels in the raster engine. It + can probably also be done much better on the other paint engines. +*/ +void _q_scrollPixmap(QPixmap *pix, const QRect &rect, int dx, int dy) +{ +#if 0 + QPainter painter(pix); + painter.setClipRect(rect); + painter.drawPixmap(rect.translated(dx, dy), *pix, rect); + painter.end(); +#elif defined Q_WS_X11 + GC gc = XCreateGC(X11->display, pix->handle(), 0, 0); + + XRectangle xrect; + xrect.x = rect.x(); + xrect.y = rect.y(); + xrect.width = rect.width(); + xrect.height = rect.height(); + XSetClipRectangles(X11->display, gc, 0, 0, &xrect, 1, YXBanded); + + XCopyArea(X11->display, pix->handle(), pix->handle(), gc, + rect.x(), rect.y(), rect.width(), rect.height(), + rect.x()+dx, rect.y()+dy); + XFreeGC(X11->display, gc); +#else + QPixmap newPix = *pix; + QPainter painter(&newPix); + painter.setClipRect(rect); + painter.drawPixmap(rect.translated(dx, dy), *pix, rect); + painter.end(); + *pix = newPix; +#endif +} /*! \since 4.4 @@ -3801,11 +3882,45 @@ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect) if (!d->scene) return; if (d->cacheMode != NoCache) { - // ### This is very slow, and can be done much better. If the cache is - // local and matches the below criteria for rotation and scaling, we - // can easily scroll. And if the cache is in device coordinates, we - // can scroll both the viewport and the cache. - update(rect); + QGraphicsItemCache *c; + bool scrollCache = qFuzzyCompare(dx - int(dx), qreal(0.0)) && qFuzzyCompare(dy - int(dy), qreal(0.0)) + && (c = (QGraphicsItemCache *)qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraCacheData))) + && (d->cacheMode == ItemCoordinateCache && !c->fixedSize.isValid()); + if (scrollCache) { + QPixmap pix; + if (QPixmapCache::find(c->key, pix)) { + // Adjust with 2 pixel margin. Notice the loss of precision + // when converting to QRect. + int adjust = 2; + QRectF br = boundingRect().adjusted(-adjust, -adjust, adjust, adjust); + QRect irect = rect.toRect().translated(-br.x(), -br.y()); + + _q_scrollPixmap(&pix, irect, dx, dy); + + QPixmapCache::insert(c->key, pix); + + // Translate the existing expose. + foreach (QRectF exposedRect, c->exposed) + c->exposed += exposedRect.translated(dx, dy) & rect; + + // Calculate exposure. + QRegion exposed; + QRect r = rect.toRect(); + exposed += r; + exposed -= r.translated(dx, dy); + foreach (QRect rect, exposed.rects()) + update(rect); + d_ptr->updateHelper(); + } else { + update(rect); + } + } else { + // ### This is very slow, and can be done much better. If the cache is + // local and matches the below criteria for rotation and scaling, we + // can easily scroll. And if the cache is in device coordinates, we + // can scroll both the viewport and the cache. + update(rect); + } return; } diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 07f6958..af0b077 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -113,6 +113,7 @@ public: : z(0), scene(0), parent(0), + siblingIndex(-1), index(-1), depth(0), acceptedMouseButtons(0x1f), @@ -160,11 +161,14 @@ public: void setPosHelper(const QPointF &pos, bool update); void setVisibleHelper(bool newVisible, bool explicitly, bool update = true); void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true); - void updateHelper(const QRectF &rect = QRectF(), bool force = false); + void updateHelper(const QRectF &rect = QRectF(), bool force = false, bool updateCache = false); void fullUpdateHelper(bool childrenOnly = false); void resolveEffectiveOpacity(qreal effectiveParentOpacity); void resolveDepth(int parentDepth); void invalidateSceneTransformCache(); + void addChild(QGraphicsItem *child); + void removeChild(QGraphicsItem *child); + void setParentItemHelper(QGraphicsItem *parent, bool deleting); virtual void resolveFont(uint inheritedMask) { @@ -230,6 +234,7 @@ public: }; QList<ExtraStruct> extras; + QGraphicsItemCache *maybeExtraItemCache() const; QGraphicsItemCache *extraItemCache() const; void removeExtraItemCache(); @@ -238,6 +243,7 @@ public: QGraphicsScene *scene; QGraphicsItem *parent; QList<QGraphicsItem *> children; + int siblingIndex; int index; int depth; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index ff46e2e..a9578f2 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -616,6 +616,30 @@ void QGraphicsScenePrivate::_q_emitUpdated() /*! \internal +*/ +void QGraphicsScenePrivate::registerTopLevelItem(QGraphicsItem *item) +{ + item->d_ptr->siblingIndex = topLevelItems.size(); + topLevelItems.append(item); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::unregisterTopLevelItem(QGraphicsItem *item) +{ + int idx = item->d_ptr->siblingIndex; + int size = topLevelItems.size(); + for (int i = idx; i < size - 1; ++i) { + QGraphicsItem *p = topLevelItems[i + 1]; + topLevelItems[i] = p; + p->d_ptr->siblingIndex = i; + } + topLevelItems.removeLast(); +} + +/*! + \internal Updates all items in the pending update list. At this point, the list is unlikely to contain partially constructed items. @@ -690,13 +714,6 @@ void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item) { Q_Q(QGraphicsScene); - if (QGraphicsItem *parent = item->d_func()->parent) { - QVariant variant; - qVariantSetValue<QGraphicsItem *>(variant, item); - parent->itemChange(QGraphicsItem::ItemChildRemovedChange, variant); - parent->d_func()->children.removeAll(item); - } - // Clear focus on the item to remove any reference in the focusWidget // chain. item->clearFocus(); @@ -747,10 +764,6 @@ void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item) freeSceneTransformSlots.append(transformIndex); } - // Remove all children recursively. - foreach (QGraphicsItem *child, item->children()) - _q_removeItemLater(child); - // Reset the mouse grabber if (mouseGrabberItems.contains(item)) ungrabMouse(item, /* item is dying */ true); @@ -1682,7 +1695,7 @@ inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item if (f1 != f2) return f2; qreal z1 = d1->z; qreal z2 = d2->z; - return z1 != z2 ? z1 > z2 : item1 > item2; + return z1 != z2 ? z1 > z2 : d1->siblingIndex > d2->siblingIndex; } /*! @@ -2607,7 +2620,6 @@ void QGraphicsScene::clear() } d->unindexedItems.clear(); qDeleteAll(unindexedParents); - d->indexedItems.clear(); d->freeItemIndexes.clear(); d->lastItemCount = 0; @@ -2716,7 +2728,6 @@ void QGraphicsScene::addItem(QGraphicsItem *item) qWarning("QGraphicsScene::addItem: item has already been added to this scene"); return; } - // Remove this item from its existing scene if (QGraphicsScene *oldScene = item->scene()) oldScene->removeItem(item); @@ -2756,6 +2767,10 @@ void QGraphicsScene::addItem(QGraphicsItem *item) item->d_func()->index = -1; d->startIndexTimer(); + // Add to list of toplevels if this item is a toplevel. + if (!item->d_ptr->parent) + d->registerTopLevelItem(item); + // Update the scene's sort cache settings. item->d_ptr->globalStackingOrder = -1; d->invalidateSortCache(); @@ -3107,13 +3122,15 @@ void QGraphicsScene::removeItem(QGraphicsItem *item) // Set the item's scene ptr to 0. item->d_func()->scene = 0; - // Detach the item from its parent. + // Remove from parent, or unregister from toplevels. if (QGraphicsItem *parentItem = item->parentItem()) { if (parentItem->scene()) { Q_ASSERT_X(parentItem->scene() == this, "QGraphicsScene::removeItem", "Parent item's scene is different from this item's scene"); item->setParentItem(0); } + } else { + d->unregisterTopLevelItem(item); } // Remove from our item lists. diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 9c165d1..7f441da 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -113,7 +113,10 @@ public: QList<QGraphicsItem *> dirtyItems; QList<QGraphicsItem *> pendingUpdateItems; QList<QGraphicsItem *> unpolishedItems; + QList<QGraphicsItem *> topLevelItems; QMap<QGraphicsItem *, QPointF> movingItemsInitialPositions; + void registerTopLevelItem(QGraphicsItem *item); + void unregisterTopLevelItem(QGraphicsItem *item); void _q_updateLater(); void _q_polishItems(); diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 2f7f57a..a661441 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -3510,6 +3510,7 @@ void QGraphicsView::paintEvent(QPaintEvent *event) QPainter backgroundPainter(&d->backgroundPixmap); backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip); backgroundPainter.setTransform(viewportTransform()); + backgroundPainter.setCompositionMode(QPainter::CompositionMode_Source); drawBackground(&backgroundPainter, exposedSceneRect); d->backgroundPixmapExposed = QRegion(); } @@ -3662,31 +3663,14 @@ void QGraphicsView::scrollContentsBy(int dx, int dy) && X11->use_xrender #endif ) { - // Invalidate the background pixmap - d->backgroundPixmapExposed.translate(dx, 0); - if (dx > 0) { - d->backgroundPixmapExposed += QRect(0, 0, dx, viewport()->height()); - } else if (dx < 0) { - d->backgroundPixmapExposed += QRect(viewport()->width() + dx, 0, - -dx, viewport()->height()); - } - d->backgroundPixmapExposed.translate(0, dy); - if (dy > 0) { - d->backgroundPixmapExposed += QRect(0, 0, viewport()->width(), dy); - } else if (dy < 0) { - d->backgroundPixmapExposed += QRect(0, viewport()->height() + dy, - viewport()->width(), -dy); - } - // Scroll the background pixmap - if (!d->backgroundPixmap.isNull()) { - QPixmap tmp = d->backgroundPixmap.copy(); - QBrush bgBrush = viewport()->palette().brush(viewport()->backgroundRole()); - if (!bgBrush.isOpaque()) - d->backgroundPixmap.fill(Qt::transparent); - QPainter painter(&d->backgroundPixmap); - painter.drawPixmap(dx, dy, tmp); - } + QRegion exposed; + if (!d->backgroundPixmap.isNull()) + d->backgroundPixmap.scroll(dx, dy, d->backgroundPixmap.rect(), &exposed); + + // Invalidate the background pixmap + d->backgroundPixmapExposed.translate(dx, dy); + d->backgroundPixmapExposed += exposed; } // Always replay on scroll. diff --git a/src/gui/gui.pro b/src/gui/gui.pro index f224e67..1aa6558 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -31,6 +31,7 @@ include(itemviews/itemviews.pri) include(inputmethod/inputmethod.pri) include(graphicsview/graphicsview.pri) include(util/util.pri) +include(math3d/math3d.pri) embedded: QT += network diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 8684a1b..e563fc9 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -379,6 +379,56 @@ QPixmap QPixmap::copy(const QRect &rect) const } /*! + \fn QPixmap::scroll(int dx, int dy, int x, int y, int width, int height, QRegion *exposed) + + This convenience function is equivalent to calling QPixmap::scroll(\a dx, + \a dy, QRect(\a x, \a y, \a width, \a height), \a exposed). + + \sa QWidget::scroll(), QGraphicsItem::scroll() +*/ + +/*! + Scrolls the area \a rect of this pixmap by (\a dx, \a dy). The exposed + region is left unchanged. You can optionally pass a pointer to an empty + QRegion to get the region that is \a exposed by the scroll operation. + + \snippet doc/src/snippets/code/src_gui_image_qpixmap.cpp 2 + + You cannot scroll while there is an active painter on the pixmap. + + \sa QWidget::scroll(), QGraphicsItem::scroll() +*/ +void QPixmap::scroll(int dx, int dy, const QRect &rect, QRegion *exposed) +{ + if (isNull() || (dx == 0 && dy == 0)) + return; + QRect dest = rect & this->rect(); + QRect src = dest.translated(-dx, -dy) & dest; + if (src.isEmpty()) { + if (exposed) + *exposed += dest; + return; + } + + detach(); + + if (!data->scroll(dx, dy, src)) { + // Fallback + QPixmap pix = *this; + QPainter painter(&pix); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.drawPixmap(src.translated(dx, dy), *this, src); + painter.end(); + *this = pix; + } + + if (exposed) { + *exposed += dest; + *exposed -= src.translated(dx, dy); + } +} + +/*! Assigns the given \a pixmap to this pixmap and returns a reference to this pixmap. diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index 1863273..cd3b465 100644 --- a/src/gui/image/qpixmap.h +++ b/src/gui/image/qpixmap.h @@ -155,6 +155,9 @@ public: inline QPixmap copy(int x, int y, int width, int height) const; QPixmap copy(const QRect &rect = QRect()) const; + inline void scroll(int dx, int dy, int x, int y, int width, int height, QRegion *exposed = 0); + void scroll(int dx, int dy, const QRect &rect, QRegion *exposed = 0); + int serialNumber() const; qint64 cacheKey() const; @@ -274,6 +277,11 @@ inline QPixmap QPixmap::copy(int ax, int ay, int awidth, int aheight) const return copy(QRect(ax, ay, awidth, aheight)); } +inline void QPixmap::scroll(int dx, int dy, int ax, int ay, int awidth, int aheight, QRegion *exposed) +{ + scroll(dx, dy, QRect(ax, ay, awidth, aheight), exposed); +} + inline bool QPixmap::loadFromData(const QByteArray &buf, const char *format, Qt::ImageConversionFlags flags) { diff --git a/src/gui/image/qpixmap_mac.cpp b/src/gui/image/qpixmap_mac.cpp index 26d9618..973cd78 100644 --- a/src/gui/image/qpixmap_mac.cpp +++ b/src/gui/image/qpixmap_mac.cpp @@ -1290,6 +1290,14 @@ void QMacPixmapData::copy(const QPixmapData *data, const QRect &rect) has_mask = macData->has_mask; } +bool QMacPixmapData::scroll(int dx, int dy, const QRect &rect) +{ + Q_UNUSED(dx); + Q_UNUSED(dy); + Q_UNUSED(rect); + return false; +} + /*! \since 4.2 diff --git a/src/gui/image/qpixmap_mac_p.h b/src/gui/image/qpixmap_mac_p.h index 75525c4..2b22e6b 100644 --- a/src/gui/image/qpixmap_mac_p.h +++ b/src/gui/image/qpixmap_mac_p.h @@ -68,6 +68,7 @@ public: void resize(int width, int height); void fromImage(const QImage &image, Qt::ImageConversionFlags flags); void copy(const QPixmapData *data, const QRect &rect); + bool scroll(int dx, int dy, const QRect &rect); int metric(QPaintDevice::PaintDeviceMetric metric) const; void fill(const QColor &color); diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp index 7dfab70..145b02a 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -181,6 +181,16 @@ void QRasterPixmapData::fromImage(const QImage &sourceImage, setSerialNumber(image.serialNumber()); } +// from qwindowsurface.cpp +extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); + +bool QRasterPixmapData::scroll(int dx, int dy, const QRect &rect) +{ + if (!image.isNull()) + qt_scrollRectInImage(image, rect, QPoint(dx, dy)); + return true; +} + void QRasterPixmapData::fill(const QColor &color) { uint pixel; diff --git a/src/gui/image/qpixmap_raster_p.h b/src/gui/image/qpixmap_raster_p.h index 095f378..48d5cf8 100644 --- a/src/gui/image/qpixmap_raster_p.h +++ b/src/gui/image/qpixmap_raster_p.h @@ -75,6 +75,7 @@ public: void fromFile(const QString &filename, Qt::ImageConversionFlags flags); void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + bool scroll(int dx, int dy, const QRect &rect); void fill(const QColor &color); void setMask(const QBitmap &mask); bool hasAlphaChannel() const; diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp index ff7c1b6..725caeb 100644 --- a/src/gui/image/qpixmap_x11.cpp +++ b/src/gui/image/qpixmap_x11.cpp @@ -2205,6 +2205,16 @@ void QX11PixmapData::copy(const QPixmapData *data, const QRect &rect) } } +bool QX11PixmapData::scroll(int dx, int dy, const QRect &rect) +{ + GC gc = XCreateGC(X11->display, hd, 0, 0); + XCopyArea(X11->display, hd, hd, gc, + rect.left(), rect.top(), rect.width(), rect.height(), + rect.left() + dx, rect.top() + dy); + XFreeGC(X11->display, gc); + return true; +} + #if !defined(QT_NO_XRENDER) void QX11PixmapData::convertToARGB32(bool preserveContents) { diff --git a/src/gui/image/qpixmap_x11_p.h b/src/gui/image/qpixmap_x11_p.h index 980b10e..c526402 100644 --- a/src/gui/image/qpixmap_x11_p.h +++ b/src/gui/image/qpixmap_x11_p.h @@ -74,6 +74,7 @@ public: void resize(int width, int height); void fromImage(const QImage &image, Qt::ImageConversionFlags flags); void copy(const QPixmapData *data, const QRect &rect); + bool scroll(int dx, int dy, const QRect &rect); void fill(const QColor &color); QBitmap mask() const; diff --git a/src/gui/image/qpixmapdata.cpp b/src/gui/image/qpixmapdata.cpp index 3d88f4b..245a866 100644 --- a/src/gui/image/qpixmapdata.cpp +++ b/src/gui/image/qpixmapdata.cpp @@ -73,6 +73,14 @@ void QPixmapData::copy(const QPixmapData *data, const QRect &rect) fromImage(data->toImage().copy(rect), Qt::AutoColor); } +bool QPixmapData::scroll(int dx, int dy, const QRect &rect) +{ + Q_UNUSED(dx); + Q_UNUSED(dy); + Q_UNUSED(rect); + return false; +} + void QPixmapData::setMask(const QBitmap &mask) { if (mask.size().isEmpty()) { diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h index 7296426..abb3181 100644 --- a/src/gui/image/qpixmapdata_p.h +++ b/src/gui/image/qpixmapdata_p.h @@ -78,6 +78,7 @@ public: virtual void fromFile(const QString &filename, const char *format, Qt::ImageConversionFlags flags); virtual void copy(const QPixmapData *data, const QRect &rect); + virtual bool scroll(int dx, int dy, const QRect &rect); virtual int metric(QPaintDevice::PaintDeviceMetric metric) const = 0; virtual void fill(const QColor &color) = 0; diff --git a/src/gui/inputmethod/qinputcontext_p.h b/src/gui/inputmethod/qinputcontext_p.h index 8c1b8de..a5e3d91 100644 --- a/src/gui/inputmethod/qinputcontext_p.h +++ b/src/gui/inputmethod/qinputcontext_p.h @@ -84,10 +84,6 @@ public: {} QWidget *focusWidget; - -#if defined(Q_WS_WIN) || defined(Q_WS_QWS) - static void updateImeStatus(QWidget *w, bool hasFocus); -#endif }; QT_END_NAMESPACE diff --git a/src/gui/inputmethod/qwininputcontext_p.h b/src/gui/inputmethod/qwininputcontext_p.h index 38d7e32..c6ad19e 100644 --- a/src/gui/inputmethod/qwininputcontext_p.h +++ b/src/gui/inputmethod/qwininputcontext_p.h @@ -83,6 +83,7 @@ public: static void TranslateMessage(const MSG *msg); static LRESULT DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + static void updateImeStatus(QWidget *w, bool hasFocus); static void enablePopupChild(QWidget *w, bool e); static void enable(QWidget *w, bool e); diff --git a/src/gui/inputmethod/qwininputcontext_win.cpp b/src/gui/inputmethod/qwininputcontext_win.cpp index 720f0b8..663184f 100644 --- a/src/gui/inputmethod/qwininputcontext_win.cpp +++ b/src/gui/inputmethod/qwininputcontext_win.cpp @@ -753,7 +753,7 @@ inline void enableIme(QWidget *w, bool value) } -void QInputContextPrivate::updateImeStatus(QWidget *w, bool hasFocus) +void QWinInputContext::updateImeStatus(QWidget *w, bool hasFocus) { if (!w) return; @@ -822,6 +822,15 @@ void QWinInputContext::enable(QWidget *w, bool e) void QWinInputContext::setFocusWidget(QWidget *w) { + QWidget *oldFocus = focusWidget(); + if (oldFocus == w) + return; + if (w) { + QWinInputContext::updateImeStatus(w, true); + } else { + if (oldFocus) + QWinInputContext::updateImeStatus(oldFocus , false); + } QInputContext::setFocusWidget(w); update(); } diff --git a/src/gui/inputmethod/qwsinputcontext_p.h b/src/gui/inputmethod/qwsinputcontext_p.h index 20811da..835cb3f 100644 --- a/src/gui/inputmethod/qwsinputcontext_p.h +++ b/src/gui/inputmethod/qwsinputcontext_p.h @@ -87,6 +87,7 @@ public: static bool translateIMEvent(QWidget *w, const QWSIMEvent *e); static bool translateIMQueryEvent(QWidget *w, const QWSIMQueryEvent *e); static bool translateIMInitEvent(const QWSIMInitEvent *e); + static void updateImeStatus(QWidget *w, bool hasFocus); }; QT_END_NAMESPACE diff --git a/src/gui/inputmethod/qwsinputcontext_qws.cpp b/src/gui/inputmethod/qwsinputcontext_qws.cpp index 46ac13d..6180c48 100644 --- a/src/gui/inputmethod/qwsinputcontext_qws.cpp +++ b/src/gui/inputmethod/qwsinputcontext_qws.cpp @@ -73,10 +73,17 @@ void QWSInputContext::reset() void QWSInputContext::setFocusWidget( QWidget *w ) { - QWidget *oldFocus = focusWidget(); + QWidget *oldFocus = focusWidget(); if (oldFocus == w) return; + if (w) { + QWSInputContext::updateImeStatus(w, true); + } else { + if (oldFocus) + QWSInputContext::updateImeStatus(oldFocus, false); + } + if (oldFocus) { QWidget *tlw = oldFocus->window(); int winid = tlw->internalWinId(); @@ -224,7 +231,7 @@ bool QWSInputContext::translateIMEvent(QWidget *w, const QWSIMEvent *e) Q_GUI_EXPORT void (*qt_qws_inputMethodStatusChanged)(QWidget*) = 0; -void QInputContextPrivate::updateImeStatus(QWidget *w, bool hasFocus) +void QWSInputContext::updateImeStatus(QWidget *w, bool hasFocus) { Q_UNUSED(hasFocus); diff --git a/src/gui/inputmethod/qximinputcontext_x11.cpp b/src/gui/inputmethod/qximinputcontext_x11.cpp index c320fb4..b43a134 100644 --- a/src/gui/inputmethod/qximinputcontext_x11.cpp +++ b/src/gui/inputmethod/qximinputcontext_x11.cpp @@ -438,7 +438,8 @@ void QXIMInputContext::create_xim() // server (like SCIM) has been launched without // requiring the user to manually switch focus. if (focusWidget->testAttribute(Qt::WA_InputMethodEnabled) - && focusWidget->testAttribute(Qt::WA_WState_Created)) + && focusWidget->testAttribute(Qt::WA_WState_Created) + && focusWidget->isEnabled()) setFocusWidget(focusWidget); } // following code fragment is not required for immodule diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 7bd6207..09ea444 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -1421,10 +1421,10 @@ bool QAbstractItemView::viewportEvent(QEvent *event) case QEvent::HoverEnter: { QHoverEvent *he = static_cast<QHoverEvent*>(event); d->hover = indexAt(he->pos()); - d->viewport->update(visualRect(d->hover)); + update(d->hover); break; } case QEvent::HoverLeave: { - d->viewport->update(visualRect(d->hover)); // update old + update(d->hover); // update old d->hover = QModelIndex(); break; } case QEvent::HoverMove: { @@ -1637,7 +1637,7 @@ void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event) if (d->isIndexValid(index) && d->isIndexEnabled(index) && d->sendDelegateEvent(index, event)) - d->viewport->update(visualRect(index)); + update(index); return; } @@ -2320,7 +2320,7 @@ bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEve } if (d->sendDelegateEvent(index, event)) { - d->viewport->update(visualRect(index)); + update(index); return true; } @@ -2921,7 +2921,7 @@ void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelInde } if (isVisible() && !d->delayedPendingLayout) { // otherwise the items will be update later anyway - d->viewport->update(visualRect(topLeft)); + update(topLeft); } return; } @@ -3126,9 +3126,7 @@ void QAbstractItemView::selectionChanged(const QItemSelection &selected, { Q_D(QAbstractItemView); if (isVisible() && updatesEnabled()) { - d->setDirtyRegion(visualRegionForSelection(deselected)); - d->setDirtyRegion(visualRegionForSelection(selected)); - d->updateDirtyRegion(); + d->viewport->update(visualRegionForSelection(deselected) | visualRegionForSelection(selected)); } } @@ -3156,15 +3154,13 @@ void QAbstractItemView::currentChanged(const QModelIndex ¤t, const QModelI closeEditor(editor, QAbstractItemDelegate::NoHint); } if (isVisible()) { - d->setDirtyRegion(visualRect(previous)); - d->updateDirtyRegion(); + update(previous); } } if (isVisible() && current.isValid() && !d->autoScrollTimer.isActive()) { if (d->autoScroll) scrollTo(current); - d->setDirtyRegion(visualRect(current)); - d->updateDirtyRegion(); + update(current); edit(current, CurrentChanged, 0); if (current.row() == (d->model->rowCount(d->root) - 1)) d->_q_fetchMore(); diff --git a/src/gui/itemviews/qheaderview.cpp b/src/gui/itemviews/qheaderview.cpp index 5bd82d4..aacfab0 100644 --- a/src/gui/itemviews/qheaderview.cpp +++ b/src/gui/itemviews/qheaderview.cpp @@ -1971,20 +1971,19 @@ void QHeaderView::currentChanged(const QModelIndex ¤t, const QModelIndex & if (d->orientation == Qt::Horizontal && current.column() != old.column()) { if (old.isValid() && old.parent() == d->root) - d->setDirtyRegion(QRect(sectionViewportPosition(old.column()), 0, + d->viewport->update(QRect(sectionViewportPosition(old.column()), 0, sectionSize(old.column()), d->viewport->height())); if (current.isValid() && current.parent() == d->root) - d->setDirtyRegion(QRect(sectionViewportPosition(current.column()), 0, + d->viewport->update(QRect(sectionViewportPosition(current.column()), 0, sectionSize(current.column()), d->viewport->height())); } else if (d->orientation == Qt::Vertical && current.row() != old.row()) { if (old.isValid() && old.parent() == d->root) - d->setDirtyRegion(QRect(0, sectionViewportPosition(old.row()), + d->viewport->update(QRect(0, sectionViewportPosition(old.row()), d->viewport->width(), sectionSize(old.row()))); if (current.isValid() && current.parent() == d->root) - d->setDirtyRegion(QRect(0, sectionViewportPosition(current.row()), + d->viewport->update(QRect(0, sectionViewportPosition(current.row()), d->viewport->width(), sectionSize(current.row()))); } - d->updateDirtyRegion(); } diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index 07f0a38..a5a194f 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -588,7 +588,7 @@ void QListView::scrollTo(const QModelIndex &index, ScrollHint hint) const QRect rect = visualRect(index); if (hint == EnsureVisible && d->viewport->rect().contains(rect)) { - d->setDirtyRegion(rect); + d->viewport->update(rect); return; } @@ -755,7 +755,7 @@ void QListView::scrollContentsBy(int dx, int dy) // update the dragged items if (d->viewMode == IconMode) // ### move to dynamic class if (!d->dynamicListView->draggedItems.isEmpty()) - d->setDirtyRegion(d->dynamicListView->draggedItemsRect().translated(dx, dy)); + d->viewport->update(d->dynamicListView->draggedItemsRect().translated(dx, dy)); } /*! @@ -835,7 +835,7 @@ void QListView::mouseMoveEvent(QMouseEvent *e) && d->selectionMode != NoSelection) { QRect rect(d->pressedPosition, e->pos() + QPoint(horizontalOffset(), verticalOffset())); rect = rect.normalized(); - d->setDirtyRegion(d->mapToViewport(rect.united(d->elasticBand), d->viewMode == QListView::ListMode)); + d->viewport->update(d->mapToViewport(rect.united(d->elasticBand), d->viewMode == QListView::ListMode)); d->elasticBand = rect; } } @@ -849,7 +849,7 @@ void QListView::mouseReleaseEvent(QMouseEvent *e) QAbstractItemView::mouseReleaseEvent(e); // #### move this implementation into a dynamic class if (d->showElasticBand && d->elasticBand.isValid()) { - d->setDirtyRegion(d->mapToViewport(d->elasticBand, d->viewMode == QListView::ListMode)); + d->viewport->update(d->mapToViewport(d->elasticBand, d->viewMode == QListView::ListMode)); d->elasticBand = QRect(); } } @@ -914,11 +914,11 @@ void QListView::dragMoveEvent(QDragMoveEvent *e) if (d->canDecode(e)) { // get old dragged items rect QRect itemsRect = d->dynamicListView->itemsRect(d->dynamicListView->draggedItems); - d->setDirtyRegion(itemsRect.translated(d->dynamicListView->draggedItemsDelta())); + d->viewport->update(itemsRect.translated(d->dynamicListView->draggedItemsDelta())); // update position d->dynamicListView->draggedItemsPos = e->pos(); // get new items rect - d->setDirtyRegion(itemsRect.translated(d->dynamicListView->draggedItemsDelta())); + d->viewport->update(itemsRect.translated(d->dynamicListView->draggedItemsDelta())); // set the item under the cursor to current QModelIndex index; if (d->movement == Snap) { @@ -1007,12 +1007,12 @@ void QListView::internalDrop(QDropEvent *event) for (int i = 0; i < indexes.count(); ++i) { QModelIndex index = indexes.at(i); QRect rect = rectForIndex(index); - d->setDirtyRegion(d->mapToViewport(rect, d->viewMode == QListView::ListMode)); + d->viewport->update(d->mapToViewport(rect, d->viewMode == QListView::ListMode)); QPoint dest = rect.topLeft() + delta; if (isRightToLeft()) dest.setX(d->flipX(dest.x()) - rect.width()); d->dynamicListView->moveItem(index.row(), dest); - d->setDirtyRegion(visualRect(index)); + update(index); } stopAutoScroll(); d->dynamicListView->draggedItems.clear(); @@ -1455,9 +1455,9 @@ void QListView::setPositionForIndex(const QPoint &position, const QModelIndex &i if (index.row() >= d->dynamicListView->items.count()) return; const QSize oldContents = d->contentsSize(); - d->setDirtyRegion(visualRect(index)); // update old position + update(index); // update old position d->dynamicListView->moveItem(index.row(), position); - d->setDirtyRegion(visualRect(index)); // update new position + update(index); // update new position if (d->contentsSize() != oldContents) updateGeometries(); // update the scroll bars diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp index 2902768..6ec79a9 100644 --- a/src/gui/itemviews/qtableview.cpp +++ b/src/gui/itemviews/qtableview.cpp @@ -2010,7 +2010,7 @@ void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint) } } - d->setDirtyRegion(visualRect(index)); + update(index); } /*! diff --git a/src/gui/itemviews/qtreeview.cpp b/src/gui/itemviews/qtreeview.cpp index 0117db2..6dddfab 100644 --- a/src/gui/itemviews/qtreeview.cpp +++ b/src/gui/itemviews/qtreeview.cpp @@ -1140,7 +1140,7 @@ void QTreeView::scrollTo(const QModelIndex &index, ScrollHint hint) if (rect.isEmpty()) { // nothing to do } else if (hint == EnsureVisible && area.contains(rect)) { - d->setDirtyRegion(rect); + d->viewport->update(rect); // nothing to do } else { bool above = (hint == EnsureVisible diff --git a/src/gui/kernel/qaction.cpp b/src/gui/kernel/qaction.cpp index abb17d7..c6addc1 100644 --- a/src/gui/kernel/qaction.cpp +++ b/src/gui/kernel/qaction.cpp @@ -1151,6 +1151,8 @@ void QAction::activate(ActionEvent event) // the checked action of an exclusive group cannot be unchecked if (d->checked && (d->group && d->group->isExclusive() && d->group->checkedAction() == this)) { + if (guard) + emit triggered(true); QMetaObject::removeGuard(&guard); return; } diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 09f0f91..d5a96bd 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -2090,8 +2090,9 @@ void QApplicationPrivate::setFocusWidget(QWidget *focus, Qt::FocusReason reason) if(focus && QApplicationPrivate::focus_widget == focus) { if (focus->testAttribute(Qt::WA_InputMethodEnabled)) { QInputContext *qic = focus->inputContext(); - if (qic && focus_widget->testAttribute(Qt::WA_WState_Created)) - qic->setFocusWidget( focus_widget ); + if (qic && focus->testAttribute(Qt::WA_WState_Created) + && focus->isEnabled()) + qic->setFocusWidget(focus); } QFocusEvent in(QEvent::FocusIn, reason); QPointer<QWidget> that = focus; diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index 5f8c572..ae99e83 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -1638,15 +1638,6 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event bool inNonClientArea = false; GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(where), 0, &where); - if(ekind == kEventMouseMoved && qt_mac_app_fullscreen && - QApplication::desktop()->screenNumber(QPoint(where.h, where.v)) == - QApplication::desktop()->primaryScreen()) { - if(where.v <= 0) - ShowMenuBar(); - else if(qt_mac_window_at(where.h, where.v, 0) != inMenuBar) - HideMenuBar(); - } - #if defined(DEBUG_MOUSE_MAPS) const char *edesc = 0; switch(ekind) { diff --git a/src/gui/kernel/qapplication_qws.cpp b/src/gui/kernel/qapplication_qws.cpp index 2deda8e..fcfd2a4 100644 --- a/src/gui/kernel/qapplication_qws.cpp +++ b/src/gui/kernel/qapplication_qws.cpp @@ -195,14 +195,14 @@ QString qws_dataDir() if (!result.isEmpty()) return result; QByteArray dataDir = QString(QLatin1String("/tmp/qtembedded-%1")).arg(qws_display_id).toLocal8Bit(); - if (mkdir(dataDir, 0700)) { + if (QT_MKDIR(dataDir, 0700)) { if (errno != EEXIST) { qFatal("Cannot create Qt for Embedded Linux data directory: %s", dataDir.constData()); } } - struct stat buf; - if (lstat(dataDir, &buf)) + QT_STATBUF buf; + if (QT_LSTAT(dataDir, &buf)) qFatal("stat failed for Qt for Embedded Linux data directory: %s", dataDir.constData()); if (!S_ISDIR(buf.st_mode)) @@ -2280,7 +2280,8 @@ void qt_init(QApplicationPrivate *priv, int type) qt_appType = QApplication::Type(type); qws_single_process = true; QWSServer::startup(flags); - setenv("QWS_DISPLAY", qws_display_spec.constData(), 0); + if (!display) // if not already set + qputenv("QWS_DISPLAY", qws_display_spec); } if(qt_is_gui_used) { diff --git a/src/gui/kernel/qmime_mac.cpp b/src/gui/kernel/qmime_mac.cpp index cf1d747..b2eeb5c 100644 --- a/src/gui/kernel/qmime_mac.cpp +++ b/src/gui/kernel/qmime_mac.cpp @@ -127,32 +127,44 @@ CFStringRef qt_mac_mime_typeUTI = CFSTR("com.pasteboard.trolltech.marker"); /*! \class QMacPasteboardMime - \brief The QMacPasteboardMime class maps open-standard MIME to Mac flavors. + \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 io \ingroup draganddrop \ingroup misc - Qt's drag and drop support and clipboard facilities use the MIME - standard. On X11, this maps trivially to the Xdnd protocol, but on - Mac although some applications use MIME types to describe clipboard - formats, others use arbitrary non-standardized naming conventions, - or unnamed built-in Mac formats. + 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. - By instantiating subclasses of QMacPasteboardMime that provide conversions - between Mac flavors and MIME formats, you can convert proprietary - clipboard formats to MIME formats. + 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. - Qt has predefined support for the following Mac flavors: + A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation. + + Qt has predefined support for the following UTIs: \list - \i kScrapFlavorTypeUnicode - converted to "text/plain;charset=ISO-10646-UCS-2" - \i kScrapFlavorTypeText - converted to "text/plain;charset=system" or "text/plain" - \i kScrapFlavorTypePicture - converted to "application/x-qt-image" - \i typeFileURL - converted to "text/uri-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 com.apple.traditional-mac-plain-text - converts to "text/plain" + \i com.apple.pict - converts to "application/x-qt-image" \endlist - You can check if a MIME type is convertible using canConvert() and - can perform conversions with convertToMime() and convertFromMime(). + 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 @@ -841,6 +853,80 @@ QList<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime 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; +} + #ifdef QT3_SUPPORT class QMacPasteboardMimeQt3Any : public QMacPasteboardMime { private: @@ -1043,6 +1129,7 @@ void QMacPasteboardMime::initialize() new QMacPasteboardMimePlainText; new QMacPasteboardMimeHTMLText; new QMacPasteboardMimeFileUri; + new QMacPasteboardMimeUrl; new QMacPasteboardMimeTypeName; //make sure our "non-standard" types are always last! --Sam new QMacPasteboardMimeAny; diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index f92d660..8ce0231 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -2949,10 +2949,15 @@ void QWidgetPrivate::setEnabled_helper(bool enable) #if defined(Q_WS_MAC) setEnabled_helper_sys(enable); #endif -#if defined (Q_WS_WIN) - if (q->hasFocus()) - QInputContextPrivate::updateImeStatus(q, true); -#endif + if (q->testAttribute(Qt::WA_InputMethodEnabled) && q->hasFocus()) { + QInputContext *qic = inputContext(); + if (enable) { + qic->setFocusWidget(q); + } else { + qic->reset(); + qic->setFocusWidget(0); + } + } QEvent e(QEvent::EnabledChange); QApplication::sendEvent(q, &e); #ifdef QT3_SUPPORT @@ -5879,6 +5884,8 @@ QWidget *QWidget::focusWidget() const /*! Returns the next widget in this widget's focus chain. + + \sa previousInFocusChain */ QWidget *QWidget::nextInFocusChain() const { @@ -5886,6 +5893,18 @@ QWidget *QWidget::nextInFocusChain() const } /*! + Returns the previous widget in this widget's focus chain. + + \sa nextInFocusChain + + \since 4.6 +*/ +QWidget *QWidget::previousInFocusChain() const +{ + return const_cast<QWidget *>(d_func()->focus_prev); +} + +/*! \property QWidget::isActiveWindow \brief whether this widget's window is the active window @@ -7612,16 +7631,10 @@ bool QWidget::event(QEvent *event) } break; case QEvent::FocusIn: -#if defined(Q_WS_WIN) - QInputContextPrivate::updateImeStatus(this, true); -#endif focusInEvent((QFocusEvent*)event); break; case QEvent::FocusOut: -#if defined(Q_WS_WIN) - QInputContextPrivate::updateImeStatus(this, false); -#endif focusOutEvent((QFocusEvent*)event); break; @@ -9786,7 +9799,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) parentWidget()->d_func()->enforceNativeChildren(); if (on && !internalWinId() && testAttribute(Qt::WA_WState_Created)) d->createWinId(); - if (ic) + if (ic && isEnabled()) ic->setFocusWidget(this); break; } @@ -9817,10 +9830,6 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) #endif break; case Qt::WA_InputMethodEnabled: { -#if defined(Q_WS_WIN) || (defined(Q_WS_QWS) && !defined(QT_NO_QWS_INPUTMETHODS)) - if (hasFocus()) - QInputContextPrivate::updateImeStatus(this, true); -#endif QInputContext *ic = d->ic; if (!ic) { // implicitly create input context only if we have a focus @@ -9828,7 +9837,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) ic = d->inputContext(); } if (ic) { - if (on && hasFocus() && ic->focusWidget() != this) { + if (on && hasFocus() && ic->focusWidget() != this && isEnabled()) { ic->setFocusWidget(this); } else if (!on && ic->focusWidget() == this) { ic->reset(); diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index f54ebf9..309463e 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -539,6 +539,7 @@ public: QWidget *focusWidget() const; QWidget *nextInFocusChain() const; + QWidget *previousInFocusChain() const; // drag and drop bool acceptDrops() const; diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index 7a586e1..3b011d5 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -392,21 +392,15 @@ QWidget *qt_mac_find_window(OSWindowRef window) inline static void qt_mac_set_fullscreen_mode(bool b) { - extern bool qt_mac_app_fullscreen; //qapplication_mac.cpp + extern bool qt_mac_app_fullscreen; //qapplication_mac.mm if(qt_mac_app_fullscreen == b) return; qt_mac_app_fullscreen = b; -#if QT_MAC_USE_COCOA - if(b) - SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar); - else + if (b) { + SetSystemUIMode(kUIModeAllSuppressed, 0); + } else { SetSystemUIMode(kUIModeNormal, 0); -#else - if(b) - HideMenuBar(); - else - ShowMenuBar(); -#endif + } } Q_GUI_EXPORT OSViewRef qt_mac_nativeview_for(const QWidget *w) @@ -4450,14 +4444,6 @@ void QWidgetPrivate::setMask_sys(const QRegion ®ion) #endif } -extern "C" { - typedef struct CGSConnection *CGSConnectionRef; - typedef struct CGSWindow *CGSWindowRef; - extern OSStatus CGSSetWindowAlpha(CGSConnectionRef, CGSWindowRef, float); - extern CGSWindowRef GetNativeWindowFromWindowRef(WindowRef); - extern CGSConnectionRef _CGSDefaultConnection(); -} - void QWidgetPrivate::setWindowOpacity_sys(qreal level) { Q_Q(QWidget); @@ -4470,12 +4456,11 @@ void QWidgetPrivate::setWindowOpacity_sys(qreal level) if (!q->testAttribute(Qt::WA_WState_Created)) return; -#if QT_MAC_USE_COCOA OSWindowRef oswindow = qt_mac_window_for(q); +#if QT_MAC_USE_COCOA [oswindow setAlphaValue:level]; #else - CGSSetWindowAlpha(_CGSDefaultConnection(), - GetNativeWindowFromWindowRef(qt_mac_window_for(q)), level); + SetWindowAlpha(oswindow, level); #endif } diff --git a/src/gui/math3d/math3d.pri b/src/gui/math3d/math3d.pri new file mode 100644 index 0000000..581adbd --- /dev/null +++ b/src/gui/math3d/math3d.pri @@ -0,0 +1,20 @@ +HEADERS += \ + math3d/qfixedpt.h \ + math3d/qgenericmatrix.h \ + math3d/qmath3dglobal.h \ + math3d/qmath3dutil_p.h \ + math3d/qmatrix4x4.h \ + math3d/qquaternion.h \ + math3d/qvector2d.h \ + math3d/qvector3d.h \ + math3d/qvector4d.h + +SOURCES += \ + math3d/qfixedpt.cpp \ + math3d/qgenericmatrix.cpp \ + math3d/qmath3dutil.cpp \ + math3d/qmatrix4x4.cpp \ + math3d/qquaternion.cpp \ + math3d/qvector2d.cpp \ + math3d/qvector3d.cpp \ + math3d/qvector4d.cpp diff --git a/src/gui/math3d/qfixedpt.cpp b/src/gui/math3d/qfixedpt.cpp new file mode 100644 index 0000000..93f2150 --- /dev/null +++ b/src/gui/math3d/qfixedpt.cpp @@ -0,0 +1,711 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfixedpt.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + Returns the fixed-point square root of \a value. +*/ +qint64 qt_math3d_fixed_sqrt(qint64 value) +{ + qint64 result = 0; + qint64 bit = ((qint64)1) << 62; + while (bit > value) + bit >>= 2; + while (bit != 0) { + if (value >= (result + bit)) { + value -= result + bit; + result += (bit << 1); + } + result >>= 1; + bit >>= 2; + } + return result; +} + +/*! + \class QFixedPt + \brief The QFixedPt class represents fixed-point numbers within a 32-bit integer with a configurable precision. + + The template parameter is the number of bits of precision after + the decimal point. For example, QFixedPt<5> indicates that there + are 27 bits before the decimal point, and 5 bits of precision after + the decimal point. +*/ + +/*! + \fn QFixedPt::QFixedPt() + + Constructs a default fixed-point number. The initial value + is undefined. +*/ + +/*! + \fn QFixedPt::QFixedPt(int value) + + Constructs a fixed-point number from the integer \a value. +*/ + +/*! + \fn QFixedPt::QFixedPt(qreal value) + + Constructs a fixed-point number from the floating-point \a value. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator=(int value) + + Assigns the integer \a value to this fixed-point variable. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator=(qreal value) + + Assigns the floating-point \a value to this fixed-point variable. +*/ + +/*! + \fn int QFixedPt::bits() const + + Returns the raw bits that represent the fixed-point value of this object. + + \sa setBits() +*/ + +/*! + \fn void QFixedPt::setBits(int value) + + Sets the raw bits that represent the fixed-point value of + this object to \a value. + + \sa bits() +*/ + +#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) + +/*! + \fn QFixedPt<Prec> QFixedPt::toPrecision() const + + Returns this fixed-point number, converted to the new fixed-point + precision Prec. + + \sa qFixedPtToPrecision() +*/ + +#endif + +/*! + \fn QFixedPt<Prec> qFixedPtToPrecision(const QFixedPt<PrecBits>& value) + + Returns the fixed-point number \a value, converted to the new fixed-point + precision Prec. + + \sa QFixedPt::toPrecision() +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator+=(const QFixedPt<PrecBits>& value) + + Adds \a value to this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator+=(int value) + + Adds an integer \a value to this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator+=(qreal value) + + Adds a floating-point \a value to this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator-=(const QFixedPt<PrecBits>& value) + + Subtracts \a value from this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator-=(int value) + + Subtracts an integer \a value from this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator-=(qreal value) + + Subtracts a floating-point \a value from this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator*=(const QFixedPt<PrecBits>& value) + + Multiplies this fixed-point number by \a value. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator*=(int value) + + Multiplies this fixed-point number by an integer \a value. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator*=(qreal value) + + Multiplies this fixed-point number by a floating-point \a value. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator/=(const QFixedPt<PrecBits>& value) + + Divides this fixed-point number by \a value. Division by zero + will result in zero. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator/=(int value) + + Divides this fixed-point number by an integer \a value. Division + by zero will result in zero. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator/=(qreal value) + + Divides this fixed-point number by a floating-point \a value. Division + by zero will result in zero. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator<<=(int value) + + Shifts this fixed-point number left by \a value bits. +*/ + +/*! + \fn QFixedPt<PrecBits>& QFixedPt::operator>>=(int value) + + Shifts this fixed-point number right by \a value bits. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator<<(int value) const + + Returns the result of shifting this fixed-point number + left by \a value bits. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator>>(int value) const + + Returns the result of shifting this fixed-point number + right by \a value bits. +*/ + +/*! + \fn bool QFixedPt::operator==(const QFixedPt<PrecBits>& value) const + + Returns true if this fixed-point number is equal to \a value; + false otherwise. +*/ + +/*! + \fn bool operator==(const QFixedPt<PrecBits>& v1, int v2) + \relates QFixedPt + + Returns true if \a v1 is equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator==(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator==(const QFixedPt<PrecBits>& v1, qreal v2) + \relates QFixedPt + + Returns true if \a v1 is equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator==(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is equal to \a v2; false otherwise. +*/ + +/*! + \fn bool QFixedPt::operator!=(const QFixedPt<PrecBits>& value) const + + Returns true if this fixed-point number is not equal to \a value; + false otherwise. +*/ + +/*! + \fn bool operator!=(const QFixedPt<PrecBits>& v1, int v2) + \relates QFixedPt + + Returns true if \a v1 is not equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator!=(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is not equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator!=(const QFixedPt<PrecBits>& v1, qreal v2) + \relates QFixedPt + + Returns true if \a v1 is not equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator!=(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is not equal to \a v2; false otherwise. +*/ + +/*! + \fn bool QFixedPt::operator<=(const QFixedPt<PrecBits>& value) const + + Returns true if this fixed-point number is less than or equal to + \a value; false otherwise. +*/ + +/*! + \fn bool operator<=(const QFixedPt<PrecBits>& v1, int v2) + \relates QFixedPt + + Returns true if \a v1 is less than or equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator<=(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is less than or equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator<=(const QFixedPt<PrecBits>& v1, qreal v2) + \relates QFixedPt + + Returns true if \a v1 is less than or equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator<=(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is less than or equal to \a v2; false otherwise. +*/ + +/*! + \fn bool QFixedPt::operator<(const QFixedPt<PrecBits>& value) const + + Returns true if this fixed-point number is less than \a value; + false otherwise. +*/ + +/*! + \fn bool operator<(const QFixedPt<PrecBits>& v1, int v2) + \relates QFixedPt + + Returns true if \a v1 is less than \a v2; false otherwise. +*/ + +/*! + \fn bool operator<(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is less than \a v2; false otherwise. +*/ + +/*! + \fn bool operator<(const QFixedPt<PrecBits>& v1, qreal v2) + \relates QFixedPt + + Returns true if \a v1 is less than \a v2; false otherwise. +*/ + +/*! + \fn bool operator<(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is less than \a v2; false otherwise. +*/ + +/*! + \fn bool QFixedPt::operator>=(const QFixedPt<PrecBits>& value) const + + Returns true if this fixed-point number is greater than or equal to + \a value; false otherwise. +*/ + +/*! + \fn bool operator>=(const QFixedPt<PrecBits>& v1, int v2) + \relates QFixedPt + + Returns true if \a v1 is greater than or equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator>=(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is greater than or equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator>=(const QFixedPt<PrecBits>& v1, qreal v2) + \relates QFixedPt + + Returns true if \a v1 is greater than or equal to \a v2; false otherwise. +*/ + +/*! + \fn bool operator>=(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is greater than or equal to \a v2; false otherwise. +*/ + +/*! + \fn bool QFixedPt::operator>(const QFixedPt<PrecBits>& value) const + + Returns true if this fixed-point number is greater than \a value; + false otherwise. +*/ + +/*! + \fn bool operator>(const QFixedPt<PrecBits>& v1, int v2) + \relates QFixedPt + + Returns true if \a v1 is greater than \a v2; false otherwise. +*/ + +/*! + \fn bool operator>(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is greater than \a v2; false otherwise. +*/ + +/*! + \fn bool operator>(const QFixedPt<PrecBits>& v1, qreal v2) + \relates QFixedPt + + Returns true if \a v1 is greater than \a v2; false otherwise. +*/ + +/*! + \fn bool operator>(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns true if \a v1 is greater than \a v2; false otherwise. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator+(const QFixedPt<PrecBits>& value) const + + Returns the result of adding this fixed-point number and \a value. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator+(int value) const + + Returns the result of adding this fixed-point number and \a value. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator+(qreal value) const + + Returns the result of adding this fixed-point number and \a value. +*/ + +/*! + \fn QFixedPt<PrecBits> operator+(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns the result of adding \a v1 and \a v2. +*/ + +/*! + \fn QFixedPt<PrecBits> operator+(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns the result of adding \a v1 and \a v2. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator-(const QFixedPt<PrecBits>& value) const + + Returns the result of subtracting \a value from this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator-(int value) const + + Returns the result of subtracting \a value from this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator-(qreal value) const + + Returns the result of subtracting \a value from this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits> operator-(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns the result of subtracting \a v2 from \a v1. +*/ + +/*! + \fn QFixedPt<PrecBits> operator-(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns the result of subtracting \a v2 from \a v1. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator*(const QFixedPt<PrecBits>& value) const + + Returns the result of multiplying this fixed-point number by \a value. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator*(int value) const + + Returns the result of multiplying this fixed-point number by \a value. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator*(qreal value) const + + Returns the result of multiplying this fixed-point number by \a value. +*/ + +/*! + \fn QFixedPt<PrecBits> operator*(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns the result of multiplying \a v1 by \a v2. +*/ + +/*! + \fn QFixedPt<PrecBits> operator*(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns the result of multiplying \a v1 by \a v2. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator/(const QFixedPt<PrecBits>& value) const + + Returns the result of dividing this fixed-point number by \a value. + Division by zero will result in zero. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator/(int value) const + + Returns the result of dividing this fixed-point number by \a value. + Division by zero will result in zero. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator/(qreal value) const + + Returns the result of dividing this fixed-point number by \a value. + Division by zero will result in zero. +*/ + +/*! + \fn QFixedPt<PrecBits> operator/(int v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns the result of dividing \a v1 by \a v2. Division by zero will + result in zero. +*/ + +/*! + \fn QFixedPt<PrecBits> operator/(qreal v1, const QFixedPt<PrecBits>& v2) + \relates QFixedPt + + Returns the result of dividing \a v1 by \a v2. Division by zero will + result in zero. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::operator-() const + + Returns the negation of this fixed-point number. +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::sqrt() const + + Returns the square root of this fixed-point number. + + \sa sqrtF() +*/ + +/*! + \fn qreal QFixedPt::sqrtF() const + + Return the square root of this fixed-point number as a + floating-point value. + + \sa sqrt() +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::round() const + + Returns this fixed-point number, rounded to the nearest integer. + + \sa floor(), ceil(), truncate() +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::floor() const; + + Returns the largest integer that is less than or equal to + this fixed-point number. + + \sa round(), ceil(), truncate() +*/ + +/*! + \fn QFixedPt<PrecBits> QFixedPt::ceil() const + + Returns the smallest integer that is greater than or equal to + this fixed-point number. + + \sa round(), floor(), truncate() +*/ + +/*! + \fn int QFixedPt::truncate() const + + Returns this fixed-point number with the bits after the + decimal point truncated. + + \sa round(), floor(), ceil() +*/ + +/*! + \fn int QFixedPt::toInt() const + + Returns this fixed-point number, rounded to the nearest integer. + + \sa toReal() +*/ + +/*! + \fn qreal QFixedPt::toReal() const + + Returns this fixed-point number as a floating-point value. + + \sa toInt() +*/ + +/*! + \fn int qCeil(const QFixedPt<PrecBits>& value) + \relates QFixedPt + + Returns the smallest integer that is greater than or equal to + \a value. + + \sa qFloor(), qRound(), QFixedPt::ceil() +*/ + +/*! + \fn int qFloor(const QFixedPt<PrecBits>& value) + \relates QFixedPt + + Returns the largest integer that is less than or equal to + \a value. + + \sa qCeil(), qRound(), QFixedPt::floor() +*/ + +/*! + \fn int qRound(const QFixedPt<PrecBits>& value) + \relates QFixedPt + + Returns \a value, rounded to the nearest integer. + + \sa qCeil(), qFloor(), QFixedPt::round() +*/ + +/*! + \fn bool qFuzzyCompare(const QFixedPt<PrecBits>& v1, const QFixedPt<PrecBits>& v2, int compareBits) + \relates QFixedPt + + Returns true if \a v1 is almost equal to \a v2; false otherwise. + The \a compareBits parameter specifies the number of bits of precision + that should be considered relevant when performing the comparison. + By default, \a compareBits is PrecBits / 4. +*/ + +/*! + \fn bool qIsNull(const QFixedPt<PrecBits>& v) + \relates QFixedPt + + Returns true if \a v is zero; false otherwise. +*/ + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qfixedpt.h b/src/gui/math3d/qfixedpt.h new file mode 100644 index 0000000..142f62f --- /dev/null +++ b/src/gui/math3d/qfixedpt.h @@ -0,0 +1,551 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFIXEDPT_H +#define QFIXEDPT_H + +#include <QtCore/qglobal.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +Q_GUI_EXPORT qint64 qt_math3d_fixed_sqrt(qint64 value); + +// Should be called QFixed or QFixedPoint, but both of those +// are already in use in src/gui/painting/qfixed_p.h. +template <int PrecBits> +class QFixedPt +{ +public: + inline QFixedPt() {} // Deliberately not initialized - don't change this. + inline QFixedPt(int value) : val(value << PrecBits) {} + inline QFixedPt(qreal value) : val(int(value * (1 << PrecBits))) {} + + inline QFixedPt<PrecBits>& operator=(int value) + { val = value << PrecBits; return *this; } + inline QFixedPt<PrecBits>& operator=(qreal value) + { val = int(value * (1 << PrecBits)); return *this; } + + inline int bits() const { return val; } + inline void setBits(int value) { val = value; } + +#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) + template <int Prec> + inline QFixedPt<Prec> toPrecision() const + { + QFixedPt<Prec> result; + if (Prec < PrecBits) + result.setBits(shiftRight(val, (PrecBits - Prec))); + else + result.setBits(shiftLeft(val, (Prec - PrecBits))); + return result; + } +#endif + + inline QFixedPt<PrecBits>& operator+=(const QFixedPt<PrecBits>& value) + { val += value.val; return *this; } + inline QFixedPt<PrecBits>& operator+=(int value) + { val += (value << PrecBits); return *this; } + inline QFixedPt<PrecBits>& operator+=(qreal value) + { val += int(value * (1 << PrecBits)); return *this; } + + inline QFixedPt<PrecBits>& operator-=(const QFixedPt<PrecBits>& value) + { val -= value.val; return *this; } + inline QFixedPt<PrecBits>& operator-=(int value) + { val -= (value << PrecBits); return *this; } + inline QFixedPt<PrecBits>& operator-=(qreal value) + { val -= int(value * (1 << PrecBits)); return *this; } + + inline QFixedPt<PrecBits>& operator*=(const QFixedPt<PrecBits>& value) + { val = mul(val, value.val); return *this; } + inline QFixedPt<PrecBits>& operator*=(int value) + { val = mul(val, (value << PrecBits)); return *this; } + inline QFixedPt<PrecBits>& operator*=(qreal value) + { val = mul(val, int(value * (1 << PrecBits))); return *this; } + + inline QFixedPt<PrecBits>& operator/=(const QFixedPt<PrecBits>& value) + { val = div(val, value.val); return *this; } + inline QFixedPt<PrecBits>& operator/=(int value) + { val = div(val, (value << PrecBits)); return *this; } + inline QFixedPt<PrecBits>& operator/=(qreal value) + { val = div(val, int(value * (1 << PrecBits))); return *this; } + + inline QFixedPt<PrecBits>& operator<<=(int value) + { val <<= value; return *this; } + inline QFixedPt<PrecBits>& operator>>=(int value) + { val >>= value; return *this; } + + inline QFixedPt<PrecBits> operator<<(int value) const + { QFixedPt<PrecBits> result; result.val = val << value; return result; } + inline QFixedPt<PrecBits> operator>>(int value) const + { QFixedPt<PrecBits> result; result.val = val >> value; return result; } + + inline bool operator==(const QFixedPt<PrecBits>& value) const + { return val == value.val; } + inline bool operator!=(const QFixedPt<PrecBits>& value) const + { return val != value.val; } + inline bool operator<=(const QFixedPt<PrecBits>& value) const + { return val <= value.val; } + inline bool operator<(const QFixedPt<PrecBits>& value) const + { return val < value.val; } + inline bool operator>=(const QFixedPt<PrecBits>& value) const + { return val >= value.val; } + inline bool operator>(const QFixedPt<PrecBits>& value) const + { return val > value.val; } + + inline QFixedPt<PrecBits> operator+(const QFixedPt<PrecBits>& value) const + { QFixedPt<PrecBits> result; + result.val = val + value.val; return result; } + inline QFixedPt<PrecBits> operator+(int value) const + { QFixedPt<PrecBits> result; + result.val = val + (value << PrecBits); return result; } + inline QFixedPt<PrecBits> operator+(qreal value) const + { QFixedPt<PrecBits> result; + result.val = val + int(value * (1 << PrecBits)); return result; } + + inline QFixedPt<PrecBits> operator-(const QFixedPt<PrecBits>& value) const + { QFixedPt<PrecBits> result; + result.val = val - value.val; return result; } + inline QFixedPt<PrecBits> operator-(int value) const + { QFixedPt<PrecBits> result; + result.val = val - (value << PrecBits); return result; } + inline QFixedPt<PrecBits> operator-(qreal value) const + { QFixedPt<PrecBits> result; + result.val = val - int(value * (1 << PrecBits)); return result; } + + inline QFixedPt<PrecBits> operator*(const QFixedPt<PrecBits>& value) const + { QFixedPt<PrecBits> result; + result.val = mul(val, value.val); return result; } + inline QFixedPt<PrecBits> operator*(int value) const + { QFixedPt<PrecBits> result; + result.val = mul(val, (value << PrecBits)); return result; } + inline QFixedPt<PrecBits> operator*(qreal value) const + { QFixedPt<PrecBits> result; + result.val = mul(val, int(value * (1 << PrecBits))); return result; } + + inline QFixedPt<PrecBits> operator/(const QFixedPt<PrecBits>& value) const + { QFixedPt<PrecBits> result; + result.val = div(val, value.val); return result; } + inline QFixedPt<PrecBits> operator/(int value) const + { QFixedPt<PrecBits> result; + result.val = div(val, (value << PrecBits)); return result; } + inline QFixedPt<PrecBits> operator/(qreal value) const + { QFixedPt<PrecBits> result; + result.val = div(val, int(value * (1 << PrecBits))); return result; } + + inline QFixedPt<PrecBits> operator-() const + { QFixedPt<PrecBits> result; result.val = -val; return result; } + + inline QFixedPt<PrecBits> sqrt() const; + inline qreal sqrtF() const; + inline QFixedPt<PrecBits> round() const; + inline QFixedPt<PrecBits> floor() const; + inline QFixedPt<PrecBits> ceil() const; + inline int truncate() const { return val >> PrecBits; } + + inline int toInt() const { return (val + (1 << (PrecBits - 1))) >> PrecBits; } + inline qreal toReal() const { return qreal(val) / qreal(1 << PrecBits); } + +#if !defined(Q_NO_TEMPLATE_FRIENDS) + template <int Prec> + friend QFixedPt<Prec> operator/(int v1, const QFixedPt<Prec>& v2); + template <int Prec> + friend QFixedPt<Prec> operator/(qreal v1, const QFixedPt<Prec>& v2); + +private: +#endif + int val; + + inline static int mul(int v1, int v2) + { + return int((qint64(v1) * qint64(v2)) >> PrecBits); + } + + inline static int div(int v1, int v2) + { + if (v2) + return int((qint64(v1) << PrecBits) / qint64(v2)); + else + return 0; + } + + // These are used by toPrecision() to avoid a silly gcc compiler warning + // related to negative shift values that will never actually be used. + inline static int shiftRight(int val, int shift) + { + return val >> shift; + } + inline static int shiftLeft(int val, int shift) + { + return val << shift; + } + +#if !defined(Q_NO_TEMPLATE_FRIENDS) + template <int Prec, int Prec2> + friend QFixedPt<Prec> qFixedPtToPrecision(const QFixedPt<Prec2>& value); +#endif +}; + +template <int PrecBits> +inline bool operator==(const QFixedPt<PrecBits>& v1, int v2) +{ + return v1.bits() == (v2 << PrecBits); +} + +template <int PrecBits> +inline bool operator==(int v1, const QFixedPt<PrecBits>& v2) +{ + return (v1 << PrecBits) == v2.bits(); +} + +template <int PrecBits> +inline bool operator==(const QFixedPt<PrecBits>& v1, qreal v2) +{ + return v1.bits() == int(v2 * (1 << PrecBits)); +} + +template <int PrecBits> +inline bool operator==(qreal v1, const QFixedPt<PrecBits>& v2) +{ + return int(v1 * (1 << PrecBits)) == v2.bits(); +} + +template <int PrecBits> +inline bool operator!=(const QFixedPt<PrecBits>& v1, int v2) +{ + return v1.bits() != (v2 << PrecBits); +} + +template <int PrecBits> +inline bool operator!=(int v1, const QFixedPt<PrecBits>& v2) +{ + return (v1 << PrecBits) != v2.bits(); +} + +template <int PrecBits> +inline bool operator!=(const QFixedPt<PrecBits>& v1, qreal v2) +{ + return v1.bits() != int(v2 * (1 << PrecBits)); +} + +template <int PrecBits> +inline bool operator!=(qreal v1, const QFixedPt<PrecBits>& v2) +{ + return int(v1 * (1 << PrecBits)) != v2.bits(); +} + +template <int PrecBits> +inline bool operator<=(const QFixedPt<PrecBits>& v1, int v2) +{ + return v1.bits() <= (v2 << PrecBits); +} + +template <int PrecBits> +inline bool operator<=(int v1, const QFixedPt<PrecBits>& v2) +{ + return (v1 << PrecBits) <= v2.bits(); +} + +template <int PrecBits> +inline bool operator<=(const QFixedPt<PrecBits>& v1, qreal v2) +{ + return v1.bits() <= int(v2 * (1 << PrecBits)); +} + +template <int PrecBits> +inline bool operator<=(qreal v1, const QFixedPt<PrecBits>& v2) +{ + return int(v1 * (1 << PrecBits)) <= v2.bits(); +} + +template <int PrecBits> +inline bool operator<(const QFixedPt<PrecBits>& v1, int v2) +{ + return v1.bits() < (v2 << PrecBits); +} + +template <int PrecBits> +inline bool operator<(int v1, const QFixedPt<PrecBits>& v2) +{ + return (v1 << PrecBits) < v2.bits(); +} + +template <int PrecBits> +inline bool operator<(const QFixedPt<PrecBits>& v1, qreal v2) +{ + return v1.bits() < int(v2 * (1 << PrecBits)); +} + +template <int PrecBits> +inline bool operator<(qreal v1, const QFixedPt<PrecBits>& v2) +{ + return int(v1 * (1 << PrecBits)) < v2.bits(); +} + +template <int PrecBits> +inline bool operator>=(const QFixedPt<PrecBits>& v1, int v2) +{ + return v1.bits() >= (v2 << PrecBits); +} + +template <int PrecBits> +inline bool operator>=(int v1, const QFixedPt<PrecBits>& v2) +{ + return (v1 << PrecBits) >= v2.bits(); +} + +template <int PrecBits> +inline bool operator>=(const QFixedPt<PrecBits>& v1, qreal v2) +{ + return v1.bits() >= int(v2 * (1 << PrecBits)); +} + +template <int PrecBits> +inline bool operator>=(qreal v1, const QFixedPt<PrecBits>& v2) +{ + return int(v1 * (1 << PrecBits)) >= v2.bits(); +} + +template <int PrecBits> +inline bool operator>(const QFixedPt<PrecBits>& v1, int v2) +{ + return v1.bits() > (v2 << PrecBits); +} + +template <int PrecBits> +inline bool operator>(int v1, const QFixedPt<PrecBits>& v2) +{ + return (v1 << PrecBits) > v2.bits(); +} + +template <int PrecBits> +inline bool operator>(const QFixedPt<PrecBits>& v1, qreal v2) +{ + return v1.bits() > int(v2 * (1 << PrecBits)); +} + +template <int PrecBits> +inline bool operator>(qreal v1, const QFixedPt<PrecBits>& v2) +{ + return int(v1 * (1 << PrecBits)) > v2.bits(); +} + +template <int PrecBits> +inline QFixedPt<PrecBits> operator+(int v1, const QFixedPt<PrecBits>& v2) +{ + return v2 + v1; +} + +template <int PrecBits> +inline QFixedPt<PrecBits> operator+(qreal v1, const QFixedPt<PrecBits>& v2) +{ + return v2 + v1; +} + +template <int PrecBits> +inline QFixedPt<PrecBits> operator-(int v1, const QFixedPt<PrecBits>& v2) +{ + return -(v2 - v1); +} + +template <int PrecBits> +inline QFixedPt<PrecBits> operator-(qreal v1, const QFixedPt<PrecBits>& v2) +{ + return -(v2 - v1); +} + +template <int PrecBits> +inline QFixedPt<PrecBits> operator*(int v1, const QFixedPt<PrecBits>& v2) +{ + return v2 * v1; +} + +template <int PrecBits> +inline QFixedPt<PrecBits> operator*(qreal v1, const QFixedPt<PrecBits>& v2) +{ + return v2 * v1; +} + +template <int PrecBits> +inline QFixedPt<PrecBits> operator/(int v1, const QFixedPt<PrecBits>& v2) +{ + QFixedPt<PrecBits> result; + result.val = QFixedPt<PrecBits>::div(v1 << PrecBits, v2.val); + return result; +} + +template <int PrecBits> +inline QFixedPt<PrecBits> operator/(qreal v1, const QFixedPt<PrecBits>& v2) +{ + QFixedPt<PrecBits> result; + result.val = QFixedPt<PrecBits>::div(int(v1 * (1 << PrecBits)), v2.val); + return result; +} + +template <int PrecBits> +inline QFixedPt<PrecBits> QFixedPt<PrecBits>::sqrt() const +{ + QFixedPt<PrecBits> result; + result.val = int(qt_math3d_fixed_sqrt + (qint64(val) << (PrecBits * 2)) >> (PrecBits / 2)); + return result; +} + +template <int PrecBits> +inline qreal QFixedPt<PrecBits>::sqrtF() const +{ + return qt_math3d_fixed_sqrt + (qint64(val) << (PrecBits * 2)) / (qreal)(1 << (PrecBits + (PrecBits / 2))); +} + +template <int PrecBits> +inline QFixedPt<PrecBits> QFixedPt<PrecBits>::round() const +{ + QFixedPt<PrecBits> result; + result.val = (val + (1 << (PrecBits - 1))) & ~((1 << PrecBits) - 1); + return result; +} + +template <int PrecBits> +inline QFixedPt<PrecBits> QFixedPt<PrecBits>::floor() const +{ + QFixedPt<PrecBits> result; + result.val = val & ~((1 << PrecBits) - 1); + return result; +} + +template <int PrecBits> +inline QFixedPt<PrecBits> QFixedPt<PrecBits>::ceil() const +{ + QFixedPt<PrecBits> result; + result.val = (val + (1 << PrecBits) - 1) & ~((1 << PrecBits) - 1); + return result; +} + +template <int PrecBits> +inline int qCeil(const QFixedPt<PrecBits>& value) +{ + return value.ceil().bits() >> PrecBits; +} + +template <int PrecBits> +inline int qFloor(const QFixedPt<PrecBits>& value) +{ + return value.floor().bits() >> PrecBits; +} + +template <int PrecBits> +inline int qRound(const QFixedPt<PrecBits>& value) +{ + return value.round().bits() >> PrecBits; +} + +template <int PrecBits> +inline bool qFuzzyCompare(const QFixedPt<PrecBits>& v1, const QFixedPt<PrecBits>& v2, int compareBits = (PrecBits / 4)) +{ + return ((v1.bits() ^ v2.bits()) & ~((1 << compareBits) - 1)) == 0; +} + +template <int PrecBits> +inline bool qIsNull(const QFixedPt<PrecBits>& v) +{ + return v.bits() == 0; +} + +template <int Prec, int PrecBits> +QFixedPt<Prec> qFixedPtToPrecision(const QFixedPt<PrecBits>& value) +{ + QFixedPt<Prec> result; + if (Prec < PrecBits) + result.setBits(QFixedPt<PrecBits>::shiftRight(value.bits(), (PrecBits - Prec))); + else + result.setBits(QFixedPt<PrecBits>::shiftLeft(value.bits(), (Prec - PrecBits))); + return result; +} + +template <int PrecBits> +inline QDebug &operator<<(QDebug &dbg, const QFixedPt<PrecBits> &f) +{ + return dbg << f.toReal(); +} + +Q_DECLARE_TYPEINFO(QFixedPt<0>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<1>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<2>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<3>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<4>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<5>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<6>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<7>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<8>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<9>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<10>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<11>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<12>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<13>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<14>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<15>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<16>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<17>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<18>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<19>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<20>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<21>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<22>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<23>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<24>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<25>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<26>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<27>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<28>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<29>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<30>, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QFixedPt<31>, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qgenericmatrix.cpp b/src/gui/math3d/qgenericmatrix.cpp new file mode 100644 index 0000000..734c1e6 --- /dev/null +++ b/src/gui/math3d/qgenericmatrix.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgenericmatrix.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGenericMatrix + \brief The QGenericMatrix class is a template class that represents a NxM transformation matrix with N columns and M rows. + \since 4.6 + + The QGenericMatrix template has four parameters: + + \table + \row \i N \i Number of columns. + \row \i M \i Number of rows. + \row \i T \i Element type that is visible to users of the class. + \row \i InnerT \i Element type that is used inside the class. + \endtable + + Normally T and InnerT are the same type; e.g. float or double. + But they can be different if the user wants to store elements + internally in a fixed-point format for the underlying hardware. + + \sa QMatrix4x4, QFixedPt +*/ + +/*! + \fn QGenericMatrix::QGenericMatrix() + + Constructs a NxM identity matrix. +*/ + +/*! + \fn QGenericMatrix::QGenericMatrix(const QGenericMatrix<N, M, T, InnerT>& other) + + Constructs a copy of \a other. +*/ + +/*! + \fn QGenericMatrix::QGenericMatrix(const T *values) + + Constructs a matrix from the given N * M floating-point \a values. + The contents of the array \a values is assumed to be in + row-major order. + + \sa toValueArray() +*/ + +/*! + \fn T QGenericMatrix::operator()(int row, int column) const + + Returns the element at position (\a row, \a column) in this matrix. +*/ + +/*! + \fn InnerT& QGenericMatrix::operator()(int row, int column) + + Returns a reference to the element at position (\a row, \a column) + in this matrix so that the element can be assigned to. +*/ + +/*! + \fn bool QGenericMatrix::isIdentity() const + + Returns true if this matrix is the identity; false otherwise. + + \sa setIdentity() +*/ + +/*! + \fn void QGenericMatrix::setIdentity() + + Sets this matrix to the identity. + + \sa isIdentity() +*/ + +/*! + \fn void QGenericMatrix::fill(qreal value) + + Fills all elements of this matrix with \a value. +*/ + +/*! + \fn QGenericMatrix<M, N> QGenericMatrix::transposed() const + + Returns this matrix, transposed about its diagonal. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT>& QGenericMatrix::operator+=(const QGenericMatrix<N, M, T, InnerT>& other) + + Adds the contents of \a other to this matrix. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT>& QGenericMatrix::operator-=(const QGenericMatrix<N, M, T, InnerT>& other) + + Subtracts the contents of \a other from this matrix. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT>& QGenericMatrix::operator*=(T factor) + + Multiplies all elements of this matrix by \a factor. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT>& QGenericMatrix::operator/=(T divisor) + + Divides all elements of this matrix by \a divisor. +*/ + +/*! + \fn bool QGenericMatrix::operator==(const QGenericMatrix<N, M, T, InnerT>& other) const + + Returns true if this matrix is identical to \a other; false otherwise. +*/ + +/*! + \fn bool QGenericMatrix::operator!=(const QGenericMatrix<N, M, T, InnerT>& other) const + + Returns true if this matrix is not identical to \a other; false otherwise. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator+(const QGenericMatrix<N, M, T, InnerT>& m1, const QGenericMatrix<N, M, T, InnerT>& m2) + \relates QGenericMatrix + + Returns the sum of \a m1 and \a m2. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator-(const QGenericMatrix<N, M, T, InnerT>& m1, const QGenericMatrix<N, M, T, InnerT>& m2) + \relates QGenericMatrix + + Returns the difference of \a m1 and \a m2. +*/ + +/*! + \fn QGenericMatrix<M1, M2, T, InnerT> operator*(const QGenericMatrix<N, M2, T, InnerT>& m1, const QGenericMatrix<M1, N, T, InnerT>& m2) + \relates QGenericMatrix + + Returns the product of the NxM2 matrix \a m1 and the M1xN matrix \a m2 + to produce a M1xM2 matrix result. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator-(const QGenericMatrix<N, M, T, InnerT>& matrix) + \overload + \relates QGenericMatrix + + Returns the negation of \a matrix. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator*(T factor, const QGenericMatrix<N, M, T, InnerT>& matrix) + \relates QGenericMatrix + + Returns the result of multiplying all elements of \a matrix by \a factor. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator*(const QGenericMatrix<N, M, T, InnerT>& matrix, T factor) + \relates QGenericMatrix + + Returns the result of multiplying all elements of \a matrix by \a factor. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator/(const QGenericMatrix<N, M, T, InnerT>& matrix, T divisor) + \relates QGenericMatrix + + Returns the result of dividing all elements of \a matrix by \a divisor. +*/ + +/*! + \fn void QGenericMatrix::toValueArray(T *values) + + Retrieves the N * M items in this matrix and writes them to \a values + in row-major order. +*/ + +/*! + \fn InnerT *QGenericMatrix::data() + + Returns a pointer to the raw data of this matrix. This is indended + for use with raw GL functions. + + \sa constData() +*/ + +/*! + \fn const InnerT *QGenericMatrix::data() const + + Returns a constant pointer to the raw data of this matrix. + This is indended for use with raw GL functions. + + \sa constData() +*/ + +/*! + \fn const InnerT *QGenericMatrix::constData() const + + Returns a constant pointer to the raw data of this matrix. + This is indended for use with raw GL functions. + + \sa data() +*/ + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qgenericmatrix.h b/src/gui/math3d/qgenericmatrix.h new file mode 100644 index 0000000..dbeaaf3 --- /dev/null +++ b/src/gui/math3d/qgenericmatrix.h @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGENERICMATRIX_H +#define QGENERICMATRIX_H + +#include <QtGui/qmath3dglobal.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +template <int N, int M, typename T, typename InnerT = T> +class QGenericMatrix +{ +public: + QGenericMatrix(); + QGenericMatrix(const QGenericMatrix<N, M, T, InnerT>& other); + explicit QGenericMatrix(const T *values); + + T operator()(int row, int column) const; + InnerT& operator()(int row, int column); + + bool isIdentity() const; + void setIdentity(); + + void fill(qreal value); + + QGenericMatrix<M, N, T, InnerT> transposed() const; + + QGenericMatrix<N, M, T, InnerT>& operator+=(const QGenericMatrix<N, M, T, InnerT>& other); + QGenericMatrix<N, M, T, InnerT>& operator-=(const QGenericMatrix<N, M, T, InnerT>& other); + QGenericMatrix<N, M, T, InnerT>& operator*=(T factor); + QGenericMatrix<N, M, T, InnerT>& operator/=(T divisor); + bool operator==(const QGenericMatrix<N, M, T, InnerT>& other) const; + bool operator!=(const QGenericMatrix<N, M, T, InnerT>& other) const; + + void toValueArray(T *values); + + InnerT *data() { return m[0]; } + const InnerT *data() const { return m[0]; } + const InnerT *constData() const { return m[0]; } + +#if !defined(Q_NO_TEMPLATE_FRIENDS) + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator+(const QGenericMatrix<NN, MM, TT, ITT>& m1, const QGenericMatrix<NN, MM, TT, ITT>& m2); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator-(const QGenericMatrix<NN, MM, TT, ITT>& m1, const QGenericMatrix<NN, MM, TT, ITT>& m2); + template<int NN, int M1, int M2, typename TT, typename ITT> + friend QGenericMatrix<M1, M2, TT, ITT> operator*(const QGenericMatrix<NN, M2, TT, ITT>& m1, const QGenericMatrix<M1, NN, TT, ITT>& m2); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator-(const QGenericMatrix<NN, MM, TT, ITT>& matrix); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator*(TT factor, const QGenericMatrix<NN, MM, TT, ITT>& matrix); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator*(const QGenericMatrix<NN, MM, TT, ITT>& matrix, TT factor); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator/(const QGenericMatrix<NN, MM, TT, ITT>& matrix, TT divisor); + +private: +#endif + InnerT m[N][M]; // Column-major order to match OpenGL. + + QGenericMatrix(int) {} // Construct without initializing identity matrix. + +#if !defined(Q_NO_TEMPLATE_FRIENDS) + template <int NN, int MM, typename TT, typename ITT> + friend class QGenericMatrix; +#endif +}; + +template <int N, int M, typename T, typename InnerT> +Q_INLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>::QGenericMatrix() +{ + setIdentity(); +} + +template <int N, int M, typename T, typename InnerT> +Q_INLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>::QGenericMatrix(const QGenericMatrix<N, M, T, InnerT>& other) +{ + qMemCopy(m, other.m, sizeof(m)); +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>::QGenericMatrix(const T *values) +{ + for (int col = 0; col < N; ++col) + for (int row = 0; row < M; ++row) + m[col][row] = values[row * N + col]; +} + +template <int N, int M, typename T, typename InnerT> +Q_INLINE_TEMPLATE T QGenericMatrix<N, M, T, InnerT>::operator()(int row, int column) const +{ + Q_ASSERT(row >= 0 && row < M && column >= 0 && column < N); + return qt_math3d_convert<T, InnerT>(m[column][row]); +} + +template <int N, int M, typename T, typename InnerT> +Q_INLINE_TEMPLATE InnerT& QGenericMatrix<N, M, T, InnerT>::operator()(int row, int column) +{ + Q_ASSERT(row >= 0 && row < M && column >= 0 && column < N); + return m[column][row]; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE bool QGenericMatrix<N, M, T, InnerT>::isIdentity() const +{ + for (int col = 0; col < N; ++col) { + for (int row = 0; row < M; ++row) { + if (row == col) { + if (m[col][row] != 1.0f) + return false; + } else { + if (m[col][row] != 0.0f) + return false; + } + } + } + return true; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE void QGenericMatrix<N, M, T, InnerT>::setIdentity() +{ + for (int col = 0; col < N; ++col) { + for (int row = 0; row < M; ++row) { + if (row == col) + m[col][row] = 1.0f; + else + m[col][row] = 0.0f; + } + } +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE void QGenericMatrix<N, M, T, InnerT>::fill(qreal value) +{ + for (int col = 0; col < N; ++col) + for (int row = 0; row < M; ++row) + m[col][row] = value; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<M, N, T, InnerT> QGenericMatrix<N, M, T, InnerT>::transposed() const +{ + QGenericMatrix<M, N, T, InnerT> result(1); + for (int row = 0; row < M; ++row) + for (int col = 0; col < N; ++col) + result.m[row][col] = m[col][row]; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>& QGenericMatrix<N, M, T, InnerT>::operator+=(const QGenericMatrix<N, M, T, InnerT>& other) +{ + for (int index = 0; index < N * M; ++index) + m[0][index] += other.m[0][index]; + return *this; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>& QGenericMatrix<N, M, T, InnerT>::operator-=(const QGenericMatrix<N, M, T, InnerT>& other) +{ + for (int index = 0; index < N * M; ++index) + m[0][index] -= other.m[0][index]; + return *this; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>& QGenericMatrix<N, M, T, InnerT>::operator*=(T factor) +{ + InnerT f(factor); + for (int index = 0; index < N * M; ++index) + m[0][index] *= f; + return *this; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE bool QGenericMatrix<N, M, T, InnerT>::operator==(const QGenericMatrix<N, M, T, InnerT>& other) const +{ + for (int index = 0; index < N * M; ++index) { + if (m[0][index] != other.m[0][index]) + return false; + } + return true; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE bool QGenericMatrix<N, M, T, InnerT>::operator!=(const QGenericMatrix<N, M, T, InnerT>& other) const +{ + for (int index = 0; index < N * M; ++index) { + if (m[0][index] != other.m[0][index]) + return true; + } + return false; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>& QGenericMatrix<N, M, T, InnerT>::operator/=(T divisor) +{ + InnerT d(divisor); + for (int index = 0; index < N * M; ++index) + m[0][index] /= d; + return *this; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator+(const QGenericMatrix<N, M, T, InnerT>& m1, const QGenericMatrix<N, M, T, InnerT>& m2) +{ + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = m1.m[0][index] + m2.m[0][index]; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator-(const QGenericMatrix<N, M, T, InnerT>& m1, const QGenericMatrix<N, M, T, InnerT>& m2) +{ + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = m1.m[0][index] - m2.m[0][index]; + return result; +} + +template <int N, int M1, int M2, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<M1, M2, T, InnerT> operator*(const QGenericMatrix<N, M2, T, InnerT>& m1, const QGenericMatrix<M1, N, T, InnerT>& m2) +{ + QGenericMatrix<M1, M2, T, InnerT> result(1); + for (int row = 0; row < M2; ++row) { + for (int col = 0; col < M1; ++col) { + InnerT sum(0.0f); + for (int j = 0; j < N; ++j) + sum += m1.m[j][row] * m2.m[col][j]; + result.m[col][row] = sum; + } + } + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator-(const QGenericMatrix<N, M, T, InnerT>& matrix) +{ + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = -matrix.m[0][index]; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator*(T factor, const QGenericMatrix<N, M, T, InnerT>& matrix) +{ + InnerT f(factor); + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = matrix.m[0][index] * f; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator*(const QGenericMatrix<N, M, T, InnerT>& matrix, T factor) +{ + InnerT f(factor); + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = matrix.m[0][index] * f; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator/(const QGenericMatrix<N, M, T, InnerT>& matrix, T divisor) +{ + InnerT d(divisor); + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = matrix.m[0][index] / d; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE void QGenericMatrix<N, M, T, InnerT>::toValueArray(T *values) +{ + for (int col = 0; col < N; ++col) + for (int row = 0; row < M; ++row) + values[row * N + col] = qt_math3d_convert<T, InnerT>(m[col][row]); +} + +// Define aliases for the useful variants of QGenericMatrix. +typedef QGenericMatrix<2, 2, qreal, qrealinner> QMatrix2x2; +typedef QGenericMatrix<2, 3, qreal, qrealinner> QMatrix2x3; +typedef QGenericMatrix<2, 4, qreal, qrealinner> QMatrix2x4; +typedef QGenericMatrix<3, 2, qreal, qrealinner> QMatrix3x2; +typedef QGenericMatrix<3, 3, qreal, qrealinner> QMatrix3x3; +typedef QGenericMatrix<3, 4, qreal, qrealinner> QMatrix3x4; +typedef QGenericMatrix<4, 2, qreal, qrealinner> QMatrix4x2; +typedef QGenericMatrix<4, 3, qreal, qrealinner> QMatrix4x3; + +#ifndef QT_NO_DEBUG_STREAM + +template <int N, int M, typename T, typename InnerT> +QDebug operator<<(QDebug dbg, const QGenericMatrix<N, M, T, InnerT> &m) +{ + dbg.nospace() << "QGenericMatrix<" << N << ", " << M + << ", " << QTypeInfo<T>::name() << ", " << QTypeInfo<InnerT>::name() + << ">(" << endl << qSetFieldWidth(10); + for (int row = 0; row < M; ++row) { + for (int col = 0; col < N; ++col) + dbg << m(row, col); + dbg << endl; + } + dbg << qSetFieldWidth(0) << ')'; + return dbg.space(); +} + +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QMatrix2x2) +Q_DECLARE_METATYPE(QMatrix2x3) +Q_DECLARE_METATYPE(QMatrix2x4) +Q_DECLARE_METATYPE(QMatrix3x2) +Q_DECLARE_METATYPE(QMatrix3x3) +Q_DECLARE_METATYPE(QMatrix3x4) +Q_DECLARE_METATYPE(QMatrix4x2) +Q_DECLARE_METATYPE(QMatrix4x3) + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qmath3dglobal.h b/src/gui/math3d/qmath3dglobal.h new file mode 100644 index 0000000..c2f7184 --- /dev/null +++ b/src/gui/math3d/qmath3dglobal.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMATH3DGLOBAL_H +#define QMATH3DGLOBAL_H + +#include <QtCore/qglobal.h> +#include <QtGui/qfixedpt.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +// Detect the presence of a fixed-point OpenGL implementation. +#if defined(QT_OPENGL_ES_1_CL) || defined(QT_NO_GL_FLOAT) +#ifndef QT_GL_FIXED_PREFERRED +#define QT_GL_FIXED_PREFERRED 1 +#endif +#endif + +// QT_GL_FIXED_PREFERRED indicates that fixed-point should be +// preferred over floating-point for operations requiring high performance. +// +// qreal is the floating-point type that should be used in +// user-visible functions. qrealinner is used internally where +// values may be stored as either floating-point or fixed-point. +// qrealinner will typically be the same size as GLfloat or GLfixed. +#if defined(QT_GL_FIXED_PREFERRED) +typedef QFixedPt<16> qrealinner; +#else +typedef float qrealinner; +#endif + +// Explicit conversion operator, primarily for converting from +// fixed point back to floating-point. This is safer than +// declaring conversion operators in the QFixedPt class. +template <typename T1, typename T2> +T1 qt_math3d_convert(T2 v) +{ + return T1(v); +} +template <> +inline float qt_math3d_convert< float, QFixedPt<16> >(QFixedPt<16> v) +{ + return float(v.toReal()); +} +template <> +inline double qt_math3d_convert< double, QFixedPt<16> >(QFixedPt<16> v) +{ + return double(v.toReal()); +} +template <> +inline int qt_math3d_convert< int, QFixedPt<16> >(QFixedPt<16> v) +{ + return v.toInt(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qmath3dutil.cpp b/src/gui/math3d/qmath3dutil.cpp new file mode 100644 index 0000000..ad84162 --- /dev/null +++ b/src/gui/math3d/qmath3dutil.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmath3dutil_p.h" + +QT_BEGIN_NAMESPACE + +#ifdef QT_GL_FIXED_PREFERRED + +// The table that follows was automatically generated by the following code: +// +//#include <math.h> +//#include <stdio.h> +// +//int main() +//{ +// double angle; +// int count = 0; +// for (angle = 0.0; angle < 360.0; angle += 1.0) { +// if ((count % 4) == 0) +// printf(" "); +// printf(" qf2vt(%f)", sin(angle * M_PI / 180.0)); +// ++count; +// if (count != 360) +// printf(","); +// if ((count % 4) == 0) +// printf("\n"); +// } +// return 0; +//} + +#define qf2vt(x) (int((x) * 65536.0)) + +static int const sinTable[360] = { + qf2vt(0.000000), qf2vt(0.017452), qf2vt(0.034899), qf2vt(0.052336), + qf2vt(0.069756), qf2vt(0.087156), qf2vt(0.104528), qf2vt(0.121869), + qf2vt(0.139173), qf2vt(0.156434), qf2vt(0.173648), qf2vt(0.190809), + qf2vt(0.207912), qf2vt(0.224951), qf2vt(0.241922), qf2vt(0.258819), + qf2vt(0.275637), qf2vt(0.292372), qf2vt(0.309017), qf2vt(0.325568), + qf2vt(0.342020), qf2vt(0.358368), qf2vt(0.374607), qf2vt(0.390731), + qf2vt(0.406737), qf2vt(0.422618), qf2vt(0.438371), qf2vt(0.453990), + qf2vt(0.469472), qf2vt(0.484810), qf2vt(0.500000), qf2vt(0.515038), + qf2vt(0.529919), qf2vt(0.544639), qf2vt(0.559193), qf2vt(0.573576), + qf2vt(0.587785), qf2vt(0.601815), qf2vt(0.615661), qf2vt(0.629320), + qf2vt(0.642788), qf2vt(0.656059), qf2vt(0.669131), qf2vt(0.681998), + qf2vt(0.694658), qf2vt(0.707107), qf2vt(0.719340), qf2vt(0.731354), + qf2vt(0.743145), qf2vt(0.754710), qf2vt(0.766044), qf2vt(0.777146), + qf2vt(0.788011), qf2vt(0.798636), qf2vt(0.809017), qf2vt(0.819152), + qf2vt(0.829038), qf2vt(0.838671), qf2vt(0.848048), qf2vt(0.857167), + qf2vt(0.866025), qf2vt(0.874620), qf2vt(0.882948), qf2vt(0.891007), + qf2vt(0.898794), qf2vt(0.906308), qf2vt(0.913545), qf2vt(0.920505), + qf2vt(0.927184), qf2vt(0.933580), qf2vt(0.939693), qf2vt(0.945519), + qf2vt(0.951057), qf2vt(0.956305), qf2vt(0.961262), qf2vt(0.965926), + qf2vt(0.970296), qf2vt(0.974370), qf2vt(0.978148), qf2vt(0.981627), + qf2vt(0.984808), qf2vt(0.987688), qf2vt(0.990268), qf2vt(0.992546), + qf2vt(0.994522), qf2vt(0.996195), qf2vt(0.997564), qf2vt(0.998630), + qf2vt(0.999391), qf2vt(0.999848), qf2vt(1.000000), qf2vt(0.999848), + qf2vt(0.999391), qf2vt(0.998630), qf2vt(0.997564), qf2vt(0.996195), + qf2vt(0.994522), qf2vt(0.992546), qf2vt(0.990268), qf2vt(0.987688), + qf2vt(0.984808), qf2vt(0.981627), qf2vt(0.978148), qf2vt(0.974370), + qf2vt(0.970296), qf2vt(0.965926), qf2vt(0.961262), qf2vt(0.956305), + qf2vt(0.951057), qf2vt(0.945519), qf2vt(0.939693), qf2vt(0.933580), + qf2vt(0.927184), qf2vt(0.920505), qf2vt(0.913545), qf2vt(0.906308), + qf2vt(0.898794), qf2vt(0.891007), qf2vt(0.882948), qf2vt(0.874620), + qf2vt(0.866025), qf2vt(0.857167), qf2vt(0.848048), qf2vt(0.838671), + qf2vt(0.829038), qf2vt(0.819152), qf2vt(0.809017), qf2vt(0.798636), + qf2vt(0.788011), qf2vt(0.777146), qf2vt(0.766044), qf2vt(0.754710), + qf2vt(0.743145), qf2vt(0.731354), qf2vt(0.719340), qf2vt(0.707107), + qf2vt(0.694658), qf2vt(0.681998), qf2vt(0.669131), qf2vt(0.656059), + qf2vt(0.642788), qf2vt(0.629320), qf2vt(0.615661), qf2vt(0.601815), + qf2vt(0.587785), qf2vt(0.573576), qf2vt(0.559193), qf2vt(0.544639), + qf2vt(0.529919), qf2vt(0.515038), qf2vt(0.500000), qf2vt(0.484810), + qf2vt(0.469472), qf2vt(0.453990), qf2vt(0.438371), qf2vt(0.422618), + qf2vt(0.406737), qf2vt(0.390731), qf2vt(0.374607), qf2vt(0.358368), + qf2vt(0.342020), qf2vt(0.325568), qf2vt(0.309017), qf2vt(0.292372), + qf2vt(0.275637), qf2vt(0.258819), qf2vt(0.241922), qf2vt(0.224951), + qf2vt(0.207912), qf2vt(0.190809), qf2vt(0.173648), qf2vt(0.156434), + qf2vt(0.139173), qf2vt(0.121869), qf2vt(0.104528), qf2vt(0.087156), + qf2vt(0.069756), qf2vt(0.052336), qf2vt(0.034899), qf2vt(0.017452), + qf2vt(0.000000), qf2vt(-0.017452), qf2vt(-0.034899), qf2vt(-0.052336), + qf2vt(-0.069756), qf2vt(-0.087156), qf2vt(-0.104528), qf2vt(-0.121869), + qf2vt(-0.139173), qf2vt(-0.156434), qf2vt(-0.173648), qf2vt(-0.190809), + qf2vt(-0.207912), qf2vt(-0.224951), qf2vt(-0.241922), qf2vt(-0.258819), + qf2vt(-0.275637), qf2vt(-0.292372), qf2vt(-0.309017), qf2vt(-0.325568), + qf2vt(-0.342020), qf2vt(-0.358368), qf2vt(-0.374607), qf2vt(-0.390731), + qf2vt(-0.406737), qf2vt(-0.422618), qf2vt(-0.438371), qf2vt(-0.453990), + qf2vt(-0.469472), qf2vt(-0.484810), qf2vt(-0.500000), qf2vt(-0.515038), + qf2vt(-0.529919), qf2vt(-0.544639), qf2vt(-0.559193), qf2vt(-0.573576), + qf2vt(-0.587785), qf2vt(-0.601815), qf2vt(-0.615661), qf2vt(-0.629320), + qf2vt(-0.642788), qf2vt(-0.656059), qf2vt(-0.669131), qf2vt(-0.681998), + qf2vt(-0.694658), qf2vt(-0.707107), qf2vt(-0.719340), qf2vt(-0.731354), + qf2vt(-0.743145), qf2vt(-0.754710), qf2vt(-0.766044), qf2vt(-0.777146), + qf2vt(-0.788011), qf2vt(-0.798636), qf2vt(-0.809017), qf2vt(-0.819152), + qf2vt(-0.829038), qf2vt(-0.838671), qf2vt(-0.848048), qf2vt(-0.857167), + qf2vt(-0.866025), qf2vt(-0.874620), qf2vt(-0.882948), qf2vt(-0.891007), + qf2vt(-0.898794), qf2vt(-0.906308), qf2vt(-0.913545), qf2vt(-0.920505), + qf2vt(-0.927184), qf2vt(-0.933580), qf2vt(-0.939693), qf2vt(-0.945519), + qf2vt(-0.951057), qf2vt(-0.956305), qf2vt(-0.961262), qf2vt(-0.965926), + qf2vt(-0.970296), qf2vt(-0.974370), qf2vt(-0.978148), qf2vt(-0.981627), + qf2vt(-0.984808), qf2vt(-0.987688), qf2vt(-0.990268), qf2vt(-0.992546), + qf2vt(-0.994522), qf2vt(-0.996195), qf2vt(-0.997564), qf2vt(-0.998630), + qf2vt(-0.999391), qf2vt(-0.999848), qf2vt(-1.000000), qf2vt(-0.999848), + qf2vt(-0.999391), qf2vt(-0.998630), qf2vt(-0.997564), qf2vt(-0.996195), + qf2vt(-0.994522), qf2vt(-0.992546), qf2vt(-0.990268), qf2vt(-0.987688), + qf2vt(-0.984808), qf2vt(-0.981627), qf2vt(-0.978148), qf2vt(-0.974370), + qf2vt(-0.970296), qf2vt(-0.965926), qf2vt(-0.961262), qf2vt(-0.956305), + qf2vt(-0.951057), qf2vt(-0.945519), qf2vt(-0.939693), qf2vt(-0.933580), + qf2vt(-0.927184), qf2vt(-0.920505), qf2vt(-0.913545), qf2vt(-0.906308), + qf2vt(-0.898794), qf2vt(-0.891007), qf2vt(-0.882948), qf2vt(-0.874620), + qf2vt(-0.866025), qf2vt(-0.857167), qf2vt(-0.848048), qf2vt(-0.838671), + qf2vt(-0.829038), qf2vt(-0.819152), qf2vt(-0.809017), qf2vt(-0.798636), + qf2vt(-0.788011), qf2vt(-0.777146), qf2vt(-0.766044), qf2vt(-0.754710), + qf2vt(-0.743145), qf2vt(-0.731354), qf2vt(-0.719340), qf2vt(-0.707107), + qf2vt(-0.694658), qf2vt(-0.681998), qf2vt(-0.669131), qf2vt(-0.656059), + qf2vt(-0.642788), qf2vt(-0.629320), qf2vt(-0.615661), qf2vt(-0.601815), + qf2vt(-0.587785), qf2vt(-0.573576), qf2vt(-0.559193), qf2vt(-0.544639), + qf2vt(-0.529919), qf2vt(-0.515038), qf2vt(-0.500000), qf2vt(-0.484810), + qf2vt(-0.469472), qf2vt(-0.453990), qf2vt(-0.438371), qf2vt(-0.422618), + qf2vt(-0.406737), qf2vt(-0.390731), qf2vt(-0.374607), qf2vt(-0.358368), + qf2vt(-0.342020), qf2vt(-0.325568), qf2vt(-0.309017), qf2vt(-0.292372), + qf2vt(-0.275637), qf2vt(-0.258819), qf2vt(-0.241922), qf2vt(-0.224951), + qf2vt(-0.207912), qf2vt(-0.190809), qf2vt(-0.173648), qf2vt(-0.156434), + qf2vt(-0.139173), qf2vt(-0.121869), qf2vt(-0.104528), qf2vt(-0.087156), + qf2vt(-0.069756), qf2vt(-0.052336), qf2vt(-0.034899), qf2vt(-0.017452) +}; + +void qt_math3d_sincos(qreal angle, qrealinner *s, qrealinner *c) +{ + if (angle == qFloor(angle)) { + // The angle is an integer number of degrees, so look up the results. + int a = (int)angle; + if (a >= 0) + a = (a % 360); + else + a = 360 - (-a % 360); + s->setBits(sinTable[a]); + c->setBits(sinTable[(a + 90) % 360]); + } else { + qreal a = angle * M_PI / 180.0f; + *s = qSin(a); + *c = qCos(a); + } +} + +#else + +void qt_math3d_sincos(qreal angle, qrealinner *s, qrealinner *c) +{ + qreal a = angle * M_PI / 180.0f; + *s = qSin(a); + *c = qCos(a); +} + +#endif + +QT_END_NAMESPACE diff --git a/src/plugins/kbddrivers/linuxis/linuxiskbdhandler.h b/src/gui/math3d/qmath3dutil_p.h index 3211309..fa4c156 100644 --- a/src/plugins/kbddrivers/linuxis/linuxiskbdhandler.h +++ b/src/gui/math3d/qmath3dutil_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** -** This file is part of the plugins of the Qt Toolkit. +** This file is part of the $MODULE$ of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,42 +39,58 @@ ** ****************************************************************************/ -#ifndef LINUXISKBDHANDLER_H -#define LINUXISKBDHANDLER_H +#ifndef QMATH3DUTIL_P_H +#define QMATH3DUTIL_P_H -#include <QObject> -#include <QWSKeyboardHandler> +#include <QtGui/qmath3dglobal.h> +#include <QtCore/qmath.h> -class QSocketNotifier; -class LinuxInputSubsystemKbdHandler : public QObject, public QWSKeyboardHandler { - Q_OBJECT -public: - LinuxInputSubsystemKbdHandler(const QString &device = QString("/dev/input/event0")); - ~LinuxInputSubsystemKbdHandler(); +QT_BEGIN_NAMESPACE - struct keytable_s { - int code; - int unicode; - int keycode; - }; +#ifdef QT_GL_FIXED_PREFERRED +#define qvtsqrt(x) ((x).sqrtF()) +#else +#define qvtsqrt(x) qSqrt((x)) +#endif - struct keymap_s { - int unicode; - int keycode; - }; +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif -private: - void initmap(); +void qt_math3d_sincos(qreal degrees, qrealinner *s, qrealinner *c); - QSocketNotifier *m_notify; - int kbdFD; - bool shift; +#ifdef QT_GL_FIXED_PREFERRED - static struct keytable_s keytable[]; - static struct keymap_s keymap[]; +inline qrealinner qf2vt_round(qreal x) +{ + QFixedPt<16> result; + if (x >= 0.0f) + result.setBits(int(x * 65536.0f + 0.5f)); + else + result.setBits(int(x * 65536.0f - 0.5f)); + return result; +} -private Q_SLOTS: - void readKbdData(); -}; +// Helper macros for computing dot products without losing precision. +// In fixed-point mode, a 64-bit intermediate result is used. +#define qvtmul64(x,y) ((qint64((x).bits())) * (qint64((y).bits()))) +#define qvtsqrt64(x) \ + (qt_math3d_fixed_sqrt((x) << 16) / (qreal)(1 << 24)) +#define qvtdot64(x) ((x) / (qreal)(((qint64)1) << 32)) -#endif // LINUXISKBDHANDLER_H +#else + +inline qrealinner qf2vt_round(qreal x) +{ + return x; +} + +#define qvtmul64(x,y) ((x) * (y)) +#define qvtsqrt64(x) (qvtsqrt((x))) +#define qvtdot64(x) ((x)) + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp new file mode 100644 index 0000000..bcb3066 --- /dev/null +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -0,0 +1,1679 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmatrix4x4.h" +#include "qmath3dutil_p.h" +#include <QtCore/qmath.h> +#include <QtGui/qmatrix.h> +#include <QtGui/qtransform.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_MATRIX4X4 + +/*! + \class QMatrix4x4 + \brief The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space. + \since 4.6 + + The matrix elements are stored internally using the most efficient + numeric representation for the underlying hardware: floating-point + or fixed-point. + + \sa QVector3D, QGenericMatrix +*/ + +/*! + \fn QMatrix4x4::QMatrix4x4() + + Constructs an identity matrix. +*/ + +/*! + Constructs a matrix from the given 16 floating-point \a values. + The contents of the array \a values is assumed to be in + row-major order. + + If the matrix has a special type (identity, translate, scale, etc), + the programmer should follow this constructor with a call to + inferSpecialType() if they wish QMatrix4x4 to optimize further + calls to translate(), scale(), etc. + + \sa toValueArray(), inferSpecialType() +*/ +QMatrix4x4::QMatrix4x4(const qreal *values) +{ + for (int row = 0; row < 4; ++row) + for (int col = 0; col < 4; ++col) + m[col][row] = values[row * 4 + col]; + flagBits = General; +} + +/*! + \fn QMatrix4x4::QMatrix4x4(qreal m11, qreal m12, qreal m13, qreal m14, qreal m21, qreal m22, qreal m23, qreal m24, qreal m31, qreal m32, qreal m33, qreal m34, qreal m41, qreal m42, qreal m43, qreal m44) + + Constructs a matrix from the 16 elements \a m11, \a m12, \a m13, \a m14, + \a m21, \a m22, \a m23, \a m24, \a m31, \a m32, \a m33, \a m34, + \a m41, \a m42, \a m43, and \a m44. The elements are specified in + row-major order. + + If the matrix has a special type (identity, translate, scale, etc), + the programmer should follow this constructor with a call to + inferSpecialType() if they wish QMatrix4x4 to optimize further + calls to translate(), scale(), etc. + + \sa inferSpecialType() +*/ + +#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) + +/*! + \fn QMatrix4x4::QMatrix4x4(const QGenericMatrix<N, M, qreal, qrealinner>& matrix) + + Constructs a 4x4 matrix from the left-most 4 columns and top-most + 4 rows of \a matrix. If \a matrix has less than 4 columns or rows, + the remaining elements are filled with elements from the identity + matrix. + + \sa toGenericMatrix(), qGenericMatrixToMatrix4x4() +*/ + +/*! + \fn QGenericMatrix<N, M, qreal, qrealinner> QMatrix4x4::toGenericMatrix() const + + Constructs a NxM generic matrix from the left-most N columns and + top-most M rows of this 4x4 matrix. If N or M is greater than 4, + then the remaining elements are filled with elements from the + identity matrix. + + \sa qGenericMatrixFromMatrix4x4() +*/ + +#endif + +/*! + \fn QMatrix4x4 qGenericMatrixToMatrix4x4(const QGenericMatrix<N, M, qreal, qrealinner>& matrix) + \relates QMatrix4x4 + + Returns a 4x4 matrix constructed from the left-most 4 columns and + top-most 4 rows of \a matrix. If \a matrix has less than 4 columns + or rows, the remaining elements are filled with elements from the + identity matrix. + + \sa qGenericMatrixFromMatrix4x4() +*/ + +/*! + \fn QGenericMatrix<N, M, qreal, qrealinner> qGenericMatrixFromMatrix4x4(const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns a NxM generic matrix constructed from the left-most N columns + and top-most M rows of \a matrix. If N or M is greater than 4, + then the remaining elements are filled with elements from the + identity matrix. + + \sa qGenericMatrixToMatrix4x4(), QMatrix4x4::toGenericMatrix() +*/ + +/*! + \internal +*/ +QMatrix4x4::QMatrix4x4(const qrealinner *values, int cols, int rows) +{ + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + if (col < cols && row < rows) + m[col][row] = values[col * rows + row]; + else if (col == row) + m[col][row] = 1.0f; + else + m[col][row] = 0.0f; + } + } + flagBits = General; +} + +/*! + Constructs a 4x4 matrix from a conventional Qt 2D affine + transformation \a matrix. + + If \a matrix has a special type (identity, translate, scale, etc), + the programmer should follow this constructor with a call to + inferSpecialType() if they wish QMatrix4x4 to optimize further + calls to translate(), scale(), etc. + + \sa toAffine(), inferSpecialType() +*/ +QMatrix4x4::QMatrix4x4(const QMatrix& matrix) +{ + m[0][0] = matrix.m11(); + m[0][1] = matrix.m12(); + m[0][2] = 0.0f; + m[0][3] = 0.0f; + m[1][0] = matrix.m21(); + m[1][1] = matrix.m22(); + m[1][2] = 0.0f; + m[1][3] = 0.0f; + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; + m[2][3] = 0.0f; + m[3][0] = matrix.dx(); + m[3][1] = matrix.dy(); + m[3][2] = 0.0f; + m[3][3] = 1.0f; + flagBits = General; +} + +/*! + Constructs a 4x4 matrix from the conventional Qt 2D + transformation matrix \a transform. + + If \a transform has a special type (identity, translate, scale, etc), + the programmer should follow this constructor with a call to + inferSpecialType() if they wish QMatrix4x4 to optimize further + calls to translate(), scale(), etc. + + \sa toTransform(), inferSpecialType() +*/ +QMatrix4x4::QMatrix4x4(const QTransform& transform) +{ + m[0][0] = transform.m11(); + m[0][1] = transform.m12(); + m[0][2] = 0.0f; + m[0][3] = transform.m13(); + m[1][0] = transform.m21(); + m[1][1] = transform.m22(); + m[1][2] = 0.0f; + m[1][3] = transform.m23(); + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; + m[2][3] = 0.0f; + m[3][0] = transform.dx(); + m[3][1] = transform.dy(); + m[3][2] = 0.0f; + m[3][3] = transform.m33(); + flagBits = General; +} + +/*! + \fn qreal QMatrix4x4::operator()(int row, int column) const + + Returns the element at position (\a row, \a column) in this matrix. + + \sa column(), row() +*/ + +/*! + \fn qrealinner& QMatrix4x4::operator()(int row, int column) + + Returns a reference to the element at position (\a row, \a column) + in this matrix so that the element can be assigned to. + + \sa inferSpecialType(), setColumn(), setRow() +*/ + +/*! + \fn QVector4D QMatrix4x4::column(int index) const + + Returns the elements of column \a index as a 4D vector. + + \sa setColumn(), row() +*/ + +/*! + \fn void QMatrix4x4::setColumn(int index, const QVector4D& value) + + Sets the elements of column \a index to the components of \a value. + + \sa column(), setRow() +*/ + +/*! + \fn QVector4D QMatrix4x4::row(int index) const + + Returns the elements of row \a index as a 4D vector. + + \sa setRow(), column() +*/ + +/*! + \fn void QMatrix4x4::setRow(int index, const QVector4D& value) + + Sets the elements of row \a index to the components of \a value. + + \sa row(), setColumn() +*/ + +/*! + \fn bool QMatrix4x4::isIdentity() const + + Returns true if this matrix is the identity; false otherwise. + + \sa setIdentity() +*/ + +/*! + \fn void QMatrix4x4::setIdentity() + + Sets this matrix to the identity. + + \sa isIdentity() +*/ + +/*! + \fn void QMatrix4x4::fill(qreal value) + + Fills all elements of this matrx with \a value. +*/ + +// The 4x4 matrix inverse algorithm is based on that described at: +// http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q24 +// Some optimization has been done to avoid making copies of 3x3 +// sub-matrices, to do calculations in fixed-point where required, +// and to unroll the loops. + +// Calculate the determinant of a 3x3 sub-matrix. +// | A B C | +// M = | D E F | det(M) = A * (EI - HF) - B * (DI - GF) + C * (DH - GE) +// | G H I | +static inline qrealinner matrixDet3 + (const qrealinner m[4][4], int col0, int col1, int col2, + int row0, int row1, int row2) +{ + return m[col0][row0] * + (m[col1][row1] * m[col2][row2] - + m[col1][row2] * m[col2][row1]) - + m[col1][row0] * + (m[col0][row1] * m[col2][row2] - + m[col0][row2] * m[col2][row1]) + + m[col2][row0] * + (m[col0][row1] * m[col1][row2] - + m[col0][row2] * m[col1][row1]); +} + +// Calculate the determinant of a 4x4 matrix. +static inline qrealinner matrixDet4(const qrealinner m[4][4]) +{ + qrealinner det; + det = m[0][0] * matrixDet3(m, 1, 2, 3, 1, 2, 3); + det -= m[1][0] * matrixDet3(m, 0, 2, 3, 1, 2, 3); + det += m[2][0] * matrixDet3(m, 0, 1, 3, 1, 2, 3); + det -= m[3][0] * matrixDet3(m, 0, 1, 2, 1, 2, 3); + return det; +} + +/*! + Returns the determinant of this matrix. +*/ +qreal QMatrix4x4::determinant() const +{ + return qt_math3d_convert<qreal, qrealinner>(matrixDet4(m)); +} + +/*! + Returns the inverse of this matrix. Returns the identity if + this matrix cannot be inverted; i.e. determinant() is zero. + If \a invertible is not null, then true will be written to + that location if the matrix can be inverted; false otherwise. + + If the matrix is recognized as the identity or an orthonormal + matrix, then this function will quickly invert the matrix + using optimized routines. + + \sa determinant(), normalMatrix() +*/ +QMatrix4x4 QMatrix4x4::inverted(bool *invertible) const +{ + // Handle some of the easy cases first. + if (flagBits == Identity) { + if (invertible) + *invertible = true; + return QMatrix4x4(); + } else if (flagBits == Translation) { + QMatrix4x4 inv; + inv.m[3][0] = -m[3][0]; + inv.m[3][1] = -m[3][1]; + inv.m[3][2] = -m[3][2]; + inv.flagBits = Translation; + if (invertible) + *invertible = true; + return inv; + } else if (flagBits == Rotation || flagBits == (Rotation | Translation)) { + if (invertible) + *invertible = true; + return orthonormalInverse(); + } + + QMatrix4x4 inv(1); // The "1" says to not load the identity. + + qrealinner det = matrixDet4(m); + if (det == 0.0f) { + if (invertible) + *invertible = false; + return QMatrix4x4(); + } + det = qrealinner(1.0f) / det; + + inv.m[0][0] = matrixDet3(m, 1, 2, 3, 1, 2, 3) * det; + inv.m[0][1] = -matrixDet3(m, 0, 2, 3, 1, 2, 3) * det; + inv.m[0][2] = matrixDet3(m, 0, 1, 3, 1, 2, 3) * det; + inv.m[0][3] = -matrixDet3(m, 0, 1, 2, 1, 2, 3) * det; + inv.m[1][0] = -matrixDet3(m, 1, 2, 3, 0, 2, 3) * det; + inv.m[1][1] = matrixDet3(m, 0, 2, 3, 0, 2, 3) * det; + inv.m[1][2] = -matrixDet3(m, 0, 1, 3, 0, 2, 3) * det; + inv.m[1][3] = matrixDet3(m, 0, 1, 2, 0, 2, 3) * det; + inv.m[2][0] = matrixDet3(m, 1, 2, 3, 0, 1, 3) * det; + inv.m[2][1] = -matrixDet3(m, 0, 2, 3, 0, 1, 3) * det; + inv.m[2][2] = matrixDet3(m, 0, 1, 3, 0, 1, 3) * det; + inv.m[2][3] = -matrixDet3(m, 0, 1, 2, 0, 1, 3) * det; + inv.m[3][0] = -matrixDet3(m, 1, 2, 3, 0, 1, 2) * det; + inv.m[3][1] = matrixDet3(m, 0, 2, 3, 0, 1, 2) * det; + inv.m[3][2] = -matrixDet3(m, 0, 1, 3, 0, 1, 2) * det; + inv.m[3][3] = matrixDet3(m, 0, 1, 2, 0, 1, 2) * det; + + if (invertible) + *invertible = true; + return inv; +} + +/*! + Returns the normal matrix corresponding to this 4x4 transformation. + The normal matrix is the transpose of the inverse of the top-left + 3x3 part of this 4x4 matrix. If the 3x3 sub-matrix is not invertible, + this function returns the identity. + + \sa inverted() +*/ +QMatrix3x3 QMatrix4x4::normalMatrix() const +{ + QMatrix3x3 inv; + + // Handle the simple cases first. + if (flagBits == Identity || flagBits == Translation) { + return inv; + } else if (flagBits == Scale || flagBits == (Translation | Scale)) { + if (m[0][0] == 0.0f || m[1][1] == 0.0f || m[2][2] == 0.0f) + return inv; + inv.data()[0] = qrealinner(1.0f) / m[0][0]; + inv.data()[4] = qrealinner(1.0f) / m[1][1]; + inv.data()[8] = qrealinner(1.0f) / m[2][2]; + return inv; + } + + qrealinner det = matrixDet3(m, 0, 1, 2, 0, 1, 2); + if (det == 0.0f) + return inv; + det = qrealinner(1.0f) / det; + + qrealinner *invm = inv.data(); + + // Invert and transpose in a single step. + invm[0 + 0 * 3] = (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * det; + invm[1 + 0 * 3] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0]) * det; + invm[2 + 0 * 3] = (m[1][0] * m[2][1] - m[1][1] * m[2][0]) * det; + invm[0 + 1 * 3] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * det; + invm[1 + 1 * 3] = (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * det; + invm[2 + 1 * 3] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0]) * det; + invm[0 + 2 * 3] = (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * det; + invm[1 + 2 * 3] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0]) * det; + invm[2 + 2 * 3] = (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * det; + + return inv; +} + +/*! + Returns this matrix, transposed about its diagonal. +*/ +QMatrix4x4 QMatrix4x4::transposed() const +{ + QMatrix4x4 result(1); // The "1" says to not load the identity. + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 4; ++col) { + result.m[col][row] = m[row][col]; + } + } + return result; +} + +/*! + \fn QMatrix4x4& QMatrix4x4::operator+=(const QMatrix4x4& other) + + Adds the contents of \a other to this matrix. +*/ + +/*! + \fn QMatrix4x4& QMatrix4x4::operator-=(const QMatrix4x4& other) + + Subtracts the contents of \a other from this matrix. +*/ + +/*! + \fn QMatrix4x4& QMatrix4x4::operator*=(const QMatrix4x4& other) + + Multiplies the contents of \a other by this matrix. +*/ + +/*! + \fn QMatrix4x4& QMatrix4x4::operator*=(qreal factor) + \overload + + Multiplies all elements of this matrix by \a factor. +*/ + +/*! + \overload + + Divides all elements of this matrix by \a divisor. +*/ +QMatrix4x4& QMatrix4x4::operator/=(qreal divisor) +{ + qrealinner d(divisor); + m[0][0] /= d; + m[0][1] /= d; + m[0][2] /= d; + m[0][3] /= d; + m[1][0] /= d; + m[1][1] /= d; + m[1][2] /= d; + m[1][3] /= d; + m[2][0] /= d; + m[2][1] /= d; + m[2][2] /= d; + m[2][3] /= d; + m[3][0] /= d; + m[3][1] /= d; + m[3][2] /= d; + m[3][3] /= d; + flagBits = General; + return *this; +} + +/*! + \fn bool QMatrix4x4::operator==(const QMatrix4x4& other) const + + Returns true if this matrix is identical to \a other; false otherwise. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool QMatrix4x4::operator!=(const QMatrix4x4& other) const + + Returns true if this matrix is not identical to \a other; false otherwise. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2) + \relates QMatrix4x4 + + Returns the sum of \a m1 and \a m2. +*/ + +/*! + \fn QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2) + \relates QMatrix4x4 + + Returns the difference of \a m1 and \a m2. +*/ + +/*! + \fn QMatrix4x4 operator*(const QMatrix4x4& m1, const QMatrix4x4& m2) + \relates QMatrix4x4 + + Returns the product of \a m1 and \a m2. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + \fn QVector3D operator*(const QVector3D& vector, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of transforming \a vector according to \a matrix, + with the matrix applied post-vector. +*/ + +/*! + \fn QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector) + \relates QMatrix4x4 + + Returns the result of transforming \a vector according to \a matrix, + with the matrix applied pre-vector. +*/ + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + \fn QVector4D operator*(const QVector4D& vector, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of transforming \a vector according to \a matrix, + with the matrix applied post-vector. +*/ + +/*! + \fn QVector4D operator*(const QMatrix4x4& matrix, const QVector4D& vector) + \relates QMatrix4x4 + + Returns the result of transforming \a vector according to \a matrix, + with the matrix applied pre-vector. +*/ + +#endif + +/*! + \fn QPoint operator*(const QPoint& point, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of transforming \a point according to \a matrix, + with the matrix applied post-point. +*/ + +/*! + \fn QPointF operator*(const QPointF& point, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of transforming \a point according to \a matrix, + with the matrix applied post-point. +*/ + +/*! + \fn QPoint operator*(const QMatrix4x4& matrix, const QPoint& point) + \relates QMatrix4x4 + + Returns the result of transforming \a point according to \a matrix, + with the matrix applied pre-point. +*/ + +/*! + \fn QPointF operator*(const QMatrix4x4& matrix, const QPointF& point) + \relates QMatrix4x4 + + Returns the result of transforming \a point according to \a matrix, + with the matrix applied pre-point. +*/ + +/*! + \fn QMatrix4x4 operator-(const QMatrix4x4& matrix) + \overload + \relates QMatrix4x4 + + Returns the negation of \a matrix. +*/ + +/*! + \fn QMatrix4x4 operator*(qreal factor, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of multiplying all elements of \a matrix by \a factor. +*/ + +/*! + \fn QMatrix4x4 operator*(const QMatrix4x4& matrix, qreal factor) + \relates QMatrix4x4 + + Returns the result of multiplying all elements of \a matrix by \a factor. +*/ + +/*! + \relates QMatrix4x4 + + Returns the result of dividing all elements of \a matrix by \a divisor. +*/ +QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor) +{ + QMatrix4x4 m(1); // The "1" says to not load the identity. + qrealinner d(divisor); + m.m[0][0] = matrix.m[0][0] / d; + m.m[0][1] = matrix.m[0][1] / d; + m.m[0][2] = matrix.m[0][2] / d; + m.m[0][3] = matrix.m[0][3] / d; + m.m[1][0] = matrix.m[1][0] / d; + m.m[1][1] = matrix.m[1][1] / d; + m.m[1][2] = matrix.m[1][2] / d; + m.m[1][3] = matrix.m[1][3] / d; + m.m[2][0] = matrix.m[2][0] / d; + m.m[2][1] = matrix.m[2][1] / d; + m.m[2][2] = matrix.m[2][2] / d; + m.m[2][3] = matrix.m[2][3] / d; + m.m[3][0] = matrix.m[3][0] / d; + m.m[3][1] = matrix.m[3][1] / d; + m.m[3][2] = matrix.m[3][2] / d; + m.m[3][3] = matrix.m[3][3] / d; + return m; +} + +/*! + \fn bool qFuzzyCompare(const QMatrix4x4& m1, const QMatrix4x4& m2) + \relates QMatrix4x4 + + Returns true if \a m1 and \a m2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + Multiplies this matrix by another that scales coordinates by + the components of \a vector. Returns this matrix. + + \sa translate(), rotate() +*/ +QMatrix4x4& QMatrix4x4::scale(const QVector3D& vector) +{ + qrealinner vx = vector.xp; + qrealinner vy = vector.yp; + qrealinner vz = vector.zp; + if (flagBits == Identity) { + m[0][0] = vx; + m[1][1] = vy; + m[2][2] = vz; + flagBits = Scale; + } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[0][0] *= vx; + m[1][1] *= vy; + m[2][2] *= vz; + } else if (flagBits == Translation) { + m[0][0] = vx; + m[1][1] = vy; + m[2][2] = vz; + flagBits |= Scale; + } else { + m[0][0] *= vx; + m[0][1] *= vx; + m[0][2] *= vx; + m[0][3] *= vx; + m[1][0] *= vy; + m[1][1] *= vy; + m[1][2] *= vy; + m[1][3] *= vy; + m[2][0] *= vz; + m[2][1] *= vz; + m[2][2] *= vz; + m[2][3] *= vz; + flagBits = General; + } + return *this; +} +#endif + +/*! + \overload + + Multiplies this matrix by another that scales coordinates by the + components \a x, \a y, and \a z. Returns this matrix. + + \sa translate(), rotate() +*/ +QMatrix4x4& QMatrix4x4::scale(qreal x, qreal y, qreal z) +{ + qrealinner vx(x); + qrealinner vy(y); + qrealinner vz(z); + if (flagBits == Identity) { + m[0][0] = vx; + m[1][1] = vy; + m[2][2] = vz; + flagBits = Scale; + } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[0][0] *= vx; + m[1][1] *= vy; + m[2][2] *= vz; + } else if (flagBits == Translation) { + m[0][0] = vx; + m[1][1] = vy; + m[2][2] = vz; + flagBits |= Scale; + } else { + m[0][0] *= vx; + m[0][1] *= vx; + m[0][2] *= vx; + m[0][3] *= vx; + m[1][0] *= vy; + m[1][1] *= vy; + m[1][2] *= vy; + m[1][3] *= vy; + m[2][0] *= vz; + m[2][1] *= vz; + m[2][2] *= vz; + m[2][3] *= vz; + flagBits = General; + } + return *this; +} + +/*! + \overload + + Multiplies this matrix by another that scales coordinates by the + given \a factor. Returns this matrix. + + \sa translate(), rotate() +*/ +QMatrix4x4& QMatrix4x4::scale(qreal factor) +{ + qrealinner f(factor); + if (flagBits == Identity) { + m[0][0] = f; + m[1][1] = f; + m[2][2] = f; + flagBits = Scale; + } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[0][0] *= f; + m[1][1] *= f; + m[2][2] *= f; + } else if (flagBits == Translation) { + m[0][0] = f; + m[1][1] = f; + m[2][2] = f; + flagBits |= Scale; + } else { + m[0][0] *= f; + m[0][1] *= f; + m[0][2] *= f; + m[0][3] *= f; + m[1][0] *= f; + m[1][1] *= f; + m[1][2] *= f; + m[1][3] *= f; + m[2][0] *= f; + m[2][1] *= f; + m[2][2] *= f; + m[2][3] *= f; + flagBits = General; + } + return *this; +} + +#ifndef QT_NO_VECTOR3D +/*! + Multiplies this matrix by another that translates coordinates by + the components of \a vector. Returns this matrix. + + \sa scale(), rotate() +*/ +QMatrix4x4& QMatrix4x4::translate(const QVector3D& vector) +{ + qrealinner vx = vector.xp; + qrealinner vy = vector.yp; + qrealinner vz = vector.zp; + if (flagBits == Identity) { + m[3][0] = vx; + m[3][1] = vy; + m[3][2] = vz; + flagBits = Translation; + } else if (flagBits == Translation) { + m[3][0] += vx; + m[3][1] += vy; + m[3][2] += vz; + } else if (flagBits == Scale) { + m[3][0] = m[0][0] * vx; + m[3][1] = m[1][1] * vy; + m[3][2] = m[2][2] * vz; + flagBits |= Translation; + } else if (flagBits == (Scale | Translation)) { + m[3][0] += m[0][0] * vx; + m[3][1] += m[1][1] * vy; + m[3][2] += m[2][2] * vz; + } else { + m[3][0] += m[0][0] * vx + m[1][0] * vy + m[2][0] * vz; + m[3][1] += m[0][1] * vx + m[1][1] * vy + m[2][1] * vz; + m[3][2] += m[0][2] * vx + m[1][2] * vy + m[2][2] * vz; + m[3][3] += m[0][3] * vx + m[1][3] * vy + m[2][3] * vz; + if (flagBits == Rotation) + flagBits |= Translation; + else if (flagBits != (Rotation | Translation)) + flagBits = General; + } + return *this; +} + +#endif + +/*! + \overload + + Multiplies this matrix by another that translates coordinates + by the components \a x, \a y, and \a z. Returns this matrix. + + \sa scale(), rotate() +*/ +QMatrix4x4& QMatrix4x4::translate(qreal x, qreal y, qreal z) +{ + qrealinner vx(x); + qrealinner vy(y); + qrealinner vz(z); + if (flagBits == Identity) { + m[3][0] = vx; + m[3][1] = vy; + m[3][2] = vz; + flagBits = Translation; + } else if (flagBits == Translation) { + m[3][0] += vx; + m[3][1] += vy; + m[3][2] += vz; + } else if (flagBits == Scale) { + m[3][0] = m[0][0] * vx; + m[3][1] = m[1][1] * vy; + m[3][2] = m[2][2] * vz; + flagBits |= Translation; + } else if (flagBits == (Scale | Translation)) { + m[3][0] += m[0][0] * vx; + m[3][1] += m[1][1] * vy; + m[3][2] += m[2][2] * vz; + } else { + m[3][0] += m[0][0] * vx + m[1][0] * vy + m[2][0] * vz; + m[3][1] += m[0][1] * vx + m[1][1] * vy + m[2][1] * vz; + m[3][2] += m[0][2] * vx + m[1][2] * vy + m[2][2] * vz; + m[3][3] += m[0][3] * vx + m[1][3] * vy + m[2][3] * vz; + if (flagBits == Rotation) + flagBits |= Translation; + else if (flagBits != (Rotation | Translation)) + flagBits = General; + } + return *this; +} + +#ifndef QT_NO_VECTOR3D + +/*! + Multiples this matrix by another that rotates coordinates through + \a angle degrees about \a vector. Returns this matrix. + + \sa scale(), translate() +*/ +QMatrix4x4& QMatrix4x4::rotate(qreal angle, const QVector3D& vector) +{ + return rotate(angle, vector.x(), vector.y(), vector.z()); +} + +#endif + +/*! + \overload + + Multiplies this matrix by another that rotates coordinates through + \a angle degrees about the vector (\a x, \a y, \a z). Returns this matrix. + + \sa scale(), translate() +*/ +QMatrix4x4& QMatrix4x4::rotate(qreal angle, qreal x, qreal y, qreal z) +{ + QMatrix4x4 m(1); // The "1" says to not load the identity. + qrealinner c, s, ic; + qt_math3d_sincos(angle, &s, &c); + bool quick = false; + if (x == 0.0f) { + if (y == 0.0f) { + if (z != 0.0f) { + // Rotate around the Z axis. + m.setIdentity(); + m.m[0][0] = c; + m.m[1][1] = c; + if (z < 0.0f) { + m.m[1][0] = s; + m.m[0][1] = -s; + } else { + m.m[1][0] = -s; + m.m[0][1] = s; + } + m.flagBits = General; + quick = true; + } + } else if (z == 0.0f) { + // Rotate around the Y axis. + m.setIdentity(); + m.m[0][0] = c; + m.m[2][2] = c; + if (y < 0.0f) { + m.m[2][0] = -s; + m.m[0][2] = s; + } else { + m.m[2][0] = s; + m.m[0][2] = -s; + } + m.flagBits = General; + quick = true; + } + } else if (y == 0.0f && z == 0.0f) { + // Rotate around the X axis. + m.setIdentity(); + m.m[1][1] = c; + m.m[2][2] = c; + if (x < 0.0f) { + m.m[2][1] = s; + m.m[1][2] = -s; + } else { + m.m[2][1] = -s; + m.m[1][2] = s; + } + m.flagBits = General; + quick = true; + } + if (!quick) { + qrealinner vx(x); + qrealinner vy(y); + qrealinner vz(z); + qrealinner len(qvtsqrt(vx * vx + vy * vy + vz * vz)); + if (len != 0) { + vx /= len; + vy /= len; + vz /= len; + } + ic = 1.0f - c; + m.m[0][0] = vx * vx * ic + c; + m.m[1][0] = vx * vy * ic - vz * s; + m.m[2][0] = vx * vz * ic + vy * s; + m.m[3][0] = 0.0f; + m.m[0][1] = vy * vx * ic + vz * s; + m.m[1][1] = vy * vy * ic + c; + m.m[2][1] = vy * vz * ic - vx * s; + m.m[3][1] = 0.0f; + m.m[0][2] = vx * vz * ic - vy * s; + m.m[1][2] = vy * vz * ic + vx * s; + m.m[2][2] = vz * vz * ic + c; + m.m[3][2] = 0.0f; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = 0.0f; + m.m[3][3] = 1.0f; + } + int flags = flagBits; + *this *= m; + if (flags != Identity) + flagBits = flags | Rotation; + else + flagBits = Rotation; + return *this; +} + +#ifndef QT_NO_VECTOR4D + +/*! + Multiples this matrix by another that rotates coordinates according + to a specified \a quaternion. The \a quaternion is assumed to have + been normalized. Returns this matrix. + + \sa scale(), translate(), QQuaternion +*/ +QMatrix4x4& QMatrix4x4::rotate(const QQuaternion& quaternion) +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54 + QMatrix4x4 m(1); + qrealinner xx = quaternion.xp * quaternion.xp; + qrealinner xy = quaternion.xp * quaternion.yp; + qrealinner xz = quaternion.xp * quaternion.zp; + qrealinner xw = quaternion.xp * quaternion.wp; + qrealinner yy = quaternion.yp * quaternion.yp; + qrealinner yz = quaternion.yp * quaternion.zp; + qrealinner yw = quaternion.yp * quaternion.wp; + qrealinner zz = quaternion.zp * quaternion.zp; + qrealinner zw = quaternion.zp * quaternion.wp; + m.m[0][0] = 1.0f - 2 * (yy + zz); + m.m[1][0] = 2 * (xy - zw); + m.m[2][0] = 2 * (xz + yw); + m.m[3][0] = 0.0f; + m.m[0][1] = 2 * (xy + zw); + m.m[1][1] = 1.0f - 2 * (xx + zz); + m.m[2][1] = 2 * (yz - xw); + m.m[3][1] = 0.0f; + m.m[0][2] = 2 * (xz - yw); + m.m[1][2] = 2 * (yz + xw); + m.m[2][2] = 1.0f - 2 * (xx + yy); + m.m[3][2] = 0.0f; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = 0.0f; + m.m[3][3] = 1.0f; + int flags = flagBits; + *this *= m; + if (flags != Identity) + flagBits = flags | Rotation; + else + flagBits = Rotation; + return *this; +} + +#endif + +/*! + \overload + + Multiplies this matrix by another that applies an orthographic + projection for a window with boundaries specified by \a rect. + The near and far clipping planes will be -1 and 1 respectively. + Returns this matrix. + + \sa frustum(), perspective() +*/ +QMatrix4x4& QMatrix4x4::ortho(const QRect& rect) +{ + // Note: rect.right() and rect.bottom() subtract 1 in QRect, + // which gives the location of a pixel within the rectangle, + // instead of the extent of the rectangle. We want the extent. + // QRectF expresses the extent properly. + return ortho(rect.x(), rect.x() + rect.width(), rect.y() + rect.height(), rect.y(), -1.0f, 1.0f); +} + +/*! + \overload + + Multiplies this matrix by another that applies an orthographic + projection for a window with boundaries specified by \a rect. + The near and far clipping planes will be -1 and 1 respectively. + Returns this matrix. + + \sa frustum(), perspective() +*/ +QMatrix4x4& QMatrix4x4::ortho(const QRectF& rect) +{ + return ortho(rect.left(), rect.right(), rect.bottom(), rect.top(), -1.0f, 1.0f); +} + +/*! + Multiplies this matrix by another that applies an orthographic + projection for a window with lower-left corner (\a left, \a bottom), + upper-right corner (\a right, \a top), and the specified \a nearPlane + and \a farPlane clipping planes. Returns this matrix. + + \sa frustum(), perspective() +*/ +QMatrix4x4& QMatrix4x4::ortho(qreal left, qreal right, qreal bottom, qreal top, qreal nearPlane, qreal farPlane) +{ + // Bail out if the projection volume is zero-sized. + if (left == right || bottom == top || nearPlane == farPlane) + return *this; + + // Construct the projection. + qreal width = right - left; + qreal invheight = top - bottom; + qreal clip = farPlane - nearPlane; +#ifndef QT_NO_VECTOR3D + if (clip == 2.0f && (nearPlane + farPlane) == 0.0f) { + // We can express this projection as a translate and scale + // which will be more efficient to modify with further + // transformations than producing a "General" matrix. + translate(QVector3D + (qf2vt_round(-(left + right) / width), + qf2vt_round(-(top + bottom) / invheight), + 0.0f, 1)); + scale(QVector3D + (qf2vt_round(2.0f / width), + qf2vt_round(2.0f / invheight), + -1.0f, 1)); + return *this; + } +#endif + QMatrix4x4 m(1); + m.m[0][0] = qf2vt_round(2.0f / width); + m.m[1][0] = qf2vt_round(0.0f); + m.m[2][0] = qf2vt_round(0.0f); + m.m[3][0] = qf2vt_round(-(left + right) / width); + m.m[0][1] = qf2vt_round(0.0f); + m.m[1][1] = qf2vt_round(2.0f / invheight); + m.m[2][1] = qf2vt_round(0.0f); + m.m[3][1] = qf2vt_round(-(top + bottom) / invheight); + m.m[0][2] = qf2vt_round(0.0f); + m.m[1][2] = qf2vt_round(0.0f); + m.m[2][2] = qf2vt_round(-2.0f / clip); + m.m[3][2] = qf2vt_round(-(nearPlane + farPlane) / clip); + m.m[0][3] = qf2vt_round(0.0f); + m.m[1][3] = qf2vt_round(0.0f); + m.m[2][3] = qf2vt_round(0.0f); + m.m[3][3] = qf2vt_round(1.0f); + + // Apply the projection. + *this *= m; + return *this; +} + +/*! + Multiplies this matrix by another that applies a perspective + frustum projection for a window with lower-left corner (\a left, \a bottom), + upper-right corner (\a right, \a top), and the specified \a nearPlane + and \a farPlane clipping planes. Returns this matrix. + + \sa ortho(), perspective() +*/ +QMatrix4x4& QMatrix4x4::frustum(qreal left, qreal right, qreal bottom, qreal top, qreal nearPlane, qreal farPlane) +{ + // Bail out if the projection volume is zero-sized. + if (left == right || bottom == top || nearPlane == farPlane) + return *this; + + // Construct the projection. + QMatrix4x4 m(1); + qreal width = right - left; + qreal invheight = top - bottom; + qreal clip = farPlane - nearPlane; + m.m[0][0] = qf2vt_round(2.0f * nearPlane / width); + m.m[1][0] = qf2vt_round(0.0f); + m.m[2][0] = qf2vt_round((left + right) / width); + m.m[3][0] = qf2vt_round(0.0f); + m.m[0][1] = qf2vt_round(0.0f); + m.m[1][1] = qf2vt_round(2.0f * nearPlane / invheight); + m.m[2][1] = qf2vt_round((top + bottom) / invheight); + m.m[3][1] = qf2vt_round(0.0f); + m.m[0][2] = qf2vt_round(0.0f); + m.m[1][2] = qf2vt_round(0.0f); + m.m[2][2] = qf2vt_round(-(nearPlane + farPlane) / clip); + m.m[3][2] = qf2vt_round(-(2.0f * nearPlane * farPlane) / clip); + m.m[0][3] = qf2vt_round(0.0f); + m.m[1][3] = qf2vt_round(0.0f); + m.m[2][3] = qf2vt_round(-1.0f); + m.m[3][3] = qf2vt_round(0.0f); + + // Apply the projection. + *this *= m; + return *this; +} + +/*! + Multiplies this matrix by another that applies a perspective + projection. The field of view will be \a angle degrees within + a window with a given \a aspect ratio. The projection will + have the specified \a nearPlane and \a farPlane clipping planes. + Returns this matrix. + + \sa ortho(), frustum() +*/ +QMatrix4x4& QMatrix4x4::perspective(qreal angle, qreal aspect, qreal nearPlane, qreal farPlane) +{ + // Bail out if the projection volume is zero-sized. + if (nearPlane == farPlane || aspect == 0.0f) + return *this; + + // Construct the projection. + QMatrix4x4 m(1); + qreal radians = (angle / 2.0f) * M_PI / 180.0f; + qreal sine = qSin(radians); + if (sine == 0.0f) + return *this; + qreal cotan = qCos(radians) / sine; + qreal clip = farPlane - nearPlane; + m.m[0][0] = qf2vt_round(cotan / aspect); + m.m[1][0] = qf2vt_round(0.0f); + m.m[2][0] = qf2vt_round(0.0f); + m.m[3][0] = qf2vt_round(0.0f); + m.m[0][1] = qf2vt_round(0.0f); + m.m[1][1] = qf2vt_round(cotan); + m.m[2][1] = qf2vt_round(0.0f); + m.m[3][1] = qf2vt_round(0.0f); + m.m[0][2] = qf2vt_round(0.0f); + m.m[1][2] = qf2vt_round(0.0f); + m.m[2][2] = qf2vt_round(-(nearPlane + farPlane) / clip); + m.m[3][2] = qf2vt_round(-(2.0f * nearPlane * farPlane) / clip); + m.m[0][3] = qf2vt_round(0.0f); + m.m[1][3] = qf2vt_round(0.0f); + m.m[2][3] = qf2vt_round(-1.0f); + m.m[3][3] = qf2vt_round(0.0f); + + // Apply the projection. + *this *= m; + return *this; +} + +#ifndef QT_NO_VECTOR3D + +/*! + Multiplies this matrix by another that applies an \a eye position + transformation. The \a center value indicates the center of the + view that the \a eye is looking at. The \a up value indicates + which direction should be considered up with respect to the \a eye. + Returns this matrix. +*/ +QMatrix4x4& QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVector3D& up) +{ + QVector3D forward = (center - eye).normalized(); + QVector3D side = QVector3D::crossProduct(forward, up).normalized(); + QVector3D upVector = QVector3D::crossProduct(side, forward); + + QMatrix4x4 m(1); + + m.m[0][0] = side.xp; + m.m[1][0] = side.yp; + m.m[2][0] = side.zp; + m.m[3][0] = 0.0f; + m.m[0][1] = upVector.xp; + m.m[1][1] = upVector.yp; + m.m[2][1] = upVector.zp; + m.m[3][1] = 0.0f; + m.m[0][2] = -forward.xp; + m.m[1][2] = -forward.yp; + m.m[2][2] = -forward.zp; + m.m[3][2] = 0.0f; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = 0.0f; + m.m[3][3] = 1.0f; + + *this *= m; + return translate(-eye); +} + +#endif + +/*! + Flips between right-handed and left-handed coordinate systems + by multiplying the y and z co-ordinates by -1. This is normally + used to create a left-handed orthographic view without scaling + the viewport as ortho() does. Returns this matrix. + + \sa ortho() +*/ +QMatrix4x4& QMatrix4x4::flipCoordinates() +{ + if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[1][1] = -m[1][1]; + m[2][2] = -m[2][2]; + } else if (flagBits == Translation) { + m[1][1] = -m[1][1]; + m[2][2] = -m[2][2]; + flagBits |= Scale; + } else if (flagBits == Identity) { + m[1][1] = -1.0f; + m[2][2] = -1.0f; + flagBits = Scale; + } else { + m[1][0] = -m[1][0]; + m[1][1] = -m[1][1]; + m[1][2] = -m[1][2]; + m[1][3] = -m[1][3]; + m[2][0] = -m[2][0]; + m[2][1] = -m[2][1]; + m[2][2] = -m[2][2]; + m[2][3] = -m[2][3]; + flagBits = General; + } + return *this; +} + +/*! + Retrieves the 16 items in this matrix and writes them to \a values + in row-major order. +*/ +void QMatrix4x4::toValueArray(qreal *values) const +{ + for (int row = 0; row < 4; ++row) + for (int col = 0; col < 4; ++col) + values[row * 4 + col] = qt_math3d_convert<qreal, qrealinner>(m[col][row]); +} + +/*! + Returns the conventional Qt 2D affine transformation matrix that + corresponds to this matrix. It is assumed that this matrix + only contains 2D affine transformation elements. + + \sa toTransform() +*/ +QMatrix QMatrix4x4::toAffine() const +{ + return QMatrix(qt_math3d_convert<qreal, qrealinner>(m[0][0]), + qt_math3d_convert<qreal, qrealinner>(m[0][1]), + qt_math3d_convert<qreal, qrealinner>(m[1][0]), + qt_math3d_convert<qreal, qrealinner>(m[1][1]), + qt_math3d_convert<qreal, qrealinner>(m[3][0]), + qt_math3d_convert<qreal, qrealinner>(m[3][1])); +} + +/*! + Returns the conventional Qt 2D transformation matrix that + corresponds to this matrix. It is assumed that this matrix + only contains 2D transformation elements. + + \sa toAffine() +*/ +QTransform QMatrix4x4::toTransform() const +{ + return QTransform(qt_math3d_convert<qreal, qrealinner>(m[0][0]), + qt_math3d_convert<qreal, qrealinner>(m[0][1]), + qt_math3d_convert<qreal, qrealinner>(m[0][3]), + qt_math3d_convert<qreal, qrealinner>(m[1][0]), + qt_math3d_convert<qreal, qrealinner>(m[1][1]), + qt_math3d_convert<qreal, qrealinner>(m[1][3]), + qt_math3d_convert<qreal, qrealinner>(m[3][0]), + qt_math3d_convert<qreal, qrealinner>(m[3][1]), + qt_math3d_convert<qreal, qrealinner>(m[3][3])); +} + +/*! + \fn QPoint QMatrix4x4::map(const QPoint& point) const + + Maps \a point by multiplying this matrix by \a point. + + \sa mapRect() +*/ + +/*! + \fn QPointF QMatrix4x4::map(const QPointF& point) const + + Maps \a point by multiplying this matrix by \a point. + + \sa mapRect() +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + \fn QVector3D QMatrix4x4::map(const QVector3D& point) const + + Maps \a point by multiplying this matrix by \a point. + + \sa mapRect() +*/ + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + \fn QVector4D QMatrix4x4::map(const QVector4D& point) const; + + Maps \a point by multiplying this matrix by \a point. + + \sa mapRect() +*/ + +#endif + +/*! + \fn QRect QMatrix4x4::mapRect(const QRect& rect) const + + Maps \a rect by multiplying this matrix by the corners + of \a rect and then forming a new rectangle from the results. + The returned rectangle will be an ordinary 2D rectangle + with sides parallel to the horizontal and vertical axes. + + \sa map() +*/ + +/*! + \fn QRectF QMatrix4x4::mapRect(const QRectF& rect) const + + Maps \a rect by multiplying this matrix by the corners + of \a rect and then forming a new rectangle from the results. + The returned rectangle will be an ordinary 2D rectangle + with sides parallel to the horizontal and vertical axes. + + \sa map() +*/ + +/*! + \fn qrealinner *QMatrix4x4::data() + + Returns a pointer to the raw data of this matrix. This is indended + for use with raw GL functions. + + \sa constData(), inferSpecialType() +*/ + +/*! + \fn const qrealinner *QMatrix4x4::data() const + + Returns a constant pointer to the raw data of this matrix. + This is indended for use with raw GL functions. + + \sa constData() +*/ + +/*! + \fn const qrealinner *QMatrix4x4::constData() const + + Returns a constant pointer to the raw data of this matrix. + This is indended for use with raw GL functions. + + \sa data() +*/ + +// Helper routine for inverting orthonormal matrices that consist +// of just rotations and translations. +QMatrix4x4 QMatrix4x4::orthonormalInverse() const +{ + QMatrix4x4 result(1); // The '1' says not to load identity + + result.m[0][0] = m[0][0]; + result.m[1][0] = m[0][1]; + result.m[2][0] = m[0][2]; + + result.m[0][1] = m[1][0]; + result.m[1][1] = m[1][1]; + result.m[2][1] = m[1][2]; + + result.m[0][2] = m[2][0]; + result.m[1][2] = m[2][1]; + result.m[2][2] = m[2][2]; + + result.m[0][3] = 0.0f; + result.m[1][3] = 0.0f; + result.m[2][3] = 0.0f; + + result.m[3][0] = -(result.m[0][0] * m[3][0] + result.m[1][0] * m[3][1] + result.m[2][0] * m[3][2]); + result.m[3][1] = -(result.m[0][1] * m[3][0] + result.m[1][1] * m[3][1] + result.m[2][1] * m[3][2]); + result.m[3][2] = -(result.m[0][2] * m[3][0] + result.m[1][2] * m[3][1] + result.m[2][2] * m[3][2]); + result.m[3][3] = 1.0f; + + return result; +} + +#ifndef QT_NO_VECTOR3D +/*! + Decomposes the current rotation matrix into an \a axis of rotation plus + an \a angle. The result can be used to construct an equivalent rotation + matrix using glRotate(). It is assumed that the homogenous coordinate + is 1.0. The returned vector is guaranteed to be normalized. + + \code + qreal angle; + QVector3D axis; + + matrix.extractAxisAngle(angle, axis); + glRotate(angle, axis[0], axis[1], axis[2]); + \endcode + + \sa rotate() +*/ +void QMatrix4x4::extractAxisRotation(qreal &angle, QVector3D &axis) const +{ + // Orientation is dependent on the upper 3x3 matrix; subtract the + // homogeneous scaling element from the trace of the 4x4 matrix + qrealinner tr = m[0][0] + m[1][1] + m[2][2]; + qreal cosa = qt_math3d_convert<qreal, qrealinner>(0.5f * (tr - 1.0f)); + angle = acos(cosa) * 180.0f / M_PI; + + // Any axis will work if r is zero (means no rotation) + if (qFuzzyCompare(angle, (qreal)0.0f)) { + axis.setX(1.0f); + axis.setY(0.0f); + axis.setZ(0.0f); + return; + } + + if (angle < 180.0f) { + axis.xp = m[1][2] - m[2][1]; + axis.yp = m[2][0] - m[0][2]; + axis.zp = m[0][1] - m[1][0]; + axis.normalize(); + return; + } + + // rads == PI + qrealinner tmp; + + // r00 is maximum + if ((m[0][0] >= m[2][2]) && (m[0][0] >= m[1][1])) { + axis.xp = 0.5f * qvtsqrt(m[0][0] - m[1][1] - m[2][2] + 1.0f); + tmp = 0.5f / axis.x(); + axis.yp = m[1][0] * tmp; + axis.zp = m[2][0] * tmp; + } + + // r11 is maximum + if ((m[1][1] >= m[2][2]) && (m[1][1] >= m[0][0])) { + axis.yp = 0.5f * qvtsqrt(m[1][1] - m[0][0] - m[2][2] + 1.0f); + tmp = 0.5f / axis.y(); + axis.xp = tmp * m[1][0]; + axis.zp = tmp * m[2][1]; + } + + // r22 is maximum + if ((m[2][2] >= m[1][1]) && (m[2][2] >= m[0][0])) { + axis.zp = 0.5f * qvtsqrt(m[2][2] - m[0][0] - m[1][1] + 1.0f); + tmp = 0.5f / axis.z(); + axis.xp = m[2][0]*tmp; + axis.yp = m[2][1]*tmp; + } +} + +/*! + If this is an orthonormal transformation matrix (e.g. only rotations and + translations have been applied to the matrix, no scaling, or shearing) + then the world translational component can be obtained by calling this function. + + This is most useful for camera matrices, where the negation of this vector + is effectively the camera world coordinates. +*/ +QVector3D QMatrix4x4::extractTranslation() const +{ + return QVector3D + (m[0][0] * m[3][0] + m[0][1] * m[3][1] + m[0][2] * m[3][2], + m[1][0] * m[3][0] + m[1][1] * m[3][1] + m[1][2] * m[3][2], + m[2][0] * m[3][0] + m[2][1] * m[3][1] + m[2][2] * m[3][2], 1); +} +#endif + +/*! + Infers the special type of this matrix from its current elements. + + Some operations such as translate(), scale(), and rotate() can be + performed more efficiently if the matrix being modified is already + known to be the identity, a previous translate(), a previous + scale(), etc. + + Normally the QMatrix4x4 class keeps track of this special type internally + as operations are performed. However, if the matrix is modified + directly with operator()() or data(), then QMatrix4x4 will lose track of + the special type and will revert to the safest but least efficient + operations thereafter. + + By calling inferSpecialType() after directly modifying the matrix, + the programmer can force QMatrix4x4 to recover the special type if + the elements appear to conform to one of the known optimized types. + + \sa operator()(), data(), translate() +*/ +void QMatrix4x4::inferSpecialType() +{ + // If the last element is not 1, then it can never be special. + if (m[3][3] != 1.0f) { + flagBits = General; + return; + } + + // If the upper three elements m12, m13, and m21 are not all zero, + // or the lower elements below the diagonal are not all zero, then + // the matrix can never be special. + if (m[1][0] != 0.0f || m[2][0] != 0.0f || m[2][1] != 0.0f) { + flagBits = General; + return; + } + if (m[0][1] != 0.0f || m[0][2] != 0.0f || m[0][3] != 0.0f || + m[1][2] != 0.0f || m[1][3] != 0.0f || m[2][3] != 0.0f) { + flagBits = General; + return; + } + + // Determine what we have in the remaining regions of the matrix. + bool identityAlongDiagonal + = (m[0][0] == 1.0f && m[1][1] == 1.0f && m[2][2] == 1.0f); + bool translationPresent + = (m[3][0] != 0.0f || m[3][1] != 0.0f || m[3][2] != 0.0f); + + // Now determine the special matrix type. + if (translationPresent && identityAlongDiagonal) + flagBits = Translation; + else if (translationPresent) + flagBits = (Translation | Scale); + else if (identityAlongDiagonal) + flagBits = Identity; + else + flagBits = Scale; +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QMatrix4x4 &m) +{ + // Create a string that represents the matrix type. + QByteArray bits; + if ((m.flagBits & QMatrix4x4::Identity) != 0) + bits += "Identity,"; + if ((m.flagBits & QMatrix4x4::General) != 0) + bits += "General,"; + if ((m.flagBits & QMatrix4x4::Translation) != 0) + bits += "Translation,"; + if ((m.flagBits & QMatrix4x4::Scale) != 0) + bits += "Scale,"; + if ((m.flagBits & QMatrix4x4::Rotation) != 0) + bits += "Rotation,"; + if (bits.size() > 0) + bits = bits.left(bits.size() - 1); + + // Output in row-major order because it is more human-readable. + dbg.nospace() << "QMatrix4x4(type:" << bits.constData() << endl + << qSetFieldWidth(10) + << m(0, 0) << m(0, 1) << m(0, 2) << m(0, 3) << endl + << m(1, 0) << m(1, 1) << m(1, 2) << m(1, 3) << endl + << m(2, 0) << m(2, 1) << m(2, 2) << m(2, 3) << endl + << m(3, 0) << m(3, 1) << m(3, 2) << m(3, 3) << endl + << qSetFieldWidth(0) << ')'; + return dbg.space(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h new file mode 100644 index 0000000..6428b20 --- /dev/null +++ b/src/gui/math3d/qmatrix4x4.h @@ -0,0 +1,984 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMATRIX4X4_H +#define QMATRIX4X4_H + +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qquaternion.h> +#include <QtGui/qgenericmatrix.h> +#include <QtCore/qrect.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_MATRIX4X4 + +class QMatrix; +class QTransform; + +class Q_GUI_EXPORT QMatrix4x4 +{ +public: + inline QMatrix4x4() { setIdentity(); } + explicit QMatrix4x4(const qreal *values); + inline QMatrix4x4(qreal m11, qreal m12, qreal m13, qreal m14, + qreal m21, qreal m22, qreal m23, qreal m24, + qreal m31, qreal m32, qreal m33, qreal m34, + qreal m41, qreal m42, qreal m43, qreal m44); +#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) + template <int N, int M> + explicit QMatrix4x4(const QGenericMatrix<N, M, qreal, qrealinner>& matrix); +#endif + QMatrix4x4(const qrealinner *values, int cols, int rows); + QMatrix4x4(const QTransform& transform); + QMatrix4x4(const QMatrix& matrix); + + inline qreal operator()(int row, int column) const; + inline qrealinner& operator()(int row, int column); + + inline QVector4D column(int index) const; + inline void setColumn(int index, const QVector4D& value); + + inline QVector4D row(int index) const; + inline void setRow(int index, const QVector4D& value); + + inline bool isIdentity() const; + inline void setIdentity(); + + inline void fill(qreal value); + + qreal determinant() const; + QMatrix4x4 inverted(bool *invertible = 0) const; + QMatrix4x4 transposed() const; + QMatrix3x3 normalMatrix() const; + + inline QMatrix4x4& operator+=(const QMatrix4x4& other); + inline QMatrix4x4& operator-=(const QMatrix4x4& other); + inline QMatrix4x4& operator*=(const QMatrix4x4& other); + inline QMatrix4x4& operator*=(qreal factor); + QMatrix4x4& operator/=(qreal divisor); + inline bool operator==(const QMatrix4x4& other) const; + inline bool operator!=(const QMatrix4x4& other) const; + + friend QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2); + friend QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2); + friend QMatrix4x4 operator*(const QMatrix4x4& m1, const QMatrix4x4& m2); +#ifndef QT_NO_VECTOR3D + friend QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector); + friend QVector3D operator*(const QVector3D& vector, const QMatrix4x4& matrix); +#endif +#ifndef QT_NO_VECTOR4D + friend QVector4D operator*(const QVector4D& vector, const QMatrix4x4& matrix); + friend QVector4D operator*(const QMatrix4x4& matrix, const QVector4D& vector); +#endif + friend QPoint operator*(const QPoint& point, const QMatrix4x4& matrix); + friend QPointF operator*(const QPointF& point, const QMatrix4x4& matrix); + friend QMatrix4x4 operator-(const QMatrix4x4& matrix); + friend QPoint operator*(const QMatrix4x4& matrix, const QPoint& point); + friend QPointF operator*(const QMatrix4x4& matrix, const QPointF& point); + friend QMatrix4x4 operator*(qreal factor, const QMatrix4x4& matrix); + friend QMatrix4x4 operator*(const QMatrix4x4& matrix, qreal factor); + friend Q_GUI_EXPORT QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor); + + friend inline bool qFuzzyCompare(const QMatrix4x4& m1, const QMatrix4x4& m2); + +#ifndef QT_NO_VECTOR3D + QMatrix4x4& scale(const QVector3D& vector); + QMatrix4x4& translate(const QVector3D& vector); + QMatrix4x4& rotate(qreal angle, const QVector3D& vector); +#endif + QMatrix4x4& scale(qreal x, qreal y, qreal z = 1.0f); + QMatrix4x4& scale(qreal factor); + QMatrix4x4& translate(qreal x, qreal y, qreal z = 0.0f); + QMatrix4x4& rotate(qreal angle, qreal x, qreal y, qreal z = 0.0f); +#ifndef QT_NO_QUATERNION + QMatrix4x4& rotate(const QQuaternion& quaternion); +#endif + +#ifndef QT_NO_VECTOR3D + void extractAxisRotation(qreal &angle, QVector3D &axis) const; + QVector3D extractTranslation() const; +#endif + + QMatrix4x4& ortho(const QRect& rect); + QMatrix4x4& ortho(const QRectF& rect); + QMatrix4x4& ortho(qreal left, qreal right, qreal bottom, qreal top, qreal nearPlane, qreal farPlane); + QMatrix4x4& frustum(qreal left, qreal right, qreal bottom, qreal top, qreal nearPlane, qreal farPlane); + QMatrix4x4& perspective(qreal angle, qreal aspect, qreal nearPlane, qreal farPlane); +#ifndef QT_NO_VECTOR3D + QMatrix4x4& lookAt(const QVector3D& eye, const QVector3D& center, const QVector3D& up); +#endif + QMatrix4x4& flipCoordinates(); + + void toValueArray(qreal *values) const; + + QMatrix toAffine() const; + QTransform toTransform() const; + + QPoint map(const QPoint& point) const; + QPointF map(const QPointF& point) const; +#ifndef QT_NO_VECTOR3D + QVector3D map(const QVector3D& point) const; +#endif +#ifndef QT_NO_VECTOR4D + QVector4D map(const QVector4D& point) const; +#endif + QRect mapRect(const QRect& rect) const; + QRectF mapRect(const QRectF& rect) const; + +#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) + template <int N, int M> + QGenericMatrix<N, M, qreal, qrealinner> toGenericMatrix() const; +#endif + + inline qrealinner *data(); + inline const qrealinner *data() const { return m[0]; } + inline const qrealinner *constData() const { return m[0]; } + + void inferSpecialType(); + +#ifndef QT_NO_DEBUG_STREAM + friend Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QMatrix4x4 &m); +#endif + +private: + qrealinner m[4][4]; // Column-major order to match OpenGL. + int flagBits; // Flag bits from the enum below. + + enum { + Identity = 0x0001, // Identity matrix + General = 0x0002, // General matrix, unknown contents + Translation = 0x0004, // Contains a simple translation + Scale = 0x0008, // Contains a simple scale + Rotation = 0x0010 // Contains a simple rotation + }; + + // Construct without initializing identity matrix. + QMatrix4x4(int) { flagBits = General; } + + QMatrix4x4 orthonormalInverse() const; +}; + +inline QMatrix4x4::QMatrix4x4 + (qreal m11, qreal m12, qreal m13, qreal m14, + qreal m21, qreal m22, qreal m23, qreal m24, + qreal m31, qreal m32, qreal m33, qreal m34, + qreal m41, qreal m42, qreal m43, qreal m44) +{ + m[0][0] = m11; m[0][1] = m21; m[0][2] = m31; m[0][3] = m41; + m[1][0] = m12; m[1][1] = m22; m[1][2] = m32; m[1][3] = m42; + m[2][0] = m13; m[2][1] = m23; m[2][2] = m33; m[2][3] = m43; + m[3][0] = m14; m[3][1] = m24; m[3][2] = m34; m[3][3] = m44; + flagBits = General; +} + +#if !defined(QT_NO_MEMBER_TEMPLATES) + +template <int N, int M> +Q_INLINE_TEMPLATE QMatrix4x4::QMatrix4x4 + (const QGenericMatrix<N, M, qreal, qrealinner>& matrix) +{ + const qrealinner *values = matrix.constData(); + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + if (col < N && row < M) + m[col][row] = values[col * M + row]; + else if (col == row) + m[col][row] = 1.0f; + else + m[col][row] = 0.0f; + } + } + flagBits = General; +} + +template <int N, int M> +QGenericMatrix<N, M, qreal, qrealinner> QMatrix4x4::toGenericMatrix() const +{ + QGenericMatrix<N, M, qreal, qrealinner> result; + qrealinner *values = result.data(); + for (int col = 0; col < N; ++col) { + for (int row = 0; row < M; ++row) { + if (col < 4 && row < 4) + values[col * M + row] = m[col][row]; + else if (col == row) + values[col * M + row] = 1.0f; + else + values[col * M + row] = 0.0f; + } + } + return result; +} + +#endif + +inline qreal QMatrix4x4::operator()(int row, int column) const +{ + Q_ASSERT(row >= 0 && row < 4 && column >= 0 && column < 4); + return qt_math3d_convert<qreal, qrealinner>(m[column][row]); +} + +inline qrealinner& QMatrix4x4::operator()(int row, int column) +{ + Q_ASSERT(row >= 0 && row < 4 && column >= 0 && column < 4); + flagBits = General; + return m[column][row]; +} + +inline QVector4D QMatrix4x4::column(int index) const +{ + Q_ASSERT(index >= 0 && index < 4); + return QVector4D(m[index][0], m[index][1], m[index][2], m[index][3], 1); +} + +inline void QMatrix4x4::setColumn(int index, const QVector4D& value) +{ + Q_ASSERT(index >= 0 && index < 4); + m[index][0] = value.xp; + m[index][1] = value.yp; + m[index][2] = value.zp; + m[index][3] = value.wp; + flagBits = General; +} + +inline QVector4D QMatrix4x4::row(int index) const +{ + Q_ASSERT(index >= 0 && index < 4); + return QVector4D(m[0][index], m[1][index], m[2][index], m[3][index], 1); +} + +inline void QMatrix4x4::setRow(int index, const QVector4D& value) +{ + Q_ASSERT(index >= 0 && index < 4); + m[0][index] = value.xp; + m[1][index] = value.yp; + m[2][index] = value.zp; + m[3][index] = value.wp; + flagBits = General; +} + +Q_GUI_EXPORT QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor); + +inline bool QMatrix4x4::isIdentity() const +{ + if (flagBits == Identity) + return true; + if (m[0][0] != 1.0f || m[0][1] != 0.0f || m[0][2] != 0.0f) + return false; + if (m[0][3] != 0.0f || m[1][0] != 0.0f || m[1][1] != 1.0f) + return false; + if (m[1][2] != 0.0f || m[1][3] != 0.0f || m[2][0] != 0.0f) + return false; + if (m[2][1] != 0.0f || m[2][2] != 1.0f || m[2][3] != 0.0f) + return false; + if (m[3][0] != 0.0f || m[3][1] != 0.0f || m[3][2] != 0.0f) + return false; + return (m[3][3] == 1.0f); +} + +inline void QMatrix4x4::setIdentity() +{ + m[0][0] = 1.0f; + m[0][1] = 0.0f; + m[0][2] = 0.0f; + m[0][3] = 0.0f; + m[1][0] = 0.0f; + m[1][1] = 1.0f; + m[1][2] = 0.0f; + m[1][3] = 0.0f; + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; + m[2][3] = 0.0f; + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; + flagBits = Identity; +} + +inline void QMatrix4x4::fill(qreal value) +{ + m[0][0] = value; + m[0][1] = value; + m[0][2] = value; + m[0][3] = value; + m[1][0] = value; + m[1][1] = value; + m[1][2] = value; + m[1][3] = value; + m[2][0] = value; + m[2][1] = value; + m[2][2] = value; + m[2][3] = value; + m[3][0] = value; + m[3][1] = value; + m[3][2] = value; + m[3][3] = value; + flagBits = General; +} + +inline QMatrix4x4& QMatrix4x4::operator+=(const QMatrix4x4& other) +{ + m[0][0] += other.m[0][0]; + m[0][1] += other.m[0][1]; + m[0][2] += other.m[0][2]; + m[0][3] += other.m[0][3]; + m[1][0] += other.m[1][0]; + m[1][1] += other.m[1][1]; + m[1][2] += other.m[1][2]; + m[1][3] += other.m[1][3]; + m[2][0] += other.m[2][0]; + m[2][1] += other.m[2][1]; + m[2][2] += other.m[2][2]; + m[2][3] += other.m[2][3]; + m[3][0] += other.m[3][0]; + m[3][1] += other.m[3][1]; + m[3][2] += other.m[3][2]; + m[3][3] += other.m[3][3]; + flagBits = General; + return *this; +} + +inline QMatrix4x4& QMatrix4x4::operator-=(const QMatrix4x4& other) +{ + m[0][0] -= other.m[0][0]; + m[0][1] -= other.m[0][1]; + m[0][2] -= other.m[0][2]; + m[0][3] -= other.m[0][3]; + m[1][0] -= other.m[1][0]; + m[1][1] -= other.m[1][1]; + m[1][2] -= other.m[1][2]; + m[1][3] -= other.m[1][3]; + m[2][0] -= other.m[2][0]; + m[2][1] -= other.m[2][1]; + m[2][2] -= other.m[2][2]; + m[2][3] -= other.m[2][3]; + m[3][0] -= other.m[3][0]; + m[3][1] -= other.m[3][1]; + m[3][2] -= other.m[3][2]; + m[3][3] -= other.m[3][3]; + flagBits = General; + return *this; +} + +inline QMatrix4x4& QMatrix4x4::operator*=(const QMatrix4x4& other) +{ + if (flagBits == Identity) { + *this = other; + return *this; + } else if (other.flagBits == Identity) { + return *this; + } else { + *this = *this * other; + return *this; + } +} + +inline QMatrix4x4& QMatrix4x4::operator*=(qreal factor) +{ + qrealinner f(factor); + m[0][0] *= f; + m[0][1] *= f; + m[0][2] *= f; + m[0][3] *= f; + m[1][0] *= f; + m[1][1] *= f; + m[1][2] *= f; + m[1][3] *= f; + m[2][0] *= f; + m[2][1] *= f; + m[2][2] *= f; + m[2][3] *= f; + m[3][0] *= f; + m[3][1] *= f; + m[3][2] *= f; + m[3][3] *= f; + flagBits = General; + return *this; +} + +inline bool QMatrix4x4::operator==(const QMatrix4x4& other) const +{ + return m[0][0] == other.m[0][0] && + m[0][1] == other.m[0][1] && + m[0][2] == other.m[0][2] && + m[0][3] == other.m[0][3] && + m[1][0] == other.m[1][0] && + m[1][1] == other.m[1][1] && + m[1][2] == other.m[1][2] && + m[1][3] == other.m[1][3] && + m[2][0] == other.m[2][0] && + m[2][1] == other.m[2][1] && + m[2][2] == other.m[2][2] && + m[2][3] == other.m[2][3] && + m[3][0] == other.m[3][0] && + m[3][1] == other.m[3][1] && + m[3][2] == other.m[3][2] && + m[3][3] == other.m[3][3]; +} + +inline bool QMatrix4x4::operator!=(const QMatrix4x4& other) const +{ + return m[0][0] != other.m[0][0] || + m[0][1] != other.m[0][1] || + m[0][2] != other.m[0][2] || + m[0][3] != other.m[0][3] || + m[1][0] != other.m[1][0] || + m[1][1] != other.m[1][1] || + m[1][2] != other.m[1][2] || + m[1][3] != other.m[1][3] || + m[2][0] != other.m[2][0] || + m[2][1] != other.m[2][1] || + m[2][2] != other.m[2][2] || + m[2][3] != other.m[2][3] || + m[3][0] != other.m[3][0] || + m[3][1] != other.m[3][1] || + m[3][2] != other.m[3][2] || + m[3][3] != other.m[3][3]; +} + +inline QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2) +{ + QMatrix4x4 m(1); + m.m[0][0] = m1.m[0][0] + m2.m[0][0]; + m.m[0][1] = m1.m[0][1] + m2.m[0][1]; + m.m[0][2] = m1.m[0][2] + m2.m[0][2]; + m.m[0][3] = m1.m[0][3] + m2.m[0][3]; + m.m[1][0] = m1.m[1][0] + m2.m[1][0]; + m.m[1][1] = m1.m[1][1] + m2.m[1][1]; + m.m[1][2] = m1.m[1][2] + m2.m[1][2]; + m.m[1][3] = m1.m[1][3] + m2.m[1][3]; + m.m[2][0] = m1.m[2][0] + m2.m[2][0]; + m.m[2][1] = m1.m[2][1] + m2.m[2][1]; + m.m[2][2] = m1.m[2][2] + m2.m[2][2]; + m.m[2][3] = m1.m[2][3] + m2.m[2][3]; + m.m[3][0] = m1.m[3][0] + m2.m[3][0]; + m.m[3][1] = m1.m[3][1] + m2.m[3][1]; + m.m[3][2] = m1.m[3][2] + m2.m[3][2]; + m.m[3][3] = m1.m[3][3] + m2.m[3][3]; + return m; +} + +inline QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2) +{ + QMatrix4x4 m(1); + m.m[0][0] = m1.m[0][0] - m2.m[0][0]; + m.m[0][1] = m1.m[0][1] - m2.m[0][1]; + m.m[0][2] = m1.m[0][2] - m2.m[0][2]; + m.m[0][3] = m1.m[0][3] - m2.m[0][3]; + m.m[1][0] = m1.m[1][0] - m2.m[1][0]; + m.m[1][1] = m1.m[1][1] - m2.m[1][1]; + m.m[1][2] = m1.m[1][2] - m2.m[1][2]; + m.m[1][3] = m1.m[1][3] - m2.m[1][3]; + m.m[2][0] = m1.m[2][0] - m2.m[2][0]; + m.m[2][1] = m1.m[2][1] - m2.m[2][1]; + m.m[2][2] = m1.m[2][2] - m2.m[2][2]; + m.m[2][3] = m1.m[2][3] - m2.m[2][3]; + m.m[3][0] = m1.m[3][0] - m2.m[3][0]; + m.m[3][1] = m1.m[3][1] - m2.m[3][1]; + m.m[3][2] = m1.m[3][2] - m2.m[3][2]; + m.m[3][3] = m1.m[3][3] - m2.m[3][3]; + return m; +} + +inline QMatrix4x4 operator*(const QMatrix4x4& m1, const QMatrix4x4& m2) +{ + if (m1.flagBits == QMatrix4x4::Identity) + return m2; + else if (m2.flagBits == QMatrix4x4::Identity) + return m1; + + QMatrix4x4 m(1); + m.m[0][0] = m1.m[0][0] * m2.m[0][0] + + m1.m[1][0] * m2.m[0][1] + + m1.m[2][0] * m2.m[0][2] + + m1.m[3][0] * m2.m[0][3]; + m.m[0][1] = m1.m[0][1] * m2.m[0][0] + + m1.m[1][1] * m2.m[0][1] + + m1.m[2][1] * m2.m[0][2] + + m1.m[3][1] * m2.m[0][3]; + m.m[0][2] = m1.m[0][2] * m2.m[0][0] + + m1.m[1][2] * m2.m[0][1] + + m1.m[2][2] * m2.m[0][2] + + m1.m[3][2] * m2.m[0][3]; + m.m[0][3] = m1.m[0][3] * m2.m[0][0] + + m1.m[1][3] * m2.m[0][1] + + m1.m[2][3] * m2.m[0][2] + + m1.m[3][3] * m2.m[0][3]; + m.m[1][0] = m1.m[0][0] * m2.m[1][0] + + m1.m[1][0] * m2.m[1][1] + + m1.m[2][0] * m2.m[1][2] + + m1.m[3][0] * m2.m[1][3]; + m.m[1][1] = m1.m[0][1] * m2.m[1][0] + + m1.m[1][1] * m2.m[1][1] + + m1.m[2][1] * m2.m[1][2] + + m1.m[3][1] * m2.m[1][3]; + m.m[1][2] = m1.m[0][2] * m2.m[1][0] + + m1.m[1][2] * m2.m[1][1] + + m1.m[2][2] * m2.m[1][2] + + m1.m[3][2] * m2.m[1][3]; + m.m[1][3] = m1.m[0][3] * m2.m[1][0] + + m1.m[1][3] * m2.m[1][1] + + m1.m[2][3] * m2.m[1][2] + + m1.m[3][3] * m2.m[1][3]; + m.m[2][0] = m1.m[0][0] * m2.m[2][0] + + m1.m[1][0] * m2.m[2][1] + + m1.m[2][0] * m2.m[2][2] + + m1.m[3][0] * m2.m[2][3]; + m.m[2][1] = m1.m[0][1] * m2.m[2][0] + + m1.m[1][1] * m2.m[2][1] + + m1.m[2][1] * m2.m[2][2] + + m1.m[3][1] * m2.m[2][3]; + m.m[2][2] = m1.m[0][2] * m2.m[2][0] + + m1.m[1][2] * m2.m[2][1] + + m1.m[2][2] * m2.m[2][2] + + m1.m[3][2] * m2.m[2][3]; + m.m[2][3] = m1.m[0][3] * m2.m[2][0] + + m1.m[1][3] * m2.m[2][1] + + m1.m[2][3] * m2.m[2][2] + + m1.m[3][3] * m2.m[2][3]; + m.m[3][0] = m1.m[0][0] * m2.m[3][0] + + m1.m[1][0] * m2.m[3][1] + + m1.m[2][0] * m2.m[3][2] + + m1.m[3][0] * m2.m[3][3]; + m.m[3][1] = m1.m[0][1] * m2.m[3][0] + + m1.m[1][1] * m2.m[3][1] + + m1.m[2][1] * m2.m[3][2] + + m1.m[3][1] * m2.m[3][3]; + m.m[3][2] = m1.m[0][2] * m2.m[3][0] + + m1.m[1][2] * m2.m[3][1] + + m1.m[2][2] * m2.m[3][2] + + m1.m[3][2] * m2.m[3][3]; + m.m[3][3] = m1.m[0][3] * m2.m[3][0] + + m1.m[1][3] * m2.m[3][1] + + m1.m[2][3] * m2.m[3][2] + + m1.m[3][3] * m2.m[3][3]; + return m; +} + +#ifndef QT_NO_VECTOR3D + +inline QVector3D operator*(const QVector3D& vector, const QMatrix4x4& matrix) +{ + qrealinner x, y, z, w; + x = vector.xp * matrix.m[0][0] + + vector.yp * matrix.m[0][1] + + vector.zp * matrix.m[0][2] + + matrix.m[0][3]; + y = vector.xp * matrix.m[1][0] + + vector.yp * matrix.m[1][1] + + vector.zp * matrix.m[1][2] + + matrix.m[1][3]; + z = vector.xp * matrix.m[2][0] + + vector.yp * matrix.m[2][1] + + vector.zp * matrix.m[2][2] + + matrix.m[2][3]; + w = vector.xp * matrix.m[3][0] + + vector.yp * matrix.m[3][1] + + vector.zp * matrix.m[3][2] + + matrix.m[3][3]; + if (w == 1.0f) + return QVector3D(x, y, z, 1); + else + return QVector3D(x / w, y / w, z / w, 1); +} + +inline QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector) +{ + qrealinner x, y, z, w; + x = vector.xp * matrix.m[0][0] + + vector.yp * matrix.m[1][0] + + vector.zp * matrix.m[2][0] + + matrix.m[3][0]; + y = vector.xp * matrix.m[0][1] + + vector.yp * matrix.m[1][1] + + vector.zp * matrix.m[2][1] + + matrix.m[3][1]; + z = vector.xp * matrix.m[0][2] + + vector.yp * matrix.m[1][2] + + vector.zp * matrix.m[2][2] + + matrix.m[3][2]; + w = vector.xp * matrix.m[0][3] + + vector.yp * matrix.m[1][3] + + vector.zp * matrix.m[2][3] + + matrix.m[3][3]; + if (w == 1.0f) + return QVector3D(x, y, z, 1); + else + return QVector3D(x / w, y / w, z / w, 1); +} + +#endif + +#ifndef QT_NO_VECTOR4D + +inline QVector4D operator*(const QVector4D& vector, const QMatrix4x4& matrix) +{ + qrealinner x, y, z, w; + x = vector.xp * matrix.m[0][0] + + vector.yp * matrix.m[0][1] + + vector.zp * matrix.m[0][2] + + vector.wp * matrix.m[0][3]; + y = vector.xp * matrix.m[1][0] + + vector.yp * matrix.m[1][1] + + vector.zp * matrix.m[1][2] + + vector.wp * matrix.m[1][3]; + z = vector.xp * matrix.m[2][0] + + vector.yp * matrix.m[2][1] + + vector.zp * matrix.m[2][2] + + vector.wp * matrix.m[2][3]; + w = vector.xp * matrix.m[3][0] + + vector.yp * matrix.m[3][1] + + vector.zp * matrix.m[3][2] + + vector.wp * matrix.m[3][3]; + return QVector4D(x, y, z, w, 1); +} + +inline QVector4D operator*(const QMatrix4x4& matrix, const QVector4D& vector) +{ + qrealinner x, y, z, w; + x = vector.xp * matrix.m[0][0] + + vector.yp * matrix.m[1][0] + + vector.zp * matrix.m[2][0] + + vector.wp * matrix.m[3][0]; + y = vector.xp * matrix.m[0][1] + + vector.yp * matrix.m[1][1] + + vector.zp * matrix.m[2][1] + + vector.wp * matrix.m[3][1]; + z = vector.xp * matrix.m[0][2] + + vector.yp * matrix.m[1][2] + + vector.zp * matrix.m[2][2] + + vector.wp * matrix.m[3][2]; + w = vector.xp * matrix.m[0][3] + + vector.yp * matrix.m[1][3] + + vector.zp * matrix.m[2][3] + + vector.wp * matrix.m[3][3]; + return QVector4D(x, y, z, w, 1); +} + +#endif + +inline QPoint operator*(const QPoint& point, const QMatrix4x4& matrix) +{ + qrealinner xin, yin; + qrealinner x, y, w; + xin = point.x(); + yin = point.y(); + x = xin * matrix.m[0][0] + + yin * matrix.m[0][1] + + matrix.m[0][3]; + y = xin * matrix.m[1][0] + + yin * matrix.m[1][1] + + matrix.m[1][3]; + w = xin * matrix.m[3][0] + + yin * matrix.m[3][1] + + matrix.m[3][3]; + if (w == 1.0f) + return QPoint(qRound(x), qRound(y)); + else + return QPoint(qRound(x / w), qRound(y / w)); +} + +inline QPointF operator*(const QPointF& point, const QMatrix4x4& matrix) +{ + qrealinner xin, yin; + qrealinner x, y, w; + xin = point.x(); + yin = point.y(); + x = xin * matrix.m[0][0] + + yin * matrix.m[0][1] + + matrix.m[0][3]; + y = xin * matrix.m[1][0] + + yin * matrix.m[1][1] + + matrix.m[1][3]; + w = xin * matrix.m[3][0] + + yin * matrix.m[3][1] + + matrix.m[3][3]; + if (w == 1.0f) { + return QPointF(qt_math3d_convert<qreal, qrealinner>(x), + qt_math3d_convert<qreal, qrealinner>(y)); + } else { + return QPointF(qt_math3d_convert<qreal, qrealinner>(x / w), + qt_math3d_convert<qreal, qrealinner>(y / w)); + } +} + +inline QPoint operator*(const QMatrix4x4& matrix, const QPoint& point) +{ + qrealinner xin, yin; + qrealinner x, y, w; + xin = point.x(); + yin = point.y(); + x = xin * matrix.m[0][0] + + yin * matrix.m[1][0] + + matrix.m[3][0]; + y = xin * matrix.m[0][1] + + yin * matrix.m[1][1] + + matrix.m[3][1]; + w = xin * matrix.m[0][3] + + yin * matrix.m[1][3] + + matrix.m[3][3]; + if (w == 1.0f) + return QPoint(qRound(x), qRound(y)); + else + return QPoint(qRound(x / w), qRound(y / w)); +} + +inline QPointF operator*(const QMatrix4x4& matrix, const QPointF& point) +{ + qrealinner xin, yin; + qrealinner x, y, w; + xin = point.x(); + yin = point.y(); + x = xin * matrix.m[0][0] + + yin * matrix.m[1][0] + + matrix.m[3][0]; + y = xin * matrix.m[0][1] + + yin * matrix.m[1][1] + + matrix.m[3][1]; + w = xin * matrix.m[0][3] + + yin * matrix.m[1][3] + + matrix.m[3][3]; + if (w == 1.0f) { + return QPointF(qt_math3d_convert<qreal, qrealinner>(x), + qt_math3d_convert<qreal, qrealinner>(y)); + } else { + return QPointF(qt_math3d_convert<qreal, qrealinner>(x / w), + qt_math3d_convert<qreal, qrealinner>(y / w)); + } +} + +inline QMatrix4x4 operator-(const QMatrix4x4& matrix) +{ + QMatrix4x4 m(1); + m.m[0][0] = -matrix.m[0][0]; + m.m[0][1] = -matrix.m[0][1]; + m.m[0][2] = -matrix.m[0][2]; + m.m[0][3] = -matrix.m[0][3]; + m.m[1][0] = -matrix.m[1][0]; + m.m[1][1] = -matrix.m[1][1]; + m.m[1][2] = -matrix.m[1][2]; + m.m[1][3] = -matrix.m[1][3]; + m.m[2][0] = -matrix.m[2][0]; + m.m[2][1] = -matrix.m[2][1]; + m.m[2][2] = -matrix.m[2][2]; + m.m[2][3] = -matrix.m[2][3]; + m.m[3][0] = -matrix.m[3][0]; + m.m[3][1] = -matrix.m[3][1]; + m.m[3][2] = -matrix.m[3][2]; + m.m[3][3] = -matrix.m[3][3]; + return m; +} + +inline QMatrix4x4 operator*(qreal factor, const QMatrix4x4& matrix) +{ + QMatrix4x4 m(1); + qrealinner f(factor); + m.m[0][0] = matrix.m[0][0] * f; + m.m[0][1] = matrix.m[0][1] * f; + m.m[0][2] = matrix.m[0][2] * f; + m.m[0][3] = matrix.m[0][3] * f; + m.m[1][0] = matrix.m[1][0] * f; + m.m[1][1] = matrix.m[1][1] * f; + m.m[1][2] = matrix.m[1][2] * f; + m.m[1][3] = matrix.m[1][3] * f; + m.m[2][0] = matrix.m[2][0] * f; + m.m[2][1] = matrix.m[2][1] * f; + m.m[2][2] = matrix.m[2][2] * f; + m.m[2][3] = matrix.m[2][3] * f; + m.m[3][0] = matrix.m[3][0] * f; + m.m[3][1] = matrix.m[3][1] * f; + m.m[3][2] = matrix.m[3][2] * f; + m.m[3][3] = matrix.m[3][3] * f; + return m; +} + +inline QMatrix4x4 operator*(const QMatrix4x4& matrix, qreal factor) +{ + QMatrix4x4 m(1); + qrealinner f(factor); + m.m[0][0] = matrix.m[0][0] * f; + m.m[0][1] = matrix.m[0][1] * f; + m.m[0][2] = matrix.m[0][2] * f; + m.m[0][3] = matrix.m[0][3] * f; + m.m[1][0] = matrix.m[1][0] * f; + m.m[1][1] = matrix.m[1][1] * f; + m.m[1][2] = matrix.m[1][2] * f; + m.m[1][3] = matrix.m[1][3] * f; + m.m[2][0] = matrix.m[2][0] * f; + m.m[2][1] = matrix.m[2][1] * f; + m.m[2][2] = matrix.m[2][2] * f; + m.m[2][3] = matrix.m[2][3] * f; + m.m[3][0] = matrix.m[3][0] * f; + m.m[3][1] = matrix.m[3][1] * f; + m.m[3][2] = matrix.m[3][2] * f; + m.m[3][3] = matrix.m[3][3] * f; + return m; +} + +inline bool qFuzzyCompare(const QMatrix4x4& m1, const QMatrix4x4& m2) +{ + return qFuzzyCompare(m1.m[0][0], m2.m[0][0]) && + qFuzzyCompare(m1.m[0][1], m2.m[0][1]) && + qFuzzyCompare(m1.m[0][2], m2.m[0][2]) && + qFuzzyCompare(m1.m[0][3], m2.m[0][3]) && + qFuzzyCompare(m1.m[1][0], m2.m[1][0]) && + qFuzzyCompare(m1.m[1][1], m2.m[1][1]) && + qFuzzyCompare(m1.m[1][2], m2.m[1][2]) && + qFuzzyCompare(m1.m[1][3], m2.m[1][3]) && + qFuzzyCompare(m1.m[2][0], m2.m[2][0]) && + qFuzzyCompare(m1.m[2][1], m2.m[2][1]) && + qFuzzyCompare(m1.m[2][2], m2.m[2][2]) && + qFuzzyCompare(m1.m[2][3], m2.m[2][3]) && + qFuzzyCompare(m1.m[3][0], m2.m[3][0]) && + qFuzzyCompare(m1.m[3][1], m2.m[3][1]) && + qFuzzyCompare(m1.m[3][2], m2.m[3][2]) && + qFuzzyCompare(m1.m[3][3], m2.m[3][3]); +} + +inline QPoint QMatrix4x4::map(const QPoint& point) const +{ + return *this * point; +} + +inline QPointF QMatrix4x4::map(const QPointF& point) const +{ + return *this * point; +} + +#ifndef QT_NO_VECTOR3D + +inline QVector3D QMatrix4x4::map(const QVector3D& point) const +{ + return *this * point; +} + +#endif + +#ifndef QT_NO_VECTOR4D + +inline QVector4D QMatrix4x4::map(const QVector4D& point) const +{ + return *this * point; +} + +#endif + +inline QRect QMatrix4x4::mapRect(const QRect& rect) const +{ + QPoint tl = map(rect.topLeft()); QPoint tr = map(rect.topRight()); + QPoint bl = map(rect.bottomLeft()); QPoint br = map(rect.bottomRight()); + + int xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x())); + int xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x())); + int ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y())); + int ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y())); + + return QRect(QPoint(xmin, ymin), QPoint(xmax, ymax)); +} + +inline QRectF QMatrix4x4::mapRect(const QRectF& rect) const +{ + QPointF tl = map(rect.topLeft()); QPointF tr = map(rect.topRight()); + QPointF bl = map(rect.bottomLeft()); QPointF br = map(rect.bottomRight()); + + qreal xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x())); + qreal xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x())); + qreal ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y())); + qreal ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y())); + + return QRectF(QPointF(xmin, ymin), QPointF(xmax, ymax)); +} + +inline qrealinner *QMatrix4x4::data() +{ + // We have to assume that the caller will modify the matrix elements, + // so we flip it over to "General" mode. + flagBits = General; + return m[0]; +} + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QMatrix4x4 &m); +#endif + +template <int N, int M> +QMatrix4x4 qGenericMatrixToMatrix4x4(const QGenericMatrix<N, M, qreal, qrealinner>& matrix) +{ + return QMatrix4x4(matrix.constData(), N, M); +} + +template <int N, int M> +QGenericMatrix<N, M, qreal, qrealinner> qGenericMatrixFromMatrix4x4(const QMatrix4x4& matrix) +{ + QGenericMatrix<N, M, qreal, qrealinner> result; + const qrealinner *m = matrix.constData(); + qrealinner *values = result.data(); + for (int col = 0; col < N; ++col) { + for (int row = 0; row < M; ++row) { + if (col < 4 && row < 4) + values[col * M + row] = m[col * 4 + row]; + else if (col == row) + values[col * M + row] = 1.0f; + else + values[col * M + row] = 0.0f; + } + } + return result; +} + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_MATRIX4X4 +Q_DECLARE_METATYPE(QMatrix4x4) +#endif + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp new file mode 100644 index 0000000..121ff51 --- /dev/null +++ b/src/gui/math3d/qquaternion.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquaternion.h" +#include "qmath3dutil_p.h" +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_QUATERNION + +/*! + \class QQuaternion + \brief The QQuaternion class represents a quaternion consisting of a vector and scalar. + \since 4.6 + + Quaternions are used to represent rotations in 3D space, and + consist of a 3D rotation axis specified by the x, y, and z + coordinates, and a scalar representing the rotation angle. + + The components of a quaternion are stored internally using the most + efficient representation for the GL rendering engine, which will be + either floating-point or fixed-point. +*/ + +/*! + \fn QQuaternion::QQuaternion() + + Constructs an identity quaternion, i.e. with coordinates (1, 0, 0, 0). +*/ + +/*! + \fn QQuaternion::QQuaternion(qreal scalar, qreal xpos, qreal ypos, qreal zpos) + + Constructs a quaternion with the vector (\a xpos, \a ypos, \a zpos) + and \a scalar. +*/ + +/*! + \fn QQuaternion::QQuaternion(int scalar, int xpos, int ypos, int zpos) + + Constructs a quaternion with the vector (\a xpos, \a ypos, \a zpos) + and \a scalar. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + \fn QQuaternion::QQuaternion(qreal scalar, const QVector3D& vector) + + Constructs a quaternion vector from the specified \a vector and + \a scalar. + + \sa vector(), scalar() +*/ + +/*! + \fn QVector3D QQuaternion::vector() const + + Returns the vector component of this quaternion. + + \sa setVector(), scalar() +*/ + +/*! + \fn void QQuaternion::setVector(const QVector3D& vector) + + Sets the vector component of this quaternion to \a vector. + + \sa vector(), setScalar() +*/ + +#endif + +/*! + \fn void QQuaternion::setVector(qreal x, qreal y, qreal z) + + Sets the vector component of this quaternion to (\a x, \a y, \a z). + + \sa vector(), setScalar() +*/ + +#ifndef QT_NO_VECTOR4D + +/*! + \fn QQuaternion::QQuaternion(const QVector4D& vector) + + Constructs a quaternion from the components of \a vector. +*/ + +/*! + \fn QVector4D QQuaternion::toVector4D() const + + Returns this quaternion as a 4D vector. +*/ + +#endif + +/*! + \fn bool QQuaternion::isNull() const + + Returns true if the x, y, z, and scalar components of this + quaternion are set to 0.0; otherwise returns false. +*/ + +/*! + \fn bool QQuaternion::isIdentity() const + + Returns true if the x, y, and z components of this + quaternion are set to 0.0, and the scalar component is set + to 1.0; otherwise returns false. +*/ + +/*! + \fn qreal QQuaternion::x() const + + Returns the x coordinate of this quaternion's vector. + + \sa setX(), y(), z(), scalar() +*/ + +/*! + \fn qreal QQuaternion::y() const + + Returns the y coordinate of this quaternion's vector. + + \sa setY(), x(), z(), scalar() +*/ + +/*! + \fn qreal QQuaternion::z() const + + Returns the z coordinate of this quaternion's vector. + + \sa setZ(), x(), y(), scalar() +*/ + +/*! + \fn qreal QQuaternion::scalar() const + + Returns the scalar component of this quaternion. + + \sa setScalar(), x(), y(), z() +*/ + +/*! + \fn void QQuaternion::setX(qreal x) + + Sets the x coordinate of this quaternion's vector to the given + \a x coordinate. + + \sa x(), setY(), setZ(), setScalar() +*/ + +/*! + \fn void QQuaternion::setY(qreal y) + + Sets the y coordinate of this quaternion's vector to the given + \a y coordinate. + + \sa y(), setX(), setZ(), setScalar() +*/ + +/*! + \fn void QQuaternion::setZ(qreal z) + + Sets the z coordinate of this quaternion's vector to the given + \a z coordinate. + + \sa z(), setX(), setY(), setScalar() +*/ + +/*! + \fn void QQuaternion::setScalar(qreal scalar) + + Sets the scalar component of this quaternion to \a scalar. + + \sa scalar(), setX(), setY(), setZ() +*/ + +/*! + Returns the length of the quaternion. This is also called the "norm". + + \sa lengthSquared(), normalized() +*/ +qreal QQuaternion::length() const +{ + return qvtsqrt64(qvtmul64(xp, xp) + qvtmul64(yp, yp) + + qvtmul64(zp, zp) + qvtmul64(wp, wp)); +} + +/*! + Returns the squared length of the quaternion. + + \sa length() +*/ +qreal QQuaternion::lengthSquared() const +{ + return qvtdot64(qvtmul64(xp, xp) + qvtmul64(yp, yp) + + qvtmul64(zp, zp) + qvtmul64(wp, wp)); +} + +/*! + Returns the normalized unit form of this quaternion. If this quaternion + is not null, the returned quaternion is guaranteed to be 1.0 in length. + If this quaternion is null, then a null quaternion is returned. + + \sa length(), normalize() +*/ +QQuaternion QQuaternion::normalized() const +{ + qreal len = length(); + if (!qIsNull(len)) + return *this / len; + else + return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f); +} + +/*! + Normalizes the currect quaternion in place. Nothing happens if this + is a null quaternion. + + \sa length(), normalized() +*/ +void QQuaternion::normalize() +{ + qreal len = length(); + if (qIsNull(len)) + return; + + xp /= len; + yp /= len; + zp /= len; + wp /= len; +} + + +/*! + \fn QQuaternion QQuaternion::conjugate() const + + Returns the conjugate of this quaternion, which is + (-x, -y, -z, scalar). +*/ + +/*! + Rotates \a vector with this quaternion to produce a new vector + in 3D space. The following code: + + \code + QVector3D result = q.rotateVector(vector); + \endcode + + is equivalent to the following: + + \code + QVector3D result = (q * QQuaternion(0, vector) * q.conjugate()).vector(); + \endcode +*/ +QVector3D QQuaternion::rotateVector(const QVector3D& vector) const +{ + return (*this * QQuaternion(0, vector) * conjugate()).vector(); +} + +/*! + \fn QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion) + + Adds the given \a quaternion to this quaternion and returns a reference to + this quaternion. + + \sa operator-=() +*/ + +/*! + \fn QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion) + + Subtracts the given \a quaternion from this quaternion and returns a + reference to this quaternion. + + \sa operator+=() +*/ + +/*! + \fn QQuaternion &QQuaternion::operator*=(qreal factor) + + Multiplies this quaternion's components by the given \a factor, and + returns a reference to this quaternion. + + \sa operator/=() +*/ + +/*! + \fn QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion) + + Multiplies this quaternion by \a quaternion and returns a reference + to this quaternion. +*/ + +/*! + \fn QQuaternion &QQuaternion::operator/=(qreal divisor) + + Divides this quaternion's components by the given \a divisor, and + returns a reference to this quaternion. + + \sa operator*=() +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + Creates a normalized quaternion that corresponds to rotating through + \a angle degrees about the specified 3D \a axis. +*/ +QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D& axis, qreal angle) +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56 + // We normalize the result just in case the values are close + // to zero, as suggested in the above FAQ. + qrealinner s, c; + QVector3D ax = axis.normalized(); + qt_math3d_sincos(angle / 2.0f, &s, &c); + return QQuaternion(c, ax.xp * s, ax.yp * s, ax.zp * s, 1).normalized(); +} + +#endif + +/*! + Creates a normalized quaternion that corresponds to rotating through + \a angle degrees about the 3D axis (\a x, \a y, \a z). +*/ +QQuaternion QQuaternion::fromAxisAndAngle + (qreal x, qreal y, qreal z, qreal angle) +{ + qrealinner xp = x; + qrealinner yp = y; + qrealinner zp = z; + qrealinner s, c; + qreal length = qvtsqrt(xp * xp + yp * yp + zp * zp); + if (!qIsNull(length)) { + xp /= length; + yp /= length; + zp /= length; + } + qt_math3d_sincos(angle / 2.0f, &s, &c); + return QQuaternion(c, xp * s, yp * s, zp * s, 1).normalized(); +} + +/*! + \fn bool operator==(const QQuaternion &q1, const QQuaternion &q2) + \relates QQuaternion + + Returns true if \a q1 is equal to \a q2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool operator!=(const QQuaternion &q1, const QQuaternion &q2) + \relates QQuaternion + + Returns true if \a q1 is not equal to \a q2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) + \relates QQuaternion + + Returns a QQuaternion object that is the sum of the given quaternions, + \a q1 and \a q2; each component is added separately. + + \sa QQuaternion::operator+=() +*/ + +/*! + \fn const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) + \relates QQuaternion + + Returns a QQuaternion object that is formed by subtracting + \a q2 from \a q1; each component is subtracted separately. + + \sa QQuaternion::operator-=() +*/ + +/*! + \fn const QQuaternion operator*(qreal factor, const QQuaternion &quaternion) + \relates QQuaternion + + Returns a copy of the given \a quaternion, multiplied by the + given \a factor. + + \sa QQuaternion::operator*=() +*/ + +/*! + \fn const QQuaternion operator*(const QQuaternion &quaternion, qreal factor) + \relates QQuaternion + + Returns a copy of the given \a quaternion, multiplied by the + given \a factor. + + \sa QQuaternion::operator*=() +*/ + +/*! + \fn const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2) + \relates QQuaternion + + Multiplies \a q1 and \a q2 using quaternion multiplication. + The result corresponds to applying both of the rotations specified + by \a q1 and \a q2. + + \sa QQuaternion::operator*=() +*/ + +/*! + \fn const QQuaternion operator-(const QQuaternion &quaternion) + \relates QQuaternion + \overload + + Returns a QQuaternion object that is formed by changing the sign of + all three components of the given \a quaternion. + + Equivalent to \c {QQuaternion(0,0,0,0) - quaternion}. +*/ + +/*! + \fn const QQuaternion operator/(const QQuaternion &quaternion, qreal divisor) + \relates QQuaternion + + Returns the QQuaternion object formed by dividing all components of + the given \a quaternion by the given \a divisor. + + \sa QQuaternion::operator/=() +*/ + +/*! + \fn bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2) + \relates QQuaternion + + Returns true if \a q1 and \a q2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +/*! + Interpolates along the shortest spherical path between the + rotational positions \a q1 and \a q2. The value \a t should + be between 0 and 1, indicating the spherical distance to travel + between \a q1 and \a q2. + + If \a t is less than or equal to 0, then \a q1 will be returned. + If \a t is greater than or equal to 1, then \a q2 will be returned. +*/ +QQuaternion QQuaternion::interpolate + (const QQuaternion& q1, const QQuaternion& q2, qreal t) +{ + // Handle the easy cases first. + if (t <= 0.0f) + return q1; + else if (t >= 1.0f) + return q2; + + // Determine the angle between the two quaternions. + QQuaternion q2b; + qreal dot; + dot = qvtdot64(qvtmul64(q1.xp, q2.xp) + qvtmul64(q1.yp, q2.yp) + + qvtmul64(q1.zp, q2.zp) + qvtmul64(q1.wp, q2.wp)); + if (dot >= 0.0f) { + q2b = q2; + } else { + q2b = -q2; + dot = -dot; + } + + // Get the scale factors. If they are too small, + // then revert to simple linear interpolation. + qreal factor1 = 1.0f - t; + qreal factor2 = t; + if ((1.0f - dot) > 0.0000001) { + qreal angle = qreal(qAcos(dot)); + qreal sinOfAngle = qreal(qSin(angle)); + if (sinOfAngle > 0.0000001) { + factor1 = qreal(qSin((1.0f - t) * angle)) / sinOfAngle; + factor2 = qreal(qSin(t * angle)) / sinOfAngle; + } + } + + // Construct the result quaternion. + return q1 * factor1 + q2b * factor2; +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QQuaternion &q) +{ + dbg.nospace() << "QQuaternion(scalar:" << q.scalar() + << ", vector:(" << q.x() << ", " + << q.y() << ", " << q.z() << "))"; + return dbg.space(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h new file mode 100644 index 0000000..6ba63ec --- /dev/null +++ b/src/gui/math3d/qquaternion.h @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUATERNION_H +#define QQUATERNION_H + +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QUATERNION + +class QMatrix4x4; + +class Q_GUI_EXPORT QQuaternion +{ +public: + QQuaternion(); + QQuaternion(qreal scalar, qreal xpos, qreal ypos, qreal zpos); + QQuaternion(int scalar, int xpos, int ypos, int zpos); +#ifndef QT_NO_VECTOR3D + QQuaternion(qreal scalar, const QVector3D& vector); +#endif +#ifndef QT_NO_VECTOR4D + explicit QQuaternion(const QVector4D& vector); +#endif + + bool isNull() const; + bool isIdentity() const; + +#ifndef QT_NO_VECTOR3D + QVector3D vector() const; + void setVector(const QVector3D& vector); +#endif + void setVector(qreal x, qreal y, qreal z); + + qreal x() const; + qreal y() const; + qreal z() const; + qreal scalar() const; + + void setX(qreal x); + void setY(qreal y); + void setZ(qreal z); + void setScalar(qreal scalar); + + qreal length() const; + qreal lengthSquared() const; + + QQuaternion normalized() const; + void normalize(); + + QQuaternion conjugate() const; + + QVector3D rotateVector(const QVector3D& vector) const; + + QQuaternion &operator+=(const QQuaternion &quaternion); + QQuaternion &operator-=(const QQuaternion &quaternion); + QQuaternion &operator*=(qreal factor); + QQuaternion &operator*=(const QQuaternion &quaternion); + QQuaternion &operator/=(qreal divisor); + + friend inline bool operator==(const QQuaternion &q1, const QQuaternion &q2); + friend inline bool operator!=(const QQuaternion &q1, const QQuaternion &q2); + friend inline const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2); + friend inline const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2); + friend inline const QQuaternion operator*(qreal factor, const QQuaternion &quaternion); + friend inline const QQuaternion operator*(const QQuaternion &quaternion, qreal factor); + friend inline const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2); + friend inline const QQuaternion operator-(const QQuaternion &quaternion); + friend inline const QQuaternion operator/(const QQuaternion &quaternion, qreal divisor); + + friend inline bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2); + +#ifndef QT_NO_VECTOR4D + QVector4D toVector4D() const; +#endif + +#ifndef QT_NO_VECTOR3D + static QQuaternion fromAxisAndAngle(const QVector3D& axis, qreal angle); +#endif + static QQuaternion fromAxisAndAngle + (qreal x, qreal y, qreal z, qreal angle); + + static QQuaternion interpolate + (const QQuaternion& q1, const QQuaternion& q2, qreal t); + +private: + qrealinner wp, xp, yp, zp; + + friend class QMatrix4x4; + + QQuaternion(qrealinner scalar, qrealinner xpos, qrealinner ypos, qrealinner zpos, int dummy); +}; + +inline QQuaternion::QQuaternion() : wp(1.0f), xp(0.0f), yp(0.0f), zp(0.0f) {} + +inline QQuaternion::QQuaternion(qreal scalar, qreal xpos, qreal ypos, qreal zpos) : wp(scalar), xp(xpos), yp(ypos), zp(zpos) {} + + +inline QQuaternion::QQuaternion(qrealinner scalar, qrealinner xpos, qrealinner ypos, qrealinner zpos, int) : wp(scalar), xp(xpos), yp(ypos), zp(zpos) {} + +inline QQuaternion::QQuaternion(int scalar, int xpos, int ypos, int zpos) : wp(scalar), xp(xpos), yp(ypos), zp(zpos) {} + +inline bool QQuaternion::isNull() const +{ + return qIsNull(xp) && qIsNull(yp) && qIsNull(zp) && qIsNull(wp); +} + +inline bool QQuaternion::isIdentity() const +{ + return qIsNull(xp) && qIsNull(yp) && qIsNull(zp) && wp == 1.0f; +} + +inline qreal QQuaternion::x() const { return qt_math3d_convert<qreal, qrealinner>(xp); } +inline qreal QQuaternion::y() const { return qt_math3d_convert<qreal, qrealinner>(yp); } +inline qreal QQuaternion::z() const { return qt_math3d_convert<qreal, qrealinner>(zp); } +inline qreal QQuaternion::scalar() const { return qt_math3d_convert<qreal, qrealinner>(wp); } + +inline void QQuaternion::setX(qreal x) { xp = x; } +inline void QQuaternion::setY(qreal y) { yp = y; } +inline void QQuaternion::setZ(qreal z) { zp = z; } +inline void QQuaternion::setScalar(qreal scalar) { wp = scalar; } + +inline QQuaternion QQuaternion::conjugate() const +{ + return QQuaternion(wp, -xp, -yp, -zp, 1); +} + +inline QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion) +{ + xp += quaternion.xp; + yp += quaternion.yp; + zp += quaternion.zp; + wp += quaternion.wp; + return *this; +} + +inline QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion) +{ + xp -= quaternion.xp; + yp -= quaternion.yp; + zp -= quaternion.zp; + wp -= quaternion.wp; + return *this; +} + +inline QQuaternion &QQuaternion::operator*=(qreal factor) +{ + qrealinner f(factor); + xp *= f; + yp *= f; + zp *= f; + wp *= f; + return *this; +} + +inline const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2) +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q53 + qrealinner x = q1.wp * q2.xp + + q1.xp * q2.wp + + q1.yp * q2.zp - + q1.zp * q2.yp; + qrealinner y = q1.wp * q2.yp + + q1.yp * q2.wp + + q1.zp * q2.xp - + q1.xp * q2.zp; + qrealinner z = q1.wp * q2.zp + + q1.zp * q2.wp + + q1.xp * q2.yp - + q1.yp * q2.xp; + qrealinner w = q1.wp * q2.wp - + q1.xp * q2.xp - + q1.yp * q2.yp - + q1.zp * q2.zp; + return QQuaternion(w, x, y, z, 1); +} + +inline QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion) +{ + *this = *this * quaternion; + return *this; +} + +inline QQuaternion &QQuaternion::operator/=(qreal divisor) +{ + qrealinner d(divisor); + xp /= d; + yp /= d; + zp /= d; + wp /= d; + return *this; +} + +inline bool operator==(const QQuaternion &q1, const QQuaternion &q2) +{ + return q1.xp == q2.xp && q1.yp == q2.yp && q1.zp == q2.zp && q1.wp == q2.wp; +} + +inline bool operator!=(const QQuaternion &q1, const QQuaternion &q2) +{ + return q1.xp != q2.xp || q1.yp != q2.yp || q1.zp != q2.zp || q1.wp != q2.wp; +} + +inline const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) +{ + return QQuaternion(q1.wp + q2.wp, q1.xp + q2.xp, q1.yp + q2.yp, q1.zp + q2.zp, 1); +} + +inline const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) +{ + return QQuaternion(q1.wp - q2.wp, q1.xp - q2.xp, q1.yp - q2.yp, q1.zp - q2.zp, 1); +} + +inline const QQuaternion operator*(qreal factor, const QQuaternion &quaternion) +{ + qrealinner f(factor); + return QQuaternion(quaternion.wp * f, quaternion.xp * f, quaternion.yp * f, quaternion.zp * f, 1); +} + +inline const QQuaternion operator*(const QQuaternion &quaternion, qreal factor) +{ + qrealinner f(factor); + return QQuaternion(quaternion.wp * f, quaternion.xp * f, quaternion.yp * f, quaternion.zp * f, 1); +} + +inline const QQuaternion operator-(const QQuaternion &quaternion) +{ + return QQuaternion(-quaternion.wp, -quaternion.xp, -quaternion.yp, -quaternion.zp, 1); +} + +inline const QQuaternion operator/(const QQuaternion &quaternion, qreal divisor) +{ + qrealinner d(divisor); + return QQuaternion(quaternion.wp / d, quaternion.xp / d, quaternion.yp / d, quaternion.zp / d, 1); +} + +inline bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2) +{ + return qFuzzyCompare(q1.xp, q2.xp) && + qFuzzyCompare(q1.yp, q2.yp) && + qFuzzyCompare(q1.zp, q2.zp) && + qFuzzyCompare(q1.wp, q2.wp); +} + +#ifndef QT_NO_VECTOR3D + +inline QQuaternion::QQuaternion(qreal scalar, const QVector3D& vector) + : wp(scalar), xp(vector.xp), yp(vector.yp), zp(vector.zp) {} + +inline void QQuaternion::setVector(const QVector3D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = vector.zp; +} + +inline QVector3D QQuaternion::vector() const +{ + return QVector3D(xp, yp, zp, 1); +} + +#endif + +inline void QQuaternion::setVector(qreal x, qreal y, qreal z) +{ + xp = x; + yp = y; + zp = z; +} + +#ifndef QT_NO_VECTOR4D + +inline QQuaternion::QQuaternion(const QVector4D& vector) + : wp(vector.wp), xp(vector.xp), yp(vector.yp), zp(vector.zp) {} + +inline QVector4D QQuaternion::toVector4D() const +{ + return QVector4D(xp, yp, zp, wp, 1); +} + +#endif + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QQuaternion &q); +#endif + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_QUATERNION +Q_DECLARE_METATYPE(QQuaternion) +#endif + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qvector2d.cpp b/src/gui/math3d/qvector2d.cpp new file mode 100644 index 0000000..0426fc5 --- /dev/null +++ b/src/gui/math3d/qvector2d.cpp @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvector2d.h" +#include "qvector3d.h" +#include "qvector4d.h" +#include "qmath3dutil_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_VECTOR2D + +/*! + \class QVector2D + \brief The QVector2D class represents a vector or vertex in 2D space. + \since 4.6 + + The QVector2D class can also be used to represent vertices in 2D space. + We therefore do not need to provide a separate vertex class. + + The coordinates are stored internally using the most efficient + representation for the GL rendering engine, which will be either + floating-point or fixed-point. +*/ + +/*! + \fn QVector2D::QVector2D() + + Constructs a null vector, i.e. with coordinates (0, 0, 0). +*/ + +/*! + \fn QVector2D::QVector2D(qreal xpos, qreal ypos) + + Constructs a vector with coordinates (\a xpos, \a ypos). +*/ + +/*! + \fn QVector2D::QVector2D(int xpos, int ypos) + + Constructs a vector with coordinates (\a xpos, \a ypos). +*/ + +/*! + \fn QVector2D::QVector2D(const QPoint& point) + + Constructs a vector with x and y coordinates from a 2D \a point. +*/ + +/*! + \fn QVector2D::QVector2D(const QPointF& point) + + Constructs a vector with x and y coordinates from a 2D \a point. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + Constructs a vector with x and y coordinates from a 3D \a vector. + The z coordinate of \a vector is dropped. + + \sa toVector3D() +*/ +QVector2D::QVector2D(const QVector3D& vector) +{ + xp = vector.xp; + yp = vector.yp; +} + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + Constructs a vector with x and y coordinates from a 3D \a vector. + The z and w coordinates of \a vector are dropped. + + \sa toVector4D() +*/ +QVector2D::QVector2D(const QVector4D& vector) +{ + xp = vector.xp; + yp = vector.yp; +} + +#endif + +/*! + \fn bool QVector2D::isNull() const + + Returns true if the x and y coordinates are set to 0.0, + otherwise returns false. +*/ + +/*! + \fn qreal QVector2D::x() const + + Returns the x coordinate of this point. + + \sa setX(), y() +*/ + +/*! + \fn qreal QVector2D::y() const + + Returns the y coordinate of this point. + + \sa setY(), x() +*/ + +/*! + \fn void QVector2D::setX(qreal x) + + Sets the x coordinate of this point to the given \a x coordinate. + + \sa x(), setY() +*/ + +/*! + \fn void QVector2D::setY(qreal y) + + Sets the y coordinate of this point to the given \a y coordinate. + + \sa y(), setX() +*/ + +/*! + Returns the length of the vector from the origin. + + \sa lengthSquared(), normalized() +*/ +qreal QVector2D::length() const +{ + return qvtsqrt64(qvtmul64(xp, xp) + qvtmul64(yp, yp)); +} + +/*! + Returns the squared length of the vector from the origin. + This is equivalent to the dot product of the vector with itself. + + \sa length(), dotProduct() +*/ +qreal QVector2D::lengthSquared() const +{ + return qvtdot64(qvtmul64(xp, xp) + qvtmul64(yp, yp)); +} + +/*! + Returns the normalized unit vector form of this vector. If this vector + is not null, the returned vector is guaranteed to be 1.0 in length. + If this vector is null, then a null vector is returned. + + \sa length(), normalize() +*/ +QVector2D QVector2D::normalized() const +{ + qreal len = length(); + if (!qIsNull(len)) + return *this / len; + else + return QVector2D(); +} + +/*! + Normalizes the currect vector in place. Nothing happens if this + vector is a null vector. + + \sa length(), normalized() +*/ +void QVector2D::normalize() +{ + qreal len = length(); + if (qIsNull(len)) + return; + + xp /= len; + yp /= len; +} + +/*! + \fn QVector2D &QVector2D::operator+=(const QVector2D &vector) + + Adds the given \a vector to this vector and returns a reference to + this vector. + + \sa operator-=() +*/ + +/*! + \fn QVector2D &QVector2D::operator-=(const QVector2D &vector) + + Subtracts the given \a vector from this vector and returns a reference to + this vector. + + \sa operator+=() +*/ + +/*! + \fn QVector2D &QVector2D::operator*=(qreal factor) + + Multiplies this vector's coordinates by the given \a factor, and + returns a reference to this vector. + + \sa operator/=() +*/ + +/*! + \fn QVector2D &QVector2D::operator*=(const QVector2D &vector) + + Multiplies the components of this vector by the corresponding + components in \a vector. +*/ + +/*! + \fn QVector2D &QVector2D::operator/=(qreal divisor) + + Divides this vector's coordinates by the given \a divisor, and + returns a reference to this vector. + + \sa operator*=() +*/ + +/*! + Returns the dot product of \a v1 and \a v2. +*/ +qreal QVector2D::dotProduct(const QVector2D& v1, const QVector2D& v2) +{ + return qvtdot64(qvtmul64(v1.xp, v2.xp) + qvtmul64(v1.yp, v2.yp)); +} + +/*! + \fn bool operator==(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Returns true if \a v1 is equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool operator!=(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Returns true if \a v1 is not equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn const QVector2D operator+(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Returns a QVector2D object that is the sum of the given vectors, \a v1 + and \a v2; each component is added separately. + + \sa QVector2D::operator+=() +*/ + +/*! + \fn const QVector2D operator-(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Returns a QVector2D object that is formed by subtracting \a v2 from \a v1; + each component is subtracted separately. + + \sa QVector2D::operator-=() +*/ + +/*! + \fn const QVector2D operator*(qreal factor, const QVector2D &vector) + \relates QVector2D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector2D::operator*=() +*/ + +/*! + \fn const QVector2D operator*(const QVector2D &vector, qreal factor) + \relates QVector2D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector2D::operator*=() +*/ + +/*! + \fn const QVector2D operator*(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Multiplies the components of \a v1 by the corresponding + components in \a v2. +*/ + +/*! + \fn const QVector2D operator-(const QVector2D &vector) + \relates QVector2D + \overload + + Returns a QVector2D object that is formed by changing the sign of + the components of the given \a vector. + + Equivalent to \c {QVector2D(0,0) - vector}. +*/ + +/*! + \fn const QVector2D operator/(const QVector2D &vector, qreal divisor) + \relates QVector2D + + Returns the QVector2D object formed by dividing all three components of + the given \a vector by the given \a divisor. + + \sa QVector2D::operator/=() +*/ + +/*! + \fn bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2) + \relates QVector2D + + Returns true if \a v1 and \a v2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + Returns the 3D form of this 2D vector, with the z coordinate set to zero. + + \sa toVector4D(), toPoint() +*/ +QVector3D QVector2D::toVector3D() const +{ + return QVector3D(xp, yp, 0.0f, 1); +} + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + Returns the 4D form of this 2D vector, with the z and w coordinates set to zero. + + \sa toVector3D(), toPoint() +*/ +QVector4D QVector2D::toVector4D() const +{ + return QVector4D(xp, yp, 0.0f, 0.0f, 1); +} + +#endif + +/*! + \fn QPoint QVector2D::toPoint() const + + Returns the QPoint form of this 2D vector. + + \sa toPointF(), toVector3D() +*/ + +/*! + \fn QPointF QVector2D::toPointF() const + + Returns the QPointF form of this 2D vector. + + \sa toPoint(), toVector3D() +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QVector2D &vector) +{ + dbg.nospace() << "QVector2D(" << vector.x() << ", " << vector.y() << ')'; + return dbg.space(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qvector2d.h b/src/gui/math3d/qvector2d.h new file mode 100644 index 0000000..a72ecc5 --- /dev/null +++ b/src/gui/math3d/qvector2d.h @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR2D_H +#define QVECTOR2D_H + +#include <QtGui/qmath3dglobal.h> +#include <QtCore/qpoint.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QVector3D; +class QVector4D; + +#ifndef QT_NO_VECTOR2D + +class Q_GUI_EXPORT QVector2D +{ +public: + QVector2D(); + QVector2D(qreal xpos, qreal ypos); + QVector2D(int xpos, int ypos); + explicit QVector2D(const QPoint& point); + explicit QVector2D(const QPointF& point); +#ifndef QT_NO_VECTOR3D + explicit QVector2D(const QVector3D& vector); +#endif +#ifndef QT_NO_VECTOR4D + explicit QVector2D(const QVector4D& vector); +#endif + + bool isNull() const; + + qreal x() const; + qreal y() const; + + void setX(qreal x); + void setY(qreal y); + + qreal length() const; + qreal lengthSquared() const; + + QVector2D normalized() const; + void normalize(); + + QVector2D &operator+=(const QVector2D &vector); + QVector2D &operator-=(const QVector2D &vector); + QVector2D &operator*=(qreal factor); + QVector2D &operator*=(const QVector2D &vector); + QVector2D &operator/=(qreal divisor); + + static qreal dotProduct(const QVector2D& v1, const QVector2D& v2); + + friend inline bool operator==(const QVector2D &v1, const QVector2D &v2); + friend inline bool operator!=(const QVector2D &v1, const QVector2D &v2); + friend inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2); + friend inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2); + friend inline const QVector2D operator*(qreal factor, const QVector2D &vector); + friend inline const QVector2D operator*(const QVector2D &vector, qreal factor); + friend inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2); + friend inline const QVector2D operator-(const QVector2D &vector); + friend inline const QVector2D operator/(const QVector2D &vector, qreal divisor); + + friend inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2); + +#ifndef QT_NO_VECTOR3D + QVector3D toVector3D() const; +#endif +#ifndef QT_NO_VECTOR4D + QVector4D toVector4D() const; +#endif + + QPoint toPoint() const; + QPointF toPointF() const; + +private: + qrealinner xp, yp; + + QVector2D(qrealinner xpos, qrealinner ypos, int dummy); + + friend class QVector3D; + friend class QVector4D; + friend class QVertexArray; +}; + +inline QVector2D::QVector2D() : xp(0.0f), yp(0.0f) {} + +inline QVector2D::QVector2D(qrealinner xpos, qrealinner ypos, int) : xp(xpos), yp(ypos) {} + +inline QVector2D::QVector2D(qreal xpos, qreal ypos) : xp(xpos), yp(ypos) {} + +inline QVector2D::QVector2D(int xpos, int ypos) : xp(xpos), yp(ypos) {} + +inline QVector2D::QVector2D(const QPoint& point) : xp(point.x()), yp(point.y()) {} + +inline QVector2D::QVector2D(const QPointF& point) : xp(point.x()), yp(point.y()) {} + +inline bool QVector2D::isNull() const +{ + return qIsNull(xp) && qIsNull(yp); +} + +inline qreal QVector2D::x() const { return qt_math3d_convert<qreal, qrealinner>(xp); } +inline qreal QVector2D::y() const { return qt_math3d_convert<qreal, qrealinner>(yp); } + +inline void QVector2D::setX(qreal x) { xp = x; } +inline void QVector2D::setY(qreal y) { yp = y; } + +inline QVector2D &QVector2D::operator+=(const QVector2D &vector) +{ + xp += vector.xp; + yp += vector.yp; + return *this; +} + +inline QVector2D &QVector2D::operator-=(const QVector2D &vector) +{ + xp -= vector.xp; + yp -= vector.yp; + return *this; +} + +inline QVector2D &QVector2D::operator*=(qreal factor) +{ + qrealinner f(factor); + xp *= f; + yp *= f; + return *this; +} + +inline QVector2D &QVector2D::operator*=(const QVector2D &vector) +{ + xp *= vector.xp; + yp *= vector.yp; + return *this; +} + +inline QVector2D &QVector2D::operator/=(qreal divisor) +{ + qrealinner d(divisor); + xp /= d; + yp /= d; + return *this; +} + +inline bool operator==(const QVector2D &v1, const QVector2D &v2) +{ + return v1.xp == v2.xp && v1.yp == v2.yp; +} + +inline bool operator!=(const QVector2D &v1, const QVector2D &v2) +{ + return v1.xp != v2.xp || v1.yp != v2.yp; +} + +inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2) +{ + return QVector2D(v1.xp + v2.xp, v1.yp + v2.yp, 1); +} + +inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2) +{ + return QVector2D(v1.xp - v2.xp, v1.yp - v2.yp, 1); +} + +inline const QVector2D operator*(qreal factor, const QVector2D &vector) +{ + qrealinner f(factor); + return QVector2D(vector.xp * f, vector.yp * f, 1); +} + +inline const QVector2D operator*(const QVector2D &vector, qreal factor) +{ + qrealinner f(factor); + return QVector2D(vector.xp * f, vector.yp * f, 1); +} + +inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2) +{ + return QVector2D(v1.xp * v2.xp, v1.yp * v2.yp, 1); +} + +inline const QVector2D operator-(const QVector2D &vector) +{ + return QVector2D(-vector.xp, -vector.yp, 1); +} + +inline const QVector2D operator/(const QVector2D &vector, qreal divisor) +{ + qrealinner d(divisor); + return QVector2D(vector.xp / d, vector.yp / d, 1); +} + +inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2) +{ + return qFuzzyCompare(v1.xp, v2.xp) && qFuzzyCompare(v1.yp, v2.yp); +} + +inline QPoint QVector2D::toPoint() const +{ + return QPoint(qRound(xp), qRound(yp)); +} + +inline QPointF QVector2D::toPointF() const +{ + return QPointF(qt_math3d_convert<qreal, qrealinner>(xp), + qt_math3d_convert<qreal, qrealinner>(yp)); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QVector2D &vector); +#endif + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_VECTOR2D +Q_DECLARE_METATYPE(QVector2D) +#endif + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp new file mode 100644 index 0000000..32068ee --- /dev/null +++ b/src/gui/math3d/qvector3d.cpp @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvector3d.h" +#include "qvector2d.h" +#include "qvector4d.h" +#include "qmath3dutil_p.h" +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_VECTOR3D + +/*! + \class QVector3D + \brief The QVector3D class represents a vector or vertex in 3D space. + \since 4.6 + + Vectors are one of the main building blocks of 3D representation and + drawing. They consist of three coordinates, traditionally called + x, y, and z. + + The QVector3D class can also be used to represent vertices in 3D space. + We therefore do not need to provide a separate vertex class. + + The coordinates are stored internally using the most efficient + representation for the GL rendering engine, which will be either + floating-point or fixed-point. +*/ + +/*! + \fn QVector3D::QVector3D() + + Constructs a null vector, i.e. with coordinates (0, 0, 0). +*/ + +/*! + \fn QVector3D::QVector3D(qreal xpos, qreal ypos, qreal zpos) + + Constructs a vector with coordinates (\a xpos, \a ypos, \a zpos). +*/ + +/*! + \fn QVector3D::QVector3D(int xpos, int ypos, int zpos) + + Constructs a vector with coordinates (\a xpos, \a ypos, \a zpos). +*/ + +/*! + \fn QVector3D::QVector3D(const QPoint& point) + + Constructs a vector with x and y coordinates from a 2D \a point, and a + z coordinate of 0. +*/ + +/*! + \fn QVector3D::QVector3D(const QPointF& point) + + Constructs a vector with x and y coordinates from a 2D \a point, and a + z coordinate of 0. +*/ + +#ifndef QT_NO_VECTOR2D + +/*! + Constructs a 3D vector from the specified 2D \a vector. The z + coordinate is set to zero. + + \sa toVector2D() +*/ +QVector3D::QVector3D(const QVector2D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = 0.0f; +} + +/*! + Constructs a 3D vector from the specified 2D \a vector. The z + coordinate is set to \a zpos. + + \sa toVector2D() +*/ +QVector3D::QVector3D(const QVector2D& vector, qreal zpos) +{ + xp = vector.xp; + yp = vector.yp; + zp = zpos; +} + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + Constructs a 3D vector from the specified 4D \a vector. The w + coordinate is dropped. + + \sa toVector4D() +*/ +QVector3D::QVector3D(const QVector4D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = vector.zp; +} + +#endif + +/*! + \fn bool QVector3D::isNull() const + + Returns true if the x, y, and z coordinates are set to 0.0, + otherwise returns false. +*/ + +/*! + \fn qreal QVector3D::x() const + + Returns the x coordinate of this point. + + \sa setX(), y(), z() +*/ + +/*! + \fn qreal QVector3D::y() const + + Returns the y coordinate of this point. + + \sa setY(), x(), z() +*/ + +/*! + \fn qreal QVector3D::z() const + + Returns the z coordinate of this point. + + \sa setZ(), x(), y() +*/ + +/*! + \fn void QVector3D::setX(qreal x) + + Sets the x coordinate of this point to the given \a x coordinate. + + \sa x(), setY(), setZ() +*/ + +/*! + \fn void QVector3D::setY(qreal y) + + Sets the y coordinate of this point to the given \a y coordinate. + + \sa y(), setX(), setZ() +*/ + +/*! + \fn void QVector3D::setZ(qreal z) + + Sets the z coordinate of this point to the given \a z coordinate. + + \sa z(), setX(), setY() +*/ + +/*! + Returns the normalized unit vector form of this vector. If this vector + is not null, the returned vector is guaranteed to be 1.0 in length. + If this vector is null, then a null vector is returned. + + \sa length(), normalize() +*/ +QVector3D QVector3D::normalized() const +{ + qreal len = length(); + if (!qIsNull(len)) + return *this / len; + else + return QVector3D(); +} + +/*! + Normalizes the currect vector in place. Nothing happens if this + vector is a null vector. + + \sa length(), normalized() +*/ +void QVector3D::normalize() +{ + qreal len = length(); + if (qIsNull(len)) + return; + + xp /= len; + yp /= len; + zp /= len; +} + +/*! + \fn QVector3D &QVector3D::operator+=(const QVector3D &vector) + + Adds the given \a vector to this vector and returns a reference to + this vector. + + \sa operator-=() +*/ + +/*! + \fn QVector3D &QVector3D::operator-=(const QVector3D &vector) + + Subtracts the given \a vector from this vector and returns a reference to + this vector. + + \sa operator+=() +*/ + +/*! + \fn QVector3D &QVector3D::operator*=(qreal factor) + + Multiplies this vector's coordinates by the given \a factor, and + returns a reference to this vector. + + \sa operator/=() +*/ + +/*! + \fn QVector3D &QVector3D::operator*=(const QVector3D& vector) + \overload + + Multiplies the components of this vector by the corresponding + components in \a vector. + + Note: this is not the same as the crossProduct() of this + vector and \a vector. + + \sa crossProduct() +*/ + +/*! + \fn QVector3D &QVector3D::operator/=(qreal divisor) + + Divides this vector's coordinates by the given \a divisor, and + returns a reference to this vector. + + \sa operator*=() +*/ + +/*! + Returns the dot product of \a v1 and \a v2. +*/ +qreal QVector3D::dotProduct(const QVector3D& v1, const QVector3D& v2) +{ + return qvtdot64(qvtmul64(v1.xp, v2.xp) + qvtmul64(v1.yp, v2.yp) + qvtmul64(v1.zp, v2.zp)); +} + +/*! + Returns the cross-product of vectors \a v1 and \a v2, which corresponds + to the normal vector of a plane defined by \a v1 and \a v2. + + \sa normal() +*/ +QVector3D QVector3D::crossProduct(const QVector3D& v1, const QVector3D& v2) +{ + return QVector3D(v1.yp * v2.zp - v1.zp * v2.yp, + v1.zp * v2.xp - v1.xp * v2.zp, + v1.xp * v2.yp - v1.yp * v2.xp, 1); +} + +/*! + Returns the normal vector of a plane defined by vectors \a v1 and \a v2, + normalized to be a unit vector. + + Use crossProduct() to compute the cross-product of \a v1 and \a v2 if you + do not need the result to be normalized to a unit vector. + + \sa crossProduct(), distanceToPlane() +*/ +QVector3D QVector3D::normal(const QVector3D& v1, const QVector3D& v2) +{ + return crossProduct(v1, v2).normalized(); +} + +/*! + \overload + + Returns the normal vector of a plane defined by vectors + \a v2 - \a v1 and \a v3 - \a v1, normalized to be a unit vector. + + Use crossProduct() to compute the cross-product of \a v2 - \a v1 and + \a v3 - \a v1 if you do not need the result to be normalized to a + unit vector. + + \sa crossProduct(), distanceToPlane() +*/ +QVector3D QVector3D::normal + (const QVector3D& v1, const QVector3D& v2, const QVector3D& v3) +{ + return crossProduct((v2 - v1), (v3 - v1)).normalized(); +} + +/*! + Returns the distance from this vertex to a plane defined by + the vertex \a plane and a \a normal unit vector. The \a normal + parameter is assumed to have been normalized to a unit vector. + + The return value will be negative if the vertex is below the plane, + or zero if it is on the plane. + + \sa normal(), distanceToLine() +*/ +qreal QVector3D::distanceToPlane + (const QVector3D& plane, const QVector3D& normal) const +{ + return dotProduct(*this - plane, normal); +} + +/*! + \overload + + Returns the distance from this vertex a plane defined by + the vertices \a plane1, \a plane2 and \a plane3. + + The return value will be negative if the vertex is below the plane, + or zero if it is on the plane. + + The two vectors that define the plane are \a plane2 - \a plane1 + and \a plane3 - \a plane1. + + \sa normal(), distanceToLine() +*/ +qreal QVector3D::distanceToPlane + (const QVector3D& plane1, const QVector3D& plane2, const QVector3D& plane3) const +{ + QVector3D n = normal(plane2 - plane1, plane3 - plane1); + return dotProduct(*this - plane1, n); +} + +/*! + Returns the distance that this vertex is from a line defined + by \a point and the unit vector \a direction. + + If \a direction is a null vector, then it does not define a line. + In that case, the distance from \a point to this vertex is returned. + + \sa distanceToPlane() +*/ +qreal QVector3D::distanceToLine + (const QVector3D& point, const QVector3D& direction) const +{ + if (direction.isNull()) + return (*this - point).length(); + QVector3D p = point + dotProduct(*this - point, direction) * direction; + return (*this - p).length(); +} + +/*! + \fn bool operator==(const QVector3D &v1, const QVector3D &v2) + \relates QVector3D + + Returns true if \a v1 is equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool operator!=(const QVector3D &v1, const QVector3D &v2) + \relates QVector3D + + Returns true if \a v1 is not equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn const QVector3D operator+(const QVector3D &v1, const QVector3D &v2) + \relates QVector3D + + Returns a QVector3D object that is the sum of the given vectors, \a v1 + and \a v2; each component is added separately. + + \sa QVector3D::operator+=() +*/ + +/*! + \fn const QVector3D operator-(const QVector3D &v1, const QVector3D &v2) + \relates QVector3D + + Returns a QVector3D object that is formed by subtracting \a v2 from \a v1; + each component is subtracted separately. + + \sa QVector3D::operator-=() +*/ + +/*! + \fn const QVector3D operator*(qreal factor, const QVector3D &vector) + \relates QVector3D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector3D::operator*=() +*/ + +/*! + \fn const QVector3D operator*(const QVector3D &vector, qreal factor) + \relates QVector3D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector3D::operator*=() +*/ + +/*! + \fn const QVector3D operator*(const QVector3D &v1, const QVector3D& v2) + \relates QVector3D + + Multiplies the components of \a v1 by the corresponding components in \a v2. + + Note: this is not the same as the crossProduct() of \a v1 and \a v2. + + \sa QVector3D::crossProduct() +*/ + +/*! + \fn const QVector3D operator-(const QVector3D &vector) + \relates QVector3D + \overload + + Returns a QVector3D object that is formed by changing the sign of + all three components of the given \a vector. + + Equivalent to \c {QVector3D(0,0,0) - vector}. +*/ + +/*! + \fn const QVector3D operator/(const QVector3D &vector, qreal divisor) + \relates QVector3D + + Returns the QVector3D object formed by dividing all three components of + the given \a vector by the given \a divisor. + + \sa QVector3D::operator/=() +*/ + +/*! + \fn bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2) + \relates QVector3D + + Returns true if \a v1 and \a v2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +#ifndef QT_NO_VECTOR2D + +/*! + Returns the 2D vector form of this 3D vector, dropping the z coordinate. + + \sa toVector4D(), toPoint() +*/ +QVector2D QVector3D::toVector2D() const +{ + return QVector2D(xp, yp, 1); +} + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + Returns the 4D form of this 3D vector, with the w coordinate set to zero. + + \sa toVector2D(), toPoint() +*/ +QVector4D QVector3D::toVector4D() const +{ + return QVector4D(xp, yp, zp, 0.0f, 1); +} + +#endif + +/*! + \fn QPoint QVector3D::toPoint() const + + Returns the QPoint form of this 3D vector. + + \sa toPointF(), toVector2D() +*/ + +/*! + \fn QPointF QVector3D::toPointF() const + + Returns the QPointF form of this 3D vector. + + \sa toPoint(), toVector2D() +*/ + +/*! + Returns the length of the vector from the origin. + + \sa lengthSquared(), normalized() +*/ +qreal QVector3D::length() const +{ + return qvtsqrt64(qvtmul64(xp, xp) + qvtmul64(yp, yp) + qvtmul64(zp, zp)); +} + +/*! + Returns the squared length of the vector from the origin. + This is equivalent to the dot product of the vector with itself. + + \sa length(), dotProduct() +*/ +qreal QVector3D::lengthSquared() const +{ + return qvtdot64(qvtmul64(xp, xp) + qvtmul64(yp, yp) + qvtmul64(zp, zp)); +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QVector3D &vector) +{ + dbg.nospace() << "QVector3D(" + << vector.x() << ", " << vector.y() << ", " << vector.z() << ')'; + return dbg.space(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qvector3d.h b/src/gui/math3d/qvector3d.h new file mode 100644 index 0000000..8f8f3b4 --- /dev/null +++ b/src/gui/math3d/qvector3d.h @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR3D_H +#define QVECTOR3D_H + +#include <QtGui/qmath3dglobal.h> +#include <QtCore/qpoint.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QMatrix4x4; +class QVector2D; +class QVector4D; +class QQuaternion; + +#ifndef QT_NO_VECTOR3D + +class Q_GUI_EXPORT QVector3D +{ +public: + QVector3D(); + QVector3D(qreal xpos, qreal ypos, qreal zpos); + QVector3D(int xpos, int ypos, int zpos); + explicit QVector3D(const QPoint& point); + explicit QVector3D(const QPointF& point); +#ifndef QT_NO_VECTOR2D + QVector3D(const QVector2D& vector); + QVector3D(const QVector2D& vector, qreal zpos); +#endif +#ifndef QT_NO_VECTOR4D + explicit QVector3D(const QVector4D& vector); +#endif + + bool isNull() const; + + qreal x() const; + qreal y() const; + qreal z() const; + + void setX(qreal x); + void setY(qreal y); + void setZ(qreal z); + + qreal length() const; + qreal lengthSquared() const; + + QVector3D normalized() const; + void normalize(); + + QVector3D &operator+=(const QVector3D &vector); + QVector3D &operator-=(const QVector3D &vector); + QVector3D &operator*=(qreal factor); + QVector3D &operator*=(const QVector3D& vector); + QVector3D &operator/=(qreal divisor); + + static qreal dotProduct(const QVector3D& v1, const QVector3D& v2); + static QVector3D crossProduct(const QVector3D& v1, const QVector3D& v2); + static QVector3D normal(const QVector3D& v1, const QVector3D& v2); + static QVector3D normal + (const QVector3D& v1, const QVector3D& v2, const QVector3D& v3); + + qreal distanceToPlane(const QVector3D& plane, const QVector3D& normal) const; + qreal distanceToPlane(const QVector3D& plane1, const QVector3D& plane2, const QVector3D& plane3) const; + qreal distanceToLine(const QVector3D& point, const QVector3D& direction) const; + + friend inline bool operator==(const QVector3D &v1, const QVector3D &v2); + friend inline bool operator!=(const QVector3D &v1, const QVector3D &v2); + friend inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2); + friend inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2); + friend inline const QVector3D operator*(qreal factor, const QVector3D &vector); + friend inline const QVector3D operator*(const QVector3D &vector, qreal factor); + friend const QVector3D operator*(const QVector3D &v1, const QVector3D& v2); + friend inline const QVector3D operator-(const QVector3D &vector); + friend inline const QVector3D operator/(const QVector3D &vector, qreal divisor); + + friend inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2); + +#ifndef QT_NO_VECTOR2D + QVector2D toVector2D() const; +#endif +#ifndef QT_NO_VECTOR4D + QVector4D toVector4D() const; +#endif + + QPoint toPoint() const; + QPointF toPointF() const; + +private: + qrealinner xp, yp, zp; + + QVector3D(qrealinner xpos, qrealinner ypos, qrealinner zpos, int dummy); + + friend class QVector2D; + friend class QVector4D; + friend class QQuaternion; + friend class QMatrix4x4; + friend class QVertexArray; + friend class QGLPainter; +#ifndef QT_NO_MATRIX4X4 + friend QVector3D operator*(const QVector3D& vector, const QMatrix4x4& matrix); + friend QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector); +#endif +}; + +inline QVector3D::QVector3D() : xp(0.0f), yp(0.0f), zp(0.0f) {} + +inline QVector3D::QVector3D(qreal xpos, qreal ypos, qreal zpos) : xp(xpos), yp(ypos), zp(zpos) {} + +inline QVector3D::QVector3D(qrealinner xpos, qrealinner ypos, qrealinner zpos, int) : xp(xpos), yp(ypos), zp(zpos) {} + +inline QVector3D::QVector3D(int xpos, int ypos, int zpos) : xp(xpos), yp(ypos), zp(zpos) {} + +inline QVector3D::QVector3D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f) {} + +inline QVector3D::QVector3D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f) {} + +inline bool QVector3D::isNull() const +{ + return qIsNull(xp) && qIsNull(yp) && qIsNull(zp); +} + +inline qreal QVector3D::x() const { return qt_math3d_convert<qreal, qrealinner>(xp); } +inline qreal QVector3D::y() const { return qt_math3d_convert<qreal, qrealinner>(yp); } +inline qreal QVector3D::z() const { return qt_math3d_convert<qreal, qrealinner>(zp); } + +inline void QVector3D::setX(qreal x) { xp = x; } +inline void QVector3D::setY(qreal y) { yp = y; } +inline void QVector3D::setZ(qreal z) { zp = z; } + +inline QVector3D &QVector3D::operator+=(const QVector3D &vector) +{ + xp += vector.xp; + yp += vector.yp; + zp += vector.zp; + return *this; +} + +inline QVector3D &QVector3D::operator-=(const QVector3D &vector) +{ + xp -= vector.xp; + yp -= vector.yp; + zp -= vector.zp; + return *this; +} + +inline QVector3D &QVector3D::operator*=(qreal factor) +{ + qrealinner f(factor); + xp *= f; + yp *= f; + zp *= f; + return *this; +} + +inline QVector3D &QVector3D::operator*=(const QVector3D& vector) +{ + xp *= vector.xp; + yp *= vector.yp; + zp *= vector.zp; + return *this; +} + +inline QVector3D &QVector3D::operator/=(qreal divisor) +{ + qrealinner d(divisor); + xp /= d; + yp /= d; + zp /= d; + return *this; +} + +inline bool operator==(const QVector3D &v1, const QVector3D &v2) +{ + return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp; +} + +inline bool operator!=(const QVector3D &v1, const QVector3D &v2) +{ + return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp; +} + +inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2) +{ + return QVector3D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp, 1); +} + +inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2) +{ + return QVector3D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp, 1); +} + +inline const QVector3D operator*(qreal factor, const QVector3D &vector) +{ + qrealinner f(factor); + return QVector3D(vector.xp * f, vector.yp * f, vector.zp * f, 1); +} + +inline const QVector3D operator*(const QVector3D &vector, qreal factor) +{ + qrealinner f(factor); + return QVector3D(vector.xp * f, vector.yp * f, vector.zp * f, 1); +} + +inline const QVector3D operator*(const QVector3D &v1, const QVector3D& v2) +{ + return QVector3D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp, 1); +} + +inline const QVector3D operator-(const QVector3D &vector) +{ + return QVector3D(-vector.xp, -vector.yp, -vector.zp, 1); +} + +inline const QVector3D operator/(const QVector3D &vector, qreal divisor) +{ + qrealinner d(divisor); + return QVector3D(vector.xp / d, vector.yp / d, vector.zp / d, 1); +} + +inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2) +{ + return qFuzzyCompare(v1.xp, v2.xp) && + qFuzzyCompare(v1.yp, v2.yp) && + qFuzzyCompare(v1.zp, v2.zp); +} + +inline QPoint QVector3D::toPoint() const +{ + return QPoint(qRound(xp), qRound(yp)); +} + +inline QPointF QVector3D::toPointF() const +{ + return QPointF(qt_math3d_convert<qreal, qrealinner>(xp), + qt_math3d_convert<qreal, qrealinner>(yp)); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QVector3D &vector); +#endif + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_VECTOR3D +Q_DECLARE_METATYPE(QVector3D) +#endif + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qvector4d.cpp b/src/gui/math3d/qvector4d.cpp new file mode 100644 index 0000000..4287806 --- /dev/null +++ b/src/gui/math3d/qvector4d.cpp @@ -0,0 +1,521 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvector4d.h" +#include "qvector3d.h" +#include "qvector2d.h" +#include "qmath3dutil_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_VECTOR4D + +/*! + \class QVector4D + \brief The QVector4D class represents a vector or vertex in 4D space. + \since 4.6 + + The QVector4D class can also be used to represent vertices in 4D space. + We therefore do not need to provide a separate vertex class. + + The coordinates are stored internally using the most efficient + representation for the GL rendering engine, which will be either + floating-point or fixed-point. + + \sa QQuaternion, QVector2D, QVector3D +*/ + +/*! + \fn QVector4D::QVector4D() + + Constructs a null vector, i.e. with coordinates (0, 0, 0, 0). +*/ + +/*! + \fn QVector4D::QVector4D(qreal xpos, qreal ypos, qreal zpos, qreal wpos) + + Constructs a vector with coordinates (\a xpos, \a ypos, \a zpos, \a wpos). +*/ + +/*! + \fn QVector4D::QVector4D(int xpos, int ypos, int zpos, int wpos) + + Constructs a vector with coordinates (\a xpos, \a ypos, \a zpos, \a wpos). +*/ + +/*! + \fn QVector4D::QVector4D(const QPoint& point) + + Constructs a vector with x and y coordinates from a 2D \a point, and + z and w coordinates of 0. +*/ + +/*! + \fn QVector4D::QVector4D(const QPointF& point) + + Constructs a vector with x and y coordinates from a 2D \a point, and + z and w coordinates of 0. +*/ + +#ifndef QT_NO_VECTOR2D + +/*! + Constructs a 4D vector from the specified 2D \a vector. The z + and w coordinates are set to zero. + + \sa toVector2D() +*/ +QVector4D::QVector4D(const QVector2D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = 0.0f; + wp = 0.0f; +} + +/*! + Constructs a 4D vector from the specified 2D \a vector. The z + and w coordinates are set to \a zpos and \a wpos respectively. + + \sa toVector2D() +*/ +QVector4D::QVector4D(const QVector2D& vector, qreal zpos, qreal wpos) +{ + xp = vector.xp; + yp = vector.yp; + zp = zpos; + wp = wpos; +} + +#endif + +#ifndef QT_NO_VECTOR3D + +/*! + Constructs a 4D vector from the specified 3D \a vector. The w + coordinate is set to zero. + + \sa toVector3D() +*/ +QVector4D::QVector4D(const QVector3D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = vector.zp; + wp = 0.0f; +} + +/*! + Constructs a 4D vector from the specified 3D \a vector. The w + coordinate is set to \a wpos. + + \sa toVector3D() +*/ +QVector4D::QVector4D(const QVector3D& vector, qreal wpos) +{ + xp = vector.xp; + yp = vector.yp; + zp = vector.zp; + wp = wpos; +} + +#endif + +/*! + \fn bool QVector4D::isNull() const + + Returns true if the x, y, z, and w coordinates are set to 0.0, + otherwise returns false. +*/ + +/*! + \fn qreal QVector4D::x() const + + Returns the x coordinate of this point. + + \sa setX(), y(), z(), w() +*/ + +/*! + \fn qreal QVector4D::y() const + + Returns the y coordinate of this point. + + \sa setY(), x(), z(), w() +*/ + +/*! + \fn qreal QVector4D::z() const + + Returns the z coordinate of this point. + + \sa setZ(), x(), y(), w() +*/ + +/*! + \fn qreal QVector4D::w() const + + Returns the w coordinate of this point. + + \sa setW(), x(), y(), z() +*/ + +/*! + \fn void QVector4D::setX(qreal x) + + Sets the x coordinate of this point to the given \a x coordinate. + + \sa x(), setY(), setZ(), setW() +*/ + +/*! + \fn void QVector4D::setY(qreal y) + + Sets the y coordinate of this point to the given \a y coordinate. + + \sa y(), setX(), setZ(), setW() +*/ + +/*! + \fn void QVector4D::setZ(qreal z) + + Sets the z coordinate of this point to the given \a z coordinate. + + \sa z(), setX(), setY(), setW() +*/ + +/*! + \fn void QVector4D::setW(qreal w) + + Sets the w coordinate of this point to the given \a w coordinate. + + \sa w(), setX(), setY(), setZ() +*/ + +/*! + Returns the length of the vector from the origin. + + \sa lengthSquared(), normalized() +*/ +qreal QVector4D::length() const +{ + return qvtsqrt64(qvtmul64(xp, xp) + qvtmul64(yp, yp) + + qvtmul64(zp, zp) + qvtmul64(wp, wp)); +} + +/*! + Returns the squared length of the vector from the origin. + This is equivalent to the dot product of the vector with itself. + + \sa length(), dotProduct() +*/ +qreal QVector4D::lengthSquared() const +{ + return qvtdot64(qvtmul64(xp, xp) + qvtmul64(yp, yp) + + qvtmul64(zp, zp) + qvtmul64(wp, wp)); +} + +/*! + Returns the normalized unit vector form of this vector. If this vector + is not null, the returned vector is guaranteed to be 1.0 in length. + If this vector is null, then a null vector is returned. + + \sa length(), normalize() +*/ +QVector4D QVector4D::normalized() const +{ + qreal len = length(); + if (!qIsNull(len)) + return *this / len; + else + return QVector4D(); +} + +/*! + Normalizes the currect vector in place. Nothing happens if this + vector is a null vector. + + \sa length(), normalized() +*/ +void QVector4D::normalize() +{ + qreal len = length(); + if (qIsNull(len)) + return; + + xp /= len; + yp /= len; + zp /= len; + wp /= len; +} + + +/*! + \fn QVector4D &QVector4D::operator+=(const QVector4D &vector) + + Adds the given \a vector to this vector and returns a reference to + this vector. + + \sa operator-=() +*/ + +/*! + \fn QVector4D &QVector4D::operator-=(const QVector4D &vector) + + Subtracts the given \a vector from this vector and returns a reference to + this vector. + + \sa operator+=() +*/ + +/*! + \fn QVector4D &QVector4D::operator*=(qreal factor) + + Multiplies this vector's coordinates by the given \a factor, and + returns a reference to this vector. + + \sa operator/=() +*/ + +/*! + \fn QVector4D &QVector4D::operator*=(const QVector4D &vector) + + Multiplies the components of this vector by the corresponding + components in \a vector. +*/ + +/*! + \fn QVector4D &QVector4D::operator/=(qreal divisor) + + Divides this vector's coordinates by the given \a divisor, and + returns a reference to this vector. + + \sa operator*=() +*/ + +/*! + Returns the dot product of \a v1 and \a v2. +*/ +qreal QVector4D::dotProduct(const QVector4D& v1, const QVector4D& v2) +{ + return qvtdot64(qvtmul64(v1.xp, v2.xp) + qvtmul64(v1.yp, v2.yp) + + qvtmul64(v1.zp, v2.zp) + qvtmul64(v1.wp, v2.wp)); +} + +/*! + \fn bool operator==(const QVector4D &v1, const QVector4D &v2) + \relates QVector4D + + Returns true if \a v1 is equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool operator!=(const QVector4D &v1, const QVector4D &v2) + \relates QVector4D + + Returns true if \a v1 is not equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn const QVector4D operator+(const QVector4D &v1, const QVector4D &v2) + \relates QVector4D + + Returns a QVector4D object that is the sum of the given vectors, \a v1 + and \a v2; each component is added separately. + + \sa QVector4D::operator+=() +*/ + +/*! + \fn const QVector4D operator-(const QVector4D &v1, const QVector4D &v2) + \relates QVector4D + + Returns a QVector4D object that is formed by subtracting \a v2 from \a v1; + each component is subtracted separately. + + \sa QVector4D::operator-=() +*/ + +/*! + \fn const QVector4D operator*(qreal factor, const QVector4D &vector) + \relates QVector4D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector4D::operator*=() +*/ + +/*! + \fn const QVector4D operator*(const QVector4D &vector, qreal factor) + \relates QVector4D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector4D::operator*=() +*/ + +/*! + \fn const QVector4D operator*(const QVector4D &v1, const QVector4D& v2) + \relates QVector4D + + Returns the vector consisting of the multiplication of the + components from \a v1 and \a v2. + + \sa QVector4D::operator*=() +*/ + +/*! + \fn const QVector4D operator-(const QVector4D &vector) + \relates QVector4D + \overload + + Returns a QVector4D object that is formed by changing the sign of + all three components of the given \a vector. + + Equivalent to \c {QVector4D(0,0,0,0) - vector}. +*/ + +/*! + \fn const QVector4D operator/(const QVector4D &vector, qreal divisor) + \relates QVector4D + + Returns the QVector4D object formed by dividing all four components of + the given \a vector by the given \a divisor. + + \sa QVector4D::operator/=() +*/ + +/*! + \fn bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2) + \relates QVector4D + + Returns true if \a v1 and \a v2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +#ifndef QT_NO_VECTOR2D + +/*! + Returns the 2D vector form of this 4D vector, dropping the z and w coordinates. + + \sa toVector2DAffine(), toVector3D(), toPoint() +*/ +QVector2D QVector4D::toVector2D() const +{ + return QVector2D(xp, yp, 1); +} + +/*! + Returns the 2D vector form of this 4D vector, dividing the x and y + coordinates by the w coordinate and dropping the z coordinate. + Returns a null vector if w is zero. + + \sa toVector2D(), toVector3DAffine(), toPoint() +*/ +QVector2D QVector4D::toVector2DAffine() const +{ + if (qIsNull(wp)) + return QVector2D(); + return QVector2D(xp / wp, yp / wp, 1); +} + +#endif + +#ifndef QT_NO_VECTOR3D + +/*! + Returns the 3D vector form of this 4D vector, dropping the w coordinate. + + \sa toVector3DAffine(), toVector2D(), toPoint() +*/ +QVector3D QVector4D::toVector3D() const +{ + return QVector3D(xp, yp, zp, 1); +} + +/*! + Returns the 3D vector form of this 4D vector, dividing the x, y, and + z coordinates by the w coordinate. Returns a null vector if w is zero. + + \sa toVector3D(), toVector2DAffine(), toPoint() +*/ +QVector3D QVector4D::toVector3DAffine() const +{ + if (qIsNull(wp)) + return QVector3D(); + return QVector3D(xp / wp, yp / wp, zp / wp, 1); +} + +#endif + +/*! + \fn QPoint QVector4D::toPoint() const + + Returns the QPoint form of this 4D vector. + + \sa toPointF(), toVector2D() +*/ + +/*! + \fn QPointF QVector4D::toPointF() const + + Returns the QPointF form of this 4D vector. + + \sa toPoint(), toVector2D() +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QVector4D &vector) +{ + dbg.nospace() << "QVector4D(" + << vector.x() << ", " << vector.y() << ", " + << vector.z() << ", " << vector.w() << ')'; + return dbg.space(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qvector4d.h b/src/gui/math3d/qvector4d.h new file mode 100644 index 0000000..4ac6ae8 --- /dev/null +++ b/src/gui/math3d/qvector4d.h @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR4D_H +#define QVECTOR4D_H + +#include <QtGui/qmath3dglobal.h> +#include <QtCore/qpoint.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QMatrix4x4; +class QVector2D; +class QVector3D; +class QQuaternion; + +#ifndef QT_NO_VECTOR4D + +class Q_GUI_EXPORT QVector4D +{ +public: + QVector4D(); + QVector4D(qreal xpos, qreal ypos, qreal zpos, qreal wpos); + QVector4D(int xpos, int ypos, int zpos, int wpos); + explicit QVector4D(const QPoint& point); + explicit QVector4D(const QPointF& point); +#ifndef QT_NO_VECTOR2D + QVector4D(const QVector2D& vector); + QVector4D(const QVector2D& vector, qreal zpos, qreal wpos); +#endif +#ifndef QT_NO_VECTOR3D + QVector4D(const QVector3D& vector); + QVector4D(const QVector3D& vector, qreal wpos); +#endif + + bool isNull() const; + + qreal x() const; + qreal y() const; + qreal z() const; + qreal w() const; + + void setX(qreal x); + void setY(qreal y); + void setZ(qreal z); + void setW(qreal w); + + qreal length() const; + qreal lengthSquared() const; + + QVector4D normalized() const; + void normalize(); + + QVector4D &operator+=(const QVector4D &vector); + QVector4D &operator-=(const QVector4D &vector); + QVector4D &operator*=(qreal factor); + QVector4D &operator*=(const QVector4D &vector); + QVector4D &operator/=(qreal divisor); + + static qreal dotProduct(const QVector4D& v1, const QVector4D& v2); + + friend inline bool operator==(const QVector4D &v1, const QVector4D &v2); + friend inline bool operator!=(const QVector4D &v1, const QVector4D &v2); + friend inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2); + friend inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2); + friend inline const QVector4D operator*(qreal factor, const QVector4D &vector); + friend inline const QVector4D operator*(const QVector4D &vector, qreal factor); + friend inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2); + friend inline const QVector4D operator-(const QVector4D &vector); + friend inline const QVector4D operator/(const QVector4D &vector, qreal divisor); + + friend inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2); + +#ifndef QT_NO_VECTOR2D + QVector2D toVector2D() const; + QVector2D toVector2DAffine() const; +#endif +#ifndef QT_NO_VECTOR3D + QVector3D toVector3D() const; + QVector3D toVector3DAffine() const; +#endif + + QPoint toPoint() const; + QPointF toPointF() const; + +private: + qrealinner xp, yp, zp, wp; + + QVector4D(qrealinner xpos, qrealinner ypos, qrealinner zpos, qrealinner wpos, int dummy); + + friend class QVector2D; + friend class QVector3D; + friend class QQuaternion; + friend class QMatrix4x4; + friend class QVertexArray; +#ifndef QT_NO_MATRIX4X4 + friend QVector4D operator*(const QVector4D& vector, const QMatrix4x4& matrix); + friend QVector4D operator*(const QMatrix4x4& matrix, const QVector4D& vector); +#endif +}; + +inline QVector4D::QVector4D() : xp(0.0f), yp(0.0f), zp(0.0f), wp(0.0f) {} + +inline QVector4D::QVector4D(qreal xpos, qreal ypos, qreal zpos, qreal wpos) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {} + +inline QVector4D::QVector4D(qrealinner xpos, qrealinner ypos, qrealinner zpos, qrealinner wpos, int) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {} + +inline QVector4D::QVector4D(int xpos, int ypos, int zpos, int wpos) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {} + +inline QVector4D::QVector4D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {} + +inline QVector4D::QVector4D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {} + +inline bool QVector4D::isNull() const +{ + return qIsNull(xp) && qIsNull(yp) && qIsNull(zp) && qIsNull(wp); +} + +inline qreal QVector4D::x() const { return qt_math3d_convert<qreal, qrealinner>(xp); } +inline qreal QVector4D::y() const { return qt_math3d_convert<qreal, qrealinner>(yp); } +inline qreal QVector4D::z() const { return qt_math3d_convert<qreal, qrealinner>(zp); } +inline qreal QVector4D::w() const { return qt_math3d_convert<qreal, qrealinner>(wp); } + +inline void QVector4D::setX(qreal x) { xp = x; } +inline void QVector4D::setY(qreal y) { yp = y; } +inline void QVector4D::setZ(qreal z) { zp = z; } +inline void QVector4D::setW(qreal w) { wp = w; } + +inline QVector4D &QVector4D::operator+=(const QVector4D &vector) +{ + xp += vector.xp; + yp += vector.yp; + zp += vector.zp; + wp += vector.wp; + return *this; +} + +inline QVector4D &QVector4D::operator-=(const QVector4D &vector) +{ + xp -= vector.xp; + yp -= vector.yp; + zp -= vector.zp; + wp -= vector.wp; + return *this; +} + +inline QVector4D &QVector4D::operator*=(qreal factor) +{ + qrealinner f(factor); + xp *= f; + yp *= f; + zp *= f; + wp *= f; + return *this; +} + +inline QVector4D &QVector4D::operator*=(const QVector4D &vector) +{ + xp *= vector.xp; + yp *= vector.yp; + zp *= vector.zp; + wp *= vector.wp; + return *this; +} + +inline QVector4D &QVector4D::operator/=(qreal divisor) +{ + qrealinner d(divisor); + xp /= d; + yp /= d; + zp /= d; + wp /= d; + return *this; +} + +inline bool operator==(const QVector4D &v1, const QVector4D &v2) +{ + return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp && v1.wp == v2.wp; +} + +inline bool operator!=(const QVector4D &v1, const QVector4D &v2) +{ + return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp || v1.wp != v2.wp; +} + +inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2) +{ + return QVector4D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp, v1.wp + v2.wp, 1); +} + +inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2) +{ + return QVector4D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp, v1.wp - v2.wp, 1); +} + +inline const QVector4D operator*(qreal factor, const QVector4D &vector) +{ + qrealinner f(factor); + return QVector4D(vector.xp * f, vector.yp * f, vector.zp * f, vector.wp * f, 1); +} + +inline const QVector4D operator*(const QVector4D &vector, qreal factor) +{ + qrealinner f(factor); + return QVector4D(vector.xp * f, vector.yp * f, vector.zp * f, vector.wp * f, 1); +} + +inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2) +{ + return QVector4D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp, v1.wp * v2.wp, 1); +} + +inline const QVector4D operator-(const QVector4D &vector) +{ + return QVector4D(-vector.xp, -vector.yp, -vector.zp, -vector.wp, 1); +} + +inline const QVector4D operator/(const QVector4D &vector, qreal divisor) +{ + qrealinner d(divisor); + return QVector4D(vector.xp / d, vector.yp / d, vector.zp / d, vector.wp / d, 1); +} + +inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2) +{ + return qFuzzyCompare(v1.xp, v2.xp) && + qFuzzyCompare(v1.yp, v2.yp) && + qFuzzyCompare(v1.zp, v2.zp) && + qFuzzyCompare(v1.wp, v2.wp); +} + +inline QPoint QVector4D::toPoint() const +{ + return QPoint(qRound(xp), qRound(yp)); +} + +inline QPointF QVector4D::toPointF() const +{ + return QPointF(qt_math3d_convert<qreal, qrealinner>(xp), + qt_math3d_convert<qreal, qrealinner>(yp)); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QVector4D &vector); +#endif + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_VECTOR4D +Q_DECLARE_METATYPE(QVector4D) +#endif + +QT_END_HEADER + +#endif diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index efdc778..789d96a 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -157,46 +157,9 @@ static uint * QT_FASTCALL destFetchRGB16(uint *buffer, QRasterBuffer *rasterBuff return buffer; } -#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL) -template <typename EnumType, int value> -class QEnumToType -{ -public: - inline EnumType operator()() const - { - return EnumType(value); - } -}; -template <QImage::Format format> -class QImageFormatToType -{ -public: - inline QImage::Format operator()() const - { - return format; - } -}; -// Would have used QEnumToType instead of creating a specialized version for QImageFormatToType, -// but that causes internal compiler error on VC6 -#define Q_TEMPLATE_IMAGEFORMAT_FIX(format) , const QImageFormatToType<format> &imageFormatType -#define Q_TEMPLATE_IMAGEFORMAT_CALL(format) , QImageFormatToType<format>() -#define Q_TEMPLATE_ENUM_FIX(Type, Value) , const QEnumToType<Type, Value> &enumTemplateType -#define Q_TEMPLATE_ENUM_CALL(Type, Value) , QEnumToType<Type, Value>() -#define Q_TEMPLATE_FIX(Type) , const QTypeInfo<Type> &templateType -#define Q_TEMPLATE_CALL(Type) , QTypeInfo<Type>() -#else -#define Q_TEMPLATE_IMAGEFORMAT_FIX(format) -#define Q_TEMPLATE_IMAGEFORMAT_CALL(format) -#define Q_TEMPLATE_ENUM_FIX(Type, Value) -#define Q_TEMPLATE_ENUM_CALL(Type, Value) -#define Q_TEMPLATE_FIX(Type) -#define Q_TEMPLATE_CALL(Type) -#endif - template <class DST> Q_STATIC_TEMPLATE_FUNCTION uint * QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer, - int x, int y, int length - Q_TEMPLATE_FIX(DST)) + int x, int y, int length) { const DST *src = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x; quint32 *dest = reinterpret_cast<quint32*>(buffer); @@ -205,28 +168,7 @@ Q_STATIC_TEMPLATE_FUNCTION uint * QT_FASTCALL destFetch(uint *buffer, QRasterBuf return buffer; } -#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL) -#define DEST_FETCH_DECL(DST) \ - static uint * QT_FASTCALL destFetch_##DST(uint *buffer, \ - QRasterBuffer *rasterBuffer, \ - int x, int y, int length) \ - { \ - return destFetch<DST>(buffer, rasterBuffer, x, y, length Q_TEMPLATE_CALL(DST)); \ - } - -DEST_FETCH_DECL(qargb8565) -DEST_FETCH_DECL(qrgb666) -DEST_FETCH_DECL(qargb6666) -DEST_FETCH_DECL(qrgb555) -DEST_FETCH_DECL(qrgb888) -DEST_FETCH_DECL(qargb8555) -DEST_FETCH_DECL(qrgb444) -DEST_FETCH_DECL(qargb4444) -#undef DEST_FETCH_DECL -# define SPANFUNC_POINTER_DESTFETCH(Arg) destFetch_##Arg -#else // !VC6 && !VC2002 # define SPANFUNC_POINTER_DESTFETCH(Arg) destFetch<Arg> -#endif static const DestFetchProc destFetchProc[QImage::NImageFormats] = { @@ -366,8 +308,7 @@ static void QT_FASTCALL destStoreRGB16(QRasterBuffer *rasterBuffer, int x, int y template <class DST> Q_STATIC_TEMPLATE_FUNCTION void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, - const uint *buffer, int length - Q_TEMPLATE_FIX(DST)) + const uint *buffer, int length) { DST *dest = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x; const quint32 *src = reinterpret_cast<const quint32*>(buffer); @@ -375,28 +316,7 @@ Q_STATIC_TEMPLATE_FUNCTION void QT_FASTCALL destStore(QRasterBuffer *rasterBuffe *dest++ = DST(*src++); } -#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL) -# define DEST_STORE_DECL(DST) \ - static void QT_FASTCALL destStore_##DST(QRasterBuffer *rasterBuffer, \ - int x, int y, \ - const uint *buffer, int length) \ - { \ - destStore<DST>(rasterBuffer, x, y, buffer, length Q_TEMPLATE_CALL(DST)); \ - } - -DEST_STORE_DECL(qargb8565) -DEST_STORE_DECL(qrgb555) -DEST_STORE_DECL(qrgb666) -DEST_STORE_DECL(qargb6666) -DEST_STORE_DECL(qargb8555) -DEST_STORE_DECL(qrgb888) -DEST_STORE_DECL(qrgb444) -DEST_STORE_DECL(qargb4444) -# undef DEST_FETCH_DECL -# define SPANFUNC_POINTER_DESTSTORE(DEST) destStore_##DEST -#else // !VC6 && !VC2002 # define SPANFUNC_POINTER_DESTSTORE(DEST) destStore<DEST> -#endif static const DestStoreProc destStoreProc[QImage::NImageFormats] = { @@ -425,10 +345,8 @@ static const DestStoreProc destStoreProc[QImage::NImageFormats] = We need 5 fetch methods per surface type: untransformed - transformed - transformed tiled - transformed bilinear - transformed bilinear tiled + transformed (tiled and not tiled) + transformed bilinear (tiled and not tiled) We don't need bounds checks for untransformed, but we need them for the other ones. @@ -436,14 +354,12 @@ static const DestStoreProc destStoreProc[QImage::NImageFormats] = */ template <QImage::Format format> -Q_STATIC_TEMPLATE_FUNCTION uint QT_FASTCALL qt_fetchPixel(const uchar *scanLine, int x, const QVector<QRgb> *rgb - Q_TEMPLATE_IMAGEFORMAT_FIX(format)); +Q_STATIC_TEMPLATE_FUNCTION uint QT_FASTCALL qt_fetchPixel(const uchar *scanLine, int x, const QVector<QRgb> *rgb); template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_Mono>(const uchar *scanLine, - int x, const QVector<QRgb> *rgb - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_Mono)) + int x, const QVector<QRgb> *rgb) { bool pixel = scanLine[x>>3] & (0x80 >> (x & 7)); if (rgb) return PREMUL(rgb->at(pixel ? 1 : 0)); @@ -453,8 +369,7 @@ uint QT_FASTCALL qt_fetchPixel<QImage::Format_Mono>(const uchar *scanLine, template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_MonoLSB>(const uchar *scanLine, - int x, const QVector<QRgb> *rgb - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_MonoLSB)) + int x, const QVector<QRgb> *rgb) { bool pixel = scanLine[x>>3] & (0x1 << (x & 7)); if (rgb) return PREMUL(rgb->at(pixel ? 1 : 0)); @@ -464,8 +379,7 @@ uint QT_FASTCALL qt_fetchPixel<QImage::Format_MonoLSB>(const uchar *scanLine, template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_Indexed8>(const uchar *scanLine, - int x, const QVector<QRgb> *rgb - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_Indexed8)) + int x, const QVector<QRgb> *rgb) { return PREMUL(rgb->at(scanLine[x])); } @@ -473,8 +387,7 @@ uint QT_FASTCALL qt_fetchPixel<QImage::Format_Indexed8>(const uchar *scanLine, template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32>(const uchar *scanLine, - int x, const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB32)) + int x, const QVector<QRgb> *) { return PREMUL(((const uint *)scanLine)[x]); } @@ -482,8 +395,7 @@ uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32>(const uchar *scanLine, template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32_Premultiplied>(const uchar *scanLine, - int x, const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB32_Premultiplied)) + int x, const QVector<QRgb> *) { return ((const uint *)scanLine)[x]; } @@ -491,8 +403,7 @@ uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32_Premultiplied>(const uchar template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB16>(const uchar *scanLine, - int x, const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB16)) + int x, const QVector<QRgb> *) { return qConvertRgb16To32(((const ushort *)scanLine)[x]); } @@ -501,8 +412,7 @@ template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB8565_Premultiplied>(const uchar *scanLine, int x, - const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB8565_Premultiplied)) + const QVector<QRgb> *) { const qargb8565 color = reinterpret_cast<const qargb8565*>(scanLine)[x]; return qt_colorConvert<quint32, qargb8565>(color, 0); @@ -512,8 +422,7 @@ template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB666>(const uchar *scanLine, int x, - const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB666)) + const QVector<QRgb> *) { const qrgb666 color = reinterpret_cast<const qrgb666*>(scanLine)[x]; return qt_colorConvert<quint32, qrgb666>(color, 0); @@ -523,8 +432,7 @@ template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB6666_Premultiplied>(const uchar *scanLine, int x, - const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB6666_Premultiplied)) + const QVector<QRgb> *) { const qargb6666 color = reinterpret_cast<const qargb6666*>(scanLine)[x]; return qt_colorConvert<quint32, qargb6666>(color, 0); @@ -534,8 +442,7 @@ template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB555>(const uchar *scanLine, int x, - const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB555)) + const QVector<QRgb> *) { const qrgb555 color = reinterpret_cast<const qrgb555*>(scanLine)[x]; return qt_colorConvert<quint32, qrgb555>(color, 0); @@ -545,8 +452,7 @@ template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB8555_Premultiplied>(const uchar *scanLine, int x, - const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB8555_Premultiplied)) + const QVector<QRgb> *) { const qargb8555 color = reinterpret_cast<const qargb8555*>(scanLine)[x]; return qt_colorConvert<quint32, qargb8555>(color, 0); @@ -556,8 +462,7 @@ template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB888>(const uchar *scanLine, int x, - const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB888)) + const QVector<QRgb> *) { const qrgb888 color = reinterpret_cast<const qrgb888*>(scanLine)[x]; return qt_colorConvert<quint32, qrgb888>(color, 0); @@ -567,8 +472,7 @@ template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB444>(const uchar *scanLine, int x, - const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_RGB444)) + const QVector<QRgb> *) { const qrgb444 color = reinterpret_cast<const qrgb444*>(scanLine)[x]; return qt_colorConvert<quint32, qrgb444>(color, 0); @@ -578,47 +482,24 @@ template<> Q_STATIC_TEMPLATE_SPECIALIZATION uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB4444_Premultiplied>(const uchar *scanLine, int x, - const QVector<QRgb> * - Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB4444_Premultiplied)) + const QVector<QRgb> *) { const qargb4444 color = reinterpret_cast<const qargb4444*>(scanLine)[x]; return qt_colorConvert<quint32, qargb4444>(color, 0); } -typedef uint (QT_FASTCALL *FetchPixelProc)(const uchar *scanLine, int x, const QVector<QRgb> *); - -#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL) - -// explicit template instantiations needed to compile with VC6 and VC2002 - -#define SPANFUNC_INSTANTIATION_FETCHPIXEL(Arg) \ - static inline uint fetchPixel_##Arg(const uchar * scanLine, int x, const QVector<QRgb> * rgb) \ -{ \ - return qt_fetchPixel<QImage::Arg>(scanLine, x, rgb Q_TEMPLATE_IMAGEFORMAT_CALL(QImage::Arg)); \ +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_Invalid>(const uchar *, + int , + const QVector<QRgb> *) +{ + return 0; } -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_Mono); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_MonoLSB); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_Indexed8); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB32_Premultiplied); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB32); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB16); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB8565_Premultiplied); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB666); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB6666_Premultiplied); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB555); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB8555_Premultiplied); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB888); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_RGB444); -SPANFUNC_INSTANTIATION_FETCHPIXEL(Format_ARGB4444_Premultiplied); - -#undef SPANFUNC_INSTANTIATION_FETCHPIXEL - -#define SPANFUNC_POINTER_FETCHPIXEL(Arg) fetchPixel_##Arg +typedef uint (QT_FASTCALL *FetchPixelProc)(const uchar *scanLine, int x, const QVector<QRgb> *); -#else // !VC6 && !VC2002 -# define SPANFUNC_POINTER_FETCHPIXEL(Arg) qt_fetchPixel<QImage::Arg> -#endif +#define SPANFUNC_POINTER_FETCHPIXEL(Arg) qt_fetchPixel<QImage::Arg> static const FetchPixelProc fetchPixelProc[QImage::NImageFormats] = @@ -653,11 +534,11 @@ enum TextureBlendType { template <QImage::Format format> Q_STATIC_TEMPLATE_FUNCTION const uint * QT_FASTCALL qt_fetchUntransformed(uint *buffer, const Operator *, const QSpanData *data, - int y, int x, int length Q_TEMPLATE_IMAGEFORMAT_FIX(format)) + int y, int x, int length) { const uchar *scanLine = data->texture.scanLine(y); for (int i = 0; i < length; ++i) - buffer[i] = qt_fetchPixel<format>(scanLine, x + i, data->texture.colorTable Q_TEMPLATE_IMAGEFORMAT_CALL(format)); + buffer[i] = qt_fetchPixel<format>(scanLine, x + i, data->texture.colorTable); return buffer; } @@ -665,13 +546,15 @@ template <> Q_STATIC_TEMPLATE_SPECIALIZATION const uint * QT_FASTCALL qt_fetchUntransformed<QImage::Format_ARGB32_Premultiplied>(uint *, const Operator *, const QSpanData *data, - int y, int x, int Q_TEMPLATE_IMAGEFORMAT_FIX(QImage::Format_ARGB32_Premultiplied)) + int y, int x, int) { const uchar *scanLine = data->texture.scanLine(y); return ((const uint *)scanLine) + x; } -static const uint * QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data, +template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */ +Q_STATIC_TEMPLATE_FUNCTION +const uint * QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length) { FetchPixelProc fetch = fetchPixelProc[data->texture.format]; @@ -698,84 +581,23 @@ static const uint * QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, int px = fx >> 16; int py = fy >> 16; - bool out = (px < 0) || (px >= image_width) - || (py < 0) || (py >= image_height); + if (blendType == BlendTransformedTiled) { + px %= image_width; + py %= image_height; + if (px < 0) px += image_width; + if (py < 0) py += image_height; - const uchar *scanLine = data->texture.scanLine(py); - *b = out ? uint(0) : fetch(scanLine, px, data->texture.colorTable); - fx += fdx; - fy += fdy; - ++b; - } - } else { - const qreal fdx = data->m11; - const qreal fdy = data->m12; - const qreal fdw = data->m13; - - qreal fx = data->m21 * cy + data->m11 * cx + data->dx; - qreal fy = data->m22 * cy + data->m12 * cx + data->dy; - qreal fw = data->m23 * cy + data->m13 * cx + data->m33; - - while (b < end) { - const qreal iw = fw == 0 ? 1 : 1 / fw; - const qreal tx = fx * iw; - const qreal ty = fy * iw; - const int px = int(tx) - (tx < 0); - const int py = int(ty) - (ty < 0); - - bool out = (px < 0) || (px >= image_width) - || (py < 0) || (py >= image_height); - - const uchar *scanLine = data->texture.scanLine(py); - *b = out ? uint(0) : fetch(scanLine, px, data->texture.colorTable); - fx += fdx; - fy += fdy; - fw += fdw; - //force increment to avoid /0 - if (!fw) { - fw += fdw; + const uchar *scanLine = data->texture.scanLine(py); + *b = fetch(scanLine, px, data->texture.colorTable); + } else { + if ((px < 0) || (px >= image_width) + || (py < 0) || (py >= image_height)) { + *b = uint(0); + } else { + const uchar *scanLine = data->texture.scanLine(py); + *b = fetch(scanLine, px, data->texture.colorTable); + } } - ++b; - } - } - - return buffer; -} - -static const uint * QT_FASTCALL fetchTransformedTiled(uint *buffer, const Operator *, const QSpanData *data, - int y, int x, int length) -{ - FetchPixelProc fetch = fetchPixelProc[data->texture.format]; - - int image_width = data->texture.width; - int image_height = data->texture.height; - - const qreal cx = x + 0.5; - const qreal cy = y + 0.5; - - const uint *end = buffer + length; - uint *b = buffer; - if (data->fast_matrix) { - // The increment pr x in the scanline - int fdx = (int)(data->m11 * fixed_scale); - int fdy = (int)(data->m12 * fixed_scale); - - int fx = int((data->m21 * cy - + data->m11 * cx + data->dx) * fixed_scale); - int fy = int((data->m22 * cy - + data->m12 * cx + data->dy) * fixed_scale); - - while (b < end) { - int px = fx >> 16; - int py = fy >> 16; - - px %= image_width; - py %= image_height; - if (px < 0) px += image_width; - if (py < 0) py += image_height; - - const uchar *scanLine = data->texture.scanLine(py); - *b = fetch(scanLine, px, data->texture.colorTable); fx += fdx; fy += fdy; ++b; @@ -796,13 +618,23 @@ static const uint * QT_FASTCALL fetchTransformedTiled(uint *buffer, const Operat int px = int(tx) - (tx < 0); int py = int(ty) - (ty < 0); - px %= image_width; - py %= image_height; - if (px < 0) px += image_width; - if (py < 0) py += image_height; + if (blendType == BlendTransformedTiled) { + px %= image_width; + py %= image_height; + if (px < 0) px += image_width; + if (py < 0) py += image_height; - const uchar *scanLine = data->texture.scanLine(py); - *b = fetch(scanLine, px, data->texture.colorTable); + const uchar *scanLine = data->texture.scanLine(py); + *b = fetch(scanLine, px, data->texture.colorTable); + } else { + if ((px < 0) || (px >= image_width) + || (py < 0) || (py >= image_height)) { + *b = uint(0); + } else { + const uchar *scanLine = data->texture.scanLine(py); + *b = fetch(scanLine, px, data->texture.colorTable); + } + } fx += fdx; fy += fdy; fw += fdw; @@ -817,10 +649,12 @@ static const uint * QT_FASTCALL fetchTransformedTiled(uint *buffer, const Operat return buffer; } -static const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *, const QSpanData *data, - int y, int x, int length) +template<TextureBlendType blendType, QImage::Format format> /* blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled */ +Q_STATIC_TEMPLATE_FUNCTION +const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *, const QSpanData *data, + int y, int x, int length) { - FetchPixelProc fetch = fetchPixelProc[data->texture.format]; + FetchPixelProc fetch = (format != QImage::Format_Invalid) ? FetchPixelProc(qt_fetchPixel<format>) : fetchPixelProc[data->texture.format]; int image_width = data->texture.width; int image_height = data->texture.height; @@ -853,130 +687,27 @@ static const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Ope int idistx = 256 - distx; int idisty = 256 - disty; - x1 = qBound(0, x1, image_width - 1); - x2 = qBound(0, x2, image_width - 1); - y1 = qBound(0, y1, image_height - 1); - y2 = qBound(0, y2, image_height - 1); - - const uchar *s1 = data->texture.scanLine(y1); - const uchar *s2 = data->texture.scanLine(y2); - - uint tl = fetch(s1, x1, data->texture.colorTable); - uint tr = fetch(s1, x2, data->texture.colorTable); - uint bl = fetch(s2, x1, data->texture.colorTable); - uint br = fetch(s2, x2, data->texture.colorTable); - - uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); - uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); - *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); - - fx += fdx; - fy += fdy; - ++b; - } - } else { - const qreal fdx = data->m11; - const qreal fdy = data->m12; - const qreal fdw = data->m13; - - qreal fx = data->m21 * cy + data->m11 * cx + data->dx; - qreal fy = data->m22 * cy + data->m12 * cx + data->dy; - qreal fw = data->m23 * cy + data->m13 * cx + data->m33; - - while (b < end) { - const qreal iw = fw == 0 ? 1 : 1 / fw; - const qreal px = fx * iw - 0.5; - const qreal py = fy * iw - 0.5; - - int x1 = int(px) - (px < 0); - int x2 = x1 + 1; - int y1 = int(py) - (py < 0); - int y2 = y1 + 1; - - int distx = int((px - x1) * 256); - int disty = int((py - y1) * 256); - int idistx = 256 - distx; - int idisty = 256 - disty; - - x1 = qBound(0, x1, image_width - 1); - x2 = qBound(0, x2, image_width - 1); - y1 = qBound(0, y1, image_height - 1); - y2 = qBound(0, y2, image_height - 1); - - const uchar *s1 = data->texture.scanLine(y1); - const uchar *s2 = data->texture.scanLine(y2); - - uint tl = fetch(s1, x1, data->texture.colorTable); - uint tr = fetch(s1, x2, data->texture.colorTable); - uint bl = fetch(s2, x1, data->texture.colorTable); - uint br = fetch(s2, x2, data->texture.colorTable); - - uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); - uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); - *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); - - fx += fdx; - fy += fdy; - fw += fdw; - //force increment to avoid /0 - if (!fw) { - fw += fdw; + if (blendType == BlendTransformedBilinearTiled) { + x1 %= image_width; + x2 %= image_width; + y1 %= image_height; + y2 %= image_height; + + if (x1 < 0) x1 += image_width; + if (x2 < 0) x2 += image_width; + if (y1 < 0) y1 += image_height; + if (y2 < 0) y2 += image_height; + + Q_ASSERT(x1 >= 0 && x1 < image_width); + Q_ASSERT(x2 >= 0 && x2 < image_width); + Q_ASSERT(y1 >= 0 && y1 < image_height); + Q_ASSERT(y2 >= 0 && y2 < image_height); + } else { + x1 = qBound(0, x1, image_width - 1); + x2 = qBound(0, x2, image_width - 1); + y1 = qBound(0, y1, image_height - 1); + y2 = qBound(0, y2, image_height - 1); } - ++b; - } - } - - return buffer; -} - -static const uint * QT_FASTCALL fetchTransformedBilinearTiled(uint *buffer, const Operator *, const QSpanData *data, - int y, int x, int length) -{ - FetchPixelProc fetch = fetchPixelProc[data->texture.format]; - - int image_width = data->texture.width; - int image_height = data->texture.height; - - const qreal cx = x + 0.5; - const qreal cy = y + 0.5; - - const uint *end = buffer + length; - uint *b = buffer; - if (data->fast_matrix) { - // The increment pr x in the scanline - int fdx = (int)(data->m11 * fixed_scale); - int fdy = (int)(data->m12 * fixed_scale); - - int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale); - int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale); - - fx -= half_point; - fy -= half_point; - while (b < end) { - int x1 = (fx >> 16); - int x2 = x1 + 1; - int y1 = (fy >> 16); - int y2 = y1 + 1; - - int distx = ((fx - (x1 << 16)) >> 8); - int disty = ((fy - (y1 << 16)) >> 8); - int idistx = 256 - distx; - int idisty = 256 - disty; - - x1 %= image_width; - x2 %= image_width; - y1 %= image_height; - y2 %= image_height; - - if (x1 < 0) x1 += image_width; - if (x2 < 0) x2 += image_width; - if (y1 < 0) y1 += image_height; - if (y2 < 0) y2 += image_height; - - Q_ASSERT(x1 >= 0 && x1 < image_width); - Q_ASSERT(x2 >= 0 && x2 < image_width); - Q_ASSERT(y1 >= 0 && y1 < image_height); - Q_ASSERT(y2 >= 0 && y2 < image_height); const uchar *s1 = data->texture.scanLine(y1); const uchar *s2 = data->texture.scanLine(y2); @@ -1018,20 +749,27 @@ static const uint * QT_FASTCALL fetchTransformedBilinearTiled(uint *buffer, cons int idistx = 256 - distx; int idisty = 256 - disty; - x1 %= image_width; - x2 %= image_width; - y1 %= image_height; - y2 %= image_height; - - if (x1 < 0) x1 += image_width; - if (x2 < 0) x2 += image_width; - if (y1 < 0) y1 += image_height; - if (y2 < 0) y2 += image_height; - - Q_ASSERT(x1 >= 0 && x1 < image_width); - Q_ASSERT(x2 >= 0 && x2 < image_width); - Q_ASSERT(y1 >= 0 && y1 < image_height); - Q_ASSERT(y2 >= 0 && y2 < image_height); + if (blendType == BlendTransformedBilinearTiled) { + x1 %= image_width; + x2 %= image_width; + y1 %= image_height; + y2 %= image_height; + + if (x1 < 0) x1 += image_width; + if (x2 < 0) x2 += image_width; + if (y1 < 0) y1 += image_height; + if (y2 < 0) y2 += image_height; + + Q_ASSERT(x1 >= 0 && x1 < image_width); + Q_ASSERT(x2 >= 0 && x2 < image_width); + Q_ASSERT(y1 >= 0 && y1 < image_height); + Q_ASSERT(y2 >= 0 && y2 < image_height); + } else { + x1 = qBound(0, x1, image_width - 1); + x2 = qBound(0, x2, image_width - 1); + y1 = qBound(0, y1, image_height - 1); + y2 = qBound(0, y2, image_height - 1); + } const uchar *s1 = data->texture.scanLine(y1); const uchar *s2 = data->texture.scanLine(y2); @@ -1059,39 +797,7 @@ static const uint * QT_FASTCALL fetchTransformedBilinearTiled(uint *buffer, cons return buffer; } -#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL) - -// explicit template instantiations needed to compile with VC6 and VC2002 - -#define SPANFUNC_POINTER_FETCHUNTRANSFORMED(Arg) \ - const uint *qt_fetchUntransformed_##Arg(uint *buffer, const Operator *op, const QSpanData *data, \ - int y, int x, int length) \ -{ \ - return qt_fetchUntransformed<QImage::Arg>(buffer, op, data, y, x, length Q_TEMPLATE_IMAGEFORMAT_CALL(QImage::Arg)); \ -} - -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_Mono); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_MonoLSB); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_Indexed8); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB32_Premultiplied); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB32); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB16); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB8565_Premultiplied); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB666); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB6666_Premultiplied); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB555); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB8555_Premultiplied); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB888); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_RGB444); -SPANFUNC_POINTER_FETCHUNTRANSFORMED(Format_ARGB4444_Premultiplied); - -#undef SPANFUNC_POINTER_FETCHUNTRANSFORMED - -#define SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Arg) qt_fetchUntransformed_##Arg - -#else // !VC6 && !VC2002 -# define SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Arg) qt_fetchUntransformed<QImage::Arg> -#endif +#define SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Arg) qt_fetchUntransformed<QImage::Arg> static const SourceFetchProc sourceFetch[NBlendTypes][QImage::NImageFormats] = { // Untransformed @@ -1135,75 +841,75 @@ static const SourceFetchProc sourceFetch[NBlendTypes][QImage::NImageFormats] = { // Transformed { 0, // Invalid - fetchTransformed, // Mono - fetchTransformed, // MonoLsb - fetchTransformed, // Indexed8 - fetchTransformed, // RGB32 - fetchTransformed, // ARGB32 - fetchTransformed, // ARGB32_Premultiplied - fetchTransformed, // RGB16 - fetchTransformed, // ARGB8565_Premultiplied - fetchTransformed, // RGB666 - fetchTransformed, // ARGB6666_Premultiplied - fetchTransformed, // RGB555 - fetchTransformed, // ARGB8555_Premultiplied - fetchTransformed, // RGB888 - fetchTransformed, // RGB444 - fetchTransformed, // ARGB4444_Premultiplied + fetchTransformed<BlendTransformed>, // Mono + fetchTransformed<BlendTransformed>, // MonoLsb + fetchTransformed<BlendTransformed>, // Indexed8 + fetchTransformed<BlendTransformed>, // RGB32 + fetchTransformed<BlendTransformed>, // ARGB32 + fetchTransformed<BlendTransformed>, // ARGB32_Premultiplied + fetchTransformed<BlendTransformed>, // RGB16 + fetchTransformed<BlendTransformed>, // ARGB8565_Premultiplied + fetchTransformed<BlendTransformed>, // RGB666 + fetchTransformed<BlendTransformed>, // ARGB6666_Premultiplied + fetchTransformed<BlendTransformed>, // RGB555 + fetchTransformed<BlendTransformed>, // ARGB8555_Premultiplied + fetchTransformed<BlendTransformed>, // RGB888 + fetchTransformed<BlendTransformed>, // RGB444 + fetchTransformed<BlendTransformed>, // ARGB4444_Premultiplied }, { 0, // TransformedTiled - fetchTransformedTiled, // Mono - fetchTransformedTiled, // MonoLsb - fetchTransformedTiled, // Indexed8 - fetchTransformedTiled, // RGB32 - fetchTransformedTiled, // ARGB32 - fetchTransformedTiled, // ARGB32_Premultiplied - fetchTransformedTiled, // RGB16 - fetchTransformedTiled, // ARGB8565_Premultiplied - fetchTransformedTiled, // RGB666 - fetchTransformedTiled, // ARGB6666_Premultiplied - fetchTransformedTiled, // RGB555 - fetchTransformedTiled, // ARGB8555_Premultiplied - fetchTransformedTiled, // RGB888 - fetchTransformedTiled, // RGB444 - fetchTransformedTiled, // ARGB4444_Premultiplied + fetchTransformed<BlendTransformedTiled>, // Mono + fetchTransformed<BlendTransformedTiled>, // MonoLsb + fetchTransformed<BlendTransformedTiled>, // Indexed8 + fetchTransformed<BlendTransformedTiled>, // RGB32 + fetchTransformed<BlendTransformedTiled>, // ARGB32 + fetchTransformed<BlendTransformedTiled>, // ARGB32_Premultiplied + fetchTransformed<BlendTransformedTiled>, // RGB16 + fetchTransformed<BlendTransformedTiled>, // ARGB8565_Premultiplied + fetchTransformed<BlendTransformedTiled>, // RGB666 + fetchTransformed<BlendTransformedTiled>, // ARGB6666_Premultiplied + fetchTransformed<BlendTransformedTiled>, // RGB555 + fetchTransformed<BlendTransformedTiled>, // ARGB8555_Premultiplied + fetchTransformed<BlendTransformedTiled>, // RGB888 + fetchTransformed<BlendTransformedTiled>, // RGB444 + fetchTransformed<BlendTransformedTiled>, // ARGB4444_Premultiplied }, { 0, // Bilinear - fetchTransformedBilinear, // Mono - fetchTransformedBilinear, // MonoLsb - fetchTransformedBilinear, // Indexed8 - fetchTransformedBilinear, // RGB32 - fetchTransformedBilinear, // ARGB32 - fetchTransformedBilinear, // ARGB32_Premultiplied - fetchTransformedBilinear, // RGB16 - fetchTransformedBilinear, // ARGB8565_Premultiplied - fetchTransformedBilinear, // RGB666 - fetchTransformedBilinear, // ARGB6666_Premultiplied - fetchTransformedBilinear, // RGB555 - fetchTransformedBilinear, // ARGB8555_Premultiplied - fetchTransformedBilinear, // RGB888 - fetchTransformedBilinear, // RGB444 - fetchTransformedBilinear // ARGB4444_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // Mono + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // MonoLsb + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // Indexed8 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_ARGB32_Premultiplied>, // RGB32 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_ARGB32>, // ARGB32 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_ARGB32_Premultiplied>, // ARGB32_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB16 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // ARGB8565_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB666 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // ARGB6666_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB555 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // ARGB8555_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB888 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB444 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid> // ARGB4444_Premultiplied }, { 0, // BilinearTiled - fetchTransformedBilinearTiled, // Mono - fetchTransformedBilinearTiled, // MonoLsb - fetchTransformedBilinearTiled, // Indexed8 - fetchTransformedBilinearTiled, // RGB32 - fetchTransformedBilinearTiled, // ARGB32 - fetchTransformedBilinearTiled, // ARGB32_Premultiplied - fetchTransformedBilinearTiled, // RGB16 - fetchTransformedBilinearTiled, // ARGB8565_Premultiplied - fetchTransformedBilinearTiled, // RGB666 - fetchTransformedBilinearTiled, // ARGB6666_Premultiplied - fetchTransformedBilinearTiled, // RGB555 - fetchTransformedBilinearTiled, // ARGB8555_Premultiplied - fetchTransformedBilinearTiled, // RGB888 - fetchTransformedBilinearTiled, // RGB444 - fetchTransformedBilinearTiled // ARGB4444_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // Mono + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // MonoLsb + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // Indexed8 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_ARGB32_Premultiplied>, // RGB32 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_ARGB32>, // ARGB32 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_ARGB32_Premultiplied>, // ARGB32_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB16 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // ARGB8565_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB666 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // ARGB6666_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB555 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // ARGB8555_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB888 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB444 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid> // ARGB4444_Premultiplied }, }; @@ -3220,8 +2926,7 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData) } template <class T> -Q_STATIC_TEMPLATE_FUNCTION void blendColor(int count, const QSpan *spans, void *userData - Q_TEMPLATE_FIX(T)) +Q_STATIC_TEMPLATE_FUNCTION void blendColor(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); Operator op = getOperator(data, spans, count); @@ -3267,28 +2972,7 @@ Q_STATIC_TEMPLATE_FUNCTION void blendColor(int count, const QSpan *spans, void * blend_color_generic(count, spans, userData); } -#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL) -#define BLEND_COLOR_DECL(DST) \ - static void blendColor_##DST(int count, \ - const QSpan *spans, \ - void *userData) \ - { \ - blendColor<DST>(count, spans, userData Q_TEMPLATE_CALL(DST)); \ - } - -BLEND_COLOR_DECL(qargb8565) -BLEND_COLOR_DECL(qrgb666) -BLEND_COLOR_DECL(qargb6666) -BLEND_COLOR_DECL(qrgb555) -BLEND_COLOR_DECL(qargb8555) -BLEND_COLOR_DECL(qrgb888) -BLEND_COLOR_DECL(qrgb444) -BLEND_COLOR_DECL(qargb4444) -#undef DEST_FETCH_DECL -#define SPANFUNC_POINTER_BLENDCOLOR(DST) blendColor_##DST -#else // !VC6 && !VC2002 -# define SPANFUNC_POINTER_BLENDCOLOR(DST) blendColor<DST> -#endif +#define SPANFUNC_POINTER_BLENDCOLOR(DST) blendColor<DST> static void blend_color_rgb16(int count, const QSpan *spans, void *userData) { @@ -3366,45 +3050,117 @@ static void blend_color_rgb16(int count, const QSpan *spans, void *userData) blend_color_generic(count, spans, userData); } -template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_src_generic(int count, const QSpan *spans, void *userData - Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod)) +template <typename T> +void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handler) { - QSpanData *data = reinterpret_cast<QSpanData *>(userData); - - uint buffer[buffer_size]; - uint src_buffer[buffer_size]; - Operator op = getOperator(data, spans, count); - uint const_alpha = 256; if (data->type == QSpanData::Texture) const_alpha = data->texture.const_alpha; - while (count--) { + int coverage = 0; + while (count) { int x = spans->x; - int length = spans->len; - const int coverage = (spans->coverage * const_alpha) >> 8; + const int y = spans->y; + int right = x + spans->len; + + // compute length of adjacent spans + for (int i = 1; i < count && spans[i].y == y && spans[i].x == right; ++i) + right += spans[i].len; + int length = right - x; + while (length) { int l = qMin(buffer_size, length); - const uint *src = op.src_fetch(src_buffer, &op, data, spans->y, x, l); - if (spanMethod == RegularSpans) { - uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; - op.func(dest, src, l, coverage); - if (op.dest_store) - op.dest_store(data->rasterBuffer, x, spans->y, dest, l); - } else { - drawBufferSpan(data, src, l, x, spans->y, l, coverage); - } - x += l; length -= l; + + int process_length = l; + int process_x = x; + + const uint *src = handler.fetch(process_x, y, process_length); + int offset = 0; + while (l > 0) { + if (x == spans->x) // new span? + coverage = (spans->coverage * const_alpha) >> 8; + + int right = spans->x + spans->len; + int len = qMin(l, right - x); + + handler.process(x, y, len, coverage, src, offset); + + l -= len; + x += len; + offset += len; + + if (x == right) { // done with current span? + ++spans; + --count; + } + } + handler.store(process_x, y, process_length); } - ++spans; } } +struct QBlendBase +{ + QBlendBase(QSpanData *d, Operator o) + : data(d) + , op(o) + , dest(0) + { + } + + QSpanData *data; + Operator op; + + uint *dest; + + uint buffer[buffer_size]; + uint src_buffer[buffer_size]; +}; + +template <SpanMethod spanMethod> +class BlendSrcGeneric : public QBlendBase +{ +public: + BlendSrcGeneric(QSpanData *d, Operator o) + : QBlendBase(d, o) + { + } + + const uint *fetch(int x, int y, int len) + { + if (spanMethod == RegularSpans) + dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, y, len) : buffer; + + return op.src_fetch(src_buffer, &op, data, y, x, len); + } + + void process(int x, int y, int len, int coverage, const uint *src, int offset) + { + if (spanMethod == RegularSpans) + op.func(dest + offset, src + offset, len, coverage); + else + drawBufferSpan(data, src + offset, len, x, y, len, coverage); + } + + void store(int x, int y, int len) + { + if (spanMethod == RegularSpans && op.dest_store) { + op.dest_store(data->rasterBuffer, x, y, dest, len); + } + } +}; + +template <SpanMethod spanMethod> +Q_STATIC_TEMPLATE_FUNCTION void blend_src_generic(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + BlendSrcGeneric<spanMethod> blend(data, getOperator(data, spans, count)); + handleSpans(count, spans, data, blend); +} + template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_generic(int count, const QSpan *spans, void *userData - Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod)) +Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_generic(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -3455,13 +3211,12 @@ Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_generic(int count, const QSp } template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_argb(int count, const QSpan *spans, void *userData - Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod)) +Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format != QImage::Format_ARGB32_Premultiplied && data->texture.format != QImage::Format_RGB32) { - blend_untransformed_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod)); + blend_untransformed_generic<spanMethod>(count, spans, userData); return; } @@ -4707,8 +4462,7 @@ void QT_FASTCALL blendUntransformed(int count, const QSpan *spans, void *userDat if (mode != QPainter::CompositionMode_SourceOver && mode != QPainter::CompositionMode_Source) { - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); return; } @@ -4770,8 +4524,7 @@ static void blend_untransformed_rgb888(int count, const QSpan *spans, blendUntransformed<qrgb888, qrgb888>(count, spans, userData); else #endif - blend_untransformed_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_untransformed_generic<RegularSpans>(count, spans, userData); } static void blend_untransformed_argb6666(int count, const QSpan *spans, @@ -4786,8 +4539,7 @@ static void blend_untransformed_argb6666(int count, const QSpan *spans, blendUntransformed<qargb6666, qrgb666>(count, spans, userData); else #endif - blend_untransformed_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_untransformed_generic<RegularSpans>(count, spans, userData); } static void blend_untransformed_rgb666(int count, const QSpan *spans, @@ -4802,8 +4554,7 @@ static void blend_untransformed_rgb666(int count, const QSpan *spans, blendUntransformed<qrgb666, qrgb666>(count, spans, userData); else #endif - blend_untransformed_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_untransformed_generic<RegularSpans>(count, spans, userData); } static void blend_untransformed_argb8565(int count, const QSpan *spans, @@ -4818,8 +4569,7 @@ static void blend_untransformed_argb8565(int count, const QSpan *spans, blendUntransformed<qargb8565, qrgb565>(count, spans, userData); else #endif - blend_untransformed_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_untransformed_generic<RegularSpans>(count, spans, userData); } static void blend_untransformed_rgb565(int count, const QSpan *spans, @@ -4834,8 +4584,7 @@ static void blend_untransformed_rgb565(int count, const QSpan *spans, blendUntransformed<qrgb565, qrgb565>(count, spans, userData); else #endif - blend_untransformed_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_untransformed_generic<RegularSpans>(count, spans, userData); } static void blend_untransformed_argb8555(int count, const QSpan *spans, @@ -4850,8 +4599,7 @@ static void blend_untransformed_argb8555(int count, const QSpan *spans, blendUntransformed<qargb8555, qrgb555>(count, spans, userData); else #endif - blend_untransformed_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_untransformed_generic<RegularSpans>(count, spans, userData); } static void blend_untransformed_rgb555(int count, const QSpan *spans, @@ -4866,8 +4614,7 @@ static void blend_untransformed_rgb555(int count, const QSpan *spans, blendUntransformed<qrgb555, qrgb555>(count, spans, userData); else #endif - blend_untransformed_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_untransformed_generic<RegularSpans>(count, spans, userData); } static void blend_untransformed_argb4444(int count, const QSpan *spans, @@ -4882,8 +4629,7 @@ static void blend_untransformed_argb4444(int count, const QSpan *spans, blendUntransformed<qargb4444, qrgb444>(count, spans, userData); else #endif - blend_untransformed_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_untransformed_generic<RegularSpans>(count, spans, userData); } static void blend_untransformed_rgb444(int count, const QSpan *spans, @@ -4898,13 +4644,11 @@ static void blend_untransformed_rgb444(int count, const QSpan *spans, blendUntransformed<qrgb444, qrgb444>(count, spans, userData); else #endif - blend_untransformed_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_untransformed_generic<RegularSpans>(count, spans, userData); } template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_generic(int count, const QSpan *spans, void *userData - Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod)) +Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_generic(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -4958,13 +4702,12 @@ Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_generic(int count, const QSpan *span } template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_argb(int count, const QSpan *spans, void *userData - Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod)) +Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format != QImage::Format_ARGB32_Premultiplied && data->texture.format != QImage::Format_RGB32) { - blend_tiled_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod)); + blend_tiled_generic<spanMethod>(count, spans, userData); return; } @@ -5020,8 +4763,7 @@ Q_STATIC_TEMPLATE_FUNCTION void blendTiled(int count, const QSpan *spans, void * if (mode != QPainter::CompositionMode_SourceOver && mode != QPainter::CompositionMode_Source) { - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); return; } @@ -5091,8 +4833,7 @@ static void blend_tiled_rgb888(int count, const QSpan *spans, void *userData) blendTiled<qrgb888, qrgb888>(count, spans, userData); else #endif - blend_tiled_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_tiled_generic<RegularSpans>(count, spans, userData); } static void blend_tiled_argb6666(int count, const QSpan *spans, void *userData) @@ -5106,8 +4847,7 @@ static void blend_tiled_argb6666(int count, const QSpan *spans, void *userData) blendTiled<qargb6666, qrgb666>(count, spans, userData); else #endif - blend_tiled_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_tiled_generic<RegularSpans>(count, spans, userData); } static void blend_tiled_rgb666(int count, const QSpan *spans, void *userData) @@ -5121,8 +4861,7 @@ static void blend_tiled_rgb666(int count, const QSpan *spans, void *userData) blendTiled<qrgb666, qrgb666>(count, spans, userData); else #endif - blend_tiled_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_tiled_generic<RegularSpans>(count, spans, userData); } static void blend_tiled_argb8565(int count, const QSpan *spans, void *userData) @@ -5136,8 +4875,7 @@ static void blend_tiled_argb8565(int count, const QSpan *spans, void *userData) blendTiled<qargb8565, qrgb565>(count, spans, userData); else #endif - blend_tiled_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_tiled_generic<RegularSpans>(count, spans, userData); } static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData) @@ -5151,8 +4889,7 @@ static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData) blendTiled<qrgb565, qrgb565>(count, spans, userData); else #endif - blend_tiled_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_tiled_generic<RegularSpans>(count, spans, userData); } static void blend_tiled_argb8555(int count, const QSpan *spans, void *userData) @@ -5166,8 +4903,7 @@ static void blend_tiled_argb8555(int count, const QSpan *spans, void *userData) blendTiled<qargb8555, qrgb555>(count, spans, userData); else #endif - blend_tiled_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_tiled_generic<RegularSpans>(count, spans, userData); } static void blend_tiled_rgb555(int count, const QSpan *spans, void *userData) @@ -5181,8 +4917,7 @@ static void blend_tiled_rgb555(int count, const QSpan *spans, void *userData) blendTiled<qrgb555, qrgb555>(count, spans, userData); else #endif - blend_tiled_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_tiled_generic<RegularSpans>(count, spans, userData); } static void blend_tiled_argb4444(int count, const QSpan *spans, void *userData) @@ -5196,8 +4931,7 @@ static void blend_tiled_argb4444(int count, const QSpan *spans, void *userData) blendTiled<qargb4444, qrgb444>(count, spans, userData); else #endif - blend_tiled_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_tiled_generic<RegularSpans>(count, spans, userData); } static void blend_tiled_rgb444(int count, const QSpan *spans, void *userData) @@ -5211,19 +4945,17 @@ static void blend_tiled_rgb444(int count, const QSpan *spans, void *userData) blendTiled<qrgb444, qrgb444>(count, spans, userData); else #endif - blend_tiled_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_tiled_generic<RegularSpans>(count, spans, userData); } template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_bilinear_argb(int count, const QSpan *spans, void *userData - Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod)) +Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_bilinear_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format != QImage::Format_ARGB32_Premultiplied && data->texture.format != QImage::Format_RGB32) { - blend_src_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod)); + blend_src_generic<spanMethod>(count, spans, userData); return; } @@ -5402,8 +5134,7 @@ Q_STATIC_TEMPLATE_FUNCTION void blendTransformedBilinear(int count, const QSpan if (mode != QPainter::CompositionMode_SourceOver) { - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); return; } @@ -5606,8 +5337,7 @@ static void blend_transformed_bilinear_rgb888(int count, const QSpan *spans, voi blendTransformedBilinear<qrgb888, qrgb888>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_bilinear_argb6666(int count, const QSpan *spans, void *userData) @@ -5621,8 +5351,7 @@ static void blend_transformed_bilinear_argb6666(int count, const QSpan *spans, v blendTransformedBilinear<qargb6666, qrgb666>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_bilinear_rgb666(int count, const QSpan *spans, void *userData) @@ -5636,8 +5365,7 @@ static void blend_transformed_bilinear_rgb666(int count, const QSpan *spans, voi blendTransformedBilinear<qrgb666, qrgb666>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_bilinear_argb8565(int count, const QSpan *spans, void *userData) @@ -5651,8 +5379,7 @@ static void blend_transformed_bilinear_argb8565(int count, const QSpan *spans, v blendTransformedBilinear<qargb8565, qrgb565>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_bilinear_rgb565(int count, const QSpan *spans, @@ -5667,8 +5394,7 @@ static void blend_transformed_bilinear_rgb565(int count, const QSpan *spans, blendTransformedBilinear<qrgb565, qargb8565>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_bilinear_argb8555(int count, const QSpan *spans, void *userData) @@ -5682,8 +5408,7 @@ static void blend_transformed_bilinear_argb8555(int count, const QSpan *spans, v blendTransformedBilinear<qargb8555, qrgb555>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_bilinear_rgb555(int count, const QSpan *spans, void *userData) @@ -5697,8 +5422,7 @@ static void blend_transformed_bilinear_rgb555(int count, const QSpan *spans, voi blendTransformedBilinear<qrgb555, qrgb555>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_bilinear_argb4444(int count, const QSpan *spans, void *userData) @@ -5712,8 +5436,7 @@ static void blend_transformed_bilinear_argb4444(int count, const QSpan *spans, v blendTransformedBilinear<qargb4444, qrgb444>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_bilinear_rgb444(int count, const QSpan *spans, void *userData) @@ -5727,18 +5450,16 @@ static void blend_transformed_bilinear_rgb444(int count, const QSpan *spans, voi blendTransformedBilinear<qrgb444, qrgb444>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_bilinear_tiled_argb(int count, const QSpan *spans, void *userData - Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod)) +Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_bilinear_tiled_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format != QImage::Format_ARGB32_Premultiplied && data->texture.format != QImage::Format_RGB32) { - blend_src_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod)); + blend_src_generic<spanMethod>(count, spans, userData); return; } @@ -5924,13 +5645,12 @@ Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_bilinear_tiled_argb(int count, } template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_argb(int count, const QSpan *spans, void *userData - Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod)) +Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format != QImage::Format_ARGB32_Premultiplied && data->texture.format != QImage::Format_RGB32) { - blend_src_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod)); + blend_src_generic<spanMethod>(count, spans, userData); return; } @@ -6052,8 +5772,7 @@ Q_STATIC_TEMPLATE_FUNCTION void blendTransformed(int count, const QSpan *spans, QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; if (mode != QPainter::CompositionMode_SourceOver) { - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); return; } @@ -6202,8 +5921,7 @@ static void blend_transformed_rgb888(int count, const QSpan *spans, blendTransformed<qrgb888, qrgb888>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_argb6666(int count, const QSpan *spans, @@ -6218,8 +5936,7 @@ static void blend_transformed_argb6666(int count, const QSpan *spans, blendTransformed<qargb6666, qrgb666>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_rgb666(int count, const QSpan *spans, @@ -6234,8 +5951,7 @@ static void blend_transformed_rgb666(int count, const QSpan *spans, blendTransformed<qrgb666, qrgb666>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_argb8565(int count, const QSpan *spans, @@ -6250,8 +5966,7 @@ static void blend_transformed_argb8565(int count, const QSpan *spans, blendTransformed<qargb8565, qrgb565>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_rgb565(int count, const QSpan *spans, @@ -6266,8 +5981,7 @@ static void blend_transformed_rgb565(int count, const QSpan *spans, blendTransformed<qrgb565, qrgb565>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_argb8555(int count, const QSpan *spans, @@ -6282,8 +5996,7 @@ static void blend_transformed_argb8555(int count, const QSpan *spans, blendTransformed<qargb8555, qrgb555>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_rgb555(int count, const QSpan *spans, @@ -6298,8 +6011,7 @@ static void blend_transformed_rgb555(int count, const QSpan *spans, blendTransformed<qrgb555, qrgb555>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_argb4444(int count, const QSpan *spans, @@ -6314,8 +6026,7 @@ static void blend_transformed_argb4444(int count, const QSpan *spans, blendTransformed<qargb4444, qrgb444>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_rgb444(int count, const QSpan *spans, @@ -6330,18 +6041,16 @@ static void blend_transformed_rgb444(int count, const QSpan *spans, blendTransformed<qrgb444, qrgb444>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } template <SpanMethod spanMethod> -Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_tiled_argb(int count, const QSpan *spans, void *userData - Q_TEMPLATE_ENUM_FIX(SpanMethod, spanMethod)) +Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_tiled_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); if (data->texture.format != QImage::Format_ARGB32_Premultiplied && data->texture.format != QImage::Format_RGB32) { - blend_src_generic<spanMethod>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, spanMethod)); + blend_src_generic<spanMethod>(count, spans, userData); return; } @@ -6475,8 +6184,7 @@ Q_STATIC_TEMPLATE_FUNCTION void blendTransformedTiled(int count, const QSpan *sp QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; if (mode != QPainter::CompositionMode_SourceOver) { - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); return; } @@ -6626,8 +6334,7 @@ static void blend_transformed_tiled_rgb888(int count, const QSpan *spans, blendTransformedTiled<qrgb888, qrgb888>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_tiled_argb6666(int count, const QSpan *spans, @@ -6642,8 +6349,7 @@ static void blend_transformed_tiled_argb6666(int count, const QSpan *spans, blendTransformedTiled<qargb6666, qrgb666>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_tiled_rgb666(int count, const QSpan *spans, @@ -6658,8 +6364,7 @@ static void blend_transformed_tiled_rgb666(int count, const QSpan *spans, blendTransformedTiled<qrgb666, qrgb666>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_tiled_argb8565(int count, const QSpan *spans, @@ -6674,8 +6379,7 @@ static void blend_transformed_tiled_argb8565(int count, const QSpan *spans, blendTransformedTiled<qargb8565, qrgb565>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_tiled_rgb565(int count, const QSpan *spans, @@ -6690,8 +6394,7 @@ static void blend_transformed_tiled_rgb565(int count, const QSpan *spans, blendTransformedTiled<qrgb565, qrgb565>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_tiled_argb8555(int count, const QSpan *spans, @@ -6706,8 +6409,7 @@ static void blend_transformed_tiled_argb8555(int count, const QSpan *spans, blendTransformedTiled<qargb8555, qrgb555>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_tiled_rgb555(int count, const QSpan *spans, @@ -6722,8 +6424,7 @@ static void blend_transformed_tiled_rgb555(int count, const QSpan *spans, blendTransformedTiled<qrgb555, qrgb555>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_tiled_argb4444(int count, const QSpan *spans, @@ -6738,8 +6439,7 @@ static void blend_transformed_tiled_argb4444(int count, const QSpan *spans, blendTransformedTiled<qargb4444, qrgb444>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } static void blend_transformed_tiled_rgb444(int count, const QSpan *spans, @@ -6754,36 +6454,10 @@ static void blend_transformed_tiled_rgb444(int count, const QSpan *spans, blendTransformedTiled<qrgb444, qrgb444>(count, spans, userData); else #endif - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); -} - -#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 && !defined(Q_CC_INTEL) - -// explicit template instantiations needed to compile with VC6 and VC2002 - -#define SPANFUNC_INSTANTIATION(Name, Arg) \ -static inline void Name##_##Arg(int count, const QSpan *spans, void *userData) \ -{ \ - Name<Arg>(count, spans, userData Q_TEMPLATE_ENUM_CALL(SpanMethod, Arg)); \ + blend_src_generic<RegularSpans>(count, spans, userData); } -SPANFUNC_INSTANTIATION(blend_untransformed_generic, RegularSpans); -SPANFUNC_INSTANTIATION(blend_untransformed_argb, RegularSpans); -SPANFUNC_INSTANTIATION(blend_tiled_generic, RegularSpans); -SPANFUNC_INSTANTIATION(blend_tiled_argb, RegularSpans); -SPANFUNC_INSTANTIATION(blend_src_generic, RegularSpans); -SPANFUNC_INSTANTIATION(blend_transformed_argb, RegularSpans); -SPANFUNC_INSTANTIATION(blend_transformed_tiled_argb, RegularSpans); -SPANFUNC_INSTANTIATION(blend_transformed_bilinear_argb, RegularSpans); -SPANFUNC_INSTANTIATION(blend_transformed_bilinear_tiled_argb, RegularSpans); -#undef SPANFUNC_INSTANTIATION - -#define SPANFUNC_POINTER(Name, Arg) Name##_##Arg - -#else // !VC6 && !VC2002 # define SPANFUNC_POINTER(Name, Arg) Name<Arg> -#endif /* Image formats here are target formats */ @@ -7150,8 +6824,7 @@ static void qt_gradient_quint32(int count, const QSpan *spans, void *userData) } } else { - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } } @@ -7199,8 +6872,7 @@ static void qt_gradient_quint16(int count, const QSpan *spans, void *userData) data->solid.color = oldColor; } else { - blend_src_generic<RegularSpans>(count, spans, userData - Q_TEMPLATE_ENUM_CALL(SpanMethod, RegularSpans)); + blend_src_generic<RegularSpans>(count, spans, userData); } } @@ -7405,7 +7077,7 @@ static void qt_alphamapblit_quint32(QRasterBuffer *rasterBuffer, #endif { int ialpha = 255 - coverage; - dest[i] = BYTE_MUL(c, uint(coverage)) + BYTE_MUL(dest[i], ialpha); + dest[i] = INTERPOLATE_PIXEL_255(c, coverage, dest[i], ialpha); } } } @@ -7446,7 +7118,7 @@ static void qt_alphamapblit_quint32(QRasterBuffer *rasterBuffer, #endif { int ialpha = 255 - coverage; - dest[xp] = BYTE_MUL(c, uint(coverage)) + BYTE_MUL(dest[xp], ialpha); + dest[xp] = INTERPOLATE_PIXEL_255(c, coverage, dest[xp], ialpha); } } diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 70036e1..b5e092c 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -2000,7 +2000,63 @@ bool QPainterPath::intersects(const QRectF &rect) const return false; } +/*! + Translates all elements in the path by (\a{dx}, \a{dy}). + + \since 4.6 + \sa translated() +*/ +void QPainterPath::translate(qreal dx, qreal dy) +{ + if (!d_ptr || (dx == 0 && dy == 0)) + return; + + int elementsLeft = d_ptr->elements.size(); + if (elementsLeft <= 0) + return; + + detach(); + QPainterPath::Element *element = d_func()->elements.data(); + Q_ASSERT(element); + while (elementsLeft--) { + element->x += dx; + element->y += dy; + ++element; + } +} + +/*! + \fn void QPainterPath::translate(const QPointF &offset) + \overload + \since 4.6 + Translates all elements in the path by the given \a offset. + + \sa translated() +*/ + +/*! + Returns a copy of the path that is translated by (\a{dx}, \a{dy}). + + \since 4.6 + \sa translate() +*/ +QPainterPath QPainterPath::translated(qreal dx, qreal dy) const +{ + QPainterPath copy(*this); + copy.translate(dx, dy); + return copy; +} + +/*! + \fn void QPainterPath::translated(const QPointF &offset) + \overload + \since 4.6 + + Returns a copy of the path that is translated by the given \a offset. + + \sa translate() +*/ /*! \fn bool QPainterPath::contains(const QRectF &rectangle) const diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index 6cd2af8..e343a28 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -147,6 +147,12 @@ public: bool contains(const QRectF &rect) const; bool intersects(const QRectF &rect) const; + void translate(qreal dx, qreal dy); + inline void translate(const QPointF &offset); + + QPainterPath translated(qreal dx, qreal dy) const; + inline QPainterPath translated(const QPointF &offset) const; + QRectF boundingRect() const; QRectF controlPointRect() const; @@ -365,6 +371,12 @@ inline void QPainterPath::addText(qreal x, qreal y, const QFont &f, const QStrin addText(QPointF(x, y), f, text); } +inline void QPainterPath::translate(const QPointF &offset) +{ translate(offset.x(), offset.y()); } + +inline QPainterPath QPainterPath::translated(const QPointF &offset) const +{ return translated(offset.x(), offset.y()); } + inline bool QPainterPath::isEmpty() const { return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.first().type == MoveToElement); diff --git a/src/gui/painting/qpolygon.cpp b/src/gui/painting/qpolygon.cpp index 87dae0f..769c095 100644 --- a/src/gui/painting/qpolygon.cpp +++ b/src/gui/painting/qpolygon.cpp @@ -208,10 +208,15 @@ QPolygon::QPolygon(int nPoints, const int *points) /*! Translates all points in the polygon by (\a{dx}, \a{dy}). + + \sa translated() */ void QPolygon::translate(int dx, int dy) { + if (dx == 0 && dy == 0) + return; + register QPoint *p = data(); register int i = size(); QPoint pt(dx, dy); @@ -226,8 +231,32 @@ void QPolygon::translate(int dx, int dy) \overload Translates all points in the polygon by the given \a offset. + + \sa translated() +*/ + +/*! + Returns a copy of the polygon that is translated by (\a{dx}, \a{dy}). + + \since 4.6 + \sa translate() */ +QPolygon QPolygon::translated(int dx, int dy) const +{ + QPolygon copy(*this); + copy.translate(dx, dy); + return copy; +} + +/*! + \fn void QPolygon::translated(const QPoint &offset) const + \overload + \since 4.6 + + Returns a copy of the polygon that is translated by the given \a offset. + \sa translate() +*/ /*! Extracts the coordinates of the point at the given \a index to @@ -565,10 +594,15 @@ QPolygonF::QPolygonF(const QPolygon &a) /*! Translate all points in the polygon by the given \a offset. + + \sa translated() */ void QPolygonF::translate(const QPointF &offset) { + if (offset.isNull()) + return; + register QPointF *p = data(); register int i = size(); while (i--) { @@ -582,6 +616,31 @@ void QPolygonF::translate(const QPointF &offset) \overload Translates all points in the polygon by (\a{dx}, \a{dy}). + + \sa translated() +*/ + +/*! + Returns a copy of the polygon that is translated by the given \a offset. + + \since 4.6 + \sa translate() +*/ +QPolygonF QPolygonF::translated(const QPointF &offset) const +{ + QPolygonF copy(*this); + copy.translate(offset); + return copy; +} + +/*! + \fn void QPolygonF::translated(qreal dx, qreal dy) const + \overload + \since 4.6 + + Returns a copy of the polygon that is translated by (\a{dx}, \a{dy}). + + \sa translate() */ /*! diff --git a/src/gui/painting/qpolygon.h b/src/gui/painting/qpolygon.h index e5e0bd1..c52f48c 100644 --- a/src/gui/painting/qpolygon.h +++ b/src/gui/painting/qpolygon.h @@ -71,6 +71,10 @@ public: void translate(int dx, int dy); void translate(const QPoint &offset); + + QPolygon translated(int dx, int dy) const; + inline QPolygon translated(const QPoint &offset) const; + QRect boundingRect() const; void point(int i, int *x, int *y) const; @@ -120,6 +124,9 @@ inline QPoint QPolygon::point(int index) const inline void QPolygon::translate(const QPoint &offset) { translate(offset.x(), offset.y()); } +inline QPolygon QPolygon::translated(const QPoint &offset) const +{ return translated(offset.x(), offset.y()); } + class QRectF; class Q_GUI_EXPORT QPolygonF : public QVector<QPointF> @@ -136,6 +143,9 @@ public: inline void translate(qreal dx, qreal dy); void translate(const QPointF &offset); + inline QPolygonF translated(qreal dx, qreal dy) const; + QPolygonF translated(const QPointF &offset) const; + QPolygon toPolygon() const; bool isClosed() const { return !isEmpty() && first() == last(); } @@ -166,6 +176,9 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QPolygonF &array); inline void QPolygonF::translate(qreal dx, qreal dy) { translate(QPointF(dx, dy)); } +inline QPolygonF QPolygonF::translated(qreal dx, qreal dy) const +{ return translated(QPointF(dx, dy)); } + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index c70208c..b25146d 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -399,6 +399,9 @@ QTransform QTransform::inverted(bool *invertible) const */ QTransform & QTransform::translate(qreal dx, qreal dy) { + if (dx == 0 && dy == 0) + return *this; + switch(type()) { case TxNone: affine._dx = dx; @@ -435,7 +438,10 @@ QTransform & QTransform::translate(qreal dx, qreal dy) QTransform QTransform::fromTranslate(qreal dx, qreal dy) { QTransform transform(1, 0, 0, 1, dx, dy); - transform.m_dirty = TxTranslate; + if (dx == 0 && dy == 0) + transform.m_dirty = TxNone; + else + transform.m_dirty = TxTranslate; return transform; } @@ -447,6 +453,9 @@ QTransform QTransform::fromTranslate(qreal dx, qreal dy) */ QTransform & QTransform::scale(qreal sx, qreal sy) { + if (sx == 1 && sy == 1) + return *this; + switch(type()) { case TxNone: case TxTranslate: @@ -481,7 +490,10 @@ QTransform & QTransform::scale(qreal sx, qreal sy) QTransform QTransform::fromScale(qreal sx, qreal sy) { QTransform transform(sx, 0, 0, sy, 0, 0); - transform.m_dirty = TxScale; + if (sx == 1 && sy == 1) + transform.m_dirty = TxNone; + else + transform.m_dirty = TxScale; return transform; } @@ -544,6 +556,9 @@ const qreal inv_dist_to_plane = 1. / 1024.; */ QTransform & QTransform::rotate(qreal a, Qt::Axis axis) { + if (a == 0) + return *this; + qreal sina = 0; qreal cosa = 0; if (a == 90. || a == -270.) @@ -715,7 +730,15 @@ bool QTransform::operator!=(const QTransform &o) const */ QTransform & QTransform::operator*=(const QTransform &o) { - TransformationType t = qMax(type(), o.type()); + const TransformationType otherType = o.type(); + if (otherType == TxNone) + return *this; + + const TransformationType thisType = type(); + if (thisType == TxNone) + return operator=(o); + + TransformationType t = qMax(thisType, otherType); switch(t) { case TxNone: break; @@ -1222,7 +1245,11 @@ static QPolygonF mapProjective(const QTransform &transform, const QPolygonF &pol */ QPolygonF QTransform::map(const QPolygonF &a) const { - if (type() >= QTransform::TxProject) + TransformationType t = type(); + if (t <= TxTranslate) + return a.translated(affine._dx, affine._dy); + + if (t >= QTransform::TxProject) return mapProjective(*this, a); int size = a.size(); @@ -1231,7 +1258,6 @@ QPolygonF QTransform::map(const QPolygonF &a) const const QPointF *da = a.constData(); QPointF *dp = p.data(); - TransformationType t = type(); for(i = 0; i < size; ++i) { MAP(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp); } @@ -1249,7 +1275,11 @@ QPolygonF QTransform::map(const QPolygonF &a) const */ QPolygon QTransform::map(const QPolygon &a) const { - if (type() >= QTransform::TxProject) + TransformationType t = type(); + if (t <= TxTranslate) + return a.translated(qRound(affine._dx), qRound(affine._dy)); + + if (t >= QTransform::TxProject) return mapProjective(*this, QPolygonF(a)).toPolygon(); int size = a.size(); @@ -1258,7 +1288,6 @@ QPolygon QTransform::map(const QPolygon &a) const const QPoint *da = a.constData(); QPoint *dp = p.data(); - TransformationType t = type(); for(i = 0; i < size; ++i) { qreal nx = 0, ny = 0; MAP(da[i].xp, da[i].yp, nx, ny); @@ -1460,15 +1489,11 @@ QPainterPath QTransform::map(const QPainterPath &path) const return mapProjective(*this, path); QPainterPath copy = path; - copy.detach(); if (t == TxTranslate) { - for (int i=0; i<path.elementCount(); ++i) { - QPainterPath::Element &e = copy.d_ptr->elements[i]; - e.x += affine._dx; - e.y += affine._dy; - } + copy.translate(affine._dx, affine._dy); } else { + copy.detach(); // Full xform for (int i=0; i<path.elementCount(); ++i) { QPainterPath::Element &e = copy.d_ptr->elements[i]; @@ -1676,6 +1701,9 @@ void QTransform::setMatrix(qreal m11, qreal m12, qreal m13, QRect QTransform::mapRect(const QRect &rect) const { TransformationType t = type(); + if (t <= TxTranslate) + return rect.translated(qRound(affine._dx), qRound(affine._dy)); + if (t <= TxScale) { int x = qRound(affine._m11*rect.x() + affine._dx); int y = qRound(affine._m22*rect.y() + affine._dy); @@ -1744,6 +1772,9 @@ QRect QTransform::mapRect(const QRect &rect) const QRectF QTransform::mapRect(const QRectF &rect) const { TransformationType t = type(); + if (t <= TxTranslate) + return rect.translated(affine._dx, affine._dy); + if (t <= TxScale) { qreal x = affine._m11*rect.x() + affine._dx; qreal y = affine._m22*rect.y() + affine._dy; @@ -2061,10 +2092,11 @@ QTransform::operator QVariant() const Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale) { - if (transform.type() <= QTransform::TxTranslate) { + const QTransform::TransformationType type = transform.type(); + if (type <= QTransform::TxTranslate) { *scale = 1; return true; - } else if (transform.type() == QTransform::TxScale) { + } else if (type == QTransform::TxScale) { const qreal xScale = qAbs(transform.m11()); const qreal yScale = qAbs(transform.m22()); *scale = qMax(xScale, yScale); @@ -2076,7 +2108,7 @@ bool qt_scaleForTransform(const QTransform &transform, qreal *scale) const qreal yScale = transform.m12() * transform.m12() + transform.m22() * transform.m22(); *scale = qSqrt(qMax(xScale, yScale)); - return transform.type() == QTransform::TxRotate && qFuzzyCompare(xScale, yScale); + return type == QTransform::TxRotate && qFuzzyCompare(xScale, yScale); } QT_END_NAMESPACE diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h index de7ebcd..c76409b 100644 --- a/src/gui/painting/qtransform.h +++ b/src/gui/painting/qtransform.h @@ -257,6 +257,8 @@ inline qreal QTransform::dy() const inline QTransform &QTransform::operator*=(qreal num) { + if (num == 1.) + return *this; affine._m11 *= num; affine._m12 *= num; m_13 *= num; @@ -271,11 +273,15 @@ inline QTransform &QTransform::operator*=(qreal num) } inline QTransform &QTransform::operator/=(qreal div) { + if (div == 0) + return *this; div = 1/div; return operator*=(div); } inline QTransform &QTransform::operator+=(qreal num) { + if (num == 0) + return *this; affine._m11 += num; affine._m12 += num; m_13 += num; @@ -290,6 +296,8 @@ inline QTransform &QTransform::operator+=(qreal num) } inline QTransform &QTransform::operator-=(qreal num) { + if (num == 0) + return *this; affine._m11 -= num; affine._m12 -= num; m_13 -= num; diff --git a/src/gui/styles/qcleanlooksstyle.cpp b/src/gui/styles/qcleanlooksstyle.cpp index 468ada9..11f4d26 100644 --- a/src/gui/styles/qcleanlooksstyle.cpp +++ b/src/gui/styles/qcleanlooksstyle.cpp @@ -44,6 +44,7 @@ #if !defined(QT_NO_STYLE_CLEANLOOKS) || defined(QT_PLUGIN) +#include <private/qstylehelper_p.h> #include "qwindowsstyle_p.h" #include <qcombobox.h> #include <qpushbutton.h> @@ -72,7 +73,7 @@ QT_BEGIN_NAMESPACE -static const bool UsePixmapCache = true; +using namespace QStyleHelper; enum Direction { TopDown, @@ -553,26 +554,6 @@ static void qt_cleanlooks_draw_buttongradient(QPainter *painter, const QRect &re delete gradient; } -static QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size) -{ - QString tmp; - const QStyleOptionComplex *complexOption = qstyleoption_cast<const QStyleOptionComplex *>(option); - tmp.sprintf("%s-%d-%d-%lld-%dx%d-%d", key.toLatin1().constData(), uint(option->state), - complexOption ? uint(complexOption->activeSubControls) : uint(0), - option->palette.cacheKey(), size.width(), size.height(), option->direction); -#ifndef QT_NO_SPINBOX - if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { - tmp.append(QLatin1Char('-')); - tmp.append(QString::number(spinBox->buttonSymbols)); - tmp.append(QLatin1Char('-')); - tmp.append(QString::number(spinBox->stepEnabled)); - tmp.append(QLatin1Char('-')); - tmp.append(QLatin1Char(spinBox->frame ? '1' : '0')); - } -#endif // QT_NO_SPINBOX - return tmp; -} - static void qt_cleanlooks_draw_mdibutton(QPainter *painter, const QStyleOptionTitleBar *option, const QRect &tmp, bool hover, bool sunken) { QColor dark; @@ -1664,7 +1645,7 @@ void QCleanlooksStyle::drawControl(ControlElement element, const QStyleOption *o // Draws the header in tables. if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { QPixmap cache; - QString pixmapName = uniqueName(QLatin1String("headersection"), option, option->rect.size()); + QString pixmapName = QStyleHelper::uniqueName(QLatin1String("headersection"), option, option->rect.size()); pixmapName += QLatin1String("-") + QString::number(int(header->position)); pixmapName += QLatin1String("-") + QString::number(int(header->orientation)); QRect r = option->rect; @@ -2456,7 +2437,7 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp case CC_SpinBox: if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { QPixmap cache; - QString pixmapName = uniqueName(QLatin1String("spinbox"), spinBox, spinBox->rect.size()); + QString pixmapName = QStyleHelper::uniqueName(QLatin1String("spinbox"), spinBox, spinBox->rect.size()); if (!UsePixmapCache || !QPixmapCache::find(pixmapName, cache)) { cache = QPixmap(spinBox->rect.size()); cache.fill(Qt::transparent); @@ -3137,7 +3118,7 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp // The AddLine (down/right) button if (scrollBar->subControls & SC_ScrollBarAddLine) { - QString addLinePixmapName = uniqueName(QLatin1String("scrollbar_addline"), option, QSize(16, 16)); + QString addLinePixmapName = QStyleHelper::uniqueName(QLatin1String("scrollbar_addline"), option, QSize(16, 16)); QRect pixmapRect = scrollBarAddLine; if (isEnabled) { QRect fillRect = pixmapRect.adjusted(1, 1, -1, -1); @@ -3198,7 +3179,7 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp bool isEnabled = (comboBox->state & State_Enabled); bool focus = isEnabled && (comboBox->state & State_HasFocus); QPixmap cache; - QString pixmapName = uniqueName(QLatin1String("combobox"), option, comboBox->rect.size()); + QString pixmapName = QStyleHelper::uniqueName(QLatin1String("combobox"), option, comboBox->rect.size()); if (sunken) pixmapName += QLatin1String("-sunken"); if (comboBox->editable) @@ -3421,7 +3402,7 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp highlightAlpha.setAlpha(80); if ((option->subControls & SC_SliderGroove) && groove.isValid()) { - QString groovePixmapName = uniqueName(QLatin1String("slider_groove"), option, groove.size()); + QString groovePixmapName = QStyleHelper::uniqueName(QLatin1String("slider_groove"), option, groove.size()); QRect pixmapRect(0, 0, groove.width(), groove.height()); // draw background groove @@ -3501,7 +3482,7 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp // draw handle if ((option->subControls & SC_SliderHandle) ) { - QString handlePixmapName = uniqueName(QLatin1String("slider_handle"), option, handle.size()); + QString handlePixmapName = QStyleHelper::uniqueName(QLatin1String("slider_handle"), option, handle.size()); if (!UsePixmapCache || !QPixmapCache::find(handlePixmapName, cache)) { cache = QPixmap(handle.size()); cache.fill(Qt::transparent); @@ -3656,6 +3637,12 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp } break; #endif // QT_NO_SLIDER +#ifndef QT_NO_DIAL + case CC_Dial: + if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option)) + QStyleHelper::drawDial(dial, painter); + break; +#endif // QT_NO_DIAL default: QWindowsStyle::drawComplexControl(control, option, painter, widget); break; diff --git a/src/gui/styles/qcommonstyle.cpp b/src/gui/styles/qcommonstyle.cpp index 3cae08a..d5ce8df 100644 --- a/src/gui/styles/qcommonstyle.cpp +++ b/src/gui/styles/qcommonstyle.cpp @@ -66,6 +66,7 @@ #include <private/qapplication_p.h> #include <private/qcommonstylepixmaps_p.h> #include <private/qmath_p.h> +#include <private/qstylehelper_p.h> #include <qdebug.h> #include <qtextformat.h> #include <qwizard.h> @@ -2267,7 +2268,7 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, drawPrimitive(PE_PanelItemViewItem, opt, p, widget); // draw the check mark - if (checkRect.isValid()) { + if (vopt->features & QStyleOptionViewItemV2::HasCheckIndicator) { QStyleOptionViewItemV4 option(*vopt); option.rect = checkRect; option.state = option.state & ~QStyle::State_HasFocus; @@ -3189,47 +3190,6 @@ QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt, } #ifndef QT_NO_DIAL -static qreal angle(const QPointF &p1, const QPointF &p2) -{ - static const qreal rad_factor = 180 / Q_PI; - qreal _angle = 0; - - if (p1.x() == p2.x()) { - if (p1.y() < p2.y()) - _angle = 270; - else - _angle = 90; - } else { - qreal x1, x2, y1, y2; - - if (p1.x() <= p2.x()) { - x1 = p1.x(); y1 = p1.y(); - x2 = p2.x(); y2 = p2.y(); - } else { - x2 = p1.x(); y2 = p1.y(); - x1 = p2.x(); y1 = p2.y(); - } - - qreal m = -(y2 - y1) / (x2 - x1); - _angle = atan(m) * rad_factor; - - if (p1.x() < p2.x()) - _angle = 180 - _angle; - else - _angle = -_angle; - } - return _angle; -} - -static int calcBigLineSize(int radius) -{ - int bigLineSize = radius / 6; - if (bigLineSize < 4) - bigLineSize = 4; - if (bigLineSize > radius / 2) - bigLineSize = radius / 2; - return bigLineSize; -} static QPolygonF calcArrow(const QStyleOptionSlider *dial, qreal &a) { @@ -3250,7 +3210,7 @@ static QPolygonF calcArrow(const QStyleOptionSlider *dial, qreal &a) int xc = width / 2; int yc = height / 2; - int len = r - calcBigLineSize(r) - 5; + int len = r - QStyleHelper::calcBigLineSize(r) - 5; if (len < 5) len = 5; int back = len / 2; @@ -3265,45 +3225,6 @@ static QPolygonF calcArrow(const QStyleOptionSlider *dial, qreal &a) return arrow; } -static QPolygonF calcLines(const QStyleOptionSlider *dial, const QWidget *) -{ - QPolygonF poly; - int width = dial->rect.width(); - int height = dial->rect.height(); - qreal r = qMin(width, height) / 2; - int bigLineSize = calcBigLineSize(int(r)); - - qreal xc = width / 2; - qreal yc = height / 2; - int ns = dial->tickInterval; - int notches = (dial->maximum + ns - 1 - dial->minimum) / ns; - if (notches <= 0) - return poly; - if (dial->maximum < dial->minimum - || dial->maximum - dial->minimum > 1000) { - int maximum = dial->minimum + 1000; - notches = (maximum + ns - 1 - dial->minimum) / ns; - } - - poly.resize(2 + 2 * notches); - int smallLineSize = bigLineSize / 2; - for (int i = 0; i <= notches; ++i) { - qreal angle = dial->dialWrapping ? Q_PI * 3 / 2 - i * 2 * Q_PI / notches - : (Q_PI * 8 - i * 10 * Q_PI / notches) / 6; - qreal s = qSin(angle); - qreal c = qCos(angle); - if (i == 0 || (((ns * i) % (dial->pageStep ? dial->pageStep : 1)) == 0)) { - poly[2 * i] = QPointF(xc + (r - bigLineSize) * c, - yc - (r - bigLineSize) * s); - poly[2 * i + 1] = QPointF(xc + r * c, yc - r * s); - } else { - poly[2 * i] = QPointF(xc + (r - 1 - smallLineSize) * c, - yc - (r - 1 - smallLineSize) * s); - poly[2 * i + 1] = QPointF(xc + (r - 1) * c, yc -(r - 1) * s); - } - } - return poly; -} #endif // QT_NO_DIAL /*! @@ -3794,7 +3715,7 @@ void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionCompl // draw notches if (dial->subControls & QStyle::SC_DialTickmarks) { p->setPen(pal.foreground().color()); - p->drawLines(calcLines(dial, widget)); // ### calcLines could be cached... + p->drawLines(QStyleHelper::calcLines(dial)); } if (dial->state & State_Enabled) { @@ -3816,7 +3737,7 @@ void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionCompl p->setBrush(pal.button()); p->drawPolygon(arrow); - a = angle(QPointF(width / 2, height / 2), arrow[0]); + a = QStyleHelper::angle(QPointF(width / 2, height / 2), arrow[0]); p->setBrush(Qt::NoBrush); if (a <= 0 || a > 200) { diff --git a/src/gui/styles/qmacstyle_mac.mm b/src/gui/styles/qmacstyle_mac.mm index 398e11d..5d08c58 100644 --- a/src/gui/styles/qmacstyle_mac.mm +++ b/src/gui/styles/qmacstyle_mac.mm @@ -50,6 +50,7 @@ #include <private/qpaintengine_mac_p.h> #include <private/qpainter_p.h> #include <private/qprintengine_mac_p.h> +#include <private/qstylehelper_p.h> #include <qapplication.h> #include <qbitmap.h> #include <qcheckbox.h> @@ -125,6 +126,20 @@ static const QColor titlebarSeparatorLineInactive(131, 131, 131); static const QColor mainWindowGradientBegin(240, 240, 240); static const QColor mainWindowGradientEnd(200, 200, 200); +#if (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5) +enum { + kThemePushButtonTextured = 31, + kThemePushButtonTexturedSmall = 32, + kThemePushButtonTexturedMini = 33 +}; + +/* Search fields */ +enum { + kHIThemeFrameTextFieldRound = 1000, + kHIThemeFrameTextFieldRoundSmall = 1001, + kHIThemeFrameTextFieldRoundMini = 1002 +}; +#endif // Resolve these at run-time, since the functions was moved in Leopard. typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *); @@ -3629,7 +3644,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter QRect cr = tb->rect; int shiftX = 0; int shiftY = 0; - if (tb->state & (State_Sunken | State_On)) { + bool needText = false; + int alignment = 0; + bool down = tb->state & (State_Sunken | State_On); + if (down) { shiftX = pixelMetric(PM_ButtonShiftHorizontal, tb, w); shiftY = pixelMetric(PM_ButtonShiftVertical, tb, w); } @@ -3637,30 +3655,29 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // The text is a bit bolder and gets a drop shadow and the icons are also darkened. // This doesn't really fit into any particular case in QIcon, so we // do the majority of the work ourselves. - if (tb->state & State_Sunken - && !(tb->features & QStyleOptionToolButton::Arrow)) { + if (!(tb->features & QStyleOptionToolButton::Arrow)) { Qt::ToolButtonStyle tbstyle = tb->toolButtonStyle; if (tb->icon.isNull() && !tb->text.isEmpty()) tbstyle = Qt::ToolButtonTextOnly; switch (tbstyle) { - case Qt::ToolButtonTextOnly: - drawItemText(p, cr, Qt::AlignCenter, tb->palette, - tb->state & State_Enabled, tb->text); - break; + case Qt::ToolButtonTextOnly: { + needText = true; + alignment = Qt::AlignCenter; + break; } case Qt::ToolButtonIconOnly: case Qt::ToolButtonTextBesideIcon: case Qt::ToolButtonTextUnderIcon: { QRect pr = cr; QIcon::Mode iconMode = (tb->state & State_Enabled) ? QIcon::Normal - : QIcon::Disabled; + : QIcon::Disabled; QIcon::State iconState = (tb->state & State_On) ? QIcon::On - : QIcon::Off; + : QIcon::Off; QPixmap pixmap = tb->icon.pixmap(tb->rect.size().boundedTo(tb->iconSize), iconMode, iconState); // Draw the text if it's needed. if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) { - int alignment = 0; + needText = true; if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) { pr.setHeight(pixmap.size().height() + 6); cr.adjust(0, pr.bottom(), 0, -3); @@ -3670,18 +3687,43 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter cr.adjust(pr.right(), 0, 0, 0); alignment |= Qt::AlignLeft | Qt::AlignVCenter; } - cr.translate(shiftX, shiftY); - drawItemText(p, cr, alignment, tb->palette, - tb->state & State_Enabled, tb->text); - cr.adjust(0, 3, 0, -3); // the drop shadow - drawItemText(p, cr, alignment, tb->palette, - tb->state & State_Enabled, tb->text); } - pr.translate(shiftX, shiftY); - pixmap = darkenPixmap(pixmap); + if (down) { + pr.translate(shiftX, shiftY); + pixmap = darkenPixmap(pixmap); + } drawItemPixmap(p, pr, Qt::AlignCenter, pixmap); break; } } + + if (needText) { + QPalette pal = tb->palette; + QPalette::ColorRole role = QPalette::NoRole; + if (down) + cr.translate(shiftX, shiftY); + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 + && (tbstyle == Qt::ToolButtonTextOnly + || (tbstyle != Qt::ToolButtonTextOnly && !down))) { + QPen pen = p->pen(); + QColor light = down ? Qt::black : Qt::white; + light.setAlphaF(0.375f); + p->setPen(light); + p->drawText(cr.adjusted(0, 1, 0, 1), alignment, tb->text); + p->setPen(pen); + if (down && tbstyle == Qt::ToolButtonTextOnly) { + pal = QApplication::palette("QMenu"); + pal.setCurrentColorGroup(tb->palette.currentColorGroup()); + role = QPalette::HighlightedText; + } + } + drawItemText(p, cr, alignment, pal, + tb->state & State_Enabled, tb->text, role); + if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5 && down) { + // Draw a "drop shadow" in earlier versions. + drawItemText(p, cr.adjusted(0, 1, 0, 1), alignment, + tb->palette, tb->state & State_Enabled, tb->text); + } + } } else { QWindowsStyle::drawControl(ce, &myTb, p, w); } @@ -5344,6 +5386,10 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } } break; + case CC_Dial: + if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt)) + QStyleHelper::drawDial(dial, p); + break; default: QWindowsStyle::drawComplexControl(cc, opt, p, widget); break; diff --git a/src/gui/styles/qplastiquestyle.cpp b/src/gui/styles/qplastiquestyle.cpp index 24d7748..66464ff 100644 --- a/src/gui/styles/qplastiquestyle.cpp +++ b/src/gui/styles/qplastiquestyle.cpp @@ -51,6 +51,7 @@ static const int ProgressBarFps = 25; static const int blueFrameWidth = 2; // with of line edit focus frame #include "qwindowsstyle_p.h" +#include <private/qstylehelper_p.h> #include <qapplication.h> #include <qbitmap.h> #include <qabstractitemview.h> @@ -4970,6 +4971,12 @@ void QPlastiqueStyle::drawComplexControl(ComplexControl control, const QStyleOpt painter->restore(); } break; +#ifndef QT_NO_DIAL + case CC_Dial: + if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option)) + QStyleHelper::drawDial(dial, painter); + break; +#endif // QT_NO_DIAL default: QWindowsStyle::drawComplexControl(control, option, painter, widget); break; diff --git a/src/gui/styles/qstyle.h b/src/gui/styles/qstyle.h index 6191d51..c1cbbdd 100644 --- a/src/gui/styles/qstyle.h +++ b/src/gui/styles/qstyle.h @@ -453,6 +453,7 @@ public: SC_MdiNormalButton = 0x00000002, SC_MdiCloseButton = 0x00000004, + SC_CustomBase = 0xf0000000, SC_All = 0xffffffff }; Q_DECLARE_FLAGS(SubControls, SubControl) diff --git a/src/gui/styles/qstylehelper.cpp b/src/gui/styles/qstylehelper.cpp new file mode 100644 index 0000000..3320970 --- /dev/null +++ b/src/gui/styles/qstylehelper.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qstylehelper_p.h" + +#include <qstyleoption.h> +#include <qpainter.h> +#include <qpixmapcache.h> +#include <private/qmath_p.h> +#include <private/qstyle_p.h> +#include <qmath.h> + +QT_BEGIN_NAMESPACE + +const bool QStyleHelper::UsePixmapCache = true; + +QString QStyleHelper::uniqueName(const QString &key, const QStyleOption *option, const QSize &size) +{ + QString tmp; + const QStyleOptionComplex *complexOption = qstyleoption_cast<const QStyleOptionComplex *>(option); + tmp.sprintf("%s-%d-%d-%lld-%dx%d-%d", key.toLatin1().constData(), uint(option->state), + complexOption ? uint(complexOption->activeSubControls) : uint(0), + option->palette.cacheKey(), size.width(), size.height(), option->direction); +#ifndef QT_NO_SPINBOX + if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { + tmp.append(QLatin1Char('-')); + tmp.append(QString::number(spinBox->buttonSymbols)); + tmp.append(QLatin1Char('-')); + tmp.append(QString::number(spinBox->stepEnabled)); + tmp.append(QLatin1Char('-')); + tmp.append(QLatin1Char(spinBox->frame ? '1' : '0')); + } +#endif // QT_NO_SPINBOX + return tmp; +} + +#ifndef QT_NO_DIAL + +int QStyleHelper::calcBigLineSize(int radius) +{ + int bigLineSize = radius / 6; + if (bigLineSize < 4) + bigLineSize = 4; + if (bigLineSize > radius / 2) + bigLineSize = radius / 2; + return bigLineSize; +} + +static QPointF calcRadialPos(const QStyleOptionSlider *dial, qreal offset) +{ + const int width = dial->rect.width(); + const int height = dial->rect.height(); + const int r = qMin(width, height) / 2; + const int currentSliderPosition = dial->upsideDown ? dial->sliderPosition : (dial->maximum - dial->sliderPosition); + qreal a = 0; + if (dial->maximum == dial->minimum) + a = Q_PI / 2; + else if (dial->dialWrapping) + a = Q_PI * 3 / 2 - (currentSliderPosition - dial->minimum) * 2 * Q_PI + / (dial->maximum - dial->minimum); + else + a = (Q_PI * 8 - (currentSliderPosition - dial->minimum) * 10 * Q_PI + / (dial->maximum - dial->minimum)) / 6; + qreal xc = width / 2.0; + qreal yc = height / 2.0; + qreal len = r - QStyleHelper::calcBigLineSize(r) - 3; + qreal back = offset * len; + QPointF pos(QPointF(xc + back * qCos(a), yc - back * qSin(a))); + return pos; +} + +qreal QStyleHelper::angle(const QPointF &p1, const QPointF &p2) +{ + static const qreal rad_factor = 180 / Q_PI; + qreal _angle = 0; + + if (p1.x() == p2.x()) { + if (p1.y() < p2.y()) + _angle = 270; + else + _angle = 90; + } else { + qreal x1, x2, y1, y2; + + if (p1.x() <= p2.x()) { + x1 = p1.x(); y1 = p1.y(); + x2 = p2.x(); y2 = p2.y(); + } else { + x2 = p1.x(); y2 = p1.y(); + x1 = p2.x(); y1 = p2.y(); + } + + qreal m = -(y2 - y1) / (x2 - x1); + _angle = atan(m) * rad_factor; + + if (p1.x() < p2.x()) + _angle = 180 - _angle; + else + _angle = -_angle; + } + return _angle; +} + +QPolygonF QStyleHelper::calcLines(const QStyleOptionSlider *dial) +{ + QPolygonF poly; + int width = dial->rect.width(); + int height = dial->rect.height(); + qreal r = qMin(width, height) / 2; + int bigLineSize = calcBigLineSize(int(r)); + + qreal xc = width / 2 + 0.5; + qreal yc = height / 2 + 0.5; + int ns = dial->tickInterval; + int notches = (dial->maximum + ns - 1 - dial->minimum) / ns; + if (notches <= 0) + return poly; + if (dial->maximum < dial->minimum || dial->maximum - dial->minimum > 1000) { + int maximum = dial->minimum + 1000; + notches = (maximum + ns - 1 - dial->minimum) / ns; + } + + poly.resize(2 + 2 * notches); + int smallLineSize = bigLineSize / 2; + for (int i = 0; i <= notches; ++i) { + qreal angle = dial->dialWrapping ? Q_PI * 3 / 2 - i * 2 * Q_PI / notches + : (Q_PI * 8 - i * 10 * Q_PI / notches) / 6; + qreal s = qSin(angle); + qreal c = qCos(angle); + if (i == 0 || (((ns * i) % (dial->pageStep ? dial->pageStep : 1)) == 0)) { + poly[2 * i] = QPointF(xc + (r - bigLineSize) * c, + yc - (r - bigLineSize) * s); + poly[2 * i + 1] = QPointF(xc + r * c, yc - r * s); + } else { + poly[2 * i] = QPointF(xc + (r - 1 - smallLineSize) * c, + yc - (r - 1 - smallLineSize) * s); + poly[2 * i + 1] = QPointF(xc + (r - 1) * c, yc -(r - 1) * s); + } + } + return poly; +} + + +// This will draw a nice and shiny QDial for us. We don't want +// all the shinyness in QWindowsStyle, hence we place it here + +void QStyleHelper::drawDial(const QStyleOptionSlider *option, QPainter *painter) +{ + QPalette pal = option->palette; + QColor buttonColor = pal.button().color(); + const int width = option->rect.width(); + const int height = option->rect.height(); + const bool enabled = option->state & QStyle::State_Enabled; + qreal r = qMin(width, height) / 2; + r -= r/50; + const qreal penSize = r/20.0; + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + + // Draw notches + if (option->subControls & QStyle::SC_DialTickmarks) { + painter->setPen(option->palette.dark().color().darker(120)); + painter->drawLines(QStyleHelper::calcLines(option)); + } + + // Cache dial background + BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("qdial")); + p->setRenderHint(QPainter::Antialiasing); + + const qreal d_ = r / 6; + const qreal dx = option->rect.x() + d_ + (width - 2 * r) / 2 + 1; + const qreal dy = option->rect.y() + d_ + (height - 2 * r) / 2 + 1; + + QRectF br = QRectF(dx + 0.5, dy + 0.5, + int(r * 2 - 2 * d_ - 2), + int(r * 2 - 2 * d_ - 2)); + buttonColor.setHsv(buttonColor .hue(), + qMin(140, buttonColor .saturation()), + qMax(180, buttonColor.value())); + QColor shadowColor(0, 0, 0, 20); + + if (enabled) { + // Drop shadow + qreal shadowSize = qMax(1.0, penSize/2.0); + QRectF shadowRect= br.adjusted(-2*shadowSize, -2*shadowSize, + 2*shadowSize, 2*shadowSize); + QRadialGradient shadowGradient(shadowRect.center().x(), + shadowRect.center().y(), shadowRect.width()/2.0, + shadowRect.center().x(), shadowRect.center().y()); + shadowGradient.setColorAt(0.91, QColor(0, 0, 0, 40)); + shadowGradient.setColorAt(1.0, Qt::transparent); + p->setBrush(shadowGradient); + p->setPen(Qt::NoPen); + p->translate(shadowSize, shadowSize); + p->drawEllipse(shadowRect); + p->translate(-shadowSize, -shadowSize); + + // Main gradient + QRadialGradient gradient(br.center().x() - br.width()/3, dy, + br.width()*1.3, br.center().x(), + br.center().y() - br.height()/2); + gradient.setColorAt(0, buttonColor.lighter(110)); + gradient.setColorAt(0.5, buttonColor); + gradient.setColorAt(0.501, buttonColor.darker(102)); + gradient.setColorAt(1, buttonColor.darker(115)); + p->setBrush(gradient); + } else { + p->setBrush(Qt::NoBrush); + } + + p->setPen(QPen(buttonColor.darker(280))); + p->drawEllipse(br); + p->setBrush(Qt::NoBrush); + p->setPen(buttonColor.lighter(110)); + p->drawEllipse(br.adjusted(1, 1, -1, -1)); + + if (option->state & QStyle::State_HasFocus) { + QColor highlight = pal.highlight().color(); + highlight.setHsv(highlight.hue(), + qMin(160, highlight.saturation()), + qMax(230, highlight.value())); + highlight.setAlpha(127); + p->setPen(QPen(highlight, 2.0)); + p->setBrush(Qt::NoBrush); + p->drawEllipse(br.adjusted(-1, -1, 1, 1)); + } + + END_STYLE_PIXMAPCACHE + + QPointF dp = calcRadialPos(option, 0.70); + buttonColor = buttonColor.lighter(104); + buttonColor.setAlphaF(0.8); + const qreal ds = r/7.0; + QRectF dialRect(dp.x() - ds, dp.y() - ds, 2*ds, 2*ds); + QRadialGradient dialGradient(dialRect.center().x() + dialRect.width()/2, + dialRect.center().y() + dialRect.width(), + dialRect.width()*2, + dialRect.center().x(), dialRect.center().y()); + dialGradient.setColorAt(1, buttonColor.darker(140)); + dialGradient.setColorAt(0.4, buttonColor.darker(120)); + dialGradient.setColorAt(0, buttonColor.darker(110)); + if (penSize > 3.0) { + painter->setPen(QPen(QColor(0, 0, 0, 25), penSize)); + painter->drawLine(calcRadialPos(option, 0.90), calcRadialPos(option, 0.96)); + } + + painter->setBrush(dialGradient); + painter->setPen(QColor(255, 255, 255, 150)); + painter->drawEllipse(dialRect.adjusted(-1, -1, 1, 1)); + painter->setPen(QColor(0, 0, 0, 80)); + painter->drawEllipse(dialRect); + painter->restore(); +} +#endif //QT_NO_DIAL + +QT_END_NAMESPACE diff --git a/src/gui/styles/qstylehelper_p.h b/src/gui/styles/qstylehelper_p.h new file mode 100644 index 0000000..d9b2e28 --- /dev/null +++ b/src/gui/styles/qstylehelper_p.h @@ -0,0 +1,39 @@ +#include <QtCore/qglobal.h> +#include <QtCore/qpoint.h> +#include <QtGui/qpolygon.h> + +#ifndef QSTYLEHELPER_P_H +#define QSTYLEHELPER_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 + +class QPainter; +class QStyleOptionSlider; +class QStyleOption; + +namespace QStyleHelper +{ + extern const bool UsePixmapCache; + QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size); +#ifndef QT_NO_DIAL + qreal angle(const QPointF &p1, const QPointF &p2); + QPolygonF calcLines(const QStyleOptionSlider *dial); + int calcBigLineSize(int radius); + void drawDial(const QStyleOptionSlider *dial, QPainter *painter); +#endif //QT_NO_DIAL +} + +QT_END_NAMESPACE + +#endif // QSTYLEHELPER_P_H diff --git a/src/gui/styles/qwindowsstyle.cpp b/src/gui/styles/qwindowsstyle.cpp index 00c3f99..bf3a3cb 100644 --- a/src/gui/styles/qwindowsstyle.cpp +++ b/src/gui/styles/qwindowsstyle.cpp @@ -67,6 +67,9 @@ #include "qpixmapcache.h" #include "qwizard.h" #include "qlistview.h" +#include <private/qmath_p.h> +#include <qmath.h> + #ifdef Q_WS_X11 #include "qfileinfo.h" @@ -3177,6 +3180,7 @@ void QWindowsStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComp } break; #endif // QT_NO_SPINBOX + default: QCommonStyle::drawComplexControl(cc, opt, p, widget); } diff --git a/src/gui/styles/qwindowsxpstyle.cpp b/src/gui/styles/qwindowsxpstyle.cpp index 9d735a7..50cd13f 100644 --- a/src/gui/styles/qwindowsxpstyle.cpp +++ b/src/gui/styles/qwindowsxpstyle.cpp @@ -46,6 +46,7 @@ #include <private/qobject_p.h> #include <private/qpaintengine_raster_p.h> #include <private/qapplication_p.h> +#include <private/qstylehelper_p.h> #include <qlibrary.h> #include <qpainter.h> #include <qpaintengine.h> @@ -3164,6 +3165,12 @@ void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionCo } break; #endif //QT_NO_WORKSPACE +#ifndef QT_NO_DIAL + case CC_Dial: + if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option)) + QStyleHelper::drawDial(dial, p); + break; +#endif // QT_NO_DIAL default: QWindowsStyle::drawComplexControl(cc, option, p, widget); break; diff --git a/src/gui/styles/styles.pri b/src/gui/styles/styles.pri index 376f834..2164e1e 100644 --- a/src/gui/styles/styles.pri +++ b/src/gui/styles/styles.pri @@ -7,12 +7,14 @@ HEADERS += \ styles/qstyleplugin.h \ styles/qcommonstylepixmaps_p.h \ styles/qcommonstyle.h \ + styles/qstylehelper_p.h \ styles/qstylesheetstyle_p.h SOURCES += \ styles/qstyle.cpp \ styles/qstylefactory.cpp \ styles/qstyleoption.cpp \ styles/qstyleplugin.cpp \ + styles/qstylehelper.cpp \ styles/qcommonstyle.cpp \ styles/qstylesheetstyle.cpp \ styles/qstylesheetstyle_default.cpp diff --git a/src/gui/text/qfontdatabase_qws.cpp b/src/gui/text/qfontdatabase_qws.cpp index eb8a0cf..f62a6d1 100644 --- a/src/gui/text/qfontdatabase_qws.cpp +++ b/src/gui/text/qfontdatabase_qws.cpp @@ -58,6 +58,7 @@ #include "qabstractfontengine_qws.h" #include "qabstractfontengine_p.h" #include <qdatetime.h> +#include "qplatformdefs.h" // for mmap #include <stdlib.h> @@ -127,7 +128,7 @@ void QFontDatabasePrivate::addQPF2File(const QByteArray &file) struct stat st; if (stat(file.constData(), &st)) return; - int f = ::open(file, O_RDONLY); + int f = ::open(file, O_RDONLY, 0); if (f < 0) return; const uchar *data = (const uchar *)mmap(0, st.st_size, PROT_READ, MAP_SHARED, f, 0); @@ -675,7 +676,7 @@ QFontEngine *loadSingleEngine(int script, const QFontPrivate *fp, qDebug() << "Resource not valid" << size->fileName; } #else - int f = ::open(size->fileName, O_RDONLY); + int f = ::open(size->fileName, O_RDONLY, 0); if (f >= 0) { QFontEngineQPF *fe = new QFontEngineQPF(request, f); if (fe->isValid()) diff --git a/src/gui/text/qfontengine_qpf.cpp b/src/gui/text/qfontengine_qpf.cpp index e9fcac4..b7d1c59 100644 --- a/src/gui/text/qfontengine_qpf.cpp +++ b/src/gui/text/qfontengine_qpf.cpp @@ -252,7 +252,7 @@ QList<QByteArray> QFontEngineQPF::cleanUpAfterClientCrash(const QList<int> &cras for (int i = 0; i < int(dir.count()); ++i) { const QByteArray fileName = QFile::encodeName(dir.absoluteFilePath(dir[i])); - int fd = ::open(fileName.constData(), O_RDONLY); + int fd = ::open(fileName.constData(), O_RDONLY, 0); if (fd >= 0) { void *header = ::mmap(0, sizeof(QFontEngineQPF::Header), PROT_READ, MAP_SHARED, fd, 0); if (header && header != MAP_FAILED) { @@ -331,9 +331,9 @@ QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEng qDebug() << "found existing qpf:" << fileName; #endif if (::access(encodedName, W_OK | R_OK) == 0) - fd = ::open(encodedName, O_RDWR); + fd = ::open(encodedName, O_RDWR, 0); else if (::access(encodedName, R_OK) == 0) - fd = ::open(encodedName, O_RDONLY); + fd = ::open(encodedName, O_RDONLY, 0); } else { #if defined(DEBUG_FONTENGINE) qDebug() << "creating qpf on the fly:" << fileName; @@ -347,7 +347,7 @@ QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEng generator.generate(); buffer.close(); const QByteArray &data = buffer.data(); - ::write(fd, data.constData(), data.size()); + QT_WRITE(fd, data.constData(), data.size()); } } } @@ -893,8 +893,8 @@ void QFontEngineQPF::loadGlyph(glyph_t glyph) g.y = qRound(metrics.y); g.advance = qRound(metrics.xoff); - ::write(fd, &g, sizeof(g)); - ::write(fd, img.bits(), img.numBytes()); + QT_WRITE(fd, &g, sizeof(g)); + QT_WRITE(fd, img.bits(), img.numBytes()); glyphPos = oldSize - glyphDataOffset; #if 0 && defined(DEBUG_FONTENGINE) diff --git a/src/gui/text/qfontengine_qws.cpp b/src/gui/text/qfontengine_qws.cpp index d776329..10aef4c 100644 --- a/src/gui/text/qfontengine_qws.cpp +++ b/src/gui/text/qfontengine_qws.cpp @@ -387,7 +387,7 @@ QFontEngineQPF1::QFontEngineQPF1(const QFontDef&, const QString &fn) { cache_cost = 1; - int f = ::open( QFile::encodeName(fn), O_RDONLY ); + int f = ::open( QFile::encodeName(fn), O_RDONLY, 0); Q_ASSERT(f>=0); QT_STATBUF st; if ( QT_FSTAT( f, &st ) ) diff --git a/src/gui/util/qdesktopservices_mac.cpp b/src/gui/util/qdesktopservices_mac.cpp index 5124068..fdafa1e 100644 --- a/src/gui/util/qdesktopservices_mac.cpp +++ b/src/gui/util/qdesktopservices_mac.cpp @@ -96,7 +96,7 @@ OSType translateLocation(QDesktopServices::StandardLocation type) static bool lsOpen(const QUrl &url) { - if (!url.isValid()) + if (!url.isValid() || url.scheme().isEmpty()) return false; QCFType<CFURLRef> cfUrl = CFURLCreateWithString(0, QCFString(QString::fromLatin1(url.toEncoded())), 0); diff --git a/src/gui/widgets/qabstractbutton.cpp b/src/gui/widgets/qabstractbutton.cpp index 330a7f8..c25253a 100644 --- a/src/gui/widgets/qabstractbutton.cpp +++ b/src/gui/widgets/qabstractbutton.cpp @@ -1236,7 +1236,9 @@ void QAbstractButton::timerEvent(QTimerEvent *e) d->repeatTimer.start(d->autoRepeatInterval, this); if (d->down) { QPointer<QAbstractButton> guard(this); - d->emitReleased(); + nextCheckState(); + if (guard) + d->emitReleased(); if (guard) d->emitClicked(); if (guard) diff --git a/src/gui/widgets/qcommandlinkbutton.cpp b/src/gui/widgets/qcommandlinkbutton.cpp index 13ee6af..92d89a1 100644 --- a/src/gui/widgets/qcommandlinkbutton.cpp +++ b/src/gui/widgets/qcommandlinkbutton.cpp @@ -140,10 +140,12 @@ QFont QCommandLinkButtonPrivate::titleFont() const Q_Q(const QCommandLinkButton); QFont font = q->font(); if (usingVistaStyle()) { - font.setPointSizeF(12.0); + if (!q->testAttribute(Qt::WA_SetFont)) + font.setPointSizeF(12.0); } else { font.setBold(true); - font.setPointSizeF(9.0); + if (!q->testAttribute(Qt::WA_SetFont)) + font.setPointSizeF(9.0); } return font; } @@ -152,7 +154,8 @@ QFont QCommandLinkButtonPrivate::descriptionFont() const { Q_Q(const QCommandLinkButton); QFont font = q->font(); - font.setPointSizeF(9.0); + if (!q->testAttribute(Qt::WA_SetFont)) + font.setPointSizeF(9.0); return font; } diff --git a/src/gui/widgets/qdatetimeedit.cpp b/src/gui/widgets/qdatetimeedit.cpp index 83bec68..e7afea0 100644 --- a/src/gui/widgets/qdatetimeedit.cpp +++ b/src/gui/widgets/qdatetimeedit.cpp @@ -220,6 +220,9 @@ QDateTimeEdit::QDateTimeEdit(const QVariant &var, QVariant::Type parserType, QWi \property QDateTimeEdit::dateTime \brief the QDateTime that is set in the QDateTimeEdit + When setting this property the timespec of the QDateTimeEdit remains the same + and the timespec of the new QDateTime is ignored. + By default, this property contains a date that refers to January 1, 2000 and a time of 00:00:00 and 0 milliseconds. @@ -239,7 +242,7 @@ void QDateTimeEdit::setDateTime(const QDateTime &datetime) d->clearCache(); if (!(d->sections & DateSections_Mask)) setDateRange(datetime.date(), datetime.date()); - d->setValue(QVariant(datetime), EmitIfChanged); + d->setValue(QDateTime(datetime.date(), datetime.time(), d->spec), EmitIfChanged); } } @@ -932,9 +935,6 @@ void QDateTimeEdit::setCalendarPopup(bool enable) \property QDateTimeEdit::timeSpec \brief the current timespec used by the date time edit. \since 4.4 - - All dates/passed to the date time edit will be converted to this - timespec. */ Qt::TimeSpec QDateTimeEdit::timeSpec() const diff --git a/src/gui/widgets/qgroupbox.cpp b/src/gui/widgets/qgroupbox.cpp index 6a82483..876c5d6 100644 --- a/src/gui/widgets/qgroupbox.cpp +++ b/src/gui/widgets/qgroupbox.cpp @@ -644,15 +644,11 @@ bool QGroupBox::isChecked() const void QGroupBox::setChecked(bool b) { Q_D(QGroupBox); - if (d->checkable) { - if (d->checked != b) - update(); - bool wasToggled = (b != d->checked); + if (d->checkable && b != d->checked) { + update(); d->checked = b; - if (wasToggled) { - d->_q_setChildrenEnabled(b); - emit toggled(b); - } + d->_q_setChildrenEnabled(b); + emit toggled(b); } } diff --git a/src/gui/widgets/qlcdnumber.cpp b/src/gui/widgets/qlcdnumber.cpp index 0136f1a..9d98dbc 100644 --- a/src/gui/widgets/qlcdnumber.cpp +++ b/src/gui/widgets/qlcdnumber.cpp @@ -403,7 +403,7 @@ QLCDNumber::QLCDNumber(QWidget *parent) Constructs an LCD number, sets the number of digits to \a numDigits, the base to decimal, the decimal point mode to 'small' and the frame style to a raised box. The segmentStyle() is set to - \c Outline. + \c Filled. The \a parent argument is passed to the QFrame constructor. @@ -427,7 +427,7 @@ void QLCDNumberPrivate::init() base = QLCDNumber::Dec; smallPoint = false; q->setNumDigits(ndigits); - q->setSegmentStyle(QLCDNumber::Outline); + q->setSegmentStyle(QLCDNumber::Filled); q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); } @@ -756,6 +756,10 @@ void QLCDNumber::paintEvent(QPaintEvent *) Q_D(QLCDNumber); QPainter p(this); drawFrame(&p); + p.setRenderHint(QPainter::Antialiasing); + if (d->shadow) + p.translate(0.5, 0.5); + if (d->smallPoint) d->drawString(d->digitStr, p, &d->points, false); else @@ -1070,7 +1074,7 @@ void QLCDNumberPrivate::drawSegment(const QPoint &pos, char segmentNo, QPainter q->objectName().toLocal8Bit().constData(), segmentNo); } // End exact copy - p.setPen(fgColor); + p.setPen(Qt::NoPen); p.setBrush(fgColor); p.drawPolygon(a); p.setBrush(Qt::NoBrush); @@ -1218,8 +1222,8 @@ void QLCDNumberPrivate::drawSegment(const QPoint &pos, char segmentNo, QPainter \header \i Style \i Result \row \i \c Outline \i Produces raised segments filled with the background color - (this is the default). \row \i \c Filled + (this is the default). \i Produces raised segments filled with the foreground color. \row \i \c Flat \i Produces flat segments filled with the foreground color. diff --git a/src/gui/widgets/qlineedit.cpp b/src/gui/widgets/qlineedit.cpp index b03df9e..e243ad0 100644 --- a/src/gui/widgets/qlineedit.cpp +++ b/src/gui/widgets/qlineedit.cpp @@ -3523,6 +3523,8 @@ void QLineEditPrivate::redo() { case RemoveSelection: case DeleteSelection: text.remove(cmd.pos, 1); + selstart = cmd.selStart; + selend = cmd.selEnd; cursor = cmd.pos; break; case Separator: diff --git a/src/gui/widgets/qsplitter.cpp b/src/gui/widgets/qsplitter.cpp index bf8af35..06774bc 100644 --- a/src/gui/widgets/qsplitter.cpp +++ b/src/gui/widgets/qsplitter.cpp @@ -119,7 +119,6 @@ QSplitterHandle::QSplitterHandle(Qt::Orientation orientation, QSplitter *parent) { Q_D(QSplitterHandle); d->s = parent; - d->hover = false; setOrientation(orientation); } @@ -269,8 +268,11 @@ void QSplitterHandle::mouseMoveEvent(QMouseEvent *e) void QSplitterHandle::mousePressEvent(QMouseEvent *e) { Q_D(QSplitterHandle); - if (e->button() == Qt::LeftButton) + if (e->button() == Qt::LeftButton) { d->mouseOffset = d->pick(e->pos()); + d->pressed = true; + update(); + } } /*! @@ -285,6 +287,10 @@ void QSplitterHandle::mouseReleaseEvent(QMouseEvent *e) d->s->setRubberBand(-1); moveSplitter(pos); } + if (e->button() == Qt::LeftButton) { + d->pressed = false; + update(); + } } /*! @@ -303,6 +309,8 @@ void QSplitterHandle::paintEvent(QPaintEvent *) opt.state = QStyle::State_None; if (d->hover) opt.state |= QStyle::State_MouseOver; + if (d->pressed) + opt.state |= QStyle::State_Sunken; if (isEnabled()) opt.state |= QStyle::State_Enabled; parentWidget()->style()->drawControl(QStyle::CE_Splitter, &opt, &p, d->s); diff --git a/src/gui/widgets/qsplitter_p.h b/src/gui/widgets/qsplitter_p.h index 5cc43af..9f6fe0c 100644 --- a/src/gui/widgets/qsplitter_p.h +++ b/src/gui/widgets/qsplitter_p.h @@ -131,16 +131,17 @@ class QSplitterHandlePrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QSplitterHandle) public: - QSplitterHandlePrivate() : orient(Qt::Horizontal), opaq(false), s(0), mouseOffset(0) {} + QSplitterHandlePrivate() : s(0), orient(Qt::Horizontal), mouseOffset(0), opaq(false), hover(false), pressed(false) {} inline int pick(const QPoint &pos) const { return orient == Qt::Horizontal ? pos.x() : pos.y(); } - Qt::Orientation orient; - bool opaq; QSplitter *s; - bool hover; + Qt::Orientation orient; int mouseOffset; + bool opaq : 1; + bool hover : 1; + bool pressed : 1; }; QT_END_NAMESPACE diff --git a/src/gui/widgets/qtoolbutton.cpp b/src/gui/widgets/qtoolbutton.cpp index 7390d04..20b84ef 100644 --- a/src/gui/widgets/qtoolbutton.cpp +++ b/src/gui/widgets/qtoolbutton.cpp @@ -859,8 +859,7 @@ void QToolButtonPrivate::_q_buttonPressed() Q_Q(QToolButton); if (!hasMenu()) return; // no menu to show - - if (delay > 0 && popupMode == QToolButton::DelayedPopup) + if (delay > 0 && !popupTimer.isActive() && popupMode == QToolButton::DelayedPopup) popupTimer.start(delay, q); else if (delay == 0 || popupMode == QToolButton::InstantPopup) q->showMenu(); diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp index fa0fccb..e472b9d 100644 --- a/src/network/access/qnetworkdiskcache.cpp +++ b/src/network/access/qnetworkdiskcache.cpp @@ -41,6 +41,8 @@ //#define QNETWORKDISKCACHE_DEBUG +#ifndef QT_NO_NETWORKDISKCACHE + #include "qnetworkdiskcache.h" #include "qnetworkdiskcache_p.h" @@ -664,3 +666,5 @@ bool QCacheItem::read(QFile *device, bool readData) } QT_END_NAMESPACE + +#endif // QT_NO_NETWORKDISKCACHE diff --git a/src/network/access/qnetworkdiskcache.h b/src/network/access/qnetworkdiskcache.h index ca4bb94..78e3f6b 100644 --- a/src/network/access/qnetworkdiskcache.h +++ b/src/network/access/qnetworkdiskcache.h @@ -50,6 +50,8 @@ QT_BEGIN_NAMESPACE QT_MODULE(Network) +#ifndef QT_NO_NETWORKDISKCACHE + class QNetworkDiskCachePrivate; class Q_NETWORK_EXPORT QNetworkDiskCache : public QAbstractNetworkCache { @@ -86,9 +88,10 @@ private: Q_DISABLE_COPY(QNetworkDiskCache) }; +#endif // QT_NO_NETWORKDISKCACHE + QT_END_NAMESPACE QT_END_HEADER #endif // QNETWORKDISKCACHE_H - diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index ea64042..39ac5da 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -397,6 +397,36 @@ void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, O } /*! + \since 4.6 + \overload + + In addition to the original behaviour of connectToHostEncrypted, + this overloaded method enables the usage of a different hostname + (\a sslPeerName) for the certificate validation instead of + the one used for the TCP connection (\a hostName). + + \sa connectToHostEncrypted() +*/ +void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, + const QString &sslPeerName, OpenMode mode) +{ + Q_D(QSslSocket); + if (d->state == ConnectedState || d->state == ConnectingState) { + qWarning("QSslSocket::connectToHostEncrypted() called when already connecting/connected"); + return; + } + + d->init(); + d->autoStartHandshake = true; + d->initialized = true; + d->verificationPeerName = sslPeerName; + + // Note: When connecting to localhost, some platforms (e.g., HP-UX and some BSDs) + // establish the connection immediately (i.e., first attempt). + connectToHost(hostName, port, mode); +} + +/*! Initializes QSslSocket with the native socket descriptor \a socketDescriptor. Returns true if \a socketDescriptor is accepted as a valid socket descriptor; otherwise returns false. diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h index b8db654..e4c683a 100644 --- a/src/network/ssl/qsslsocket.h +++ b/src/network/ssl/qsslsocket.h @@ -86,6 +86,7 @@ public: // Autostarting the SSL client handshake. void connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode = ReadWrite); + void connectToHostEncrypted(const QString &hostName, quint16 port, const QString &sslPeerName, OpenMode mode = ReadWrite); bool setSocketDescriptor(int socketDescriptor, SocketState state = ConnectedState, OpenMode openMode = ReadWrite); diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 6f8cf42..827f461 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -815,7 +815,7 @@ bool QSslSocketBackendPrivate::testConnection() // but only if we're a client connecting to a server // if we're the server, don't check CN if (mode == QSslSocket::SslClientMode) { - QString peerName = q->peerName(); + QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); QString commonName = configuration.peerCertificate.subjectInfo(QSslCertificate::CommonName); QRegExp regexp(commonName, Qt::CaseInsensitive, QRegExp::Wildcard); diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 825df46..69d3cf3 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -88,6 +88,10 @@ public: QSslConfigurationPrivate configuration; QList<QSslError> sslErrors; + // if set, this hostname is used for certificate validation instead of the hostname + // that was used for connecting to. + QString verificationPeerName; + static bool ensureInitialized(); static void deinitialize(); static QList<QSslCipher> defaultCiphers(); diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 2d90342..4c152e2 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -1297,6 +1297,7 @@ void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format) max_texture_size = -1; version_flags_cached = false; version_flags = QGLFormat::OpenGL_Version_None; + current_fbo = 0; } QGLContext* QGLContext::currentCtx = 0; diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index 01b1d6f..32fbce2 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -371,8 +371,8 @@ private: friend class QMacGLWindowChangeEvent; friend QGLContextPrivate *qt_phonon_get_dptr(const QGLContext *); #endif -#ifdef Q_WS_WIN friend class QGLFramebufferObject; +#ifdef Q_WS_WIN friend class QGLFramebufferObjectPrivate; friend bool qt_resolve_GLSL_functions(QGLContext *ctx); friend bool qt_createGLSLProgram(QGLContext *ctx, GLuint &program, const char *shader_src, GLuint &shader); diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index b8bbeaf..b15eebc 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -254,6 +254,8 @@ public: QGLExtensionFuncs extensionFuncs; GLint max_texture_size; + GLuint current_fbo; + #ifdef Q_WS_WIN static inline QGLExtensionFuncs& qt_get_extension_funcs(const QGLContext *ctx) { return ctx->d_ptr->extensionFuncs; } #endif @@ -267,7 +269,7 @@ public: }; // ### make QGLContext a QObject in 5.0 and remove the proxy stuff -class QGLSignalProxy : public QObject +class Q_OPENGL_EXPORT QGLSignalProxy : public QObject { Q_OBJECT public: diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp index fb22272..8524dfa 100644 --- a/src/opengl/qglframebufferobject.cpp +++ b/src/opengl/qglframebufferobject.cpp @@ -66,7 +66,7 @@ extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool); class QGLFramebufferObjectPrivate { public: - QGLFramebufferObjectPrivate() : depth_stencil_buffer(0), valid(false), bound(false), ctx(0) {} + QGLFramebufferObjectPrivate() : depth_stencil_buffer(0), valid(false), bound(false), ctx(0), previous_fbo(0) {} ~QGLFramebufferObjectPrivate() {} void init(const QSize& sz, QGLFramebufferObject::Attachment attachment, @@ -81,6 +81,7 @@ public: uint bound : 1; QGLFramebufferObject::Attachment fbo_attachment; QGLContext *ctx; // for Windows extension ptrs + GLuint previous_fbo; }; bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const @@ -469,6 +470,15 @@ bool QGLFramebufferObject::isValid() const Switches rendering from the default, windowing system provided framebuffer to this framebuffer object. Returns true upon success, false otherwise. + + Since 4.6: if another QGLFramebufferObject instance was already bound + to the current context, then its handle() will be remembered and + automatically restored when release() is called. This allows multiple + framebuffer rendering targets to be stacked up. It is important that + release() is called on the stacked framebuffer objects in the reverse + order of the calls to bind(). + + \sa release() */ bool QGLFramebufferObject::bind() { @@ -478,6 +488,12 @@ bool QGLFramebufferObject::bind() QGL_FUNC_CONTEXT; glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, d->fbo); d->bound = d->valid = d->checkFramebufferStatus(); + const QGLContext *context = QGLContext::currentContext(); + if (d->valid && context) { + // Save the previous setting to automatically restore in release(). + d->previous_fbo = context->d_ptr->current_fbo; + context->d_ptr->current_fbo = d->fbo; + } return d->valid; } @@ -487,6 +503,12 @@ bool QGLFramebufferObject::bind() Switches rendering back to the default, windowing system provided framebuffer. Returns true upon success, false otherwise. + + Since 4.6: if another QGLFramebufferObject instance was already bound + to the current context when bind() was called, then this function will + automatically re-bind it to the current context. + + \sa bind() */ bool QGLFramebufferObject::release() { @@ -497,6 +519,14 @@ bool QGLFramebufferObject::release() glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); d->valid = d->checkFramebufferStatus(); d->bound = false; + const QGLContext *context = QGLContext::currentContext(); + if (d->valid && context) { + // Restore the previous setting for stacked framebuffer objects. + context->d_ptr->current_fbo = d->previous_fbo; + if (d->previous_fbo) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, d->previous_fbo); + d->previous_fbo = 0; + } return d->valid; } diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index 5d668cd..b079557 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -205,6 +205,14 @@ void QGLPixmapData::fromImage(const QImage &image, m_dirty = true; } +bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect) +{ + Q_UNUSED(dx); + Q_UNUSED(dy); + Q_UNUSED(rect); + return false; +} + void QGLPixmapData::fill(const QColor &color) { if (!isValid()) diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h index 63703fd..e450f01 100644 --- a/src/opengl/qpixmapdata_gl_p.h +++ b/src/opengl/qpixmapdata_gl_p.h @@ -73,6 +73,8 @@ public: void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + bool scroll(int dx, int dy, const QRect &rect); + void fill(const QColor &color); bool hasAlphaChannel() const; QImage toImage() const; diff --git a/src/plugins/kbddrivers/kbddrivers.pro b/src/plugins/kbddrivers/kbddrivers.pro index 6605972..a34b780 100644 --- a/src/plugins/kbddrivers/kbddrivers.pro +++ b/src/plugins/kbddrivers/kbddrivers.pro @@ -1,6 +1,5 @@ TEMPLATE = subdirs -contains(kbd-plugins, usb): SUBDIRS += usb +contains(kbd-plugins, linuxinput): SUBDIRS += linuxinput contains(kbd-plugins, sl5000): SUBDIRS += sl5000 contains(kbd-plugins, vr41xx): SUBDIRS += vr41xx contains(kbd-plugins, yopy): SUBDIRS += yopy -contains(kbd-plugins, linuxis): SUBDIRS += linuxis diff --git a/src/plugins/kbddrivers/linuxinput/linuxinput.pro b/src/plugins/kbddrivers/linuxinput/linuxinput.pro new file mode 100644 index 0000000..862a220 --- /dev/null +++ b/src/plugins/kbddrivers/linuxinput/linuxinput.pro @@ -0,0 +1,14 @@ +TARGET = qlinuxinputkbddriver +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/kbddrivers +target.path = $$[QT_INSTALL_PLUGINS]/kbddrivers +INSTALLS += target + +DEFINES += QT_QWS_KBD_LINUXINPUT + +HEADERS = $$QT_SOURCE_TREE/src/gui/embedded/qkbdlinuxinput_qws.h + +SOURCES = main.cpp \ + $$QT_SOURCE_TREE/src/gui/embedded/qkbdlinuxinput_qws.cpp + diff --git a/src/plugins/kbddrivers/usb/main.cpp b/src/plugins/kbddrivers/linuxinput/main.cpp index 1d6ab89..45d06c4 100644 --- a/src/plugins/kbddrivers/usb/main.cpp +++ b/src/plugins/kbddrivers/linuxinput/main.cpp @@ -40,38 +40,38 @@ ****************************************************************************/ #include <qkbddriverplugin_qws.h> -#include <qkbdusb_qws.h> +#include <qkbdlinuxinput_qws.h> QT_BEGIN_NAMESPACE -class QUsbKbdDriver : public QKbdDriverPlugin +class QLinuxInputKbdDriver : public QKbdDriverPlugin { public: - QUsbKbdDriver(); + QLinuxInputKbdDriver(); QStringList keys() const; QWSKeyboardHandler* create(const QString &driver, const QString &device); }; -QUsbKbdDriver::QUsbKbdDriver() +QLinuxInputKbdDriver::QLinuxInputKbdDriver() : QKbdDriverPlugin() { } -QStringList QUsbKbdDriver::keys() const +QStringList QLinuxInputKbdDriver::keys() const { - return (QStringList() << QLatin1String("Usb")); + return (QStringList() << QLatin1String("LinuxInput")); } -QWSKeyboardHandler* QUsbKbdDriver::create(const QString &driver, - const QString &device) +QWSKeyboardHandler* QLinuxInputKbdDriver::create(const QString &driver, + const QString &device) { Q_UNUSED(device); - if (driver.compare(QLatin1String("Usb"), Qt::CaseInsensitive)) + if (driver.compare(QLatin1String("LinuxInput"), Qt::CaseInsensitive)) return 0; - return new QWSUsbKeyboardHandler(driver); + return new QWSLinuxInputKeyboardHandler(driver, device); } -Q_EXPORT_PLUGIN2(qwsusbkbddriver, QUsbKbdDriver) +Q_EXPORT_PLUGIN2(qwslinuxinputkbddriver, QLinuxInputKbdDriver) QT_END_NAMESPACE diff --git a/src/plugins/kbddrivers/linuxis/README b/src/plugins/kbddrivers/linuxis/README deleted file mode 100644 index 37a9a89..0000000 --- a/src/plugins/kbddrivers/linuxis/README +++ /dev/null @@ -1 +0,0 @@ -This is a keypad/only keyboard driver based on the Linux input subsystem. diff --git a/src/plugins/kbddrivers/linuxis/linuxis.pro b/src/plugins/kbddrivers/linuxis/linuxis.pro deleted file mode 100644 index 5e652b5..0000000 --- a/src/plugins/kbddrivers/linuxis/linuxis.pro +++ /dev/null @@ -1,11 +0,0 @@ -TARGET = linuxiskbdhandler -include(../../qpluginbase.pri) - -QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/kbddrivers -target.path = $$[QT_INSTALL_PLUGINS]/kbddrivers -INSTALLS += target - -CONFIG+=no_tr - -HEADERS = linuxiskbddriverplugin.h linuxiskbdhandler.h -SOURCES = linuxiskbddriverplugin.cpp linuxiskbdhandler.cpp diff --git a/src/plugins/kbddrivers/linuxis/linuxiskbdhandler.cpp b/src/plugins/kbddrivers/linuxis/linuxiskbdhandler.cpp deleted file mode 100644 index 99b98b7..0000000 --- a/src/plugins/kbddrivers/linuxis/linuxiskbdhandler.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (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 either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** 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.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "linuxiskbdhandler.h" - -#include <QSocketNotifier> - -#include <stdlib.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> -#include <string.h> - -#include <linux/input.h> -#include <linux/kd.h> - -#include <qdebug.h> -#if 1 -#define qLog(x) qDebug() -#else -#define qLog(x) while (0) qDebug() -#endif - -struct LinuxInputSubsystemKbdHandler::keytable_s LinuxInputSubsystemKbdHandler::keytable[] = { - { KEY_PAGEDOWN, 0xffff, Qt::Key_Context1 }, - { KEY_END, 0xffff, Qt::Key_Back }, - { KEY_RIGHTCTRL, 0xffff, Qt::Key_Home }, - { KEY_SPACE, 0xffff, Qt::Key_Menu }, - { KEY_ENTER, 0xffff, Qt::Key_Select }, - { KEY_UP, 0xffff, Qt::Key_Up }, - { KEY_LEFT, 0xffff, Qt::Key_Left }, - { KEY_RIGHT, 0xffff, Qt::Key_Right }, - { KEY_DOWN, 0xffff, Qt::Key_Down }, - { KEY_POWER, 0xffff, Qt::Key_Call }, - { KEY_BACKSPACE, 0xffff, Qt::Key_Backspace }, - { KEY_F1, 0xffff, Qt::Key_Hangup }, - { KEY_KP1, '1', Qt::Key_1 }, - { KEY_KP2, '2', Qt::Key_2 }, - { KEY_KP3, '3', Qt::Key_3 }, - { KEY_KP4, '4', Qt::Key_4 }, - { KEY_KP5, '5', Qt::Key_5 }, - { KEY_KP6, '6', Qt::Key_6 }, - { KEY_KP7, '7', Qt::Key_7 }, - { KEY_KP8, '8', Qt::Key_8 }, - { KEY_KP9, '9', Qt::Key_9 }, - { KEY_KP0, '0', Qt::Key_0 }, - { KEY_APOSTROPHE, '*', Qt::Key_Asterisk }, - { KEY_3, '#', Qt::Key_NumberSign }, - { KEY_F2, 0xffff, Qt::Key_F2 }, - { KEY_F3, 0xffff, Qt::Key_F3 }, - { KEY_F4, 0xffff, Qt::Key_F4 }, - { KEY_F5, 0xffff, Qt::Key_F5 }, - { KEY_F6, 0xffff, Qt::Key_F6 }, - { KEY_F7, 0xffff, Qt::Key_VolumeUp }, - { KEY_F8, 0xffff, Qt::Key_VolumeDown }, - { KEY_F9, 0xffff, Qt::Key_F9 }, - { 0, 0, Qt::Key_unknown }, -}; - -struct LinuxInputSubsystemKbdHandler::keymap_s LinuxInputSubsystemKbdHandler::keymap[KEY_MAX]; - -LinuxInputSubsystemKbdHandler::LinuxInputSubsystemKbdHandler(const QString &device) -{ - qLog(Input) << "Loaded LinuxInputSubsystem keypad plugin!"; - setObjectName( "LinuxInputSubsystem Keypad Handler" ); - kbdFD = ::open(device.toLocal8Bit().constData(), O_RDONLY, 0); - if (kbdFD >= 0) { - qLog(Input) << "Opened" << device << "as keypad input"; -#if 0 - struct kbd_repeat kbdrep; - kbdrep.delay = 500; /* ms */ - kbdrep.period = 250; /* ms */ - ioctl(kbdFD, KDKBDREP, &kbdrep); -#endif - m_notify = new QSocketNotifier( kbdFD, QSocketNotifier::Read, this ); - connect( m_notify, SIGNAL(activated(int)), this, SLOT(readKbdData())); - } else { - qWarning("Cannot open '%s' for keypad (%s)", - device.toLocal8Bit().constData(), strerror(errno)); - return; - } - shift = false; - - initmap(); -} - -LinuxInputSubsystemKbdHandler::~LinuxInputSubsystemKbdHandler() -{ -} - -void LinuxInputSubsystemKbdHandler::initmap() -{ - for (int i = 0; i < KEY_MAX; i++) { - keymap[i].unicode = 0xffff; - keymap[i].keycode = Qt::Key_unknown; - } - for (int i = 0; keytable[i].unicode; i++) { - int idx = keytable[i].code; - keymap[idx].unicode = keytable[i].unicode; - keymap[idx].keycode = keytable[i].keycode; - } -} - -void LinuxInputSubsystemKbdHandler::readKbdData() -{ - struct input_event *ie; - struct input_event iebuf[32]; - - uint n = ::read(kbdFD, iebuf, sizeof(iebuf)); - - bool pressed; - bool autorepeat; - int modifiers = 0; - int unicode, keycode; - - n /= sizeof(struct input_event); - ie = iebuf; - for (uint i = 0; i < n; i++) { - - pressed = ie->value != 0; - autorepeat = ie->value == 2; - qLog() << "keyEvent" << hex << ie->type << ie->code << ie->value; - unicode = keymap[ie->code].unicode; - keycode = keymap[ie->code].keycode; - - processKeyEvent(unicode, keycode, (Qt::KeyboardModifiers)modifiers, - pressed, autorepeat); - - ie++; - } - -} - diff --git a/src/plugins/kbddrivers/usb/usb.pro b/src/plugins/kbddrivers/usb/usb.pro deleted file mode 100644 index 4187255..0000000 --- a/src/plugins/kbddrivers/usb/usb.pro +++ /dev/null @@ -1,14 +0,0 @@ -TARGET = qusbkbddriver -include(../../qpluginbase.pri) - -QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/kbddrivers -target.path = $$[QT_INSTALL_PLUGINS]/kbddrivers -INSTALLS += target - -DEFINES += QT_QWS_KBD_USB - -HEADERS = $$QT_SOURCE_TREE/src/gui/embedded/qkbdusb_qws.h - -SOURCES = main.cpp \ - $$QT_SOURCE_TREE/src/gui/embedded/qkbdusb_qws.cpp - diff --git a/src/qbase.pri b/src/qbase.pri index e9da61d..9ba8e70 100644 --- a/src/qbase.pri +++ b/src/qbase.pri @@ -4,7 +4,7 @@ INCLUDEPATH *= $$QMAKE_INCDIR_QT/$$TARGET #just for today to have some compat isEmpty(QT_ARCH):!isEmpty(ARCH):QT_ARCH=$$ARCH #another compat that will rot for change #215700 TEMPLATE = lib isEmpty(QT_MAJOR_VERSION) { - VERSION=4.5.1 + VERSION=4.6.0 } else { VERSION=$${QT_MAJOR_VERSION}.$${QT_MINOR_VERSION}.$${QT_PATCH_VERSION} } diff --git a/src/qt3support/dialogs/q3filedialog.cpp b/src/qt3support/dialogs/q3filedialog.cpp index 4874b6d..85f81b3 100644 --- a/src/qt3support/dialogs/q3filedialog.cpp +++ b/src/qt3support/dialogs/q3filedialog.cpp @@ -4606,11 +4606,11 @@ void Q3FileDialog::setPreviewMode(PreviewMode m) } Q3FileDialog::PreviewMode Q3FileDialog::previewMode() const { - if (d->infoPreview && d->infoPreviewWidget->isVisible()) + if (d->infoPreview && d->infoPreviewWidget->isVisibleTo(const_cast<Q3FileDialog *>(this))) return Info; - else if (d->contentsPreview && d->contentsPreviewWidget->isVisible()) + else if (d->contentsPreview + && d->contentsPreviewWidget->isVisibleTo(const_cast<Q3FileDialog *>(this))) return Contents; - return NoPreview; } diff --git a/src/qt3support/dialogs/q3tabdialog.cpp b/src/qt3support/dialogs/q3tabdialog.cpp index 6242dce..52086a2 100644 --- a/src/qt3support/dialogs/q3tabdialog.cpp +++ b/src/qt3support/dialogs/q3tabdialog.cpp @@ -49,6 +49,7 @@ #include "qapplication.h" #include "q3widgetstack.h" #include "qlayout.h" +#include "qevent.h" QT_BEGIN_NAMESPACE @@ -961,6 +962,16 @@ void Q3TabDialog::paintEvent(QPaintEvent *) } +/*!\reimp +*/ +void Q3TabDialog::showEvent(QShowEvent *e) +{ + if (!e->spontaneous()) + show(); + QDialog::showEvent(e); +} + + /*! Adds an OK button to the dialog and sets the button's text to \a text. diff --git a/src/qt3support/dialogs/q3tabdialog.h b/src/qt3support/dialogs/q3tabdialog.h index 3645df1..30acc7d 100644 --- a/src/qt3support/dialogs/q3tabdialog.h +++ b/src/qt3support/dialogs/q3tabdialog.h @@ -111,6 +111,7 @@ public: protected: void paintEvent(QPaintEvent *); void resizeEvent(QResizeEvent *); + void showEvent(QShowEvent *); void styleChange(QStyle&); void setTabBar(QTabBar*); QTabBar* tabBar() const; diff --git a/src/testlib/qabstracttestlogger.cpp b/src/testlib/qabstracttestlogger.cpp index e5d5d59..a884641 100644 --- a/src/testlib/qabstracttestlogger.cpp +++ b/src/testlib/qabstracttestlogger.cpp @@ -77,6 +77,7 @@ bool QAbstractTestLogger::isTtyOutput() #endif } + void QAbstractTestLogger::startLogging() { QTEST_ASSERT(!QTest::stream); diff --git a/src/testlib/qabstracttestlogger_p.h b/src/testlib/qabstracttestlogger_p.h index 298fbad..a550b3c 100644 --- a/src/testlib/qabstracttestlogger_p.h +++ b/src/testlib/qabstracttestlogger_p.h @@ -1,4 +1,4 @@ -/**************************************************************************** + /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) diff --git a/src/testlib/qbenchmarkvalgrind.cpp b/src/testlib/qbenchmarkvalgrind.cpp index 4b4ccd7..bcce147 100644 --- a/src/testlib/qbenchmarkvalgrind.cpp +++ b/src/testlib/qbenchmarkvalgrind.cpp @@ -1,4 +1,3 @@ - /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). @@ -52,6 +51,8 @@ #include <QtCore/qset.h> #include "3rdparty/callgrind_p.h" +QT_BEGIN_NAMESPACE + // Returns true iff a sufficiently recent valgrind is available. bool QBenchmarkValgrindUtils::haveValgrind() { @@ -272,4 +273,6 @@ QString QBenchmarkCallgrindMeasurer::metricText() return QLatin1String("callgrind"); } +QT_END_NAMESPACE + #endif // QTESTLIB_USE_VALGRIND diff --git a/src/testlib/qtestbasicstreamer.cpp b/src/testlib/qtestbasicstreamer.cpp new file mode 100644 index 0000000..b133aae --- /dev/null +++ b/src/testlib/qtestbasicstreamer.cpp @@ -0,0 +1,178 @@ +#include "qtestbasicstreamer.h" +#include "qtestlogger_p.h" +#include "qtestelement.h" +#include "qtestelementattribute.h" +#include "QtTest/private/qtestlog_p.h" +#include "qtestassert.h" + +#include <stdio.h> +#include <stdlib.h> + +#ifndef Q_OS_WIN +#include <unistd.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace QTest +{ + static FILE *stream = 0; +} + +QTestBasicStreamer::QTestBasicStreamer() + :testLogger(0) +{ +} + +QTestBasicStreamer::~QTestBasicStreamer() +{} + +void QTestBasicStreamer::formatStart(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted ) + return; + QTest::qt_snprintf(formatted, 10, ""); +} + +void QTestBasicStreamer::formatEnd(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted ) + return; + QTest::qt_snprintf(formatted, 10, ""); +} + +void QTestBasicStreamer::formatBeforeAttributes(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted ) + return; + QTest::qt_snprintf(formatted, 10, ""); +} + +void QTestBasicStreamer::formatAfterAttributes(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted ) + return; + QTest::qt_snprintf(formatted, 10, ""); +} + +void QTestBasicStreamer::formatAttributes(const QTestElementAttribute *attribute, char *formatted) const +{ + if(!attribute || !formatted ) + return; + QTest::qt_snprintf(formatted, 10, ""); +} + +void QTestBasicStreamer::output(QTestElement *element) const +{ + if(!element) + return; + + outputElements(element); +} + +void QTestBasicStreamer::outputElements(QTestElement *element, bool) const +{ + char buf[1024]; + bool hasChildren; + /* + Elements are in reverse order of occurrence, so start from the end and work + our way backwards. + */ + while (element && element->nextElement()) { + element = element->nextElement(); + } + while (element) { + hasChildren = element->childElements(); + + formatStart(element, buf); + outputString(buf); + + formatBeforeAttributes(element, buf); + outputString(buf); + + outputElementAttributes(element->attributes()); + + formatAfterAttributes(element, buf); + outputString(buf); + + if(hasChildren) + outputElements(element->childElements(), true); + + formatEnd(element, buf); + outputString(buf); + + element = element->previousElement(); + } +} + +void QTestBasicStreamer::outputElementAttributes(QTestElementAttribute *attribute) const +{ + char buf[1024]; + while(attribute){ + formatAttributes(attribute, buf); + outputString(buf); + attribute = attribute->nextElement(); + } +} + +void QTestBasicStreamer::outputString(const char *msg) const +{ + QTEST_ASSERT(QTest::stream); + + ::fputs(msg, QTest::stream); + ::fflush(QTest::stream); +} + +void QTestBasicStreamer::startStreaming() +{ + QTEST_ASSERT(!QTest::stream); + + const char *out = QTestLog::outputFileName(); + if (!out) { + QTest::stream = stdout; + return; + } + #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE) + if (::fopen_s(&QTest::stream, out, "wt")) { + #else + QTest::stream = ::fopen(out, "wt"); + if (!QTest::stream) { + #endif + printf("Unable to open file for logging: %s", out); + ::exit(1); + } +} + +bool QTestBasicStreamer::isTtyOutput() +{ + QTEST_ASSERT(QTest::stream); + +#if defined(Q_OS_WIN) || defined(Q_OS_INTEGRITY) + return true; +#else + static bool ttyoutput = isatty(fileno(QTest::stream)); + return ttyoutput; +#endif +} + +void QTestBasicStreamer::stopStreaming() +{ + QTEST_ASSERT(QTest::stream); + if (QTest::stream != stdout) + fclose(QTest::stream); + + QTest::stream = 0; +} + +void QTestBasicStreamer::setLogger(const QTestLogger *tstLogger) +{ + testLogger = tstLogger; +} + +const QTestLogger *QTestBasicStreamer::logger() const +{ + return testLogger; +} + +QT_END_NAMESPACE + diff --git a/src/testlib/qtestbasicstreamer.h b/src/testlib/qtestbasicstreamer.h new file mode 100644 index 0000000..cfd6b94 --- /dev/null +++ b/src/testlib/qtestbasicstreamer.h @@ -0,0 +1,43 @@ +#ifndef QTESTBASICSTREAMER_H +#define QTESTBASICSTREAMER_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QTestElement; +class QTestElementAttribute; +class QTestLogger; + +class QTestBasicStreamer +{ + public: + QTestBasicStreamer(); + virtual ~QTestBasicStreamer(); + + virtual void output(QTestElement *element) const; + + void outputString(const char *msg) const; + bool isTtyOutput(); + void startStreaming(); + void stopStreaming(); + + void setLogger(const QTestLogger *tstLogger); + const QTestLogger *logger() const; + + protected: + virtual void formatStart(const QTestElement *element = 0, char *formatted = 0) const; + virtual void formatEnd(const QTestElement *element = 0, char *formatted = 0) const; + virtual void formatBeforeAttributes(const QTestElement *element = 0, char *formatted = 0) const; + virtual void formatAfterAttributes(const QTestElement *element = 0, char *formatted = 0) const; + virtual void formatAttributes(const QTestElementAttribute *attribute = 0, char *formatted = 0) const; + virtual void outputElements(QTestElement *element, bool isChildElement = false) const; + virtual void outputElementAttributes(QTestElementAttribute *attribute) const; + + private: + const QTestLogger *testLogger; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index f17d95d..a9b85e8 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -818,8 +818,10 @@ static void qParseArgs(int argc, char *argv[]) const char *testOptions = " options:\n" " -functions : Returns a list of current testfunctions\n" + " -xunitxml : Outputs results as XML XUnit document\n" " -xml : Outputs results as XML document\n" " -lightxml : Outputs results as stream of XML tags\n" + " -flush : Flushes the resutls\n" " -o filename: Writes all output into a file\n" " -silent : Only outputs warnings and failures\n" " -v1 : Print enter messages for each testfunction\n" @@ -845,7 +847,7 @@ static void qParseArgs(int argc, char *argv[]) " -median n : Sets the number of median iterations.\n" " -vb : Print out verbose benchmarking information.\n" #ifndef QT_NO_PROCESS -// Will be enabled when tools are integrated. +// Will be enabled when tools are integrated. // " -chart : Runs the chart generator after the test. No output is printed to the console\n" #endif "\n" @@ -861,10 +863,14 @@ static void qParseArgs(int argc, char *argv[]) } else if (strcmp(argv[i], "-functions") == 0) { qPrintTestSlots(); exit(0); + } else if(strcmp(argv[i], "-xunitxml") == 0){ + QTestLog::setLogMode(QTestLog::XunitXML); } else if (strcmp(argv[i], "-xml") == 0) { QTestLog::setLogMode(QTestLog::XML); } else if (strcmp(argv[i], "-lightxml") == 0) { QTestLog::setLogMode(QTestLog::LightXML); + }else if(strcmp(argv[i], "-flush") == 0){ + QTestLog::setFlushMode(QTestLog::FLushOn); } else if (strcmp(argv[i], "-silent") == 0) { QTestLog::setVerboseLevel(-1); } else if (strcmp(argv[i], "-v1") == 0) { @@ -1006,7 +1012,7 @@ QBenchmarkResult qMedian(const QList<QBenchmarkResult> &container) if (count == 1) return container.at(0); - + QList<QBenchmarkResult> containerCopy = container; qSort(containerCopy); @@ -1056,7 +1062,7 @@ static void qInvokeTestMethodDataEntry(char *slot) QTestResult::currentDataTag() ? QTestResult::currentDataTag() : ""); - invokeOk = QMetaObject::invokeMethod(QTest::currentTestObject, slot, + invokeOk = QMetaObject::invokeMethod(QTest::currentTestObject, slot, Qt::DirectConnection); if (!invokeOk) QTestResult::addFailure("Unable to execute slot", __FILE__, __LINE__); @@ -1077,7 +1083,7 @@ static void qInvokeTestMethodDataEntry(char *slot) if (i > -1) // iteration -1 is the warmup iteration. results.append(QBenchmarkTestMethodData::current->result); - if (QBenchmarkTestMethodData::current->isBenchmark() && + if (QBenchmarkTestMethodData::current->isBenchmark() && QBenchmarkGlobalData::current->verboseOutput) { if (i == -1) { qDebug() << "warmup stage result :" << QBenchmarkTestMethodData::current->result.value; @@ -1208,13 +1214,13 @@ void *fetchData(QTestData *data, const char *tagName, int typeId) /*! \fn char* QTest::toHexRepresentation(const char *ba, int length) - + Returns a pointer to a string that is the string \a ba represented as a space-separated sequence of hex characters. If the input is considered too long, it is truncated. A trucation is indicated in the returned string as an ellipsis at the end. - \a length is the length of the string \a ba. + \a length is the length of the string \a ba. */ char *toHexRepresentation(const char *ba, int length) { @@ -1273,56 +1279,56 @@ char *toHexRepresentation(const char *ba, int length) return result; } -static void qInvokeTestMethods(QObject *testObject) -{ - const QMetaObject *metaObject = testObject->metaObject(); - QTEST_ASSERT(metaObject); - - QTestLog::startLogging(); - - QTestResult::setCurrentTestFunction("initTestCase"); - QTestResult::setCurrentTestLocation(QTestResult::DataFunc); - QTestTable::globalTestTable(); - QMetaObject::invokeMethod(testObject, "initTestCase_data", Qt::DirectConnection); - - if (!QTestResult::skipCurrentTest() && !QTest::currentTestFailed()) { - QTestResult::setCurrentTestLocation(QTestResult::InitFunc); - QMetaObject::invokeMethod(testObject, "initTestCase"); - - // finishedCurrentTestFunction() resets QTestResult::testFailed(), so use a local copy. - const bool previousFailed = QTestResult::testFailed(); - QTestResult::finishedCurrentTestFunction(); - - if(!QTestResult::skipCurrentTest() && !previousFailed) { - - if (lastTestFuncIdx >= 0) { - for (int i = 0; i <= lastTestFuncIdx; ++i) { - if (!qInvokeTestMethod(metaObject->method(testFuncs[i].function).signature(), - testFuncs[i].data)) - break; - } - } else { - int methodCount = metaObject->methodCount(); - for (int i = 0; i < methodCount; ++i) { - QMetaMethod slotMethod = metaObject->method(i); - if (!isValidSlot(slotMethod)) - continue; - if (!qInvokeTestMethod(slotMethod.signature())) - break; - } - } - } - - QTestResult::setSkipCurrentTest(false); - QTestResult::setCurrentTestFunction("cleanupTestCase"); - QMetaObject::invokeMethod(testObject, "cleanupTestCase"); - } - QTestResult::finishedCurrentTestFunction(); - QTestResult::setCurrentTestFunction(0); - QTestTable::clearGlobalTestTable(); - - QTestLog::stopLogging(); -} +static void qInvokeTestMethods(QObject *testObject) +{ + const QMetaObject *metaObject = testObject->metaObject(); + QTEST_ASSERT(metaObject); + + QTestLog::startLogging(); + + QTestResult::setCurrentTestFunction("initTestCase"); + QTestResult::setCurrentTestLocation(QTestResult::DataFunc); + QTestTable::globalTestTable(); + QMetaObject::invokeMethod(testObject, "initTestCase_data", Qt::DirectConnection); + + if (!QTestResult::skipCurrentTest() && !QTest::currentTestFailed()) { + QTestResult::setCurrentTestLocation(QTestResult::InitFunc); + QMetaObject::invokeMethod(testObject, "initTestCase"); + + // finishedCurrentTestFunction() resets QTestResult::testFailed(), so use a local copy. + const bool previousFailed = QTestResult::testFailed(); + QTestResult::finishedCurrentTestFunction(); + + if(!QTestResult::skipCurrentTest() && !previousFailed) { + + if (lastTestFuncIdx >= 0) { + for (int i = 0; i <= lastTestFuncIdx; ++i) { + if (!qInvokeTestMethod(metaObject->method(testFuncs[i].function).signature(), + testFuncs[i].data)) + break; + } + } else { + int methodCount = metaObject->methodCount(); + for (int i = 0; i < methodCount; ++i) { + QMetaMethod slotMethod = metaObject->method(i); + if (!isValidSlot(slotMethod)) + continue; + if (!qInvokeTestMethod(slotMethod.signature())) + break; + } + } + } + + QTestResult::setSkipCurrentTest(false); + QTestResult::setCurrentTestFunction("cleanupTestCase"); + QMetaObject::invokeMethod(testObject, "cleanupTestCase"); + } + QTestResult::finishedCurrentTestFunction(); + QTestResult::setCurrentTestFunction(0); + QTestTable::clearGlobalTestTable(); + + QTestLog::stopLogging(); +} } // namespace diff --git a/src/testlib/qtestcoreelement.h b/src/testlib/qtestcoreelement.h new file mode 100644 index 0000000..ea05c19 --- /dev/null +++ b/src/testlib/qtestcoreelement.h @@ -0,0 +1,124 @@ +#ifndef QTESTCOREELEMENT_H +#define QTESTCOREELEMENT_H + +#include "qtestcorelist.h" +#include "qtestelementattribute.h" + +QT_BEGIN_NAMESPACE + +template <class ElementType> +class QTestCoreElement: public QTestCoreList<ElementType> +{ + public: + QTestCoreElement( int type = -1 ); + virtual ~QTestCoreElement(); + + void addAttribute(const QTest::AttributeIndex index, const char *value); + QTestElementAttribute *attributes() const; + const char *attributeValue(QTest::AttributeIndex index) const; + const char *attributeName(QTest::AttributeIndex index) const; + const QTestElementAttribute *attribute(QTest::AttributeIndex index) const; + + const char *elementName() const; + QTest::LogElementType elementType() const; + + private: + QTestElementAttribute *listOfAttributes; + QTest::LogElementType type; +}; + +template<class ElementType> +QTestCoreElement<ElementType>::QTestCoreElement(int t) +:listOfAttributes(0), type((QTest::LogElementType)t) +{ +} + +template<class ElementType> +QTestCoreElement<ElementType>::~QTestCoreElement() +{ + delete listOfAttributes; +} + +template <class ElementType> +void QTestCoreElement<ElementType>::addAttribute(const QTest::AttributeIndex attributeIndex, const char *value) +{ + if(attributeIndex == -1) + return; + + if (attribute(attributeIndex)) + return; + + QTestElementAttribute *attribute = new QTestElementAttribute; + attribute->setPair(attributeIndex, value); + attribute->addToList(&listOfAttributes); +} + +template <class ElementType> +QTestElementAttribute *QTestCoreElement<ElementType>::attributes() const +{ + return listOfAttributes; +} + +template <class ElementType> +const char *QTestCoreElement<ElementType>::attributeValue(QTest::AttributeIndex index) const +{ + const QTestElementAttribute *attrb = attribute(index); + if(attrb) + return attrb->value(); + + return 0; +} + +template <class ElementType> +const char *QTestCoreElement<ElementType>::attributeName(QTest::AttributeIndex index) const +{ + const QTestElementAttribute *attrb = attribute(index); + if(attrb) + return attrb->name(); + + return 0; +} + +template <class ElementType> +const char *QTestCoreElement<ElementType>::elementName() const +{ + const char *xmlElementNames[] = + { + "property", + "properties", + "failure", + "error", + "testcase", + "testsuite", + "benchmark" + }; + + if(type != QTest::LET_Undefined) + return xmlElementNames[type]; + + return 0; +} + +template <class ElementType> +QTest::LogElementType QTestCoreElement<ElementType>::elementType() const +{ + return type; +} + +template <class ElementType> +const QTestElementAttribute *QTestCoreElement<ElementType>::attribute(QTest::AttributeIndex index) const +{ + QTestElementAttribute *iterator = listOfAttributes; + while(iterator){ + if(iterator->index() == index) + return iterator; + + iterator = iterator->nextElement(); + } + + return 0; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestcorelist.h b/src/testlib/qtestcorelist.h new file mode 100644 index 0000000..8e008a4 --- /dev/null +++ b/src/testlib/qtestcorelist.h @@ -0,0 +1,89 @@ +#ifndef QTESTCORELIST_H +#define QTESTCORELIST_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +template <class T> +class QTestCoreList +{ + public: + QTestCoreList(); + virtual ~QTestCoreList(); + + void addToList(T **list); + T *nextElement(); + T *previousElement(); + int count(T *list); + int count(); + + private: + T *next; + T *prev; +}; + +template <class T> +QTestCoreList<T>::QTestCoreList() +:next(0) +,prev(0) +{ +} + +template <class T> +QTestCoreList<T>::~QTestCoreList() +{ + if (prev) { + prev->next = 0; + } + delete prev; + + if (next) { + next->prev = 0; + } + delete next; +} + +template <class T> +void QTestCoreList<T>::addToList(T **list) +{ + if (next) + next->addToList(list); + else { + next = *list; + if (next) + next->prev = static_cast<T*>(this); + } + + *list = static_cast<T*>(this); +} + +template <class T> +T *QTestCoreList<T>::nextElement() +{ + return next; +} + +template <class T> +T *QTestCoreList<T>::previousElement() +{ + return prev; +} + +template <class T> +int QTestCoreList<T>::count() +{ + int numOfElements = 0; + T *it = next; + + while(it){ + ++numOfElements; + it = it->nextElement(); + } + + return numOfElements; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestelement.cpp b/src/testlib/qtestelement.cpp new file mode 100644 index 0000000..55341d4 --- /dev/null +++ b/src/testlib/qtestelement.cpp @@ -0,0 +1,47 @@ +#include "qtestelement.h" + +QT_BEGIN_NAMESPACE + +QTestElement::QTestElement(int type) + :QTestCoreElement<QTestElement>(type), + listOfChildren(0), + parent(0) +{ +} + +QTestElement::~QTestElement() +{ + delete listOfChildren; +} + +bool QTestElement::addLogElement(QTestElement *element) +{ + if(!element) + return false; + + if(element->elementType() != QTest::LET_Undefined){ + element->addToList(&listOfChildren); + element->setParent(this); + return true; + } + + return false; +} + +QTestElement *QTestElement::childElements() const +{ + return listOfChildren; +} + +const QTestElement *QTestElement::parentElement() const +{ + return parent; +} + +void QTestElement::setParent(const QTestElement *p) +{ + parent = p; +} + +QT_END_NAMESPACE + diff --git a/src/testlib/qtestelement.h b/src/testlib/qtestelement.h new file mode 100644 index 0000000..f5d7466 --- /dev/null +++ b/src/testlib/qtestelement.h @@ -0,0 +1,28 @@ +#ifndef QTESTELEMENT_H +#define QTESTELEMENT_H + +#include "qtestcoreelement.h" + +QT_BEGIN_NAMESPACE + +class QTestElement: public QTestCoreElement<QTestElement> +{ + public: + QTestElement(int type = -1); + ~QTestElement(); + + bool addLogElement(QTestElement *element); + QTestElement *childElements() const; + + const QTestElement *parentElement() const; + void setParent(const QTestElement *p); + + private: + QTestElement *listOfChildren; + const QTestElement * parent; + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestelementattribute.cpp b/src/testlib/qtestelementattribute.cpp new file mode 100644 index 0000000..64f9da5 --- /dev/null +++ b/src/testlib/qtestelementattribute.cpp @@ -0,0 +1,76 @@ +#include "qtestelementattribute.h" +#include <QtCore/qbytearray.h> +#include <string.h> +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +QTestElementAttribute::QTestElementAttribute() + :attributeValue(0), + attributeIndex(QTest::AI_Undefined) +{ +} + +QTestElementAttribute::~QTestElementAttribute() +{ + delete[] attributeValue; +} + +const char *QTestElementAttribute::value() const +{ + return attributeValue; +} + +const char *QTestElementAttribute::name() const +{ + const char *AttributeNames[] = + { + "name", + "result", + "tests", + "failures", + "errors", + "type", + "description", + "value", + "qtestversion", + "qtversion", + "file", + "line", + "metric", + "tag", + "value", + "iterations" + }; + + if(attributeIndex != QTest::AI_Undefined) + return AttributeNames[attributeIndex]; + + return 0; +} + +QTest::AttributeIndex QTestElementAttribute::index() const +{ + return attributeIndex; +} + +bool QTestElementAttribute::isNull() const +{ + return attributeIndex == QTest::AI_Undefined; +} + +bool QTestElementAttribute::setPair(QTest::AttributeIndex index, const char *value) +{ + if(!value) + return false; + + delete[] attributeValue; + + attributeIndex = index; + attributeValue = qstrdup(value); + + return (attributeValue!=0) ? true:false; +} + +QT_END_NAMESPACE + diff --git a/src/testlib/qtestelementattribute.h b/src/testlib/qtestelementattribute.h new file mode 100644 index 0000000..2e23cd9 --- /dev/null +++ b/src/testlib/qtestelementattribute.h @@ -0,0 +1,63 @@ +#ifndef QTESTELEMENTATTRIBUTE_H +#define QTESTELEMENTATTRIBUTE_H + +#include "qtestcorelist.h" + +QT_BEGIN_NAMESPACE + +namespace QTest { + + enum AttributeIndex + { + AI_Undefined = -1, + AI_Name = 0, + AI_Result = 1, + AI_Tests = 2, + AI_Failures = 3, + AI_Errors = 4, + AI_Type = 5, + AI_Description = 6, + AI_PropertyValue = 7, + AI_QTestVersion = 8, + AI_QtVersion = 9, + AI_File = 10, + AI_Line = 11, + AI_Metric = 12, + AI_Tag = 13, + AI_Value = 14, + AI_Iterations = 15 + }; + + enum LogElementType + { + LET_Undefined = -1, + LET_Property = 0, + LET_Properties = 1, + LET_Failure = 2, + LET_Error = 3, + LET_TestCase = 4, + LET_TestSuite = 5, + LET_Benchmark = 6 + }; +} + +class QTestElementAttribute: public QTestCoreList<QTestElementAttribute> +{ + public: + QTestElementAttribute(); + ~QTestElementAttribute(); + + const char *value() const; + const char *name() const; + QTest::AttributeIndex index() const; + bool isNull() const; + bool setPair(QTest::AttributeIndex attributeIndex, const char *value); + + private: + char *attributeValue; + QTest::AttributeIndex attributeIndex; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestfilelogger.cpp b/src/testlib/qtestfilelogger.cpp new file mode 100644 index 0000000..f753b83 --- /dev/null +++ b/src/testlib/qtestfilelogger.cpp @@ -0,0 +1,54 @@ +#include "qtestfilelogger.h" +#include "qtestassert.h" +#include "QtTest/private/qtestlog_p.h" +#include "QtTest/private/qtestresult_p.h" + +#include <stdlib.h> +#include <stdio.h> + +QT_BEGIN_NAMESPACE + +namespace QTest +{ + static FILE *stream = 0; +} + +QTestFileLogger::QTestFileLogger() +{ +} + +QTestFileLogger::~QTestFileLogger() +{ + if(QTest::stream) + fclose(QTest::stream); + + QTest::stream = 0; +} + +void QTestFileLogger::init() +{ + char filename[100]; + QTest::qt_snprintf(filename, sizeof(filename), "%s.log", + QTestResult::currentTestObjectName()); + + #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE) + if (::fopen_s(&QTest::stream, filename, "wt")) { + #else + QTest::stream = ::fopen(filename, "wt"); + if (!QTest::stream) { + #endif + printf("Unable to open file for simple logging: %s", filename); + ::exit(1); + } +} + +void QTestFileLogger::flush(const char *msg) +{ + QTEST_ASSERT(QTest::stream); + + ::fputs(msg, QTest::stream); + ::fflush(QTest::stream); +} + +QT_END_NAMESPACE + diff --git a/src/testlib/qtestfilelogger.h b/src/testlib/qtestfilelogger.h new file mode 100644 index 0000000..4dca090 --- /dev/null +++ b/src/testlib/qtestfilelogger.h @@ -0,0 +1,20 @@ +#ifndef QTESTFILELOGGER_H +#define QTESTFILELOGGER_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QTestFileLogger +{ + public: + QTestFileLogger(); + ~QTestFileLogger(); + + void init(); + void flush(const char *msg); +}; + +QT_END_NAMESPACE + +#endif // QTESTFILELOGGER_H diff --git a/src/testlib/qtestlightxmlstreamer.cpp b/src/testlib/qtestlightxmlstreamer.cpp new file mode 100644 index 0000000..73b33ef --- /dev/null +++ b/src/testlib/qtestlightxmlstreamer.cpp @@ -0,0 +1,144 @@ +#include "qtestlightxmlstreamer.h" +#include "qtestelement.h" +#include "qtestelementattribute.h" + +#include "QtTest/private/qtestlog_p.h" +#include "QtTest/private/qtestresult_p.h" +#include "QtTest/private/qxmltestlogger_p.h" + +#include <string.h> + +QT_BEGIN_NAMESPACE + +QTestLightXmlStreamer::QTestLightXmlStreamer() + :QTestBasicStreamer() +{ +} + +QTestLightXmlStreamer::~QTestLightXmlStreamer() +{} + +void QTestLightXmlStreamer::formatStart(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted) + return; + + switch(element->elementType()){ + case QTest::LET_TestCase: { + char quotedTf[950]; + QXmlTestLogger::xmlQuote(quotedTf, element->attributeValue(QTest::AI_Name), + sizeof(quotedTf)); + + QTest::qt_snprintf(formatted, 1024, "<TestFunction name=\"%s\">\n", quotedTf); + break; + } + case QTest::LET_Failure: { + char cdataDesc[900]; + QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description), + sizeof(cdataDesc)); + + QTest::qt_snprintf(formatted, 1024, " <Description><![CDATA[%s]]></Description>\n", + cdataDesc); + break; + } + case QTest::LET_Error: { + // assuming type and attribute names don't need quoting + char quotedFile[128]; + char cdataDesc[700]; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), + sizeof(quotedFile)); + QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description), + sizeof(cdataDesc)); + + QTest::qt_snprintf(formatted, 1024, "<Message type=\"%s\" %s=\"%s\" %s=\"%s\">\n <Description><![CDATA[%s]]></Description>\n</Message>\n", + element->attributeValue(QTest::AI_Type), + element->attributeName(QTest::AI_File), + quotedFile, + element->attributeName(QTest::AI_Line), + element->attributeValue(QTest::AI_Line), + cdataDesc); + break; + } + case QTest::LET_Benchmark: { + // assuming value and iterations don't need quoting + char quotedMetric[256]; + char quotedTag[256]; + QXmlTestLogger::xmlQuote(quotedMetric, element->attributeValue(QTest::AI_Metric), + sizeof(quotedMetric)); + QXmlTestLogger::xmlQuote(quotedTag, element->attributeValue(QTest::AI_Tag), + sizeof(quotedTag)); + + QTest::qt_snprintf(formatted, 1024, "<BenchmarkResult %s=\"%s\" %s=\"%s\" %s=\"%s\" %s=\"%s\" />\n", + element->attributeName(QTest::AI_Metric), + quotedMetric, + element->attributeName(QTest::AI_Tag), + quotedTag, + element->attributeName(QTest::AI_Value), + element->attributeValue(QTest::AI_Value), + element->attributeName(QTest::AI_Iterations), + element->attributeValue(QTest::AI_Iterations) ); + break; + } + default: + QTest::qt_snprintf(formatted, 10, ""); + } +} + +void QTestLightXmlStreamer::formatEnd(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted) + return; + + if (element->elementType() == QTest::LET_TestCase) { + if( element->attribute(QTest::AI_Result) && element->childElements()) + QTest::qt_snprintf(formatted, 1024, "</Incident>\n</TestFunction>\n"); + else + QTest::qt_snprintf(formatted, 1024, "</TestFunction>\n"); + } + else + QTest::qt_snprintf(formatted, 10, ""); +} + +void QTestLightXmlStreamer::formatBeforeAttributes(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted) + return; + + if (element->elementType() == QTest::LET_TestCase && element->attribute(QTest::AI_Result)){ + char buf[900]; + char quotedFile[700]; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), + sizeof(quotedFile)); + + QTest::qt_snprintf(buf, sizeof(buf), "%s=\"%s\" %s=\"%s\"", + element->attributeName(QTest::AI_File), + quotedFile, + element->attributeName(QTest::AI_Line), + element->attributeValue(QTest::AI_Line)); + + if( !element->childElements() ) + QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s/>\n", + element->attributeValue(QTest::AI_Result), buf); + else + QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s>\n", + element->attributeValue(QTest::AI_Result), buf); + }else{ + QTest::qt_snprintf(formatted, 10, ""); + } +} + +void QTestLightXmlStreamer::output(QTestElement *element) const +{ + char buf[1024]; + QTest::qt_snprintf(buf, sizeof(buf), "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n", + qVersion(), QTEST_VERSION_STR ); + outputString(buf); + + QTest::qt_snprintf(buf, sizeof(buf), "</Environment>\n"); + outputString(buf); + + QTestBasicStreamer::output(element); +} + +QT_END_NAMESPACE + diff --git a/src/testlib/qtestlightxmlstreamer.h b/src/testlib/qtestlightxmlstreamer.h new file mode 100644 index 0000000..872eed2 --- /dev/null +++ b/src/testlib/qtestlightxmlstreamer.h @@ -0,0 +1,25 @@ +#ifndef QTESTLIGHTXMLSTREAMER_H +#define QTESTLIGHTXMLSTREAMER_H + +#include "qtestbasicstreamer.h" + +QT_BEGIN_NAMESPACE + +class QTestElement; +class QTestElementAttribute; + +class QTestLightXmlStreamer: public QTestBasicStreamer +{ + public: + QTestLightXmlStreamer(); + ~QTestLightXmlStreamer(); + + void formatStart(const QTestElement *element = 0, char *formatted = 0) const; + void formatEnd(const QTestElement *element = 0, char *formatted = 0) const; + void formatBeforeAttributes(const QTestElement *element = 0, char *formatted = 0) const; + void output(QTestElement *element) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index aa56e6c..bcb1c1b 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -46,7 +46,6 @@ #include "QtTest/private/qabstracttestlogger_p.h" #include "QtTest/private/qplaintestlogger_p.h" #include "QtTest/private/qxmltestlogger_p.h" - #include <QtCore/qatomic.h> #include <QtCore/qbytearray.h> @@ -54,6 +53,9 @@ #include <string.h> #include <limits.h> + +#include "qtestlogger_p.h" + QT_BEGIN_NAMESPACE namespace QTest { @@ -83,6 +85,7 @@ namespace QTest { static IgnoreResultList *ignoreResultList = 0; static QTestLog::LogMode logMode = QTestLog::Plain; + static QTestLog::FlushMode flushMode = QTestLog::NoFlush; static int verbosity = 0; static int maxWarnings = 2002; @@ -270,15 +273,24 @@ void QTestLog::startLogging() QTEST_ASSERT(!QTest::testLogger); switch (QTest::logMode) { - case QTestLog::Plain: - QTest::testLogger = new QPlainTestLogger(); - break; - case QTestLog::XML: - QTest::testLogger = new QXmlTestLogger(QXmlTestLogger::Complete); - break; - case QTestLog::LightXML: - QTest::testLogger = new QXmlTestLogger(QXmlTestLogger::Light); - } + case QTestLog::Plain: + QTest::testLogger = new QPlainTestLogger; + break; + case QTestLog::XML:{ + if(QTest::flushMode == QTestLog::FLushOn) + QTest::testLogger = new QXmlTestLogger(QXmlTestLogger::Complete); + else + QTest::testLogger = new QTestLogger(QTestLogger::TLF_XML); + break; + }case QTestLog::LightXML:{ + if(QTest::flushMode == QTestLog::FLushOn) + QTest::testLogger = new QXmlTestLogger(QXmlTestLogger::Light); + else + QTest::testLogger = new QTestLogger(QTestLogger::TLF_LightXml); + break; + }case QTestLog::XunitXML: + QTest::testLogger = new QTestLogger(QTestLogger::TLF_XunitXml); + } QTest::testLogger->startLogging(); @@ -361,4 +373,9 @@ void QTestLog::setMaxWarnings(int m) QTest::maxWarnings = m <= 0 ? INT_MAX : m + 2; } +void QTestLog::setFlushMode(FlushMode mode) +{ + QTest::flushMode = mode; +} + QT_END_NAMESPACE diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h index fa49a38..0d761e3 100644 --- a/src/testlib/qtestlog_p.h +++ b/src/testlib/qtestlog_p.h @@ -62,7 +62,8 @@ class QBenchmarkResult; class QTestLog { public: - enum LogMode { Plain = 0, XML, LightXML }; + enum LogMode { Plain = 0, XML, LightXML, XunitXML }; + enum FlushMode { NoFlush = 0, FLushOn }; static void enterTestFunction(const char* function); static void leaveTestFunction(); @@ -95,6 +96,8 @@ public: static void setMaxWarnings(int max); + static void setFlushMode(FlushMode mode); + private: QTestLog(); ~QTestLog(); diff --git a/src/testlib/qtestlogger.cpp b/src/testlib/qtestlogger.cpp new file mode 100644 index 0000000..c053c30 --- /dev/null +++ b/src/testlib/qtestlogger.cpp @@ -0,0 +1,339 @@ +#include "qtestlogger_p.h" +#include "qtestelement.h" +#include "qtestxunitstreamer.h" +#include "qtestxmlstreamer.h" +#include "qtestlightxmlstreamer.h" +#include "qtestfilelogger.h" + +#include "QtTest/qtestcase.h" +#include "QtTest/private/qtestresult_p.h" +#include "QtTest/private/qbenchmark_p.h" + +#include <string.h> + +QT_BEGIN_NAMESPACE + +QTestLogger::QTestLogger(int fm) + :listOfTestcases(0), currentLogElement(0), + logFormatter(0), format( (TestLoggerFormat)fm ), filelogger(new QTestFileLogger), + testCounter(0), passCounter(0), + failureCounter(0), errorCounter(0), + warningCounter(0), skipCounter(0), + systemCounter(0), qdebugCounter(0), + qwarnCounter(0), qfatalCounter(0), + infoCounter(0) +{ +} + +QTestLogger::~QTestLogger() +{ + if(format == TLF_XunitXml) + delete currentLogElement; + else + delete listOfTestcases; + + delete logFormatter; + delete filelogger; +} + +void QTestLogger::startLogging() +{ + switch(format){ + case TLF_LightXml:{ + logFormatter = new QTestLightXmlStreamer; + filelogger->init(); + break; + }case TLF_XML:{ + logFormatter = new QTestXmlStreamer; + filelogger->init(); + break; + }case TLF_XunitXml:{ + logFormatter = new QTestXunitStreamer; + filelogger->init(); + break; + } + } + + logFormatter->setLogger(this); + logFormatter->startStreaming(); +} + +void QTestLogger::stopLogging() +{ + QTestElement *iterator = listOfTestcases; + + if(format == TLF_XunitXml ){ + char buf[10]; + + currentLogElement = new QTestElement(QTest::LET_TestSuite); + currentLogElement->addAttribute(QTest::AI_Name, QTestResult::currentTestObjectName()); + + QTest::qt_snprintf(buf, sizeof(buf), "%i", testCounter); + currentLogElement->addAttribute(QTest::AI_Tests, buf); + + QTest::qt_snprintf(buf, sizeof(buf), "%i", failureCounter); + currentLogElement->addAttribute(QTest::AI_Failures, buf); + + QTest::qt_snprintf(buf, sizeof(buf), "%i", errorCounter); + currentLogElement->addAttribute(QTest::AI_Errors, buf); + + QTestElement *property; + QTestElement *properties = new QTestElement(QTest::LET_Properties); + + property = new QTestElement(QTest::LET_Property); + property->addAttribute(QTest::AI_Name, "QTestVersion"); + property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR); + properties->addLogElement(property); + + property = new QTestElement(QTest::LET_Property); + property->addAttribute(QTest::AI_Name, "QtVersion"); + property->addAttribute(QTest::AI_PropertyValue, qVersion()); + properties->addLogElement(property); + + currentLogElement->addLogElement(properties); + + currentLogElement->addLogElement(iterator); + + /* For correct indenting, make sure every testcase knows its parent */ + QTestElement* testcase = iterator; + while (testcase) { + testcase->setParent(currentLogElement); + testcase = testcase->nextElement(); + } + + QTestElement *it = currentLogElement; + logFormatter->output(it); + }else{ + logFormatter->output(iterator); + } + + logFormatter->stopStreaming(); +} + +void QTestLogger::enterTestFunction(const char *function) +{ + char buf[1024]; + QTest::qt_snprintf(buf, sizeof(buf), "Entered test-function: %s\n", function); + filelogger->flush(buf); + + currentLogElement = new QTestElement(QTest::LET_TestCase); + currentLogElement->addAttribute(QTest::AI_Name, function); + currentLogElement->addToList(&listOfTestcases); + + ++testCounter; +} + +void QTestLogger::leaveTestFunction() +{ +} + +void QTestLogger::addIncident(IncidentTypes type, const char *description, + const char *file, int line) +{ + const char *typeBuf = 0; + char buf[100]; + + switch (type) { + case QAbstractTestLogger::XPass: + ++passCounter; + typeBuf = "xpass"; + break; + case QAbstractTestLogger::Pass: + ++passCounter; + typeBuf = "pass"; + break; + case QAbstractTestLogger::XFail: + ++failureCounter; + typeBuf = "xfail"; + break; + case QAbstractTestLogger::Fail: + ++failureCounter; + typeBuf = "fail"; + break; + default: + typeBuf = "??????"; + break; + } + + if (type == QAbstractTestLogger::Fail || type == QAbstractTestLogger::XFail) { + QTestElement *failureElement = new QTestElement(QTest::LET_Failure); + failureElement->addAttribute(QTest::AI_Result, typeBuf); + failureElement->addAttribute(QTest::AI_File, file); + QTest::qt_snprintf(buf, sizeof(buf), "%i", line); + failureElement->addAttribute(QTest::AI_Line, buf); + failureElement->addAttribute(QTest::AI_Description, description); + const char* tag = QTestResult::currentDataTag(); + if (tag) { + failureElement->addAttribute(QTest::AI_Tag, tag); + } + currentLogElement->addLogElement(failureElement); + } + + /* + Only one result can be shown for the whole testfunction. + Check if we currently have a result, and if so, overwrite it + iff the new result is worse. + */ + QTestElementAttribute* resultAttr = + const_cast<QTestElementAttribute*>(currentLogElement->attribute(QTest::AI_Result)); + if (resultAttr) { + const char* oldResult = resultAttr->value(); + bool overwrite = false; + if (!strcmp(oldResult, "pass")) { + overwrite = true; + } + else if (!strcmp(oldResult, "xpass")) { + overwrite = (type == QAbstractTestLogger::XFail || type == QAbstractTestLogger::Fail); + } + else if (!strcmp(oldResult, "xfail")) { + overwrite = (type == QAbstractTestLogger::Fail); + } + if (overwrite) { + resultAttr->setPair(QTest::AI_Result, typeBuf); + } + } + else { + currentLogElement->addAttribute(QTest::AI_Result, typeBuf); + } + + if(file) + currentLogElement->addAttribute(QTest::AI_File, file); + else + currentLogElement->addAttribute(QTest::AI_File, ""); + + QTest::qt_snprintf(buf, sizeof(buf), "%i", line); + currentLogElement->addAttribute(QTest::AI_Line, buf); +} + +void QTestLogger::addBenchmarkResult(const QBenchmarkResult &result) +{ + QTestElement *benchmarkElement = new QTestElement(QTest::LET_Benchmark); +// printf("element %i", benchmarkElement->elementType()); + + benchmarkElement->addAttribute(QTest::AI_Metric, QBenchmarkGlobalData::current->measurer->metricText().toAscii().data()); + benchmarkElement->addAttribute(QTest::AI_Tag, result.context.tag.toAscii().data()); + benchmarkElement->addAttribute(QTest::AI_Value, QByteArray::number(result.value).constData()); + + char buf[100]; + QTest::qt_snprintf(buf, sizeof(buf), "%i", result.iterations); + benchmarkElement->addAttribute(QTest::AI_Iterations, buf); + currentLogElement->addLogElement(benchmarkElement); +} + +void QTestLogger::addMessage(MessageTypes type, const char *message, const char *file, int line) +{ + QTestElement *errorElement = new QTestElement(QTest::LET_Error); + const char *typeBuf = 0; + + switch (type) { + case QAbstractTestLogger::Warn: + ++warningCounter; + typeBuf = "warn"; + break; + case QAbstractTestLogger::QSystem: + ++systemCounter; + typeBuf = "system"; + break; + case QAbstractTestLogger::QDebug: + ++qdebugCounter; + typeBuf = "qdebug"; + break; + case QAbstractTestLogger::QWarning: + ++qwarnCounter; + typeBuf = "qwarning"; + break; + case QAbstractTestLogger::QFatal: + ++qfatalCounter; + typeBuf = "qfatal"; + break; + case QAbstractTestLogger::Skip: + ++skipCounter; + typeBuf = "skip"; + break; + case QAbstractTestLogger::Info: + ++infoCounter; + typeBuf = "info"; + break; + default: + typeBuf = "??????"; + break; + } + + errorElement->addAttribute(QTest::AI_Type, typeBuf); + errorElement->addAttribute(QTest::AI_Description, message); + + if(file) + errorElement->addAttribute(QTest::AI_File, file); + else + errorElement->addAttribute(QTest::AI_File, ""); + + char buf[100]; + QTest::qt_snprintf(buf, sizeof(buf), "%i", line); + errorElement->addAttribute(QTest::AI_Line, buf); + + currentLogElement->addLogElement(errorElement); + ++errorCounter; +} + +void QTestLogger::setLogFormat(TestLoggerFormat fm) +{ + format = fm; +} + +QTestLogger::TestLoggerFormat QTestLogger::logFormat() +{ + return format; +} + +int QTestLogger::passCount() const +{ + return passCounter; +} + +int QTestLogger::failureCount() const +{ + return failureCounter; +} + +int QTestLogger::errorCount() const +{ + return errorCounter; +} + +int QTestLogger::warningCount() const +{ + return warningCounter; +} + +int QTestLogger::skipCount() const +{ + return skipCounter; +} + +int QTestLogger::systemCount() const +{ + return systemCounter; +} + +int QTestLogger::qdebugCount() const +{ + return qdebugCounter; +} + +int QTestLogger::qwarnCount() const +{ + return qwarnCounter; +} + +int QTestLogger::qfatalCount() const +{ + return qfatalCounter; +} + +int QTestLogger::infoCount() const +{ + return infoCounter; +} + +QT_END_NAMESPACE + diff --git a/src/testlib/qtestlogger_p.h b/src/testlib/qtestlogger_p.h new file mode 100644 index 0000000..3badb1d --- /dev/null +++ b/src/testlib/qtestlogger_p.h @@ -0,0 +1,74 @@ +#ifndef QTESTLOGGER_P_H +#define QTESTLOGGER_P_H + +#include <QtTest/private/qabstracttestlogger_p.h> + +QT_BEGIN_NAMESPACE + +class QTestBasicStreamer; +class QTestElement; +class QTestFileLogger; + +class QTestLogger : public QAbstractTestLogger +{ + public: + QTestLogger(int fm = 0); + ~QTestLogger(); + + enum TestLoggerFormat + { + TLF_XML = 0, + TLF_LightXml = 1, + TLF_XunitXml = 2 + }; + + void startLogging(); + void stopLogging(); + + void enterTestFunction(const char *function); + void leaveTestFunction(); + + void addIncident(IncidentTypes type, const char *description, + const char *file = 0, int line = 0); + void addBenchmarkResult(const QBenchmarkResult &result); + + void addMessage(MessageTypes type, const char *message, + const char *file = 0, int line = 0); + + void setLogFormat(TestLoggerFormat fm); + TestLoggerFormat logFormat(); + + int passCount() const; + int failureCount() const; + int errorCount() const; + int warningCount() const; + int skipCount() const; + int systemCount() const; + int qdebugCount() const; + int qwarnCount() const; + int qfatalCount() const; + int infoCount() const; + + private: + QTestElement *listOfTestcases; + QTestElement *currentLogElement; + QTestBasicStreamer *logFormatter; + TestLoggerFormat format; + QTestFileLogger *filelogger; + + int testCounter; + int passCounter; + int failureCounter; + int errorCounter; + int warningCounter; + int skipCounter; + int systemCounter; + int qdebugCounter; + int qwarnCounter; + int qfatalCounter; + int infoCounter; +}; + +QT_END_NAMESPACE + +#endif // QTESTLOGGER_P_H diff --git a/src/testlib/qtestxmlstreamer.cpp b/src/testlib/qtestxmlstreamer.cpp new file mode 100644 index 0000000..cf99b96 --- /dev/null +++ b/src/testlib/qtestxmlstreamer.cpp @@ -0,0 +1,178 @@ +#include "qtestxmlstreamer.h" +#include "qtestelement.h" +#include "qtestelementattribute.h" + +#include "QtTest/private/qtestlog_p.h" +#include "QtTest/private/qtestresult_p.h" +#include "QtTest/private/qxmltestlogger_p.h" + +#include <string.h> +#include <stdio.h> + +QT_BEGIN_NAMESPACE + +QTestXmlStreamer::QTestXmlStreamer() + :QTestBasicStreamer() +{ +} + +QTestXmlStreamer::~QTestXmlStreamer() +{} + +void QTestXmlStreamer::formatStart(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted) + return; + + switch(element->elementType()){ + case QTest::LET_TestCase: { + char quotedTf[950]; + QXmlTestLogger::xmlQuote(quotedTf, element->attributeValue(QTest::AI_Name), + sizeof(quotedTf)); + + QTest::qt_snprintf(formatted, 1024, "<TestFunction name=\"%s\">\n", quotedTf); + break; + } + case QTest::LET_Failure: { + char cdataDesc[800]; + QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description), + sizeof(cdataDesc)); + + char location[100]; + char quotedFile[70]; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), + sizeof(quotedFile)); + + QTest::qt_snprintf(location, sizeof(location), "%s=\"%s\" %s=\"%s\"", + element->attributeName(QTest::AI_File), + quotedFile, + element->attributeName(QTest::AI_Line), + element->attributeValue(QTest::AI_Line)); + + if (element->attribute(QTest::AI_Tag)) { + char cdataTag[100]; + QXmlTestLogger::xmlCdata(cdataTag, element->attributeValue(QTest::AI_Tag), + sizeof(cdataTag)); + QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s>\n" + " <DataTag><![CDATA[%s]]></Description>\n" + " <Description><![CDATA[%s]]></Description>\n" + "</Incident>\n", element->attributeValue(QTest::AI_Result), + location, cdataTag, cdataDesc); + } + else { + QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s>\n" + " <Description><![CDATA[%s]]></Description>\n" + "</Incident>\n", element->attributeValue(QTest::AI_Result), + location, cdataDesc); + } + break; + } + case QTest::LET_Error: { + // assuming type and attribute names don't need quoting + char quotedFile[128]; + char cdataDesc[700]; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), + sizeof(quotedFile)); + QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description), + sizeof(cdataDesc)); + + QTest::qt_snprintf(formatted, 1024, "<Message type=\"%s\" %s=\"%s\" %s=\"%s\">\n <Description><![CDATA[%s]]></Description>\n</Message>\n", + element->attributeValue(QTest::AI_Type), + element->attributeName(QTest::AI_File), + quotedFile, + element->attributeName(QTest::AI_Line), + element->attributeValue(QTest::AI_Line), + cdataDesc); + break; + } + case QTest::LET_Benchmark: { + // assuming value and iterations don't need quoting + char quotedMetric[256]; + char quotedTag[256]; + QXmlTestLogger::xmlQuote(quotedMetric, element->attributeValue(QTest::AI_Metric), + sizeof(quotedMetric)); + QXmlTestLogger::xmlQuote(quotedTag, element->attributeValue(QTest::AI_Tag), + sizeof(quotedTag)); + + QTest::qt_snprintf(formatted, 1024, "<BenchmarkResult %s=\"%s\" %s=\"%s\" %s=\"%s\" %s=\"%s\" />\n", + element->attributeName(QTest::AI_Metric), + quotedMetric, + element->attributeName(QTest::AI_Tag), + quotedTag, + element->attributeName(QTest::AI_Value), + element->attributeValue(QTest::AI_Value), + element->attributeName(QTest::AI_Iterations), + element->attributeValue(QTest::AI_Iterations) ); + break; + } + default: + QTest::qt_snprintf(formatted, 10, ""); + } +} + +void QTestXmlStreamer::formatEnd(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted) + return; + + if (element->elementType() == QTest::LET_TestCase) { + QTest::qt_snprintf(formatted, 1024, "</TestFunction>\n"); + } + else + QTest::qt_snprintf(formatted, 10, ""); +} + +void QTestXmlStreamer::formatBeforeAttributes(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted) + return; + + if (element->elementType() == QTest::LET_TestCase && element->attribute(QTest::AI_Result)){ + char buf[900]; + char quotedFile[700]; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), + sizeof(quotedFile)); + + QTest::qt_snprintf(buf, sizeof(buf), "%s=\"%s\" %s=\"%s\"", + element->attributeName(QTest::AI_File), + quotedFile, + element->attributeName(QTest::AI_Line), + element->attributeValue(QTest::AI_Line)); + + if( !element->childElements() ) { + QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s/>\n", + element->attributeValue(QTest::AI_Result), buf); + } + else { + QTest::qt_snprintf(formatted, 10, ""); + } + }else{ + QTest::qt_snprintf(formatted, 10, ""); + } +} + +void QTestXmlStreamer::output(QTestElement *element) const +{ + char buf[1024]; + char quotedTc[800]; + QXmlTestLogger::xmlQuote(quotedTc, QTestResult::currentTestObjectName(), sizeof(quotedTc)); + + QTest::qt_snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<TestCase name=\"%s\">\n", + quotedTc); + outputString(buf); + + QTest::qt_snprintf(buf, sizeof(buf), "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n", + qVersion(), QTEST_VERSION_STR ); + outputString(buf); + + QTest::qt_snprintf(buf, sizeof(buf), "</Environment>\n"); + outputString(buf); + + QTestBasicStreamer::output(element); + + QTest::qt_snprintf(buf, sizeof(buf), "</TestCase>\n"); + outputString(buf); +} + +QT_END_NAMESPACE + diff --git a/src/testlib/qtestxmlstreamer.h b/src/testlib/qtestxmlstreamer.h new file mode 100644 index 0000000..e6858c6 --- /dev/null +++ b/src/testlib/qtestxmlstreamer.h @@ -0,0 +1,25 @@ +#ifndef QTESTXMLSTREAMER_H +#define QTESXMLSTREAMER_H + +#include "qtestbasicstreamer.h" + +QT_BEGIN_NAMESPACE + +class QTestElement; +class QTestElementAttribute; + +class QTestXmlStreamer: public QTestBasicStreamer +{ + public: + QTestXmlStreamer(); + ~QTestXmlStreamer(); + + void formatStart(const QTestElement *element = 0, char *formatted = 0) const; + void formatEnd(const QTestElement *element = 0, char *formatted = 0) const; + void formatBeforeAttributes(const QTestElement *element = 0, char *formatted = 0) const; + void output(QTestElement *element) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestxunitstreamer.cpp b/src/testlib/qtestxunitstreamer.cpp new file mode 100644 index 0000000..2d8b7c4 --- /dev/null +++ b/src/testlib/qtestxunitstreamer.cpp @@ -0,0 +1,141 @@ +#include "qtestxunitstreamer.h" +#include "qtestelement.h" + +#include "QtTest/private/qtestlog_p.h" +#include "QtTest/private/qtestresult_p.h" +#include "QtTest/private/qxmltestlogger_p.h" + +QT_BEGIN_NAMESPACE + +QTestXunitStreamer::QTestXunitStreamer() + :QTestBasicStreamer() +{} + +QTestXunitStreamer::~QTestXunitStreamer() +{} + +void QTestXunitStreamer::indentForElement(const QTestElement* element, char* buf, int size) +{ + if (size == 0) return; + + buf[0] = 0; + + if (!element) return; + + char* endbuf = buf + size; + element = element->parentElement(); + while (element && buf+2 < endbuf) { + *(buf++) = ' '; + *(buf++) = ' '; + *buf = 0; + element = element->parentElement(); + } +} + +void QTestXunitStreamer::formatStart(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted ) + return; + + char indent[20]; + indentForElement(element, indent, sizeof(indent)); + + QTest::qt_snprintf(formatted, 1024, "%s<%s", indent, element->elementName()); +} + +void QTestXunitStreamer::formatEnd(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted ) + return; + + if(!element->childElements()){ + QTest::qt_snprintf(formatted, 10, ""); + return; + } + + char indent[20]; + indentForElement(element, indent, sizeof(indent)); + + QTest::qt_snprintf(formatted, 1024, "%s</%s>\n", indent, element->elementName()); +} + +void QTestXunitStreamer::formatAttributes(const QTestElementAttribute *attribute, char *formatted) const +{ + if(!attribute || !formatted ) + return; + + QTest::AttributeIndex attrindex = attribute->index(); + + char const* key = 0; + if (attrindex == QTest::AI_Description) + key = "message"; + else if (attrindex != QTest::AI_File && attrindex != QTest::AI_Line) + key = attribute->name(); + + if (key) { + char quotedValue[900]; + QXmlTestLogger::xmlQuote(quotedValue, attribute->value(), sizeof(quotedValue)); + QTest::qt_snprintf(formatted, 1024, " %s=\"%s\"", key, quotedValue); + } + else { + QTest::qt_snprintf(formatted, 10, ""); + } +} + +void QTestXunitStreamer::formatAfterAttributes(const QTestElement *element, char *formatted) const +{ + if(!element || !formatted ) + return; + + if(!element->childElements()) + QTest::qt_snprintf(formatted, 10, "/>\n"); + else + QTest::qt_snprintf(formatted, 10, ">\n"); +} + +void QTestXunitStreamer::output(QTestElement *element) const +{ + char buf[1024]; + QTest::qt_snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); + outputString(buf); + QTestBasicStreamer::output(element); +} + +void QTestXunitStreamer::outputElements(QTestElement *element, bool) const +{ + char buf[1024]; + bool hasChildren; + /* + Elements are in reverse order of occurrence, so start from the end and work + our way backwards. + */ + while (element && element->nextElement()) { + element = element->nextElement(); + } + while (element) { + hasChildren = element->childElements(); + + if(element->elementType() != QTest::LET_Benchmark){ + formatStart(element, buf); + outputString(buf); + + formatBeforeAttributes(element, buf); + outputString(buf); + + outputElementAttributes(element->attributes()); + + formatAfterAttributes(element, buf); + outputString(buf); + + if(hasChildren) + outputElements(element->childElements(), true); + + formatEnd(element, buf); + outputString(buf); + } + element = element->previousElement(); + } +} + +QT_END_NAMESPACE + diff --git a/src/testlib/qtestxunitstreamer.h b/src/testlib/qtestxunitstreamer.h new file mode 100644 index 0000000..1ce9576 --- /dev/null +++ b/src/testlib/qtestxunitstreamer.h @@ -0,0 +1,30 @@ +#ifndef QTESTXUNITSTREAMER_H +#define QTESTXUNITSTREAMER_H + +#include "qtestbasicstreamer.h" + +QT_BEGIN_NAMESPACE + +class QTestLogger; + +class QTestXunitStreamer: public QTestBasicStreamer +{ + public: + QTestXunitStreamer(); + ~QTestXunitStreamer(); + + void formatStart(const QTestElement *element = 0, char *formatted = 0) const; + void formatEnd(const QTestElement *element = 0, char *formatted = 0) const; + void formatAfterAttributes(const QTestElement *element = 0, char *formatted = 0) const; + void formatAttributes(const QTestElementAttribute *attribute = 0, char *formatted = 0) const; + void output(QTestElement *element) const; + void outputElements(QTestElement *element, bool isChildElement = false) const; + + private: + void displayXunitXmlHeader() const; + static void indentForElement(const QTestElement* element, char* buf, int size); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qxmltestlogger.cpp b/src/testlib/qxmltestlogger.cpp index bba98da..ec9a5d2 100644 --- a/src/testlib/qxmltestlogger.cpp +++ b/src/testlib/qxmltestlogger.cpp @@ -46,6 +46,7 @@ #include "QtTest/private/qxmltestlogger_p.h" #include "QtTest/private/qtestresult_p.h" #include "QtTest/private/qbenchmark_p.h" +#include "QtTest/qtestcase.h" QT_BEGIN_NAMESPACE @@ -90,27 +91,27 @@ namespace QTest { } -QXmlTestLogger::QXmlTestLogger(XmlMode mode ): - xmlmode(mode) +QXmlTestLogger::QXmlTestLogger(XmlMode mode ) + :xmlmode(mode) { } QXmlTestLogger::~QXmlTestLogger() { - } - void QXmlTestLogger::startLogging() { QAbstractTestLogger::startLogging(); char buf[1024]; if (xmlmode == QXmlTestLogger::Complete) { + char quotedTc[900]; + xmlQuote(quotedTc, QTestResult::currentTestObjectName(), sizeof(quotedTc)); QTest::qt_snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n" - "<TestCase name=\"%s\">\n", QTestResult::currentTestObjectName()); + "<TestCase name=\"%s\">\n", quotedTc); outputString(buf); } @@ -134,7 +135,9 @@ void QXmlTestLogger::stopLogging() void QXmlTestLogger::enterTestFunction(const char *function) { char buf[1024]; - QTest::qt_snprintf(buf, sizeof(buf), "<TestFunction name=\"%s\">\n", function); + char quotedFunction[950]; + xmlQuote(quotedFunction, function, sizeof(quotedFunction)); + QTest::qt_snprintf(buf, sizeof(buf), "<TestFunction name=\"%s\">\n", quotedFunction); outputString(buf); } @@ -158,18 +161,18 @@ static const char *incidentFormatString(bool noDescription, bool noTag) return "<Incident type=\"%s\" file=\"%s\" line=\"%d\" />\n"; else return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n" - " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n" - "</Incident>\n"; + " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n" + "</Incident>\n"; } else { if (noTag) return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n" - " <Description><![CDATA[%s%s%s%s]]></Description>\n" - "</Incident>\n"; + " <Description><![CDATA[%s%s%s%s]]></Description>\n" + "</Incident>\n"; else return "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n" - " <DataTag><![CDATA[%s%s%s]]></DataTag>\n" - " <Description><![CDATA[%s]]></Description>\n" - "</Incident>\n"; + " <DataTag><![CDATA[%s%s%s]]></DataTag>\n" + " <Description><![CDATA[%s]]></Description>\n" + "</Incident>\n"; } } @@ -185,40 +188,51 @@ static const char *messageFormatString(bool noDescription, bool noTag) return "<Message type=\"%s\" file=\"%s\" line=\"%d\" />\n"; else return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n" - " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n" - "</Message>\n"; + " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n" + "</Message>\n"; } else { if (noTag) return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n" - " <Description><![CDATA[%s%s%s%s]]></Description>\n" - "</Message>\n"; + " <Description><![CDATA[%s%s%s%s]]></Description>\n" + "</Message>\n"; else return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n" - " <DataTag><![CDATA[%s%s%s]]></DataTag>\n" - " <Description><![CDATA[%s]]></Description>\n" - "</Message>\n"; + " <DataTag><![CDATA[%s%s%s]]></DataTag>\n" + " <Description><![CDATA[%s]]></Description>\n" + "</Message>\n"; } } } // namespace void QXmlTestLogger::addIncident(IncidentTypes type, const char *description, - const char *file, int line) + const char *file, int line) { - char buf[1536]; + // buffer must be large enough to hold all quoted/cdata buffers plus the format string itself + char buf[5000]; const char *tag = QTestResult::currentDataTag(); const char *gtag = QTestResult::currentGlobalDataTag(); const char *filler = (tag && gtag) ? ":" : ""; const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag); + char quotedFile[1024]; + char cdataGtag[1024]; + char cdataTag[1024]; + char cdataDescription[1024]; + + xmlQuote(quotedFile, file, sizeof(quotedFile)); + xmlCdata(cdataGtag, gtag, sizeof(cdataGtag)); + xmlCdata(cdataTag, tag, sizeof(cdataTag)); + xmlCdata(cdataDescription, description, sizeof(cdataDescription)); + QTest::qt_snprintf(buf, sizeof(buf), QTest::incidentFormatString(QTest::isEmpty(description), notag), QTest::xmlIncidentType2String(type), - file ? file : "", line, - gtag ? gtag : "", + quotedFile, line, + cdataGtag, filler, - tag ? tag : "", - description ? description : ""); + cdataTag, + cdataDescription); outputString(buf); } @@ -226,39 +240,152 @@ void QXmlTestLogger::addIncident(IncidentTypes type, const char *description, void QXmlTestLogger::addBenchmarkResult(const QBenchmarkResult &result) { char buf[1536]; + char quotedMetric[64]; + char quotedTag[1024]; + + xmlQuote(quotedMetric, + QBenchmarkGlobalData::current->measurer->metricText().toAscii().constData(), + sizeof(quotedMetric)); + xmlQuote(quotedTag, result.context.tag.toAscii().constData(), sizeof(quotedTag)); + QTest::qt_snprintf( buf, sizeof(buf), QTest::benchmarkResultFormatString(), - QBenchmarkGlobalData::current->measurer->metricText().toAscii().data(), - result.context.tag.toAscii().data(), + quotedMetric, + quotedTag, QByteArray::number(result.value).constData(), //no 64-bit qt_snprintf support - result.iterations); + result.iterations); outputString(buf); } void QXmlTestLogger::addMessage(MessageTypes type, const char *message, const char *file, int line) { - char buf[1536]; - char msgbuf[1024]; + char buf[5000]; const char *tag = QTestResult::currentDataTag(); const char *gtag = QTestResult::currentGlobalDataTag(); const char *filler = (tag && gtag) ? ":" : ""; const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag); - QTest::qt_snprintf(msgbuf, sizeof(msgbuf), "%s", - message ? message : ""); + char quotedFile[1024]; + char cdataGtag[1024]; + char cdataTag[1024]; + char cdataDescription[1024]; + + xmlQuote(quotedFile, file, sizeof(quotedFile)); + xmlCdata(cdataGtag, gtag, sizeof(cdataGtag)); + xmlCdata(cdataTag, tag, sizeof(cdataTag)); + xmlCdata(cdataDescription, message, sizeof(cdataDescription)); QTest::qt_snprintf(buf, sizeof(buf), QTest::messageFormatString(QTest::isEmpty(message), notag), QTest::xmlMessageType2String(type), - file ? file : "", line, - gtag ? gtag : "", + quotedFile, line, + cdataGtag, filler, - tag ? tag : "", - msgbuf); + cdataTag, + cdataDescription); outputString(buf); } +/* + Copy up to n characters from the src string into dest, escaping any special + XML characters as necessary so that dest is suitable for use in an XML + quoted attribute string. +*/ +void QXmlTestLogger::xmlQuote(char* dest, char const* src, size_t n) +{ + if (n == 0) return; + + *dest = 0; + if (!src) return; + + char* end = dest + n; + + while (dest < end) { + switch (*src) { + +#define MAP_ENTITY(chr, ent) \ + case chr: \ + if (dest + sizeof(ent) < end) { \ + strcpy(dest, ent); \ + dest += sizeof(ent) - 1; \ + } \ + else { \ + *dest = 0; \ + return; \ + } \ + ++src; \ + break; + + MAP_ENTITY('>', ">"); + MAP_ENTITY('<', "<"); + MAP_ENTITY('\'', "'"); + MAP_ENTITY('"', """); + MAP_ENTITY('&', "&"); + +#undef MAP_ENTITY + + case 0: + *dest = 0; + return; + + default: + *dest = *src; + ++dest; + ++src; + break; + } + } + + // If we get here, dest was completely filled (dest == end) + *(dest-1) = 0; +} + +/* + Copy up to n characters from the src string into dest, escaping any + special strings such that dest is suitable for use in an XML CDATA section. +*/ +void QXmlTestLogger::xmlCdata(char* dest, char const* src, size_t n) +{ + if (!n) return; + + if (!src || n == 1) { + *dest = 0; + return; + } + + char const CDATA_END[] = "]]>"; + char const CDATA_END_ESCAPED[] = "]]]><![CDATA[]>"; + + char* end = dest + n; + while (dest < end) { + if (!*src) { + *dest = 0; + return; + } + + if (!strncmp(src, CDATA_END, sizeof(CDATA_END)-1)) { + if (dest + sizeof(CDATA_END_ESCAPED) < end) { + strcpy(dest, CDATA_END_ESCAPED); + src += sizeof(CDATA_END)-1; + dest += sizeof(CDATA_END_ESCAPED) - 1; + } + else { + *dest = 0; + return; + } + continue; + } + + *dest = *src; + ++src; + ++dest; + } + + // If we get here, dest was completely filled (dest == end) + *(dest-1) = 0; +} + QT_END_NAMESPACE diff --git a/src/testlib/qxmltestlogger_p.h b/src/testlib/qxmltestlogger_p.h index 3e78969..79e34ff 100644 --- a/src/testlib/qxmltestlogger_p.h +++ b/src/testlib/qxmltestlogger_p.h @@ -79,6 +79,9 @@ public: void addMessage(MessageTypes type, const char *message, const char *file = 0, int line = 0); + static void xmlCdata(char* dest, char const* src, size_t n); + static void xmlQuote(char* dest, char const* src, size_t n); + private: XmlMode xmlmode; }; diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro index 90bd92d..ae4f182 100644 --- a/src/testlib/testlib.pro +++ b/src/testlib/testlib.pro @@ -1,27 +1,68 @@ -TARGET = QtTest +TARGET = QtTest QPRO_PWD = $$PWD -QT = core +QT = core INCLUDEPATH += . - -unix:!embedded { - QMAKE_PKGCONFIG_DESCRIPTION = Qt Unit Testing Library - QMAKE_PKGCONFIG_REQUIRES = QtCore -} +unix:!embedded:QMAKE_PKGCONFIG_DESCRIPTION = Qt \ + Unit \ + Testing \ + Library # Input -HEADERS = qtest_global.h qtestcase.h qtestdata.h qtesteventloop.h -SOURCES = qtestcase.cpp qtestlog.cpp qtesttable.cpp qtestdata.cpp qtestresult.cpp qasciikey.cpp qplaintestlogger.cpp qxmltestlogger.cpp qsignaldumper.cpp qabstracttestlogger.cpp qbenchmark.cpp qbenchmarkmeasurement.cpp qbenchmarkvalgrind.cpp qbenchmarkevent.cpp - -DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII QTESTLIB_MAKEDLL QT_NO_DATASTREAM - -wince*:{ - LIBS += libcmt.lib corelibc.lib ole32.lib oleaut32.lib uuid.lib commctrl.lib coredll.lib winsock.lib -} - -mac { - LIBS += -framework IOKit -framework Security -} - +HEADERS = qtest_global.h \ + qtestcase.h \ + qtestdata.h \ + qtesteventloop.h \ + qtestcorelist.h \ + qtestcoreelement.h \ + qtestelement.h \ + qtestelementattribute.h \ + qtestbasicstreamer.h \ + qtestxunitstreamer.h \ + qtestxmlstreamer.h \ + qtestlightxmlstreamer.h \ + qtestfilelogger.h +SOURCES = qtestcase.cpp \ + qtestlog.cpp \ + qtesttable.cpp \ + qtestdata.cpp \ + qtestresult.cpp \ + qasciikey.cpp \ + qplaintestlogger.cpp \ + qxmltestlogger.cpp \ + qsignaldumper.cpp \ + qabstracttestlogger.cpp \ + qbenchmark.cpp \ + qbenchmarkmeasurement.cpp \ + qbenchmarkvalgrind.cpp \ + qbenchmarkevent.cpp \ + qtestelement.cpp \ + qtestelementattribute.cpp \ + qtestbasicstreamer.cpp \ + qtestxunitstreamer.cpp \ + qtestxmlstreamer.cpp \ + qtestlightxmlstreamer.cpp \ + qtestlogger.cpp \ + qtestfilelogger.cpp +DEFINES += QT_NO_CAST_TO_ASCII \ + QT_NO_CAST_FROM_ASCII \ + QTESTLIB_MAKEDLL \ + QT_NO_DATASTREAM +embedded:QMAKE_CXXFLAGS += -fno-rtti +wince*::LIBS += libcmt.lib \ + corelibc.lib \ + ole32.lib \ + oleaut32.lib \ + uuid.lib \ + commctrl.lib \ + coredll.lib \ + winsock.lib +mac:LIBS += -framework \ + IOKit \ + -framework \ + Security include(../qbase.pri) QMAKE_TARGET_PRODUCT = QTestLib -QMAKE_TARGET_DESCRIPTION = Qt Unit Testing Library +QMAKE_TARGET_DESCRIPTION = Qt \ + Unit \ + Testing \ + Library diff --git a/src/tools/uic/ui4.cpp b/src/tools/uic/ui4.cpp index d6cd759..69d0c53 100644 --- a/src/tools/uic/ui4.cpp +++ b/src/tools/uic/ui4.cpp @@ -2368,6 +2368,7 @@ void DomCustomWidget::clear(bool clear_all) delete m_script; delete m_properties; delete m_slots; + delete m_propertyspecifications; if (clear_all) { m_text.clear(); @@ -2381,6 +2382,7 @@ void DomCustomWidget::clear(bool clear_all) m_script = 0; m_properties = 0; m_slots = 0; + m_propertyspecifications = 0; } DomCustomWidget::DomCustomWidget() @@ -2393,6 +2395,7 @@ DomCustomWidget::DomCustomWidget() m_script = 0; m_properties = 0; m_slots = 0; + m_propertyspecifications = 0; } DomCustomWidget::~DomCustomWidget() @@ -2403,6 +2406,7 @@ DomCustomWidget::~DomCustomWidget() delete m_script; delete m_properties; delete m_slots; + delete m_propertyspecifications; } void DomCustomWidget::read(QXmlStreamReader &reader) @@ -2468,6 +2472,12 @@ void DomCustomWidget::read(QXmlStreamReader &reader) setElementSlots(v); continue; } + if (tag == QLatin1String("propertyspecifications")) { + DomPropertySpecifications *v = new DomPropertySpecifications(); + v->read(reader); + setElementPropertyspecifications(v); + continue; + } reader.raiseError(QLatin1String("Unexpected element ") + tag); } break; @@ -2548,6 +2558,12 @@ void DomCustomWidget::read(const QDomElement &node) setElementSlots(v); continue; } + if (tag == QLatin1String("propertyspecifications")) { + DomPropertySpecifications *v = new DomPropertySpecifications(); + v->read(e); + setElementPropertyspecifications(v); + continue; + } } m_text.clear(); for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { @@ -2605,6 +2621,10 @@ void DomCustomWidget::write(QXmlStreamWriter &writer, const QString &tagName) co m_slots->write(writer, QLatin1String("slots")); } + if (m_children & Propertyspecifications) { + m_propertyspecifications->write(writer, QLatin1String("propertyspecifications")); + } + if (!m_text.isEmpty()) writer.writeCharacters(m_text); @@ -2731,6 +2751,21 @@ void DomCustomWidget::setElementSlots(DomSlots* a) m_slots = a; } +DomPropertySpecifications* DomCustomWidget::takeElementPropertyspecifications() +{ + DomPropertySpecifications* a = m_propertyspecifications; + m_propertyspecifications = 0; + m_children ^= Propertyspecifications; + return a; +} + +void DomCustomWidget::setElementPropertyspecifications(DomPropertySpecifications* a) +{ + delete m_propertyspecifications; + m_children |= Propertyspecifications; + m_propertyspecifications = a; +} + void DomCustomWidget::clearElementClass() { m_children &= ~Class; @@ -2798,6 +2833,13 @@ void DomCustomWidget::clearElementSlots() m_children &= ~Slots; } +void DomCustomWidget::clearElementPropertyspecifications() +{ + delete m_propertyspecifications; + m_propertyspecifications = 0; + m_children &= ~Propertyspecifications; +} + void DomProperties::clear(bool clear_all) { qDeleteAll(m_property); @@ -10883,5 +10925,208 @@ void DomSlots::setElementSlot(const QStringList& a) m_slot = a; } +void DomPropertySpecifications::clear(bool clear_all) +{ + qDeleteAll(m_stringpropertyspecification); + m_stringpropertyspecification.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomPropertySpecifications::DomPropertySpecifications() +{ + m_children = 0; +} + +DomPropertySpecifications::~DomPropertySpecifications() +{ + qDeleteAll(m_stringpropertyspecification); + m_stringpropertyspecification.clear(); +} + +void DomPropertySpecifications::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("stringpropertyspecification")) { + DomStringPropertySpecification *v = new DomStringPropertySpecification(); + v->read(reader); + m_stringpropertyspecification.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomPropertySpecifications::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("stringpropertyspecification")) { + DomStringPropertySpecification *v = new DomStringPropertySpecification(); + v->read(e); + m_stringpropertyspecification.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomPropertySpecifications::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("propertyspecifications") : tagName.toLower()); + + for (int i = 0; i < m_stringpropertyspecification.size(); ++i) { + DomStringPropertySpecification* v = m_stringpropertyspecification[i]; + v->write(writer, QLatin1String("stringpropertyspecification")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomPropertySpecifications::setElementStringpropertyspecification(const QList<DomStringPropertySpecification*>& a) +{ + m_children |= Stringpropertyspecification; + m_stringpropertyspecification = a; +} + +void DomStringPropertySpecification::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + m_has_attr_type = false; + m_has_attr_notr = false; + } + + m_children = 0; +} + +DomStringPropertySpecification::DomStringPropertySpecification() +{ + m_children = 0; + m_has_attr_name = false; + m_has_attr_type = false; + m_has_attr_notr = false; +} + +DomStringPropertySpecification::~DomStringPropertySpecification() +{ +} + +void DomStringPropertySpecification::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + if (name == QLatin1String("type")) { + setAttributeType(attribute.value().toString()); + continue; + } + if (name == QLatin1String("notr")) { + setAttributeNotr(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomStringPropertySpecification::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + if (node.hasAttribute(QLatin1String("type"))) + setAttributeType(node.attribute(QLatin1String("type"))); + if (node.hasAttribute(QLatin1String("notr"))) + setAttributeNotr(node.attribute(QLatin1String("notr"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomStringPropertySpecification::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("stringpropertyspecification") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (hasAttributeType()) + writer.writeAttribute(QLatin1String("type"), attributeType()); + + if (hasAttributeNotr()) + writer.writeAttribute(QLatin1String("notr"), attributeNotr()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + QT_END_NAMESPACE diff --git a/src/tools/uic/ui4.h b/src/tools/uic/ui4.h index df02a39..fa70573 100644 --- a/src/tools/uic/ui4.h +++ b/src/tools/uic/ui4.h @@ -161,6 +161,8 @@ class DomScript; class DomWidgetData; class DomDesignerData; class DomSlots; +class DomPropertySpecifications; +class DomStringPropertySpecification; /******************************************************************************* ** Declarations @@ -1015,6 +1017,12 @@ public: inline bool hasElementSlots() const { return m_children & Slots; } void clearElementSlots(); + inline DomPropertySpecifications* elementPropertyspecifications() const { return m_propertyspecifications; } + DomPropertySpecifications* takeElementPropertyspecifications(); + void setElementPropertyspecifications(DomPropertySpecifications* a); + inline bool hasElementPropertyspecifications() const { return m_children & Propertyspecifications; } + void clearElementPropertyspecifications(); + private: QString m_text; void clear(bool clear_all = true); @@ -1033,6 +1041,7 @@ private: DomScript* m_script; DomProperties* m_properties; DomSlots* m_slots; + DomPropertySpecifications* m_propertyspecifications; enum Child { Class = 1, Extends = 2, @@ -1044,7 +1053,8 @@ private: Pixmap = 128, Script = 256, Properties = 512, - Slots = 1024 + Slots = 1024, + Propertyspecifications = 2048 }; DomCustomWidget(const DomCustomWidget &other); @@ -3686,6 +3696,91 @@ private: void operator = (const DomSlots&other); }; +class QDESIGNER_UILIB_EXPORT DomPropertySpecifications { +public: + DomPropertySpecifications(); + ~DomPropertySpecifications(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList<DomStringPropertySpecification*> elementStringpropertyspecification() const { return m_stringpropertyspecification; } + void setElementStringpropertyspecification(const QList<DomStringPropertySpecification*>& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList<DomStringPropertySpecification*> m_stringpropertyspecification; + enum Child { + Stringpropertyspecification = 1 + }; + + DomPropertySpecifications(const DomPropertySpecifications &other); + void operator = (const DomPropertySpecifications&other); +}; + +class QDESIGNER_UILIB_EXPORT DomStringPropertySpecification { +public: + DomStringPropertySpecification(); + ~DomStringPropertySpecification(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + inline bool hasAttributeType() const { return m_has_attr_type; } + inline QString attributeType() const { return m_attr_type; } + inline void setAttributeType(const QString& a) { m_attr_type = a; m_has_attr_type = true; } + inline void clearAttributeType() { m_has_attr_type = false; } + + inline bool hasAttributeNotr() const { return m_has_attr_notr; } + inline QString attributeNotr() const { return m_attr_notr; } + inline void setAttributeNotr(const QString& a) { m_attr_notr = a; m_has_attr_notr = true; } + inline void clearAttributeNotr() { m_has_attr_notr = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + QString m_attr_type; + bool m_has_attr_type; + + QString m_attr_notr; + bool m_has_attr_notr; + + // child element data + uint m_children; + + DomStringPropertySpecification(const DomStringPropertySpecification &other); + void operator = (const DomStringPropertySpecification&other); +}; + #ifdef QFORMINTERNAL_NAMESPACE } diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 60e6657..443ee7e 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -431,3 +431,4 @@ contains(QT_CONFIG, webkit): SUBDIRS += \ qwebframe \ qwebpage +SUBDIRS += math3d diff --git a/tests/auto/math3d/math3d.pro b/tests/auto/math3d/math3d.pro new file mode 100644 index 0000000..3977e92 --- /dev/null +++ b/tests/auto/math3d/math3d.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = qfixedpt qmatrixnxn qquaternion qvectornd +SUBDIRS += qmatrixnxn_fixed qquaternion_fixed qvectornd_fixed diff --git a/tests/auto/math3d/qfixedpt/qfixedpt.pro b/tests/auto/math3d/qfixedpt/qfixedpt.pro new file mode 100644 index 0000000..94598b5 --- /dev/null +++ b/tests/auto/math3d/qfixedpt/qfixedpt.pro @@ -0,0 +1,2 @@ +load(qttest_p4) +SOURCES += tst_qfixedpt.cpp diff --git a/tests/auto/math3d/qfixedpt/tst_qfixedpt.cpp b/tests/auto/math3d/qfixedpt/tst_qfixedpt.cpp new file mode 100644 index 0000000..ef333a6 --- /dev/null +++ b/tests/auto/math3d/qfixedpt/tst_qfixedpt.cpp @@ -0,0 +1,643 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qmath.h> +#include <QtGui/qfixedpt.h> + +class tst_QFixedPt : public QObject +{ + Q_OBJECT +public: + tst_QFixedPt() {} + ~tst_QFixedPt() {} + +private slots: + void create_data(); + void create(); + void add_data(); + void add(); + void sub_data(); + void sub(); + void mul_data(); + void mul(); + void div_data(); + void div(); + void neg_data(); + void neg(); + void shift_data(); + void shift(); + void sqrt_data(); + void sqrt(); + void round_data(); + void round(); + void compare_data(); + void compare(); +}; + +// qFuzzyCompare isn't quite "fuzzy" enough to handle conversion +// to fixed-point and back again. So create a "fuzzier" compare. +static bool fuzzyCompare(double x, double y) +{ + double diff = x - y; + if (diff < 0.0f) + diff = -diff; + return (diff < 0.0001); +} + +// Test the creation of QFixedPt values in various ways. +void tst_QFixedPt::create_data() +{ + QTest::addColumn<int>("value"); + QTest::addColumn<int>("intValue"); + QTest::addColumn<float>("realValue"); + + QTest::newRow("zero") << 0x00000000 << 0 << 0.0f; + QTest::newRow("one") << 0x00010000 << 1 << 1.0f; + QTest::newRow("1.5") << 0x00018000 << 2 << 1.5f; // int rounds up + QTest::newRow("-1.5") << (int)0xFFFE8000 << -1 << -1.5f; // int rounds up + QTest::newRow("-7") << (int)0xFFF90000 << -7 << -7.0f; +} +void tst_QFixedPt::create() +{ + QFETCH(int, value); + QFETCH(int, intValue); + QFETCH(float, realValue); + + if (qFloor(realValue) == realValue) { + QFixedPt<16> ivalue(intValue); + QCOMPARE(ivalue.bits(), value); + QCOMPARE(ivalue.toInt(), intValue); + QCOMPARE(ivalue.toReal(), (qreal)realValue); + } + + QFixedPt<16> fvalue(realValue); + QCOMPARE(fvalue.bits(), value); + QCOMPARE(fvalue.toInt(), intValue); + QCOMPARE(fvalue.toReal(), (qreal)realValue); + + QFixedPt<16> fpvalue; + fpvalue.setBits(value); + QCOMPARE(fpvalue.bits(), value); + QCOMPARE(fpvalue.toInt(), intValue); + QCOMPARE(fpvalue.toReal(), (qreal)realValue); + + QFixedPt<16> fpvalue2(fpvalue); + QCOMPARE(fpvalue2.bits(), value); + QCOMPARE(fpvalue2.toInt(), intValue); + QCOMPARE(fpvalue2.toReal(), (qreal)realValue); + + if (qFloor(realValue) == realValue) { + QFixedPt<16> ivalue2(63); // Initialize the something else. + ivalue2 = intValue; // Then change the value. + QCOMPARE(ivalue2.bits(), value); + QCOMPARE(ivalue2.toInt(), intValue); + QCOMPARE(ivalue2.toReal(), (qreal)realValue); + } + + QFixedPt<16> fvalue2(36); + fvalue2 = realValue; + QCOMPARE(fvalue2.bits(), value); + QCOMPARE(fvalue2.toInt(), intValue); + QCOMPARE(fvalue2.toReal(), (qreal)realValue); + + QFixedPt<16> fpvalue3; + fpvalue3 = fpvalue; + QCOMPARE(fpvalue3.bits(), value); + QCOMPARE(fpvalue3.toInt(), intValue); + QCOMPARE(fpvalue3.toReal(), (qreal)realValue); + + // Now do some of the tests again with a different precision value. + + if (qFloor(realValue) == realValue) { + QFixedPt<4> ivalue3(intValue); + QCOMPARE(ivalue3.bits(), value >> 12); + QCOMPARE(ivalue3.toInt(), intValue); + QCOMPARE(ivalue3.toReal(), (qreal)realValue); + } + + QFixedPt<4> fvalue3(realValue); + QCOMPARE(fvalue3.bits(), value >> 12); + QCOMPARE(fvalue3.toInt(), intValue); + QCOMPARE(fvalue3.toReal(), (qreal)realValue); + + QFixedPt<4> fpvalue4; + fpvalue4.setBits(value >> 12); + QCOMPARE(fpvalue4.bits(), value >> 12); + QCOMPARE(fpvalue4.toInt(), intValue); + QCOMPARE(fpvalue4.toReal(), (qreal)realValue); + + // Test conversion between the precision values. + +#if !defined(QT_NO_MEMBER_TEMPLATES) + if (qFloor(realValue) == realValue) { + QFixedPt<16> ivalue(intValue); + QFixedPt<4> ivalue3(intValue); + QVERIFY(ivalue.toPrecision<4>() == ivalue3); + QVERIFY(ivalue3.toPrecision<16>() == ivalue); + } + QVERIFY(fvalue.toPrecision<4>() == fvalue3); + QVERIFY(fvalue3.toPrecision<16>() == fvalue); +#endif + + if (qFloor(realValue) == realValue) { + QFixedPt<16> ivalue(intValue); + QFixedPt<4> ivalue3(intValue); + QVERIFY(qFixedPtToPrecision<4>(ivalue) == ivalue3); + QVERIFY(qFixedPtToPrecision<16>(ivalue3) == ivalue); + } + QVERIFY(qFixedPtToPrecision<4>(fvalue) == fvalue3); + QVERIFY(qFixedPtToPrecision<16>(fvalue3) == fvalue); +} + +// Test fixed point addition. +void tst_QFixedPt::add_data() +{ + QTest::addColumn<float>("a"); + QTest::addColumn<float>("b"); + + QTest::newRow("zero") << 0.0f << 0.0f; + QTest::newRow("test1") << 1.0f << 0.0f; + QTest::newRow("test2") << 0.0f << 1.0f; + QTest::newRow("test3") << 10.0f << -3.0f; + QTest::newRow("test4") << 10.5f << 3.25f; +} +void tst_QFixedPt::add() +{ + QFETCH(float, a); + QFETCH(float, b); + + QFixedPt<16> avalue(a); + QFixedPt<16> bvalue(b); + + QFixedPt<16> cvalue = avalue + bvalue; + QFixedPt<16> dvalue = a + bvalue; + QFixedPt<16> evalue = avalue + b; + + QCOMPARE(cvalue.toReal(), (qreal)(a + b)); + QCOMPARE(dvalue.toReal(), (qreal)(a + b)); + QCOMPARE(evalue.toReal(), (qreal)(a + b)); + + QFixedPt<16> fvalue(avalue); + fvalue += bvalue; + QCOMPARE(fvalue.toReal(), (qreal)(a + b)); + + QFixedPt<16> gvalue(avalue); + gvalue += b; + QCOMPARE(gvalue.toReal(), (qreal)(a + b)); + + if (qFloor(a) == a && qFloor(b) == b) { + QFixedPt<16> hvalue = int(a) + bvalue; + QFixedPt<16> ivalue = avalue + int(b); + + QCOMPARE(hvalue.toInt(), int(a) + int(b)); + QCOMPARE(ivalue.toInt(), int(a) + int(b)); + QCOMPARE(hvalue.toReal(), (qreal)(a + b)); + QCOMPARE(ivalue.toReal(), (qreal)(a + b)); + + QFixedPt<16> jvalue(avalue); + jvalue += int(b); + QCOMPARE(jvalue.toReal(), (qreal)(a + b)); + } +} + +// Test fixed point subtraction. +void tst_QFixedPt::sub_data() +{ + // Use the same test data as the add() test. + add_data(); +} +void tst_QFixedPt::sub() +{ + QFETCH(float, a); + QFETCH(float, b); + + QFixedPt<16> avalue(a); + QFixedPt<16> bvalue(b); + + QFixedPt<16> cvalue = avalue - bvalue; + QFixedPt<16> dvalue = a - bvalue; + QFixedPt<16> evalue = avalue - b; + + QCOMPARE(cvalue.toReal(), (qreal)(a - b)); + QCOMPARE(dvalue.toReal(), (qreal)(a - b)); + QCOMPARE(evalue.toReal(), (qreal)(a - b)); + + QFixedPt<16> fvalue(avalue); + fvalue -= bvalue; + QCOMPARE(fvalue.toReal(), (qreal)(a - b)); + + QFixedPt<16> gvalue(avalue); + gvalue -= b; + QCOMPARE(gvalue.toReal(), (qreal)(a - b)); + + if (qFloor(a) == a && qFloor(b) == b) { + QFixedPt<16> hvalue = int(a) - bvalue; + QFixedPt<16> ivalue = avalue - int(b); + + QCOMPARE(hvalue.toInt(), int(a) - int(b)); + QCOMPARE(ivalue.toInt(), int(a) - int(b)); + QCOMPARE(hvalue.toReal(), (qreal)(a - b)); + QCOMPARE(ivalue.toReal(), (qreal)(a - b)); + + QFixedPt<16> jvalue(avalue); + jvalue -= int(b); + QCOMPARE(jvalue.toReal(), (qreal)(a - b)); + } +} + +// Test fixed point multiplication. +void tst_QFixedPt::mul_data() +{ + // Use the same test data as the add() test. + add_data(); +} +void tst_QFixedPt::mul() +{ + QFETCH(float, a); + QFETCH(float, b); + + QFixedPt<16> avalue(a); + QFixedPt<16> bvalue(b); + + QFixedPt<16> cvalue = avalue * bvalue; + QFixedPt<16> dvalue = a * bvalue; + QFixedPt<16> evalue = avalue * b; + + QCOMPARE(cvalue.toReal(), (qreal)(a * b)); + QCOMPARE(dvalue.toReal(), (qreal)(a * b)); + QCOMPARE(evalue.toReal(), (qreal)(a * b)); + + QFixedPt<16> fvalue(avalue); + fvalue *= bvalue; + QCOMPARE(fvalue.toReal(), (qreal)(a * b)); + + QFixedPt<16> gvalue(avalue); + gvalue *= b; + QCOMPARE(gvalue.toReal(), (qreal)(a * b)); + + if (qFloor(a) == a && qFloor(b) == b) { + QFixedPt<16> hvalue = int(a) * bvalue; + QFixedPt<16> ivalue = avalue * int(b); + + QCOMPARE(hvalue.toInt(), int(a) * int(b)); + QCOMPARE(ivalue.toInt(), int(a) * int(b)); + QCOMPARE(hvalue.toReal(), (qreal)(a * b)); + QCOMPARE(ivalue.toReal(), (qreal)(a * b)); + + QFixedPt<16> jvalue(avalue); + jvalue *= int(b); + QCOMPARE(jvalue.toReal(), (qreal)(a * b)); + } +} + +// Test fixed point division. +void tst_QFixedPt::div_data() +{ + // Use the same test data as the add() test. + add_data(); +} +void tst_QFixedPt::div() +{ + QFETCH(float, a); + QFETCH(float, b); + + QFixedPt<16> avalue(a); + QFixedPt<16> bvalue(b); + + qreal result; + if (b == 0.0f) + result = 0.0f; // Divide by zero results in zero. + else + result = a / b; + + QFixedPt<16> cvalue = avalue / bvalue; + QFixedPt<16> dvalue = a / bvalue; + QFixedPt<16> evalue = avalue / b; + + QVERIFY(fuzzyCompare(cvalue.toReal(), result)); + QVERIFY(fuzzyCompare(dvalue.toReal(), result)); + QVERIFY(fuzzyCompare(evalue.toReal(), result)); + + QFixedPt<16> fvalue(avalue); + fvalue /= bvalue; + QVERIFY(fuzzyCompare(fvalue.toReal(), result)); + + QFixedPt<16> gvalue(avalue); + gvalue /= b; + QVERIFY(fuzzyCompare(gvalue.toReal(), result)); + + if (qFloor(a) == a && qFloor(b) == b) { + QFixedPt<16> hvalue = int(a) / bvalue; + QFixedPt<16> ivalue = avalue / int(b); + + QCOMPARE(hvalue.toInt(), int(result)); + QCOMPARE(ivalue.toInt(), int(result)); + QVERIFY(fuzzyCompare(hvalue.toReal(), result)); + QVERIFY(fuzzyCompare(ivalue.toReal(), result)); + + QFixedPt<16> jvalue(avalue); + jvalue /= int(b); + QVERIFY(fuzzyCompare(jvalue.toReal(), result)); + } +} + +// Test fixed point negation. +void tst_QFixedPt::neg_data() +{ + // Use the same test data as the add() test. + add_data(); +} +void tst_QFixedPt::neg() +{ + QFETCH(float, a); + QFETCH(float, b); + + QFixedPt<16> avalue(a); + QFixedPt<16> bvalue(b); + + QFixedPt<16> cvalue = -avalue; + QCOMPARE(cvalue.bits(), -avalue.bits()); + QCOMPARE(cvalue.toInt(), int(-a)); + QCOMPARE(cvalue.toReal(), (qreal)-a); + + QFixedPt<16> dvalue = -bvalue; + QCOMPARE(dvalue.bits(), -bvalue.bits()); + QCOMPARE(dvalue.toInt(), int(-b)); + QCOMPARE(dvalue.toReal(), (qreal)-b); +} + +// Test left and right shift operators on fixed point values. +void tst_QFixedPt::shift_data() +{ + QTest::addColumn<float>("a"); + QTest::addColumn<int>("amount"); + + QTest::newRow("zero") << 0.0f << 5; + QTest::newRow("one") << 1.0f << 4; + QTest::newRow("-1.75") << -1.75f << 4; +} +void tst_QFixedPt::shift() +{ + QFETCH(float, a); + QFETCH(int, amount); + + int lresult = int((a * 65536.0) * (1 << amount)); + int rresult = int((a * 65536.0) / (1 << amount)); + + QFixedPt<16> avalue(a); + avalue <<= amount; + QCOMPARE(avalue.bits(), lresult); + + QFixedPt<16> bvalue(a); + bvalue >>= amount; + QCOMPARE(bvalue.bits(), rresult); + + QFixedPt<16> cvalue(a); + + QFixedPt<16> dvalue; + dvalue = cvalue << amount; + QCOMPARE(dvalue.bits(), lresult); + + QFixedPt<16> evalue; + evalue = cvalue >> amount; + QCOMPARE(evalue.bits(), rresult); +} + +// Test fixed point square root. +void tst_QFixedPt::sqrt_data() +{ + QTest::addColumn<float>("a"); + + QTest::newRow("zero") << 0.0f; + QTest::newRow("one") << 1.0f; + QTest::newRow("two") << 2.0f; + QTest::newRow("sixteen") << 16.0f; + QTest::newRow("1.5") << 1.5f; +} +void tst_QFixedPt::sqrt() +{ + QFETCH(float, a); + + QFixedPt<16> avalue(a); + QVERIFY(fuzzyCompare(avalue.sqrt().toReal(), qSqrt(a))); + QVERIFY(fuzzyCompare(avalue.sqrtF(), qSqrt(a))); +} + +// Test fixed point rounding. +void tst_QFixedPt::round_data() +{ + QTest::addColumn<float>("a"); + QTest::addColumn<int>("rounded"); + QTest::addColumn<int>("ceiling"); + QTest::addColumn<int>("flooring"); + QTest::addColumn<int>("truncated"); + + QTest::newRow("zero") << 0.0f << 0 << 0 << 0 << 0; + QTest::newRow("test1") << 1.0f << 1 << 1 << 1 << 1; + QTest::newRow("test2") << 2.5f << 3 << 3 << 2 << 2; + QTest::newRow("test3") << 2.3f << 2 << 3 << 2 << 2; + QTest::newRow("test4") << -2.5f << -2 << -2 << -3 << -3; + QTest::newRow("test5") << -2.3f << -2 << -2 << -3 << -3; + QTest::newRow("test6") << -2.7f << -3 << -2 << -3 << -3; +} +void tst_QFixedPt::round() +{ + QFETCH(float, a); + QFETCH(int, rounded); + QFETCH(int, ceiling); + QFETCH(int, flooring); + QFETCH(int, truncated); + + QFixedPt<16> avalue(a); + QVERIFY(avalue.round() == rounded); + QVERIFY(avalue.ceil() == ceiling); + QVERIFY(avalue.floor() == flooring); + QVERIFY(avalue.truncate() == truncated); + + QCOMPARE(qRound(avalue), rounded); + QCOMPARE(qCeil(avalue), ceiling); + QCOMPARE(qFloor(avalue), flooring); +} + +// Test comparison operators. +void tst_QFixedPt::compare_data() +{ + QTest::addColumn<float>("a"); + QTest::addColumn<float>("b"); + + QTest::newRow("test1") << 0.0f << 0.0f; + QTest::newRow("test2") << 1.0f << 0.0f; + QTest::newRow("test3") << 2.5f << 2.5f; + QTest::newRow("test4") << 2.5f << -2.5f; + QTest::newRow("test5") << -2.5f << 2.5f; +} +void tst_QFixedPt::compare() +{ + QFETCH(float, a); + QFETCH(float, b); + + QFixedPt<16> avalue(a); + QFixedPt<16> bvalue(b); + + if (a == b) { + QVERIFY(avalue == bvalue); + QVERIFY(avalue == b); + QVERIFY(a == bvalue); + QVERIFY(!(avalue != bvalue)); + QVERIFY(!(avalue != b)); + QVERIFY(!(a != bvalue)); + QVERIFY(!(avalue < bvalue)); + QVERIFY(!(avalue < b)); + QVERIFY(!(a < bvalue)); + QVERIFY(!(avalue > bvalue)); + QVERIFY(!(avalue > b)); + QVERIFY(!(a > bvalue)); + QVERIFY(avalue <= bvalue); + QVERIFY(avalue <= b); + QVERIFY(a <= bvalue); + QVERIFY(avalue >= bvalue); + QVERIFY(avalue >= b); + QVERIFY(a >= bvalue); + if (qFloor(a) == a) { + QVERIFY(int(a) == bvalue); + QVERIFY(!(int(a) != bvalue)); + QVERIFY(!(int(a) < bvalue)); + QVERIFY(!(int(a) > bvalue)); + QVERIFY(int(a) <= bvalue); + QVERIFY(int(a) >= bvalue); + } + if (qFloor(b) == b) { + QVERIFY(avalue == int(b)); + QVERIFY(!(avalue != int(b))); + QVERIFY(!(avalue < int(b))); + QVERIFY(!(avalue > int(b))); + QVERIFY(avalue <= int(b)); + QVERIFY(avalue >= int(b)); + } + } + + if (a != b) { + QVERIFY(avalue != bvalue); + QVERIFY(avalue != b); + QVERIFY(a != bvalue); + QVERIFY(!(avalue == bvalue)); + QVERIFY(!(avalue == b)); + QVERIFY(!(a == bvalue)); + if (qFloor(a) == a) { + QVERIFY(int(a) != bvalue); + QVERIFY(!(int(a) == bvalue)); + } + if (qFloor(b) == b) { + QVERIFY(avalue != int(b)); + QVERIFY(!(avalue == int(b))); + } + } + + if (a < b) { + QVERIFY(avalue < bvalue); + QVERIFY(avalue < b); + QVERIFY(a < bvalue); + QVERIFY(!(avalue >= bvalue)); + QVERIFY(!(avalue >= b)); + QVERIFY(!(a >= bvalue)); + QVERIFY(!(avalue > bvalue)); + QVERIFY(!(avalue > b)); + QVERIFY(!(a > bvalue)); + QVERIFY(avalue <= bvalue); + QVERIFY(avalue <= b); + QVERIFY(a <= bvalue); + QVERIFY(!(avalue >= bvalue)); + QVERIFY(!(avalue >= b)); + QVERIFY(!(a >= bvalue)); + if (qFloor(a) == a) { + QVERIFY(int(a) < bvalue); + QVERIFY(!(int(a) >= bvalue)); + QVERIFY(!(int(a) > bvalue)); + QVERIFY(int(a) <= bvalue); + } + if (qFloor(b) == b) { + QVERIFY(avalue < int(b)); + QVERIFY(!(avalue >= int(b))); + QVERIFY(!(avalue > int(b))); + QVERIFY(avalue <= int(b)); + } + } + + if (a > b) { + QVERIFY(avalue > bvalue); + QVERIFY(avalue > b); + QVERIFY(a > bvalue); + QVERIFY(!(avalue <= bvalue)); + QVERIFY(!(avalue <= b)); + QVERIFY(!(a <= bvalue)); + QVERIFY(!(avalue < bvalue)); + QVERIFY(!(avalue < b)); + QVERIFY(!(a < bvalue)); + QVERIFY(avalue >= bvalue); + QVERIFY(avalue >= b); + QVERIFY(a >= bvalue); + QVERIFY(!(avalue <= bvalue)); + QVERIFY(!(avalue <= b)); + QVERIFY(!(a <= bvalue)); + if (qFloor(a) == a) { + QVERIFY(int(a) > bvalue); + QVERIFY(!(int(a) <= bvalue)); + QVERIFY(!(int(a) < bvalue)); + QVERIFY(int(a) >= bvalue); + } + if (qFloor(b) == b) { + QVERIFY(avalue > int(b)); + QVERIFY(!(avalue <= int(b))); + QVERIFY(!(avalue < int(b))); + QVERIFY(avalue >= int(b)); + } + } + + if (qFuzzyCompare(a, b)) + QVERIFY(qFuzzyCompare(avalue, bvalue)); + else + QVERIFY(!qFuzzyCompare(avalue, bvalue)); +} + +QTEST_APPLESS_MAIN(tst_QFixedPt) + +#include "tst_qfixedpt.moc" diff --git a/tests/auto/math3d/qmatrixnxn/qmatrixnxn.pro b/tests/auto/math3d/qmatrixnxn/qmatrixnxn.pro new file mode 100644 index 0000000..40c6cc0 --- /dev/null +++ b/tests/auto/math3d/qmatrixnxn/qmatrixnxn.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +VPATH += ../shared +INCLUDEPATH += ../shared +HEADERS += math3dincludes.h +SOURCES += tst_qmatrixnxn.cpp diff --git a/tests/auto/math3d/qmatrixnxn/tst_qmatrixnxn.cpp b/tests/auto/math3d/qmatrixnxn/tst_qmatrixnxn.cpp new file mode 100644 index 0000000..8a7dd49 --- /dev/null +++ b/tests/auto/math3d/qmatrixnxn/tst_qmatrixnxn.cpp @@ -0,0 +1,3248 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qmath.h> +#include "math3dincludes.h" + +class tst_QMatrix : public QObject +{ + Q_OBJECT +public: + tst_QMatrix() {} + ~tst_QMatrix() {} + +private slots: + void create2x2(); + void create3x3(); + void create4x4(); + void create4x3(); + + void isIdentity2x2(); + void isIdentity3x3(); + void isIdentity4x4(); + void isIdentity4x3(); + + void compare2x2(); + void compare3x3(); + void compare4x4(); + void compare4x3(); + + void transposed2x2(); + void transposed3x3(); + void transposed4x4(); + void transposed4x3(); + + void add2x2_data(); + void add2x2(); + void add3x3_data(); + void add3x3(); + void add4x4_data(); + void add4x4(); + void add4x3_data(); + void add4x3(); + + void subtract2x2_data(); + void subtract2x2(); + void subtract3x3_data(); + void subtract3x3(); + void subtract4x4_data(); + void subtract4x4(); + void subtract4x3_data(); + void subtract4x3(); + + void multiply2x2_data(); + void multiply2x2(); + void multiply3x3_data(); + void multiply3x3(); + void multiply4x4_data(); + void multiply4x4(); + void multiply4x3_data(); + void multiply4x3(); + + void multiplyFactor2x2_data(); + void multiplyFactor2x2(); + void multiplyFactor3x3_data(); + void multiplyFactor3x3(); + void multiplyFactor4x4_data(); + void multiplyFactor4x4(); + void multiplyFactor4x3_data(); + void multiplyFactor4x3(); + + void divideFactor2x2_data(); + void divideFactor2x2(); + void divideFactor3x3_data(); + void divideFactor3x3(); + void divideFactor4x4_data(); + void divideFactor4x4(); + void divideFactor4x3_data(); + void divideFactor4x3(); + + void negate2x2_data(); + void negate2x2(); + void negate3x3_data(); + void negate3x3(); + void negate4x4_data(); + void negate4x4(); + void negate4x3_data(); + void negate4x3(); + + void inverted4x4_data(); + void inverted4x4(); + + void orthonormalInverse4x4(); + + void scale4x4_data(); + void scale4x4(); + + void translate4x4_data(); + void translate4x4(); + + void rotate4x4_data(); + void rotate4x4(); + + void normalMatrix_data(); + void normalMatrix(); + + void optimizedTransforms(); + + void ortho(); + void frustum(); + void perspective(); + void flipCoordinates(); + + void convertGeneric(); + + void extractAxisRotation_data(); + void extractAxisRotation(); + + void extractTranslation_data(); + void extractTranslation(); + + void inferSpecialType_data(); + void inferSpecialType(); + + void columnsAndRows(); + + void convertQMatrix(); + void convertQTransform(); + + void fill(); + +private: + static void setMatrix(QMatrix2x2& m, const qreal *values); + static void setMatrixFixed(QMatrix2x2& m, const qreal *values); + static bool isSame(const QMatrix2x2& m, const qreal *values); + static bool isIdentity(const QMatrix2x2& m); + + static void setMatrix(QMatrix3x3& m, const qreal *values); + static void setMatrixFixed(QMatrix3x3& m, const qreal *values); + static bool isSame(const QMatrix3x3& m, const qreal *values); + static bool isIdentity(const QMatrix3x3& m); + + static void setMatrix(QMatrix4x4& m, const qreal *values); + static void setMatrixFixed(QMatrix4x4& m, const qreal *values); + static bool isSame(const QMatrix4x4& m, const qreal *values); + static bool isIdentity(const QMatrix4x4& m); + + static void setMatrix(QMatrix4x3& m, const qreal *values); + static void setMatrixFixed(QMatrix4x3& m, const qreal *values); + static bool isSame(const QMatrix4x3& m, const qreal *values); + static bool isIdentity(const QMatrix4x3& m); +}; + +static const qreal nullValues2[] = + {0.0f, 0.0f, + 0.0f, 0.0f}; + +static qreal const identityValues2[16] = + {1.0f, 0.0f, + 0.0f, 1.0f}; + +static const qreal doubleIdentity2[] = + {2.0f, 0.0f, + 0.0f, 2.0f}; + +static qreal const uniqueValues2[16] = + {1.0f, 2.0f, + 5.0f, 6.0f}; + +static qreal const transposedValues2[16] = + {1.0f, 5.0f, + 2.0f, 6.0f}; + +static const qreal nullValues3[] = + {0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f}; + +static qreal const identityValues3[16] = + {1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f}; + +static const qreal doubleIdentity3[] = + {2.0f, 0.0f, 0.0f, + 0.0f, 2.0f, 0.0f, + 0.0f, 0.0f, 2.0f}; + +static qreal const uniqueValues3[16] = + {1.0f, 2.0f, 3.0f, + 5.0f, 6.0f, 7.0f, + 9.0f, 10.0f, 11.0f}; + +static qreal const transposedValues3[16] = + {1.0f, 5.0f, 9.0f, + 2.0f, 6.0f, 10.0f, + 3.0f, 7.0f, 11.0f}; + +static const qreal nullValues4[] = + {0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f}; + +static qreal const identityValues4[16] = + {1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + +static const qreal doubleIdentity4[] = + {2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 2.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 2.0f}; + +static qreal const uniqueValues4[16] = + {1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f}; + +static qreal const transposedValues4[16] = + {1.0f, 5.0f, 9.0f, 13.0f, + 2.0f, 6.0f, 10.0f, 14.0f, + 3.0f, 7.0f, 11.0f, 15.0f, + 4.0f, 8.0f, 12.0f, 16.0f}; + +static const qreal nullValues4x3[] = + {0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f}; + +static qreal const identityValues4x3[12] = + {1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f}; + +static qreal const doubleIdentity4x3[12] = + {2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 2.0f, 0.0f}; + +static qreal const uniqueValues4x3[12] = + {1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f}; + +static qreal const transposedValues3x4[12] = + {1.0f, 5.0f, 9.0f, + 2.0f, 6.0f, 10.0f, + 3.0f, 7.0f, 11.0f, + 4.0f, 8.0f, 12.0f}; + +// Set a matrix to a specified array of values, which are assumed +// to be in row-major order. This sets the values using floating-point. +void tst_QMatrix::setMatrix(QMatrix2x2& m, const qreal *values) +{ + for (int row = 0; row < 2; ++row) + for (int col = 0; col < 2; ++col) + m(row, col) = values[row * 2 + col]; +} +void tst_QMatrix::setMatrix(QMatrix3x3& m, const qreal *values) +{ + for (int row = 0; row < 3; ++row) + for (int col = 0; col < 3; ++col) + m(row, col) = values[row * 3 + col]; +} +void tst_QMatrix::setMatrix(QMatrix4x4& m, const qreal *values) +{ + for (int row = 0; row < 4; ++row) + for (int col = 0; col < 4; ++col) + m(row, col) = values[row * 4 + col]; +} +void tst_QMatrix::setMatrix(QMatrix4x3& m, const qreal *values) +{ + for (int row = 0; row < 3; ++row) + for (int col = 0; col < 4; ++col) + m(row, col) = values[row * 4 + col]; +} + +// Set a matrix to a specified array of values, which are assumed +// to be in row-major order. This sets the values using fixed-point. +void tst_QMatrix::setMatrixFixed(QMatrix2x2& m, const qreal *values) +{ + qrealinner *data = m.data(); + for (int row = 0; row < 2; ++row) { + for (int col = 0; col < 2; ++col) { + data[row + col * 2] = values[row * 2 + col]; + } + } +} +void tst_QMatrix::setMatrixFixed(QMatrix3x3& m, const qreal *values) +{ + qrealinner *data = m.data(); + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + data[row + col * 3] = values[row * 3 + col]; + } + } +} +void tst_QMatrix::setMatrixFixed(QMatrix4x4& m, const qreal *values) +{ + qrealinner *data = m.data(); + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 4; ++col) { + data[row + col * 4] = values[row * 4 + col]; + } + } +} +void tst_QMatrix::setMatrixFixed(QMatrix4x3& m, const qreal *values) +{ + qrealinner *data = m.data(); + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 4; ++col) { + data[row + col * 3] = values[row * 4 + col]; + } + } +} + +// qFuzzyCompare isn't quite "fuzzy" enough to handle conversion +// to fixed-point and back again. So create "fuzzier" compares. +static bool fuzzyCompare(float x, float y, qreal epsilon = 0.001) +{ + float diff = x - y; + if (diff < 0.0f) + diff = -diff; + return (diff < epsilon); +} +#ifdef QT_GL_FIXED_PREFERRED +static bool fuzzyCompareFixed(qrealinner x, int y) +{ + int diff = x.bits() - y; + if (diff < 0) + diff = -diff; + return (diff < 50); +} +#endif + +static bool fuzzyCompare(const QVector3D &v1, const QVector3D &v2, qreal epsilon = 0.001) +{ + if (!fuzzyCompare(v1.x(), v2.x(), epsilon)) + return false; + if (!fuzzyCompare(v1.y(), v2.y(), epsilon)) + return false; + if (!fuzzyCompare(v1.z(), v2.z(), epsilon)) + return false; + return true; +} + +static bool matrixFuzzyCompare(const QMatrix4x4 &m1, const QMatrix4x4 &m2) +{ + bool ret = true; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + ret = ret && fuzzyCompare(m1(i, j), m2(i, j)); + } + } + + return ret; +} + +// Determine if a matrix is the same as a specified array of values. +// The values are assumed to be specified in row-major order. +bool tst_QMatrix::isSame(const QMatrix2x2& m, const qreal *values) +{ + const qrealinner *mv = m.constData(); + for (int row = 0; row < 2; ++row) { + for (int col = 0; col < 2; ++col) { + // Check the values using the operator() function. + if (!fuzzyCompare((float)(m(row, col)), (float)(values[row * 2 + col]))) { + qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 2 + col]; + return false; + } + + // Check the values using direct access, which verifies that the values + // are stored internally in column-major order. +#ifdef QT_GL_FIXED_PREFERRED + if (!fuzzyCompareFixed(mv[col * 2 + row], (int)(values[row * 2 + col] * 65536.0))) { + qDebug() << "column fixed-point failure at" << row << col << "actual =" << mv[col * 2 + row] << "expected =" << (int)(values[row * 2 + col] * 65536.0); + return false; + } +#else + if (!fuzzyCompare((float)(mv[col * 2 + row]), (float)(values[row * 2 + col]))) { + qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 2 + row] << "expected =" << values[row * 2 + col]; + return false; + } +#endif + } + } + return true; +} +bool tst_QMatrix::isSame(const QMatrix3x3& m, const qreal *values) +{ + const qrealinner *mv = m.constData(); + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + // Check the values using the operator() access function. + if (!fuzzyCompare((float)(m(row, col)), (float)(values[row * 3 + col]))) { + qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 3 + col]; + return false; + } + + // Check the values using direct access, which verifies that the values + // are stored internally in column-major order. +#ifdef QT_GL_FIXED_PREFERRED + if (!fuzzyCompareFixed(mv[col * 3 + row], (int)(values[row * 3 + col] * 65536.0))) { + qDebug() << "column fixed-point failure at" << row << col << "actual =" << mv[col * 3 + row] << "expected =" << (int)(values[row * 3 + col] * 65536.0); + return false; + } +#else + if (!fuzzyCompare((float)(mv[col * 3 + row]), (float)(values[row * 3 + col]))) { + qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 3 + row] << "expected =" << values[row * 3 + col]; + return false; + } +#endif + } + } + return true; +} +bool tst_QMatrix::isSame(const QMatrix4x4& m, const qreal *values) +{ + const qrealinner *mv = m.constData(); + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 4; ++col) { + // Check the values using the operator() access function. + if (!fuzzyCompare((float)(m(row, col)), (float)(values[row * 4 + col]))) { + qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 4 + col]; + return false; + } + + // Check the values using direct access, which verifies that the values + // are stored internally in column-major order. +#ifdef QT_GL_FIXED_PREFERRED + if (!fuzzyCompareFixed(mv[col * 4 + row], (int)(values[row * 4 + col] * 65536.0))) { + qDebug() << "column fixed-point failure at" << row << col << "actual =" << mv[col * 4 + row] << "expected =" << (int)(values[row * 4 + col] * 65536.0); + return false; + } +#else + if (!fuzzyCompare((float)(mv[col * 4 + row]), (float)(values[row * 4 + col]))) { + qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 4 + row] << "expected =" << values[row * 4 + col]; + return false; + } +#endif + } + } + return true; +} +bool tst_QMatrix::isSame(const QMatrix4x3& m, const qreal *values) +{ + const qrealinner *mv = m.constData(); + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 4; ++col) { + // Check the values using the operator() access function. + if (!fuzzyCompare((float)(m(row, col)), (float)(values[row * 4 + col]))) { + qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 4 + col]; + return false; + } + + // Check the values using direct access, which verifies that the values + // are stored internally in column-major order. +#ifdef QT_GL_FIXED_PREFERRED + if (!fuzzyCompareFixed(mv[col * 3 + row], (int)(values[row * 4 + col] * 65536.0))) { + qDebug() << "column fixed-point failure at" << row << col << "actual =" << mv[col * 3 + row] << "expected =" << (int)(values[row * 4 + col] * 65536.0); + return false; + } +#else + if (!fuzzyCompare((float)(mv[col * 3 + row]), (float)(values[row * 4 + col]))) { + qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 3 + row] << "expected =" << values[row * 4 + col]; + return false; + } +#endif + } + } + return true; +} + +// Determine if a matrix is the identity. +bool tst_QMatrix::isIdentity(const QMatrix2x2& m) +{ + return isSame(m, identityValues2); +} +bool tst_QMatrix::isIdentity(const QMatrix3x3& m) +{ + return isSame(m, identityValues3); +} +bool tst_QMatrix::isIdentity(const QMatrix4x4& m) +{ + return isSame(m, identityValues4); +} +bool tst_QMatrix::isIdentity(const QMatrix4x3& m) +{ + return isSame(m, identityValues4x3); +} + +// Test the creation of QMatrix2x2 objects in various ways: +// construct, copy, and modify. +void tst_QMatrix::create2x2() +{ + QMatrix2x2 m1; + QVERIFY(isIdentity(m1)); + QVERIFY(m1.isIdentity()); + + QMatrix2x2 m2; + setMatrix(m2, uniqueValues2); + QVERIFY(isSame(m2, uniqueValues2)); + QVERIFY(!m2.isIdentity()); + + QMatrix2x2 m3; + setMatrixFixed(m3, uniqueValues2); + QVERIFY(isSame(m3, uniqueValues2)); + + QMatrix2x2 m4(m3); + QVERIFY(isSame(m4, uniqueValues2)); + + QMatrix2x2 m5; + m5 = m3; + QVERIFY(isSame(m5, uniqueValues2)); + + m5.setIdentity(); + QVERIFY(isIdentity(m5)); + + QMatrix2x2 m6(uniqueValues2); + QVERIFY(isSame(m6, uniqueValues2)); + qreal vals[4]; + m6.toValueArray(vals); + for (int index = 0; index < 4; ++index) + QCOMPARE((float)(vals[index]), (float)(uniqueValues2[index])); +} + +// Test the creation of QMatrix3x3 objects in various ways: +// construct, copy, and modify. +void tst_QMatrix::create3x3() +{ + QMatrix3x3 m1; + QVERIFY(isIdentity(m1)); + QVERIFY(m1.isIdentity()); + + QMatrix3x3 m2; + setMatrix(m2, uniqueValues3); + QVERIFY(isSame(m2, uniqueValues3)); + QVERIFY(!m2.isIdentity()); + + QMatrix3x3 m3; + setMatrixFixed(m3, uniqueValues3); + QVERIFY(isSame(m3, uniqueValues3)); + + QMatrix3x3 m4(m3); + QVERIFY(isSame(m4, uniqueValues3)); + + QMatrix3x3 m5; + m5 = m3; + QVERIFY(isSame(m5, uniqueValues3)); + + m5.setIdentity(); + QVERIFY(isIdentity(m5)); + + QMatrix3x3 m6(uniqueValues3); + QVERIFY(isSame(m6, uniqueValues3)); + qreal vals[9]; + m6.toValueArray(vals); + for (int index = 0; index < 9; ++index) + QCOMPARE((float)(vals[index]), (float)(uniqueValues3[index])); +} + +// Test the creation of QMatrix4x4 objects in various ways: +// construct, copy, and modify. +void tst_QMatrix::create4x4() +{ + QMatrix4x4 m1; + QVERIFY(isIdentity(m1)); + QVERIFY(m1.isIdentity()); + + QMatrix4x4 m2; + setMatrix(m2, uniqueValues4); + QVERIFY(isSame(m2, uniqueValues4)); + QVERIFY(!m2.isIdentity()); + + QMatrix4x4 m3; + setMatrixFixed(m3, uniqueValues4); + QVERIFY(isSame(m3, uniqueValues4)); + + QMatrix4x4 m4(m3); + QVERIFY(isSame(m4, uniqueValues4)); + + QMatrix4x4 m5; + m5 = m3; + QVERIFY(isSame(m5, uniqueValues4)); + + m5.setIdentity(); + QVERIFY(isIdentity(m5)); + + QMatrix4x4 m6(uniqueValues4); + QVERIFY(isSame(m6, uniqueValues4)); + qreal vals[16]; + m6.toValueArray(vals); + for (int index = 0; index < 16; ++index) + QCOMPARE((float)(vals[index]), (float)(uniqueValues4[index])); + + QMatrix4x4 m8 + (uniqueValues4[0], uniqueValues4[1], uniqueValues4[2], uniqueValues4[3], + uniqueValues4[4], uniqueValues4[5], uniqueValues4[6], uniqueValues4[7], + uniqueValues4[8], uniqueValues4[9], uniqueValues4[10], uniqueValues4[11], + uniqueValues4[12], uniqueValues4[13], uniqueValues4[14], uniqueValues4[15]); + QVERIFY(isSame(m8, uniqueValues4)); +} + +// Test the creation of QMatrix4x3 objects in various ways: +// construct, copy, and modify. +void tst_QMatrix::create4x3() +{ + QMatrix4x3 m1; + QVERIFY(isIdentity(m1)); + QVERIFY(m1.isIdentity()); + + QMatrix4x3 m2; + setMatrix(m2, uniqueValues4x3); + QVERIFY(isSame(m2, uniqueValues4x3)); + QVERIFY(!m2.isIdentity()); + + QMatrix4x3 m3; + setMatrixFixed(m3, uniqueValues4x3); + QVERIFY(isSame(m3, uniqueValues4x3)); + + QMatrix4x3 m4(m3); + QVERIFY(isSame(m4, uniqueValues4x3)); + + QMatrix4x3 m5; + m5 = m3; + QVERIFY(isSame(m5, uniqueValues4x3)); + + m5.setIdentity(); + QVERIFY(isIdentity(m5)); + + QMatrix4x3 m6(uniqueValues4x3); + QVERIFY(isSame(m6, uniqueValues4x3)); + qreal vals[12]; + m6.toValueArray(vals); + for (int index = 0; index < 12; ++index) + QCOMPARE((float)(vals[index]), (float)(uniqueValues4x3[index])); +} + +// Test isIdentity() for 2x2 matrices. +void tst_QMatrix::isIdentity2x2() +{ + for (int i = 0; i < 2 * 2; ++i) { + QMatrix2x2 m; + QVERIFY(m.isIdentity()); + m.data()[i] = 42.0f; + QVERIFY(!m.isIdentity()); + } +} + +// Test isIdentity() for 3x3 matrices. +void tst_QMatrix::isIdentity3x3() +{ + for (int i = 0; i < 3 * 3; ++i) { + QMatrix3x3 m; + QVERIFY(m.isIdentity()); + m.data()[i] = 42.0f; + QVERIFY(!m.isIdentity()); + } +} + +// Test isIdentity() for 4x4 matrices. +void tst_QMatrix::isIdentity4x4() +{ + for (int i = 0; i < 4 * 4; ++i) { + QMatrix4x4 m; + QVERIFY(m.isIdentity()); + m.data()[i] = 42.0f; + QVERIFY(!m.isIdentity()); + } + + // Force the "Identity" flag bit to be lost and check again. + QMatrix4x4 m2; + m2.data()[0] = 1.0f; + QVERIFY(m2.isIdentity()); +} + +// Test isIdentity() for 4x3 matrices. +void tst_QMatrix::isIdentity4x3() +{ + for (int i = 0; i < 4 * 3; ++i) { + QMatrix4x3 m; + QVERIFY(m.isIdentity()); + m.data()[i] = 42.0f; + QVERIFY(!m.isIdentity()); + } +} + +// Test 2x2 matrix comparisons. +void tst_QMatrix::compare2x2() +{ + QMatrix2x2 m1(uniqueValues2); + QMatrix2x2 m2(uniqueValues2); + QMatrix2x2 m3(transposedValues2); + + QVERIFY(m1 == m2); + QVERIFY(!(m1 != m2)); + QVERIFY(m1 != m3); + QVERIFY(!(m1 == m3)); +} + +// Test 3x3 matrix comparisons. +void tst_QMatrix::compare3x3() +{ + QMatrix3x3 m1(uniqueValues3); + QMatrix3x3 m2(uniqueValues3); + QMatrix3x3 m3(transposedValues3); + + QVERIFY(m1 == m2); + QVERIFY(!(m1 != m2)); + QVERIFY(m1 != m3); + QVERIFY(!(m1 == m3)); +} + +// Test 4x4 matrix comparisons. +void tst_QMatrix::compare4x4() +{ + QMatrix4x4 m1(uniqueValues4); + QMatrix4x4 m2(uniqueValues4); + QMatrix4x4 m3(transposedValues4); + + QVERIFY(m1 == m2); + QVERIFY(!(m1 != m2)); + QVERIFY(m1 != m3); + QVERIFY(!(m1 == m3)); +} + +// Test 4x3 matrix comparisons. +void tst_QMatrix::compare4x3() +{ + QMatrix4x3 m1(uniqueValues4x3); + QMatrix4x3 m2(uniqueValues4x3); + QMatrix4x3 m3(transposedValues3x4); + + QVERIFY(m1 == m2); + QVERIFY(!(m1 != m2)); + QVERIFY(m1 != m3); + QVERIFY(!(m1 == m3)); +} + +// Test matrix 2x2 transpose operations. +void tst_QMatrix::transposed2x2() +{ + // Transposing the identity should result in the identity. + QMatrix2x2 m1; + QMatrix2x2 m2 = m1.transposed(); + QVERIFY(isIdentity(m2)); + + // Transpose a more interesting matrix that allows us to track + // exactly where each source element ends up. + QMatrix2x2 m3(uniqueValues2); + QMatrix2x2 m4 = m3.transposed(); + QVERIFY(isSame(m4, transposedValues2)); + + // Transpose in-place, just to check that the compiler is sane. + m3 = m3.transposed(); + QVERIFY(isSame(m3, transposedValues2)); +} + +// Test matrix 3x3 transpose operations. +void tst_QMatrix::transposed3x3() +{ + // Transposing the identity should result in the identity. + QMatrix3x3 m1; + QMatrix3x3 m2 = m1.transposed(); + QVERIFY(isIdentity(m2)); + + // Transpose a more interesting matrix that allows us to track + // exactly where each source element ends up. + QMatrix3x3 m3(uniqueValues3); + QMatrix3x3 m4 = m3.transposed(); + QVERIFY(isSame(m4, transposedValues3)); + + // Transpose in-place, just to check that the compiler is sane. + m3 = m3.transposed(); + QVERIFY(isSame(m3, transposedValues3)); +} + +// Test matrix 4x4 transpose operations. +void tst_QMatrix::transposed4x4() +{ + // Transposing the identity should result in the identity. + QMatrix4x4 m1; + QMatrix4x4 m2 = m1.transposed(); + QVERIFY(isIdentity(m2)); + + // Transpose a more interesting matrix that allows us to track + // exactly where each source element ends up. + QMatrix4x4 m3(uniqueValues4); + QMatrix4x4 m4 = m3.transposed(); + QVERIFY(isSame(m4, transposedValues4)); + + // Transpose in-place, just to check that the compiler is sane. + m3 = m3.transposed(); + QVERIFY(isSame(m3, transposedValues4)); +} + +// Test matrix 4x3 transpose operations. +void tst_QMatrix::transposed4x3() +{ + QMatrix4x3 m3(uniqueValues4x3); + QMatrix3x4 m4 = m3.transposed(); + qreal values[12]; + m4.toValueArray(values); + for (int index = 0; index < 12; ++index) + QCOMPARE(values[index], transposedValues3x4[index]); +} + +// Test matrix addition for 2x2 matrices. +void tst_QMatrix::add2x2_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<void *>("m2Values"); + QTest::addColumn<void *>("m3Values"); + + QTest::newRow("null") + << (void *)nullValues2 << (void *)nullValues2 << (void *)nullValues2; + + QTest::newRow("identity/null") + << (void *)identityValues2 << (void *)nullValues2 << (void *)identityValues2; + + QTest::newRow("identity/identity") + << (void *)identityValues2 << (void *)identityValues2 << (void *)doubleIdentity2; + + static qreal const sumValues[16] = + {2.0f, 7.0f, + 7.0f, 12.0f}; + QTest::newRow("unique") + << (void *)uniqueValues2 << (void *)transposedValues2 << (void *)sumValues; +} +void tst_QMatrix::add2x2() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix2x2 m1((const qreal *)m1Values); + QMatrix2x2 m2((const qreal *)m2Values); + + QMatrix2x2 m4(m1); + m4 += m2; + QVERIFY(isSame(m4, (const qreal *)m3Values)); + + QMatrix2x2 m5; + m5 = m1 + m2; + QVERIFY(isSame(m5, (const qreal *)m3Values)); +} + +// Test matrix addition for 3x3 matrices. +void tst_QMatrix::add3x3_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<void *>("m2Values"); + QTest::addColumn<void *>("m3Values"); + + QTest::newRow("null") + << (void *)nullValues3 << (void *)nullValues3 << (void *)nullValues3; + + QTest::newRow("identity/null") + << (void *)identityValues3 << (void *)nullValues3 << (void *)identityValues3; + + QTest::newRow("identity/identity") + << (void *)identityValues3 << (void *)identityValues3 << (void *)doubleIdentity3; + + static qreal const sumValues[16] = + {2.0f, 7.0f, 12.0f, + 7.0f, 12.0f, 17.0f, + 12.0f, 17.0f, 22.0f}; + QTest::newRow("unique") + << (void *)uniqueValues3 << (void *)transposedValues3 << (void *)sumValues; +} +void tst_QMatrix::add3x3() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix3x3 m1((const qreal *)m1Values); + QMatrix3x3 m2((const qreal *)m2Values); + + QMatrix3x3 m4(m1); + m4 += m2; + QVERIFY(isSame(m4, (const qreal *)m3Values)); + + QMatrix3x3 m5; + m5 = m1 + m2; + QVERIFY(isSame(m5, (const qreal *)m3Values)); +} + +// Test matrix addition for 4x4 matrices. +void tst_QMatrix::add4x4_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<void *>("m2Values"); + QTest::addColumn<void *>("m3Values"); + + QTest::newRow("null") + << (void *)nullValues4 << (void *)nullValues4 << (void *)nullValues4; + + QTest::newRow("identity/null") + << (void *)identityValues4 << (void *)nullValues4 << (void *)identityValues4; + + QTest::newRow("identity/identity") + << (void *)identityValues4 << (void *)identityValues4 << (void *)doubleIdentity4; + + static qreal const sumValues[16] = + {2.0f, 7.0f, 12.0f, 17.0f, + 7.0f, 12.0f, 17.0f, 22.0f, + 12.0f, 17.0f, 22.0f, 27.0f, + 17.0f, 22.0f, 27.0f, 32.0f}; + QTest::newRow("unique") + << (void *)uniqueValues4 << (void *)transposedValues4 << (void *)sumValues; +} +void tst_QMatrix::add4x4() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix4x4 m1((const qreal *)m1Values); + QMatrix4x4 m2((const qreal *)m2Values); + + QMatrix4x4 m4(m1); + m4 += m2; + QVERIFY(isSame(m4, (const qreal *)m3Values)); + + QMatrix4x4 m5; + m5 = m1 + m2; + QVERIFY(isSame(m5, (const qreal *)m3Values)); +} + +// Test matrix addition for 4x3 matrices. +void tst_QMatrix::add4x3_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<void *>("m2Values"); + QTest::addColumn<void *>("m3Values"); + + QTest::newRow("null") + << (void *)nullValues4x3 << (void *)nullValues4x3 << (void *)nullValues4x3; + + QTest::newRow("identity/null") + << (void *)identityValues4x3 << (void *)nullValues4x3 << (void *)identityValues4x3; + + QTest::newRow("identity/identity") + << (void *)identityValues4x3 << (void *)identityValues4x3 << (void *)doubleIdentity4x3; + + static qreal const sumValues[16] = + {2.0f, 7.0f, 12.0f, 6.0f, + 11.0f, 16.0f, 10.0f, 15.0f, + 20.0f, 14.0f, 19.0f, 24.0f}; + QTest::newRow("unique") + << (void *)uniqueValues4x3 << (void *)transposedValues3x4 << (void *)sumValues; +} +void tst_QMatrix::add4x3() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix4x3 m1((const qreal *)m1Values); + QMatrix4x3 m2((const qreal *)m2Values); + + QMatrix4x3 m4(m1); + m4 += m2; + QVERIFY(isSame(m4, (const qreal *)m3Values)); + + QMatrix4x3 m5; + m5 = m1 + m2; + QVERIFY(isSame(m5, (const qreal *)m3Values)); +} + +// Test matrix subtraction for 2x2 matrices. +void tst_QMatrix::subtract2x2_data() +{ + // Use the same test cases as the add test. + add2x2_data(); +} +void tst_QMatrix::subtract2x2() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix2x2 m1((const qreal *)m1Values); + QMatrix2x2 m2((const qreal *)m2Values); + QMatrix2x2 m3((const qreal *)m3Values); + + QMatrix2x2 m4(m3); + m4 -= m1; + QVERIFY(isSame(m4, (const qreal *)m2Values)); + + QMatrix2x2 m5; + m5 = m3 - m1; + QVERIFY(isSame(m5, (const qreal *)m2Values)); + + QMatrix2x2 m6(m3); + m6 -= m2; + QVERIFY(isSame(m6, (const qreal *)m1Values)); + + QMatrix2x2 m7; + m7 = m3 - m2; + QVERIFY(isSame(m7, (const qreal *)m1Values)); +} + +// Test matrix subtraction for 3x3 matrices. +void tst_QMatrix::subtract3x3_data() +{ + // Use the same test cases as the add test. + add3x3_data(); +} +void tst_QMatrix::subtract3x3() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix3x3 m1((const qreal *)m1Values); + QMatrix3x3 m2((const qreal *)m2Values); + QMatrix3x3 m3((const qreal *)m3Values); + + QMatrix3x3 m4(m3); + m4 -= m1; + QVERIFY(isSame(m4, (const qreal *)m2Values)); + + QMatrix3x3 m5; + m5 = m3 - m1; + QVERIFY(isSame(m5, (const qreal *)m2Values)); + + QMatrix3x3 m6(m3); + m6 -= m2; + QVERIFY(isSame(m6, (const qreal *)m1Values)); + + QMatrix3x3 m7; + m7 = m3 - m2; + QVERIFY(isSame(m7, (const qreal *)m1Values)); +} + +// Test matrix subtraction for 4x4 matrices. +void tst_QMatrix::subtract4x4_data() +{ + // Use the same test cases as the add test. + add4x4_data(); +} +void tst_QMatrix::subtract4x4() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix4x4 m1((const qreal *)m1Values); + QMatrix4x4 m2((const qreal *)m2Values); + QMatrix4x4 m3((const qreal *)m3Values); + + QMatrix4x4 m4(m3); + m4 -= m1; + QVERIFY(isSame(m4, (const qreal *)m2Values)); + + QMatrix4x4 m5; + m5 = m3 - m1; + QVERIFY(isSame(m5, (const qreal *)m2Values)); + + QMatrix4x4 m6(m3); + m6 -= m2; + QVERIFY(isSame(m6, (const qreal *)m1Values)); + + QMatrix4x4 m7; + m7 = m3 - m2; + QVERIFY(isSame(m7, (const qreal *)m1Values)); +} + +// Test matrix subtraction for 4x3 matrices. +void tst_QMatrix::subtract4x3_data() +{ + // Use the same test cases as the add test. + add4x3_data(); +} +void tst_QMatrix::subtract4x3() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix4x3 m1((const qreal *)m1Values); + QMatrix4x3 m2((const qreal *)m2Values); + QMatrix4x3 m3((const qreal *)m3Values); + + QMatrix4x3 m4(m3); + m4 -= m1; + QVERIFY(isSame(m4, (const qreal *)m2Values)); + + QMatrix4x3 m5; + m5 = m3 - m1; + QVERIFY(isSame(m5, (const qreal *)m2Values)); + + QMatrix4x3 m6(m3); + m6 -= m2; + QVERIFY(isSame(m6, (const qreal *)m1Values)); + + QMatrix4x3 m7; + m7 = m3 - m2; + QVERIFY(isSame(m7, (const qreal *)m1Values)); +} + +// Test matrix multiplication for 2x2 matrices. +void tst_QMatrix::multiply2x2_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<void *>("m2Values"); + QTest::addColumn<void *>("m3Values"); + + QTest::newRow("null") + << (void *)nullValues2 << (void *)nullValues2 << (void *)nullValues2; + + QTest::newRow("null/unique") + << (void *)nullValues2 << (void *)uniqueValues2 << (void *)nullValues2; + + QTest::newRow("unique/null") + << (void *)uniqueValues2 << (void *)nullValues2 << (void *)nullValues2; + + QTest::newRow("unique/identity") + << (void *)uniqueValues2 << (void *)identityValues2 << (void *)uniqueValues2; + + QTest::newRow("identity/unique") + << (void *)identityValues2 << (void *)uniqueValues2 << (void *)uniqueValues2; + + static qreal uniqueResult[4]; + for (int row = 0; row < 2; ++row) { + for (int col = 0; col < 2; ++col) { + qreal sum = 0.0f; + for (int j = 0; j < 2; ++j) + sum += uniqueValues2[row * 2 + j] * transposedValues2[j * 2 + col]; + uniqueResult[row * 2 + col] = sum; + } + } + + QTest::newRow("unique/transposed") + << (void *)uniqueValues2 << (void *)transposedValues2 << (void *)uniqueResult; +} +void tst_QMatrix::multiply2x2() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix2x2 m1((const qreal *)m1Values); + QMatrix2x2 m2((const qreal *)m2Values); + + QMatrix2x2 m5; + m5 = m1 * m2; + QVERIFY(isSame(m5, (const qreal *)m3Values)); +} + +// Test matrix multiplication for 3x3 matrices. +void tst_QMatrix::multiply3x3_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<void *>("m2Values"); + QTest::addColumn<void *>("m3Values"); + + QTest::newRow("null") + << (void *)nullValues3 << (void *)nullValues3 << (void *)nullValues3; + + QTest::newRow("null/unique") + << (void *)nullValues3 << (void *)uniqueValues3 << (void *)nullValues3; + + QTest::newRow("unique/null") + << (void *)uniqueValues3 << (void *)nullValues3 << (void *)nullValues3; + + QTest::newRow("unique/identity") + << (void *)uniqueValues3 << (void *)identityValues3 << (void *)uniqueValues3; + + QTest::newRow("identity/unique") + << (void *)identityValues3 << (void *)uniqueValues3 << (void *)uniqueValues3; + + static qreal uniqueResult[9]; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + qreal sum = 0.0f; + for (int j = 0; j < 3; ++j) + sum += uniqueValues3[row * 3 + j] * transposedValues3[j * 3 + col]; + uniqueResult[row * 3 + col] = sum; + } + } + + QTest::newRow("unique/transposed") + << (void *)uniqueValues3 << (void *)transposedValues3 << (void *)uniqueResult; +} +void tst_QMatrix::multiply3x3() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix3x3 m1((const qreal *)m1Values); + QMatrix3x3 m2((const qreal *)m2Values); + + QMatrix3x3 m5; + m5 = m1 * m2; + QVERIFY(isSame(m5, (const qreal *)m3Values)); +} + +// Test matrix multiplication for 4x4 matrices. +void tst_QMatrix::multiply4x4_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<void *>("m2Values"); + QTest::addColumn<void *>("m3Values"); + + QTest::newRow("null") + << (void *)nullValues4 << (void *)nullValues4 << (void *)nullValues4; + + QTest::newRow("null/unique") + << (void *)nullValues4 << (void *)uniqueValues4 << (void *)nullValues4; + + QTest::newRow("unique/null") + << (void *)uniqueValues4 << (void *)nullValues4 << (void *)nullValues4; + + QTest::newRow("unique/identity") + << (void *)uniqueValues4 << (void *)identityValues4 << (void *)uniqueValues4; + + QTest::newRow("identity/unique") + << (void *)identityValues4 << (void *)uniqueValues4 << (void *)uniqueValues4; + + static qreal uniqueResult[16]; + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 4; ++col) { + qreal sum = 0.0f; + for (int j = 0; j < 4; ++j) + sum += uniqueValues4[row * 4 + j] * transposedValues4[j * 4 + col]; + uniqueResult[row * 4 + col] = sum; + } + } + + QTest::newRow("unique/transposed") + << (void *)uniqueValues4 << (void *)transposedValues4 << (void *)uniqueResult; +} +void tst_QMatrix::multiply4x4() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix4x4 m1((const qreal *)m1Values); + QMatrix4x4 m2((const qreal *)m2Values); + + QMatrix4x4 m4; + m4 = m1; + m4 *= m2; + QVERIFY(isSame(m4, (const qreal *)m3Values)); + + QMatrix4x4 m5; + m5 = m1 * m2; + QVERIFY(isSame(m5, (const qreal *)m3Values)); +} + +// Test matrix multiplication for 4x3 matrices. +void tst_QMatrix::multiply4x3_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<void *>("m2Values"); + QTest::addColumn<void *>("m3Values"); + + QTest::newRow("null") + << (void *)nullValues4x3 << (void *)nullValues4x3 << (void *)nullValues3; + + QTest::newRow("null/unique") + << (void *)nullValues4x3 << (void *)uniqueValues4x3 << (void *)nullValues3; + + QTest::newRow("unique/null") + << (void *)uniqueValues4x3 << (void *)nullValues4x3 << (void *)nullValues3; + + static qreal uniqueResult[9]; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + qreal sum = 0.0f; + for (int j = 0; j < 4; ++j) + sum += uniqueValues4x3[row * 4 + j] * transposedValues3x4[j * 3 + col]; + uniqueResult[row * 3 + col] = sum; + } + } + + QTest::newRow("unique/transposed") + << (void *)uniqueValues4x3 << (void *)transposedValues3x4 << (void *)uniqueResult; +} +void tst_QMatrix::multiply4x3() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(void *, m3Values); + + QMatrix4x3 m1((const qreal *)m1Values); + QMatrix3x4 m2((const qreal *)m2Values); + + QGenericMatrix<3, 3, qreal, qrealinner> m4; + m4 = m1 * m2; + qreal values[9]; + m4.toValueArray(values); + for (int index = 0; index < 9; ++index) + QCOMPARE(values[index], ((const qreal *)m3Values)[index]); +} + +// Test matrix multiplication by a factor for 2x2 matrices. +void tst_QMatrix::multiplyFactor2x2_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<qreal>("factor"); + QTest::addColumn<void *>("m2Values"); + + QTest::newRow("null") + << (void *)nullValues2 << (qreal)1.0f << (void *)nullValues2; + + QTest::newRow("double identity") + << (void *)identityValues2 << (qreal)2.0f << (void *)doubleIdentity2; + + static qreal const values[16] = + {1.0f, 2.0f, + 5.0f, 6.0f}; + static qreal const doubleValues[16] = + {2.0f, 4.0f, + 10.0f, 12.0f}; + static qreal const negDoubleValues[16] = + {-2.0f, -4.0f, + -10.0f, -12.0f}; + + QTest::newRow("unique") + << (void *)values << (qreal)2.0f << (void *)doubleValues; + + QTest::newRow("neg") + << (void *)values << (qreal)-2.0f << (void *)negDoubleValues; + + QTest::newRow("zero") + << (void *)values << (qreal)0.0f << (void *)nullValues4; +} +void tst_QMatrix::multiplyFactor2x2() +{ + QFETCH(void *, m1Values); + QFETCH(qreal, factor); + QFETCH(void *, m2Values); + + QMatrix2x2 m1((const qreal *)m1Values); + + QMatrix2x2 m3; + m3 = m1; + m3 *= factor; + QVERIFY(isSame(m3, (const qreal *)m2Values)); + + QMatrix2x2 m4; + m4 = m1 * factor; + QVERIFY(isSame(m4, (const qreal *)m2Values)); + + QMatrix2x2 m5; + m5 = factor * m1; + QVERIFY(isSame(m5, (const qreal *)m2Values)); +} + +// Test matrix multiplication by a factor for 3x3 matrices. +void tst_QMatrix::multiplyFactor3x3_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<qreal>("factor"); + QTest::addColumn<void *>("m2Values"); + + QTest::newRow("null") + << (void *)nullValues3 << (qreal)1.0f << (void *)nullValues3; + + QTest::newRow("double identity") + << (void *)identityValues3 << (qreal)2.0f << (void *)doubleIdentity3; + + static qreal const values[16] = + {1.0f, 2.0f, 3.0f, + 5.0f, 6.0f, 7.0f, + 9.0f, 10.0f, 11.0f}; + static qreal const doubleValues[16] = + {2.0f, 4.0f, 6.0f, + 10.0f, 12.0f, 14.0f, + 18.0f, 20.0f, 22.0f}; + static qreal const negDoubleValues[16] = + {-2.0f, -4.0f, -6.0f, + -10.0f, -12.0f, -14.0f, + -18.0f, -20.0f, -22.0f}; + + QTest::newRow("unique") + << (void *)values << (qreal)2.0f << (void *)doubleValues; + + QTest::newRow("neg") + << (void *)values << (qreal)-2.0f << (void *)negDoubleValues; + + QTest::newRow("zero") + << (void *)values << (qreal)0.0f << (void *)nullValues4; +} +void tst_QMatrix::multiplyFactor3x3() +{ + QFETCH(void *, m1Values); + QFETCH(qreal, factor); + QFETCH(void *, m2Values); + + QMatrix3x3 m1((const qreal *)m1Values); + + QMatrix3x3 m3; + m3 = m1; + m3 *= factor; + QVERIFY(isSame(m3, (const qreal *)m2Values)); + + QMatrix3x3 m4; + m4 = m1 * factor; + QVERIFY(isSame(m4, (const qreal *)m2Values)); + + QMatrix3x3 m5; + m5 = factor * m1; + QVERIFY(isSame(m5, (const qreal *)m2Values)); +} + +// Test matrix multiplication by a factor for 4x4 matrices. +void tst_QMatrix::multiplyFactor4x4_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<qreal>("factor"); + QTest::addColumn<void *>("m2Values"); + + QTest::newRow("null") + << (void *)nullValues4 << (qreal)1.0f << (void *)nullValues4; + + QTest::newRow("double identity") + << (void *)identityValues4 << (qreal)2.0f << (void *)doubleIdentity4; + + static qreal const values[16] = + {1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f}; + static qreal const doubleValues[16] = + {2.0f, 4.0f, 6.0f, 8.0f, + 10.0f, 12.0f, 14.0f, 16.0f, + 18.0f, 20.0f, 22.0f, 24.0f, + 26.0f, 28.0f, 30.0f, 32.0f}; + static qreal const negDoubleValues[16] = + {-2.0f, -4.0f, -6.0f, -8.0f, + -10.0f, -12.0f, -14.0f, -16.0f, + -18.0f, -20.0f, -22.0f, -24.0f, + -26.0f, -28.0f, -30.0f, -32.0f}; + + QTest::newRow("unique") + << (void *)values << (qreal)2.0f << (void *)doubleValues; + + QTest::newRow("neg") + << (void *)values << (qreal)-2.0f << (void *)negDoubleValues; + + QTest::newRow("zero") + << (void *)values << (qreal)0.0f << (void *)nullValues4; +} +void tst_QMatrix::multiplyFactor4x4() +{ + QFETCH(void *, m1Values); + QFETCH(qreal, factor); + QFETCH(void *, m2Values); + + QMatrix4x4 m1((const qreal *)m1Values); + + QMatrix4x4 m3; + m3 = m1; + m3 *= factor; + QVERIFY(isSame(m3, (const qreal *)m2Values)); + + QMatrix4x4 m4; + m4 = m1 * factor; + QVERIFY(isSame(m4, (const qreal *)m2Values)); + + QMatrix4x4 m5; + m5 = factor * m1; + QVERIFY(isSame(m5, (const qreal *)m2Values)); +} + +// Test matrix multiplication by a factor for 4x3 matrices. +void tst_QMatrix::multiplyFactor4x3_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<qreal>("factor"); + QTest::addColumn<void *>("m2Values"); + + QTest::newRow("null") + << (void *)nullValues4x3 << (qreal)1.0f << (void *)nullValues4x3; + + QTest::newRow("double identity") + << (void *)identityValues4x3 << (qreal)2.0f << (void *)doubleIdentity4x3; + + static qreal const values[12] = + {1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f}; + static qreal const doubleValues[12] = + {2.0f, 4.0f, 6.0f, 8.0f, + 10.0f, 12.0f, 14.0f, 16.0f, + 18.0f, 20.0f, 22.0f, 24.0f}; + static qreal const negDoubleValues[12] = + {-2.0f, -4.0f, -6.0f, -8.0f, + -10.0f, -12.0f, -14.0f, -16.0f, + -18.0f, -20.0f, -22.0f, -24.0f}; + + QTest::newRow("unique") + << (void *)values << (qreal)2.0f << (void *)doubleValues; + + QTest::newRow("neg") + << (void *)values << (qreal)-2.0f << (void *)negDoubleValues; + + QTest::newRow("zero") + << (void *)values << (qreal)0.0f << (void *)nullValues4x3; +} +void tst_QMatrix::multiplyFactor4x3() +{ + QFETCH(void *, m1Values); + QFETCH(qreal, factor); + QFETCH(void *, m2Values); + + QMatrix4x3 m1((const qreal *)m1Values); + + QMatrix4x3 m3; + m3 = m1; + m3 *= factor; + QVERIFY(isSame(m3, (const qreal *)m2Values)); + + QMatrix4x3 m4; + m4 = m1 * factor; + QVERIFY(isSame(m4, (const qreal *)m2Values)); + + QMatrix4x3 m5; + m5 = factor * m1; + QVERIFY(isSame(m5, (const qreal *)m2Values)); +} + +// Test matrix division by a factor for 2x2 matrices. +void tst_QMatrix::divideFactor2x2_data() +{ + // Use the same test cases as the multiplyFactor test. + multiplyFactor2x2_data(); +} +void tst_QMatrix::divideFactor2x2() +{ + QFETCH(void *, m1Values); + QFETCH(qreal, factor); + QFETCH(void *, m2Values); + + if (factor == 0.0f) + return; + + QMatrix2x2 m2((const qreal *)m2Values); + + QMatrix2x2 m3; + m3 = m2; + m3 /= factor; + QVERIFY(isSame(m3, (const qreal *)m1Values)); + + QMatrix2x2 m4; + m4 = m2 / factor; + QVERIFY(isSame(m4, (const qreal *)m1Values)); +} + +// Test matrix division by a factor for 3x3 matrices. +void tst_QMatrix::divideFactor3x3_data() +{ + // Use the same test cases as the multiplyFactor test. + multiplyFactor3x3_data(); +} +void tst_QMatrix::divideFactor3x3() +{ + QFETCH(void *, m1Values); + QFETCH(qreal, factor); + QFETCH(void *, m2Values); + + if (factor == 0.0f) + return; + + QMatrix3x3 m2((const qreal *)m2Values); + + QMatrix3x3 m3; + m3 = m2; + m3 /= factor; + QVERIFY(isSame(m3, (const qreal *)m1Values)); + + QMatrix3x3 m4; + m4 = m2 / factor; + QVERIFY(isSame(m4, (const qreal *)m1Values)); +} + +// Test matrix division by a factor for 4x4 matrices. +void tst_QMatrix::divideFactor4x4_data() +{ + // Use the same test cases as the multiplyFactor test. + multiplyFactor4x4_data(); +} +void tst_QMatrix::divideFactor4x4() +{ + QFETCH(void *, m1Values); + QFETCH(qreal, factor); + QFETCH(void *, m2Values); + + if (factor == 0.0f) + return; + + QMatrix4x4 m2((const qreal *)m2Values); + + QMatrix4x4 m3; + m3 = m2; + m3 /= factor; + QVERIFY(isSame(m3, (const qreal *)m1Values)); + + QMatrix4x4 m4; + m4 = m2 / factor; + QVERIFY(isSame(m4, (const qreal *)m1Values)); +} + +// Test matrix division by a factor for 4x3 matrices. +void tst_QMatrix::divideFactor4x3_data() +{ + // Use the same test cases as the multiplyFactor test. + multiplyFactor4x3_data(); +} +void tst_QMatrix::divideFactor4x3() +{ + QFETCH(void *, m1Values); + QFETCH(qreal, factor); + QFETCH(void *, m2Values); + + if (factor == 0.0f) + return; + + QMatrix4x3 m2((const qreal *)m2Values); + + QMatrix4x3 m3; + m3 = m2; + m3 /= factor; + QVERIFY(isSame(m3, (const qreal *)m1Values)); + + QMatrix4x3 m4; + m4 = m2 / factor; + QVERIFY(isSame(m4, (const qreal *)m1Values)); +} + +// Test matrix negation for 2x2 matrices. +void tst_QMatrix::negate2x2_data() +{ + // Use the same test cases as the multiplyFactor test. + multiplyFactor2x2_data(); +} +void tst_QMatrix::negate2x2() +{ + QFETCH(void *, m1Values); + + const qreal *values = (const qreal *)m1Values; + + QMatrix2x2 m1(values); + + qreal negated[4]; + for (int index = 0; index < 4; ++index) + negated[index] = -values[index]; + + QMatrix2x2 m2; + m2 = -m1; + QVERIFY(isSame(m2, negated)); +} + +// Test matrix negation for 3x3 matrices. +void tst_QMatrix::negate3x3_data() +{ + // Use the same test cases as the multiplyFactor test. + multiplyFactor3x3_data(); +} +void tst_QMatrix::negate3x3() +{ + QFETCH(void *, m1Values); + + const qreal *values = (const qreal *)m1Values; + + QMatrix3x3 m1(values); + + qreal negated[9]; + for (int index = 0; index < 9; ++index) + negated[index] = -values[index]; + + QMatrix3x3 m2; + m2 = -m1; + QVERIFY(isSame(m2, negated)); +} + +// Test matrix negation for 4x4 matrices. +void tst_QMatrix::negate4x4_data() +{ + // Use the same test cases as the multiplyFactor test. + multiplyFactor4x4_data(); +} +void tst_QMatrix::negate4x4() +{ + QFETCH(void *, m1Values); + + const qreal *values = (const qreal *)m1Values; + + QMatrix4x4 m1(values); + + qreal negated[16]; + for (int index = 0; index < 16; ++index) + negated[index] = -values[index]; + + QMatrix4x4 m2; + m2 = -m1; + QVERIFY(isSame(m2, negated)); +} + +// Test matrix negation for 4x3 matrices. +void tst_QMatrix::negate4x3_data() +{ + // Use the same test cases as the multiplyFactor test. + multiplyFactor4x3_data(); +} +void tst_QMatrix::negate4x3() +{ + QFETCH(void *, m1Values); + + const qreal *values = (const qreal *)m1Values; + + QMatrix4x3 m1(values); + + qreal negated[12]; + for (int index = 0; index < 12; ++index) + negated[index] = -values[index]; + + QMatrix4x3 m2; + m2 = -m1; + QVERIFY(isSame(m2, negated)); +} + +// Matrix inverted. This is a more straight-forward implementation +// of the algorithm at http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q24 +// than the optimized version in the QMatrix4x4 code. Hopefully it is +// easier to verify that this version is the same as the reference. + +struct Matrix3 +{ + qreal v[9]; +}; +struct Matrix4 +{ + qreal v[16]; +}; + +static qreal m3Determinant(const Matrix3& m) +{ + return m.v[0] * (m.v[4] * m.v[8] - m.v[7] * m.v[5]) - + m.v[1] * (m.v[3] * m.v[8] - m.v[6] * m.v[5]) + + m.v[2] * (m.v[3] * m.v[7] - m.v[6] * m.v[4]); +} + +static bool m3Inverse(const Matrix3& min, Matrix3& mout) +{ + qreal det = m3Determinant(min); + if (det == 0.0f) + return false; + mout.v[0] = (min.v[4] * min.v[8] - min.v[5] * min.v[7]) / det; + mout.v[1] = -(min.v[1] * min.v[8] - min.v[2] * min.v[7]) / det; + mout.v[2] = (min.v[1] * min.v[5] - min.v[4] * min.v[2]) / det; + mout.v[3] = -(min.v[3] * min.v[8] - min.v[5] * min.v[6]) / det; + mout.v[4] = (min.v[0] * min.v[8] - min.v[6] * min.v[2]) / det; + mout.v[5] = -(min.v[0] * min.v[5] - min.v[3] * min.v[2]) / det; + mout.v[6] = (min.v[3] * min.v[7] - min.v[6] * min.v[4]) / det; + mout.v[7] = -(min.v[0] * min.v[7] - min.v[6] * min.v[1]) / det; + mout.v[8] = (min.v[0] * min.v[4] - min.v[1] * min.v[3]) / det; + return true; +} + +static void m3Transpose(Matrix3& m) +{ + qSwap(m.v[1], m.v[3]); + qSwap(m.v[2], m.v[6]); + qSwap(m.v[5], m.v[7]); +} + +static void m4Submatrix(const Matrix4& min, Matrix3& mout, int i, int j) +{ + for (int di = 0; di < 3; ++di) { + for (int dj = 0; dj < 3; ++dj) { + int si = di + ((di >= i) ? 1 : 0); + int sj = dj + ((dj >= j) ? 1 : 0); + mout.v[di * 3 + dj] = min.v[si * 4 + sj]; + } + } +} + +static qreal m4Determinant(const Matrix4& m) +{ + qreal det; + qreal result = 0.0f; + qreal i = 1.0f; + Matrix3 msub; + for (int n = 0; n < 4; ++n, i *= -1.0f) { + m4Submatrix(m, msub, 0, n); + det = m3Determinant(msub); + result += m.v[n] * det * i; + } + return result; +} + +static void m4Inverse(const Matrix4& min, Matrix4& mout) +{ + qreal det = m4Determinant(min); + Matrix3 msub; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + qreal sign = 1.0f - ((i + j) % 2) * 2.0f; + m4Submatrix(min, msub, i, j); + mout.v[i + j * 4] = (m3Determinant(msub) * sign) / det; + } + } +} + +// Test matrix inverted for 4x4 matrices. +void tst_QMatrix::inverted4x4_data() +{ + QTest::addColumn<void *>("m1Values"); + QTest::addColumn<void *>("m2Values"); + QTest::addColumn<bool>("invertible"); + + QTest::newRow("null") + << (void *)nullValues4 << (void *)identityValues4 << false; + + QTest::newRow("identity") + << (void *)identityValues4 << (void *)identityValues4 << true; + + QTest::newRow("unique") + << (void *)uniqueValues4 << (void *)identityValues4 << false; + + static Matrix4 const invertible = { + {5.0f, 0.0f, 0.0f, 2.0f, + 0.0f, 6.0f, 0.0f, 3.0f, + 0.0f, 0.0f, 7.0f, 4.0f, + 0.0f, 0.0f, 0.0f, 1.0f} + }; + static Matrix4 inverted; + m4Inverse(invertible, inverted); + + QTest::newRow("invertible") + << (void *)invertible.v << (void *)inverted.v << true; + + static Matrix4 const translate = { + {1.0f, 0.0f, 0.0f, 2.0f, + 0.0f, 1.0f, 0.0f, 3.0f, + 0.0f, 0.0f, 1.0f, 4.0f, + 0.0f, 0.0f, 0.0f, 1.0f} + }; + static Matrix4 const inverseTranslate = { + {1.0f, 0.0f, 0.0f, -2.0f, + 0.0f, 1.0f, 0.0f, -3.0f, + 0.0f, 0.0f, 1.0f, -4.0f, + 0.0f, 0.0f, 0.0f, 1.0f} + }; + + QTest::newRow("translate") + << (void *)translate.v << (void *)inverseTranslate.v << true; +} +void tst_QMatrix::inverted4x4() +{ + QFETCH(void *, m1Values); + QFETCH(void *, m2Values); + QFETCH(bool, invertible); + + QMatrix4x4 m1((const qreal *)m1Values); + + if (invertible) + QVERIFY(m1.determinant() != 0.0f); + else + QVERIFY(m1.determinant() == 0.0f); + + Matrix4 m1alt; + memcpy(m1alt.v, (const qreal *)m1Values, sizeof(m1alt.v)); + + QCOMPARE((float)(m1.determinant()), (float)(m4Determinant(m1alt))); + + QMatrix4x4 m2; + bool inv; + m2 = m1.inverted(&inv); + QVERIFY(isSame(m2, (const qreal *)m2Values)); + + if (invertible) { + QVERIFY(inv); + + Matrix4 m2alt; + m4Inverse(m1alt, m2alt); + QVERIFY(isSame(m2, m2alt.v)); + + QMatrix4x4 m3; + m3 = m1 * m2; + QVERIFY(isIdentity(m3)); + + QMatrix4x4 m4; + m4 = m2 * m1; + QVERIFY(isIdentity(m4)); + } else { + QVERIFY(!inv); + } + + // Test again, after inferring the special matrix type. + m1.inferSpecialType(); + m2 = m1.inverted(&inv); + QVERIFY(isSame(m2, (const qreal *)m2Values)); + QCOMPARE(inv, invertible); +} + +void tst_QMatrix::orthonormalInverse4x4() +{ + QMatrix4x4 m1; + QVERIFY(matrixFuzzyCompare(m1.inverted(), m1)); + + QMatrix4x4 m2; + m2.rotate(45.0, 1.0, 0.0, 0.0); + m2.translate(10.0, 0.0, 0.0); + + // Use inferSpecialType() to drop the internal flags that + // mark the matrix as orthonormal. This will force inverted() + // to compute m3.inverted() the long way. We can then compare + // the result to what the faster algorithm produces on m2. + QMatrix4x4 m3 = m2; + m3.inferSpecialType(); + bool invertible; + QVERIFY(matrixFuzzyCompare(m2.inverted(&invertible), m3.inverted())); + QVERIFY(invertible); + + QMatrix4x4 m4; + m4.rotate(45.0, 0.0, 1.0, 0.0); + QMatrix4x4 m5 = m4; + m5.inferSpecialType(); + QVERIFY(matrixFuzzyCompare(m4.inverted(), m5.inverted())); + + QMatrix4x4 m6; + m1.rotate(88, 0.0, 0.0, 1.0); + m1.translate(-20.0, 20.0, 15.0); + m1.rotate(25, 1.0, 0.0, 0.0); + QMatrix4x4 m7 = m6; + m7.inferSpecialType(); + QVERIFY(matrixFuzzyCompare(m6.inverted(), m7.inverted())); +} + +// Test the generation and use of 4x4 scale matrices. +void tst_QMatrix::scale4x4_data() +{ + QTest::addColumn<qreal>("x"); + QTest::addColumn<qreal>("y"); + QTest::addColumn<qreal>("z"); + QTest::addColumn<void *>("resultValues"); + + static const qreal nullScale[] = + {0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (void *)nullScale; + + QTest::newRow("identity") + << (qreal)1.0f << (qreal)1.0f << (qreal)1.0f << (void *)identityValues4; + + static const qreal doubleScale[] = + {2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 2.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("double") + << (qreal)2.0f << (qreal)2.0f << (qreal)2.0f << (void *)doubleScale; + + static const qreal complexScale[] = + {2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 11.0f, 0.0f, 0.0f, + 0.0f, 0.0f, -6.5f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("complex") + << (qreal)2.0f << (qreal)11.0f << (qreal)-6.5f << (void *)complexScale; +} +void tst_QMatrix::scale4x4() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(void *, resultValues); + + QMatrix4x4 result((const qreal *)resultValues); + + QMatrix4x4 m1; + m1.scale(QVector3D(x, y, z)); + QVERIFY(isSame(m1, (const qreal *)resultValues)); + + QMatrix4x4 m2; + m2.scale(x, y, z); + QVERIFY(isSame(m2, (const qreal *)resultValues)); + + QVector3D v1(2.0f, 3.0f, -4.0f); + QVector3D v2 = m1 * v1; + QCOMPARE(v2.x(), (qreal)(2.0f * x)); + QCOMPARE(v2.y(), (qreal)(3.0f * y)); + QCOMPARE(v2.z(), (qreal)(-4.0f * z)); + + v2 = v1 * m1; + QCOMPARE(v2.x(), (qreal)(2.0f * x)); + QCOMPARE(v2.y(), (qreal)(3.0f * y)); + QCOMPARE(v2.z(), (qreal)(-4.0f * z)); + + QVector4D v3(2.0f, 3.0f, -4.0f, 34.0f); + QVector4D v4 = m1 * v3; + QCOMPARE(v4.x(), (qreal)(2.0f * x)); + QCOMPARE(v4.y(), (qreal)(3.0f * y)); + QCOMPARE(v4.z(), (qreal)(-4.0f * z)); + QCOMPARE(v4.w(), (qreal)34.0f); + + v4 = v3 * m1; + QCOMPARE(v4.x(), (qreal)(2.0f * x)); + QCOMPARE(v4.y(), (qreal)(3.0f * y)); + QCOMPARE(v4.z(), (qreal)(-4.0f * z)); + QCOMPARE(v4.w(), (qreal)34.0f); + + QPoint p1(2, 3); + QPoint p2 = m1 * p1; + QCOMPARE(p2.x(), (int)(2.0f * x)); + QCOMPARE(p2.y(), (int)(3.0f * y)); + + p2 = p1 * m1; + QCOMPARE(p2.x(), (int)(2.0f * x)); + QCOMPARE(p2.y(), (int)(3.0f * y)); + + QPointF p3(2.0f, 3.0f); + QPointF p4 = m1 * p3; + QCOMPARE(p4.x(), (qreal)(2.0f * x)); + QCOMPARE(p4.y(), (qreal)(3.0f * y)); + + p4 = p3 * m1; + QCOMPARE(p4.x(), (qreal)(2.0f * x)); + QCOMPARE(p4.y(), (qreal)(3.0f * y)); + + QMatrix4x4 m3(uniqueValues4); + QMatrix4x4 m4(m3); + m4.scale(x, y, z); + QVERIFY(m4 == m3 * m1); + + if (x == y && y == z) { + QMatrix4x4 m5; + m5.scale(x); + QVERIFY(isSame(m5, (const qreal *)resultValues)); + } + + // Test coverage when the special matrix type is unknown. + + QMatrix4x4 m6; + m6(0, 0) = 1.0f; + m6.scale(QVector3D(x, y, z)); + QVERIFY(isSame(m6, (const qreal *)resultValues)); + + QMatrix4x4 m7; + m7(0, 0) = 1.0f; + m7.scale(x, y, z); + QVERIFY(isSame(m7, (const qreal *)resultValues)); + + if (x == y && y == z) { + QMatrix4x4 m8; + m8(0, 0) = 1.0f; + m8.scale(x); + QVERIFY(isSame(m8, (const qreal *)resultValues)); + + m8.inferSpecialType(); + m8.scale(1.0f); + QVERIFY(isSame(m8, (const qreal *)resultValues)); + + QMatrix4x4 m9; + m9.translate(0.0f, 0.0f, 0.0f); + m9.scale(x); + QVERIFY(isSame(m9, (const qreal *)resultValues)); + } +} + +// Test the generation and use of 4x4 translation matrices. +void tst_QMatrix::translate4x4_data() +{ + QTest::addColumn<qreal>("x"); + QTest::addColumn<qreal>("y"); + QTest::addColumn<qreal>("z"); + QTest::addColumn<void *>("resultValues"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (void *)identityValues4; + + static const qreal identityTranslate[] = + {1.0f, 0.0f, 0.0f, 1.0f, + 0.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("identity") + << (qreal)1.0f << (qreal)1.0f << (qreal)1.0f << (void *)identityTranslate; + + static const qreal complexTranslate[] = + {1.0f, 0.0f, 0.0f, 2.0f, + 0.0f, 1.0f, 0.0f, 11.0f, + 0.0f, 0.0f, 1.0f, -6.5f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("complex") + << (qreal)2.0f << (qreal)11.0f << (qreal)-6.5f << (void *)complexTranslate; +} +void tst_QMatrix::translate4x4() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(void *, resultValues); + + QMatrix4x4 result((const qreal *)resultValues); + + QMatrix4x4 m1; + m1.translate(QVector3D(x, y, z)); + QVERIFY(isSame(m1, (const qreal *)resultValues)); + + QMatrix4x4 m2; + m2.translate(x, y, z); + QVERIFY(isSame(m2, (const qreal *)resultValues)); + + QVector3D v1(2.0f, 3.0f, -4.0f); + QVector3D v2 = m1 * v1; + QCOMPARE(v2.x(), (qreal)(2.0f + x)); + QCOMPARE(v2.y(), (qreal)(3.0f + y)); + QCOMPARE(v2.z(), (qreal)(-4.0f + z)); + + QVector4D v3(2.0f, 3.0f, -4.0f, 1.0f); + QVector4D v4 = m1 * v3; + QCOMPARE(v4.x(), (qreal)(2.0f + x)); + QCOMPARE(v4.y(), (qreal)(3.0f + y)); + QCOMPARE(v4.z(), (qreal)(-4.0f + z)); + QCOMPARE(v4.w(), (qreal)1.0f); + + QVector4D v5(2.0f, 3.0f, -4.0f, 34.0f); + QVector4D v6 = m1 * v5; + QCOMPARE(v6.x(), (qreal)(2.0f + x * 34.0f)); + QCOMPARE(v6.y(), (qreal)(3.0f + y * 34.0f)); + QCOMPARE(v6.z(), (qreal)(-4.0f + z * 34.0f)); + QCOMPARE(v6.w(), (qreal)34.0f); + + QPoint p1(2, 3); + QPoint p2 = m1 * p1; + QCOMPARE(p2.x(), (int)(2.0f + x)); + QCOMPARE(p2.y(), (int)(3.0f + y)); + + QPointF p3(2.0f, 3.0f); + QPointF p4 = m1 * p3; + QCOMPARE(p4.x(), (qreal)(2.0f + x)); + QCOMPARE(p4.y(), (qreal)(3.0f + y)); + + QMatrix4x4 m3(uniqueValues4); + QMatrix4x4 m4(m3); + m4.translate(x, y, z); + QVERIFY(m4 == m3 * m1); +} + +// Test the generation and use of 4x4 rotation matrices. +void tst_QMatrix::rotate4x4_data() +{ + QTest::addColumn<qreal>("angle"); + QTest::addColumn<qreal>("x"); + QTest::addColumn<qreal>("y"); + QTest::addColumn<qreal>("z"); + QTest::addColumn<void *>("resultValues"); + + static const qreal nullRotate[] = + {0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("null") + << (qreal)90.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (void *)nullRotate; + + static const qreal noRotate[] = + {1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("zerodegrees") + << (qreal)0.0f + << (qreal)2.0f << (qreal)3.0f << (qreal)-4.0f + << (void *)noRotate; + + static const qreal xRotate[] = + {1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("xrotate") + << (qreal)90.0f + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (void *)xRotate; + + static const qreal xRotateNeg[] = + {1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("-xrotate") + << (qreal)90.0f + << (qreal)-1.0f << (qreal)0.0f << (qreal)0.0f + << (void *)xRotateNeg; + + static const qreal yRotate[] = + {0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("yrotate") + << (qreal)90.0f + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (void *)yRotate; + + static const qreal yRotateNeg[] = + {0.0f, 0.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("-yrotate") + << (qreal)90.0f + << (qreal)0.0f << (qreal)-1.0f << (qreal)0.0f + << (void *)yRotateNeg; + + static const qreal zRotate[] = + {0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("zrotate") + << (qreal)90.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (void *)zRotate; + + static const qreal zRotateNeg[] = + {0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + QTest::newRow("-zrotate") + << (qreal)90.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)-1.0f + << (void *)zRotateNeg; + + // Algorithm from http://en.wikipedia.org/wiki/Rotation_matrix. + // Deliberately different from the one in the code for cross-checking. + static qreal complexRotate[16]; + qreal x = 1.0f; + qreal y = 2.0f; + qreal z = -6.0f; + qreal angle = -45.0f; + qreal c = qCos(angle * M_PI / 180.0f); + qreal s = qSin(angle * M_PI / 180.0f); + qreal len = qSqrt(x * x + y * y + z * z); + qreal xu = x / len; + qreal yu = y / len; + qreal zu = z / len; + complexRotate[0] = (qreal)((1 - xu * xu) * c + xu * xu); + complexRotate[1] = (qreal)(-zu * s - xu * yu * c + xu * yu); + complexRotate[2] = (qreal)(yu * s - xu * zu * c + xu * zu); + complexRotate[3] = 0; + complexRotate[4] = (qreal)(zu * s - xu * yu * c + xu * yu); + complexRotate[5] = (qreal)((1 - yu * yu) * c + yu * yu); + complexRotate[6] = (qreal)(-xu * s - yu * zu * c + yu * zu); + complexRotate[7] = 0; + complexRotate[8] = (qreal)(-yu * s - xu * zu * c + xu * zu); + complexRotate[9] = (qreal)(xu * s - yu * zu * c + yu * zu); + complexRotate[10] = (qreal)((1 - zu * zu) * c + zu * zu); + complexRotate[11] = 0; + complexRotate[12] = 0; + complexRotate[13] = 0; + complexRotate[14] = 0; + complexRotate[15] = 1; + + QTest::newRow("complex") + << (qreal)angle + << (qreal)x << (qreal)y << (qreal)z + << (void *)complexRotate; +} +void tst_QMatrix::rotate4x4() +{ + QFETCH(qreal, angle); + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(void *, resultValues); + + QMatrix4x4 m1; + m1.rotate(angle, QVector3D(x, y, z)); + QVERIFY(isSame(m1, (const qreal *)resultValues)); + + QMatrix4x4 m2; + m2.rotate(angle, x, y, z); + QVERIFY(isSame(m2, (const qreal *)resultValues)); + + QMatrix4x4 m3(uniqueValues4); + QMatrix4x4 m4(m3); + m4.rotate(angle, x, y, z); + QVERIFY(matrixFuzzyCompare(m4, m3 * m1)); + + // Null vectors don't make sense for quaternion rotations. + if (x != 0 || y != 0 || z != 0) { + QMatrix4x4 m5; + m5.rotate(QQuaternion::fromAxisAndAngle(QVector3D(x, y, z), angle)); + QVERIFY(isSame(m5, (const qreal *)resultValues)); + } + +#define ROTATE4(xin,yin,zin,win,xout,yout,zout,wout) \ + do { \ + xout = ((const qreal *)resultValues)[0] * xin + \ + ((const qreal *)resultValues)[1] * yin + \ + ((const qreal *)resultValues)[2] * zin + \ + ((const qreal *)resultValues)[3] * win; \ + yout = ((const qreal *)resultValues)[4] * xin + \ + ((const qreal *)resultValues)[5] * yin + \ + ((const qreal *)resultValues)[6] * zin + \ + ((const qreal *)resultValues)[7] * win; \ + zout = ((const qreal *)resultValues)[8] * xin + \ + ((const qreal *)resultValues)[9] * yin + \ + ((const qreal *)resultValues)[10] * zin + \ + ((const qreal *)resultValues)[11] * win; \ + wout = ((const qreal *)resultValues)[12] * xin + \ + ((const qreal *)resultValues)[13] * yin + \ + ((const qreal *)resultValues)[14] * zin + \ + ((const qreal *)resultValues)[15] * win; \ + } while (0) + + // Rotate various test vectors using the straight-forward approach. + qreal v1x, v1y, v1z, v1w; + ROTATE4(2.0f, 3.0f, -4.0f, 1.0f, v1x, v1y, v1z, v1w); + v1x /= v1w; + v1y /= v1w; + v1z /= v1w; + qreal v3x, v3y, v3z, v3w; + ROTATE4(2.0f, 3.0f, -4.0f, 1.0f, v3x, v3y, v3z, v3w); + qreal v5x, v5y, v5z, v5w; + ROTATE4(2.0f, 3.0f, -4.0f, 34.0f, v5x, v5y, v5z, v5w); + qreal p1x, p1y, p1z, p1w; + ROTATE4(2.0f, 3.0f, 0.0f, 1.0f, p1x, p1y, p1z, p1w); + p1x /= p1w; + p1y /= p1w; + p1z /= p1w; + + QVector3D v1(2.0f, 3.0f, -4.0f); + QVector3D v2 = m1 * v1; + QVERIFY(fuzzyCompare(v2.x(), v1x)); + QVERIFY(fuzzyCompare(v2.y(), v1y)); + QVERIFY(fuzzyCompare(v2.z(), v1z)); + + QVector4D v3(2.0f, 3.0f, -4.0f, 1.0f); + QVector4D v4 = m1 * v3; + QVERIFY(fuzzyCompare(v4.x(), v3x)); + QVERIFY(fuzzyCompare(v4.y(), v3y)); + QVERIFY(fuzzyCompare(v4.z(), v3z)); + QVERIFY(fuzzyCompare(v4.w(), v3w)); + + QVector4D v5(2.0f, 3.0f, -4.0f, 34.0f); + QVector4D v6 = m1 * v5; + QVERIFY(fuzzyCompare(v6.x(), v5x)); + QVERIFY(fuzzyCompare(v6.y(), v5y)); + QVERIFY(fuzzyCompare(v6.z(), v5z)); + QVERIFY(fuzzyCompare(v6.w(), v5w)); + + QPoint p1(2, 3); + QPoint p2 = m1 * p1; + QCOMPARE(p2.x(), qRound(p1x)); + QCOMPARE(p2.y(), qRound(p1y)); + + QPointF p3(2.0f, 3.0f); + QPointF p4 = m1 * p3; + QVERIFY(fuzzyCompare((float)(p4.x()), p1x)); + QVERIFY(fuzzyCompare((float)(p4.y()), p1y)); + + if (x != 0 || y != 0 || z != 0) { + QQuaternion q = QQuaternion::fromAxisAndAngle(QVector3D(x, y, z), angle); + QVector3D vq = q.rotateVector(v1); + QVERIFY(fuzzyCompare(vq.x(), v1x)); + QVERIFY(fuzzyCompare(vq.y(), v1y)); + QVERIFY(fuzzyCompare(vq.z(), v1z)); + } +} + +static bool isSame(const QMatrix3x3& m1, const Matrix3& m2) +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + if (!fuzzyCompare(m1(row, col), m2.v[row * 3 + col])) + return false; + } + } + return true; +} + +// Test the computation of normal matrices from 4x4 transformation matrices. +void tst_QMatrix::normalMatrix_data() +{ + QTest::addColumn<void *>("mValues"); + + QTest::newRow("identity") + << (void *)identityValues4; + QTest::newRow("unique") + << (void *)uniqueValues4; // Not invertible because determinant == 0. + + static qreal const translateValues[16] = + {1.0f, 0.0f, 0.0f, 4.0f, + 0.0f, 1.0f, 0.0f, 5.0f, + 0.0f, 0.0f, 1.0f, -3.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const scaleValues[16] = + {2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 7.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 9.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const bothValues[16] = + {2.0f, 0.0f, 0.0f, 4.0f, + 0.0f, 7.0f, 0.0f, 5.0f, + 0.0f, 0.0f, 9.0f, -3.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const nullScaleValues1[16] = + {0.0f, 0.0f, 0.0f, 4.0f, + 0.0f, 7.0f, 0.0f, 5.0f, + 0.0f, 0.0f, 9.0f, -3.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const nullScaleValues2[16] = + {2.0f, 0.0f, 0.0f, 4.0f, + 0.0f, 0.0f, 0.0f, 5.0f, + 0.0f, 0.0f, 9.0f, -3.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const nullScaleValues3[16] = + {2.0f, 0.0f, 0.0f, 4.0f, + 0.0f, 7.0f, 0.0f, 5.0f, + 0.0f, 0.0f, 0.0f, -3.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + + QTest::newRow("translate") << (void *)translateValues; + QTest::newRow("scale") << (void *)scaleValues; + QTest::newRow("both") << (void *)bothValues; + QTest::newRow("null scale 1") << (void *)nullScaleValues1; + QTest::newRow("null scale 2") << (void *)nullScaleValues2; + QTest::newRow("null scale 3") << (void *)nullScaleValues3; +} +void tst_QMatrix::normalMatrix() +{ + QFETCH(void *, mValues); + const qreal *values = (const qreal *)mValues; + + // Compute the expected answer the long way. + Matrix3 min; + Matrix3 answer; + min.v[0] = values[0]; + min.v[1] = values[1]; + min.v[2] = values[2]; + min.v[3] = values[4]; + min.v[4] = values[5]; + min.v[5] = values[6]; + min.v[6] = values[8]; + min.v[7] = values[9]; + min.v[8] = values[10]; + bool invertible = m3Inverse(min, answer); + m3Transpose(answer); + + // Perform the test. + QMatrix4x4 m1(values); + QMatrix3x3 n1 = m1.normalMatrix(); + + if (invertible) + QVERIFY(::isSame(n1, answer)); + else + QVERIFY(isIdentity(n1)); + + // Perform the test again, after inferring special matrix types. + // This tests the optimized paths in the normalMatrix() function. + m1.inferSpecialType(); + n1 = m1.normalMatrix(); + + if (invertible) + QVERIFY(::isSame(n1, answer)); + else + QVERIFY(isIdentity(n1)); +} + +// Test optimized transformations on 4x4 matrices. +void tst_QMatrix::optimizedTransforms() +{ + static qreal const translateValues[16] = + {1.0f, 0.0f, 0.0f, 4.0f, + 0.0f, 1.0f, 0.0f, 5.0f, + 0.0f, 0.0f, 1.0f, -3.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const translateDoubleValues[16] = + {1.0f, 0.0f, 0.0f, 8.0f, + 0.0f, 1.0f, 0.0f, 10.0f, + 0.0f, 0.0f, 1.0f, -6.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const scaleValues[16] = + {2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 7.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 9.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const scaleDoubleValues[16] = + {4.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 49.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 81.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const bothValues[16] = + {2.0f, 0.0f, 0.0f, 4.0f, + 0.0f, 7.0f, 0.0f, 5.0f, + 0.0f, 0.0f, 9.0f, -3.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const bothReverseValues[16] = + {2.0f, 0.0f, 0.0f, 4.0f * 2.0f, + 0.0f, 7.0f, 0.0f, 5.0f * 7.0f, + 0.0f, 0.0f, 9.0f, -3.0f * 9.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const bothThenTranslateValues[16] = + {2.0f, 0.0f, 0.0f, 4.0f + 2.0f * 4.0f, + 0.0f, 7.0f, 0.0f, 5.0f + 7.0f * 5.0f, + 0.0f, 0.0f, 9.0f, -3.0f + 9.0f * -3.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const bothThenScaleValues[16] = + {4.0f, 0.0f, 0.0f, 4.0f, + 0.0f, 49.0f, 0.0f, 5.0f, + 0.0f, 0.0f, 81.0f, -3.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + + QMatrix4x4 translate(translateValues); + QMatrix4x4 scale(scaleValues); + QMatrix4x4 both(bothValues); + + QMatrix4x4 m1; + m1.translate(4.0f, 5.0f, -3.0f); + QVERIFY(isSame(m1, translateValues)); + m1.translate(4.0f, 5.0f, -3.0f); + QVERIFY(isSame(m1, translateDoubleValues)); + + QMatrix4x4 m2; + m2.translate(QVector3D(4.0f, 5.0f, -3.0f)); + QVERIFY(isSame(m2, translateValues)); + m2.translate(QVector3D(4.0f, 5.0f, -3.0f)); + QVERIFY(isSame(m2, translateDoubleValues)); + + QMatrix4x4 m3; + m3.scale(2.0f, 7.0f, 9.0f); + QVERIFY(isSame(m3, scaleValues)); + m3.scale(2.0f, 7.0f, 9.0f); + QVERIFY(isSame(m3, scaleDoubleValues)); + + QMatrix4x4 m4; + m4.scale(QVector3D(2.0f, 7.0f, 9.0f)); + QVERIFY(isSame(m4, scaleValues)); + m4.scale(QVector3D(2.0f, 7.0f, 9.0f)); + QVERIFY(isSame(m4, scaleDoubleValues)); + + QMatrix4x4 m5; + m5.translate(4.0f, 5.0f, -3.0f); + m5.scale(2.0f, 7.0f, 9.0f); + QVERIFY(isSame(m5, bothValues)); + m5.translate(4.0f, 5.0f, -3.0f); + QVERIFY(isSame(m5, bothThenTranslateValues)); + + QMatrix4x4 m6; + m6.translate(QVector3D(4.0f, 5.0f, -3.0f)); + m6.scale(QVector3D(2.0f, 7.0f, 9.0f)); + QVERIFY(isSame(m6, bothValues)); + m6.translate(QVector3D(4.0f, 5.0f, -3.0f)); + QVERIFY(isSame(m6, bothThenTranslateValues)); + + QMatrix4x4 m7; + m7.scale(2.0f, 7.0f, 9.0f); + m7.translate(4.0f, 5.0f, -3.0f); + QVERIFY(isSame(m7, bothReverseValues)); + + QMatrix4x4 m8; + m8.scale(QVector3D(2.0f, 7.0f, 9.0f)); + m8.translate(QVector3D(4.0f, 5.0f, -3.0f)); + QVERIFY(isSame(m8, bothReverseValues)); + + QMatrix4x4 m9; + m9.translate(4.0f, 5.0f, -3.0f); + m9.scale(2.0f, 7.0f, 9.0f); + QVERIFY(isSame(m9, bothValues)); + m9.scale(2.0f, 7.0f, 9.0f); + QVERIFY(isSame(m9, bothThenScaleValues)); + + QMatrix4x4 m10; + m10.translate(QVector3D(4.0f, 5.0f, -3.0f)); + m10.scale(QVector3D(2.0f, 7.0f, 9.0f)); + QVERIFY(isSame(m10, bothValues)); + m10.scale(QVector3D(2.0f, 7.0f, 9.0f)); + QVERIFY(isSame(m10, bothThenScaleValues)); +} + +// Test orthographic projections. +void tst_QMatrix::ortho() +{ + QMatrix4x4 m1; + m1.ortho(QRect(0, 0, 300, 150)); + QPointF p1 = m1 * QPointF(0, 0); + QPointF p2 = m1 * QPointF(300, 0); + QPointF p3 = m1 * QPointF(0, 150); + QPointF p4 = m1 * QPointF(300, 150); + QVector3D p5 = m1 * QVector3D(300, 150, 1); + QVERIFY(fuzzyCompare(p1.x(), -1.0)); + QVERIFY(fuzzyCompare(p1.y(), 1.0)); + QVERIFY(fuzzyCompare(p2.x(), 1.0)); + QVERIFY(fuzzyCompare(p2.y(), 1.0)); + QVERIFY(fuzzyCompare(p3.x(), -1.0)); + QVERIFY(fuzzyCompare(p3.y(), -1.0)); + QVERIFY(fuzzyCompare(p4.x(), 1.0)); + QVERIFY(fuzzyCompare(p4.y(), -1.0)); + QVERIFY(fuzzyCompare(p5.x(), (qreal)1.0)); + QVERIFY(fuzzyCompare(p5.y(), (qreal)-1.0)); + QVERIFY(fuzzyCompare(p5.z(), (qreal)-1.0)); + + QMatrix4x4 m2; + m2.ortho(QRectF(0, 0, 300, 150)); + p1 = m2 * QPointF(0, 0); + p2 = m2 * QPointF(300, 0); + p3 = m2 * QPointF(0, 150); + p4 = m2 * QPointF(300, 150); + p5 = m2 * QVector3D(300, 150, 1); + QVERIFY(fuzzyCompare(p1.x(), -1.0)); + QVERIFY(fuzzyCompare(p1.y(), 1.0)); + QVERIFY(fuzzyCompare(p2.x(), 1.0)); + QVERIFY(fuzzyCompare(p2.y(), 1.0)); + QVERIFY(fuzzyCompare(p3.x(), -1.0)); + QVERIFY(fuzzyCompare(p3.y(), -1.0)); + QVERIFY(fuzzyCompare(p4.x(), 1.0)); + QVERIFY(fuzzyCompare(p4.y(), -1.0)); + QVERIFY(fuzzyCompare(p5.x(), (qreal)1.0)); + QVERIFY(fuzzyCompare(p5.y(), (qreal)-1.0)); + QVERIFY(fuzzyCompare(p5.z(), (qreal)-1.0)); + + QMatrix4x4 m3; + m3.ortho(0, 300, 150, 0, -1, 1); + p1 = m3 * QPointF(0, 0); + p2 = m3 * QPointF(300, 0); + p3 = m3 * QPointF(0, 150); + p4 = m3 * QPointF(300, 150); + p5 = m3 * QVector3D(300, 150, 1); + QVERIFY(fuzzyCompare(p1.x(), -1.0)); + QVERIFY(fuzzyCompare(p1.y(), 1.0)); + QVERIFY(fuzzyCompare(p2.x(), 1.0)); + QVERIFY(fuzzyCompare(p2.y(), 1.0)); + QVERIFY(fuzzyCompare(p3.x(), -1.0)); + QVERIFY(fuzzyCompare(p3.y(), -1.0)); + QVERIFY(fuzzyCompare(p4.x(), 1.0)); + QVERIFY(fuzzyCompare(p4.y(), -1.0)); + QVERIFY(fuzzyCompare(p5.x(), (qreal)1.0)); + QVERIFY(fuzzyCompare(p5.y(), (qreal)-1.0)); + QVERIFY(fuzzyCompare(p5.z(), (qreal)-1.0)); + + QMatrix4x4 m4; + m4.ortho(0, 300, 150, 0, -2, 3); + p1 = m4 * QPointF(0, 0); + p2 = m4 * QPointF(300, 0); + p3 = m4 * QPointF(0, 150); + p4 = m4 * QPointF(300, 150); + p5 = m4 * QVector3D(300, 150, 1); + QVERIFY(fuzzyCompare(p1.x(), -1.0)); + QVERIFY(fuzzyCompare(p1.y(), 1.0)); + QVERIFY(fuzzyCompare(p2.x(), 1.0)); + QVERIFY(fuzzyCompare(p2.y(), 1.0)); + QVERIFY(fuzzyCompare(p3.x(), -1.0)); + QVERIFY(fuzzyCompare(p3.y(), -1.0)); + QVERIFY(fuzzyCompare(p4.x(), 1.0)); + QVERIFY(fuzzyCompare(p4.y(), -1.0)); + QVERIFY(fuzzyCompare(p5.x(), (qreal)1.0)); + QVERIFY(fuzzyCompare(p5.y(), (qreal)-1.0)); + QVERIFY(fuzzyCompare(p5.z(), (qreal)-0.6)); + + // An empty view volume should leave the matrix alone. + QMatrix4x4 m5; + m5.ortho(0, 0, 150, 0, -2, 3); + QVERIFY(m5.isIdentity()); + m5.ortho(0, 300, 150, 150, -2, 3); + QVERIFY(m5.isIdentity()); + m5.ortho(0, 300, 150, 0, 2, 2); + QVERIFY(m5.isIdentity()); +} + +// Test perspective frustum projections. +void tst_QMatrix::frustum() +{ + QMatrix4x4 m1; + m1.frustum(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + QVector3D p1 = m1 * QVector3D(-1.0f, -1.0f, 1.0f); + QVector3D p2 = m1 * QVector3D(1.0f, -1.0f, 1.0f); + QVector3D p3 = m1 * QVector3D(-1.0f, 1.0f, 1.0f); + QVector3D p4 = m1 * QVector3D(1.0f, 1.0f, 1.0f); + QVector3D p5 = m1 * QVector3D(0.0f, 0.0f, 2.0f); + QVERIFY(fuzzyCompare(p1.x(), -1.0f)); + QVERIFY(fuzzyCompare(p1.y(), -1.0f)); + QVERIFY(fuzzyCompare(p1.z(), -1.0f)); + QVERIFY(fuzzyCompare(p2.x(), 1.0f)); + QVERIFY(fuzzyCompare(p2.y(), -1.0f)); + QVERIFY(fuzzyCompare(p2.z(), -1.0f)); + QVERIFY(fuzzyCompare(p3.x(), -1.0f)); + QVERIFY(fuzzyCompare(p3.y(), 1.0f)); + QVERIFY(fuzzyCompare(p3.z(), -1.0f)); + QVERIFY(fuzzyCompare(p4.x(), 1.0f)); + QVERIFY(fuzzyCompare(p4.y(), 1.0f)); + QVERIFY(fuzzyCompare(p4.z(), -1.0f)); + QVERIFY(fuzzyCompare(p5.x(), 0.0f)); + QVERIFY(fuzzyCompare(p5.y(), 0.0f)); + QVERIFY(fuzzyCompare(p5.z(), -0.5f)); + + // An empty view volume should leave the matrix alone. + QMatrix4x4 m5; + m5.frustum(0, 0, 150, 0, -2, 3); + QVERIFY(m5.isIdentity()); + m5.frustum(0, 300, 150, 150, -2, 3); + QVERIFY(m5.isIdentity()); + m5.frustum(0, 300, 150, 0, 2, 2); + QVERIFY(m5.isIdentity()); +} + +// Test perspective field-of-view projections. +void tst_QMatrix::perspective() +{ + QMatrix4x4 m1; + m1.perspective(45.0f, 1.0f, -1.0f, 1.0f); + QVector3D p1 = m1 * QVector3D(-1.0f, -1.0f, 1.0f); + QVector3D p2 = m1 * QVector3D(1.0f, -1.0f, 1.0f); + QVector3D p3 = m1 * QVector3D(-1.0f, 1.0f, 1.0f); + QVector3D p4 = m1 * QVector3D(1.0f, 1.0f, 1.0f); + QVector3D p5 = m1 * QVector3D(0.0f, 0.0f, 2.0f); + QVERIFY(fuzzyCompare(p1.x(), 2.41421)); + QVERIFY(fuzzyCompare(p1.y(), 2.41421)); + QVERIFY(fuzzyCompare(p1.z(), -1)); + QVERIFY(fuzzyCompare(p2.x(), -2.41421)); + QVERIFY(fuzzyCompare(p2.y(), 2.41421)); + QVERIFY(fuzzyCompare(p2.z(), -1.0f)); + QVERIFY(fuzzyCompare(p3.x(), 2.41421)); + QVERIFY(fuzzyCompare(p3.y(), -2.41421)); + QVERIFY(fuzzyCompare(p3.z(), -1.0f)); + QVERIFY(fuzzyCompare(p4.x(), -2.41421)); + QVERIFY(fuzzyCompare(p4.y(), -2.41421)); + QVERIFY(fuzzyCompare(p4.z(), -1.0f)); + QVERIFY(fuzzyCompare(p5.x(), 0.0f)); + QVERIFY(fuzzyCompare(p5.y(), 0.0f)); + QVERIFY(fuzzyCompare(p5.z(), -0.5f)); + + // An empty view volume should leave the matrix alone. + QMatrix4x4 m5; + m5.perspective(45.0f, 1.0f, 0.0f, 0.0f); + QVERIFY(m5.isIdentity()); + m5.perspective(45.0f, 0.0f, -1.0f, 1.0f); + QVERIFY(m5.isIdentity()); + m5.perspective(0.0f, 1.0f, -1.0f, 1.0f); + QVERIFY(m5.isIdentity()); +} + +// Test left-handed vs right-handed coordinate flipping. +void tst_QMatrix::flipCoordinates() +{ + QMatrix4x4 m1; + m1.flipCoordinates(); + QVector3D p1 = m1 * QVector3D(2, 3, 4); + QVERIFY(p1 == QVector3D(2, -3, -4)); + + QMatrix4x4 m2; + m2.scale(2.0f, 3.0f, 1.0f); + m2.flipCoordinates(); + QVector3D p2 = m2 * QVector3D(2, 3, 4); + QVERIFY(p2 == QVector3D(4, -9, -4)); + + QMatrix4x4 m3; + m3.translate(2.0f, 3.0f, 1.0f); + m3.flipCoordinates(); + QVector3D p3 = m3 * QVector3D(2, 3, 4); + QVERIFY(p3 == QVector3D(4, 0, -3)); + + QMatrix4x4 m4; + m4.rotate(90.0f, 0.0f, 0.0f, 1.0f); + m4.flipCoordinates(); + QVector3D p4 = m4 * QVector3D(2, 3, 4); + QVERIFY(p4 == QVector3D(3, 2, -4)); +} + +// Test conversion of generic matrices to and from the non-generic types. +void tst_QMatrix::convertGeneric() +{ + QMatrix4x3 m1(uniqueValues4x3); + + static qreal const unique4x4[16] = { + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; +#if !defined(QT_NO_MEMBER_TEMPLATES) + QMatrix4x4 m4(m1); + QVERIFY(isSame(m4, unique4x4)); +#endif + QMatrix4x4 m5 = qGenericMatrixToMatrix4x4(m1); + QVERIFY(isSame(m5, unique4x4)); + + static qreal const conv4x4[12] = { + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f + }; + QMatrix4x4 m9(uniqueValues4); +#if !defined(QT_NO_MEMBER_TEMPLATES) + QMatrix4x3 m10 = m9.toGenericMatrix<4, 3>(); + QVERIFY(isSame(m10, conv4x4)); +#endif + + QMatrix4x3 m11 = qGenericMatrixFromMatrix4x4<4, 3>(m9); + QVERIFY(isSame(m11, conv4x4)); +} + +void tst_QMatrix::extractAxisRotation_data() +{ + QTest::addColumn<float>("x"); + QTest::addColumn<float>("y"); + QTest::addColumn<float>("z"); + QTest::addColumn<float>("angle"); + + QTest::newRow("1, 0, 0, 0 deg") << 1.0f << 0.0f << 0.0f << 0.0f; + QTest::newRow("1, 0, 0, 90 deg") << 1.0f << 0.0f << 0.0f << 90.0f; + QTest::newRow("1, 0, 0, 270 deg") << 1.0f << 0.0f << 0.0f << 270.0f; + QTest::newRow("1, 0, 0, 45 deg") << 1.0f << 0.0f << 0.0f << 45.0f; + QTest::newRow("1, 0, 0, 120 deg") << 1.0f << 0.0f << 0.0f << 120.0f; + QTest::newRow("1, 0, 0, 300 deg") << 1.0f << 0.0f << 0.0f << 300.0f; + + QTest::newRow("0, 1, 0, 90 deg") << 0.0f << 1.0f << 0.0f << 90.0f; + QTest::newRow("0, 1, 0, 270 deg") << 0.0f << 1.0f << 0.0f << 270.0f; + QTest::newRow("0, 1, 0, 45 deg") << 0.0f << 1.0f << 0.0f << 45.0f; + QTest::newRow("0, 1, 0, 120 deg") << 0.0f << 1.0f << 0.0f << 120.0f; + QTest::newRow("0, 1, 0, 300 deg") << 0.0f << 1.0f << 0.0f << 300.0f; + + QTest::newRow("0, 0, 1, 90 deg") << 0.0f << 0.0f << 1.0f << 90.0f; + QTest::newRow("0, 0, 1, 270 deg") << 0.0f << 0.0f << 1.0f << 270.0f; + QTest::newRow("0, 0, 1, 45 deg") << 0.0f << 0.0f << 1.0f << 45.0f; + QTest::newRow("0, 0, 1, 120 deg") << 0.0f << 0.0f << 1.0f << 120.0f; + QTest::newRow("0, 0, 1, 300 deg") << 0.0f << 0.0f << 1.0f << 300.0f; + + QTest::newRow("1, 1, 1, 90 deg") << 1.0f << 1.0f << 1.0f << 90.0f; + QTest::newRow("1, 1, 1, 270 deg") << 1.0f << 1.0f << 1.0f << 270.0f; + QTest::newRow("1, 1, 1, 45 deg") << 1.0f << 1.0f << 1.0f << 45.0f; + QTest::newRow("1, 1, 1, 120 deg") << 1.0f << 1.0f << 1.0f << 120.0f; + QTest::newRow("1, 1, 1, 300 deg") << 1.0f << 1.0f << 1.0f << 300.0f; +} + +void tst_QMatrix::extractAxisRotation() +{ + QFETCH(float, x); + QFETCH(float, y); + QFETCH(float, z); + QFETCH(float, angle); + + QMatrix4x4 m; + QVector3D origAxis(x, y, z); + + m.rotate(angle, x, y, z); + + origAxis.normalize(); + QVector3D extractedAxis; + qreal extractedAngle; + + m.extractAxisRotation(extractedAngle, extractedAxis); + +#ifdef QT_GL_FIXED_PREFERRED + qreal epsilon = 0.003; +#else + qreal epsilon = 0.001; +#endif + + if (angle > 180) { + QVERIFY(fuzzyCompare(360.0f - angle, extractedAngle, epsilon)); + QVERIFY(fuzzyCompare(extractedAxis, -origAxis, epsilon)); + } else { + QVERIFY(fuzzyCompare(angle, extractedAngle, epsilon)); + QVERIFY(fuzzyCompare(extractedAxis, origAxis, epsilon)); + } +} + +void tst_QMatrix::extractTranslation_data() +{ + QTest::addColumn<QMatrix4x4>("rotation"); + QTest::addColumn<float>("x"); + QTest::addColumn<float>("y"); + QTest::addColumn<float>("z"); + + static QMatrix4x4 m1; + + QTest::newRow("identity, 100, 50, 25") + << m1 << 100.0f << 50.0f << 250.0f; + + m1.rotate(45.0, 1.0, 0.0, 0.0); + QTest::newRow("rotX 45 + 100, 50, 25") << m1 << 100.0f << 50.0f << 25.0f; + + m1.setIdentity(); + m1.rotate(45.0, 0.0, 1.0, 0.0); + QTest::newRow("rotY 45 + 100, 50, 25") << m1 << 100.0f << 50.0f << 25.0f; + + m1.setIdentity(); + m1.rotate(75, 0.0, 0.0, 1.0); + m1.rotate(25, 1.0, 0.0, 0.0); + m1.rotate(45, 0.0, 1.0, 0.0); + QTest::newRow("rotZ 75, rotX 25, rotY 45, 100, 50, 25") << m1 << 100.0f << 50.0f << 25.0f; +} + +void tst_QMatrix::extractTranslation() +{ + QFETCH(QMatrix4x4, rotation); + QFETCH(float, x); + QFETCH(float, y); + QFETCH(float, z); + + rotation.translate(x, y, z); + + QVector3D vec = rotation.extractTranslation(); + +#ifdef QT_GL_FIXED_PREFERRED + qreal epsilon = 0.01; +#else + qreal epsilon = 0.001; +#endif + + QVERIFY(fuzzyCompare(vec.x(), x, epsilon)); + QVERIFY(fuzzyCompare(vec.y(), y, epsilon)); + QVERIFY(fuzzyCompare(vec.z(), z, epsilon)); + + // Have to be careful with numbers here, it is really easy to blow away + // the precision of a fixed pointer number, especially when doing distance + // formula for vector normalization + + QMatrix4x4 lookAt; + QVector3D eye(1.5f, -2.5f, 2.5f); + lookAt.lookAt(eye, + QVector3D(10.0f, 10.0f, 10.0f), + QVector3D(0.0f, 1.0f, 0.0f)); + + QVector3D extEye = lookAt.extractTranslation(); + + QVERIFY(fuzzyCompare(eye.x(), -extEye.x(), epsilon)); + QVERIFY(fuzzyCompare(eye.y(), -extEye.y(), epsilon)); + QVERIFY(fuzzyCompare(eye.z(), -extEye.z(), epsilon)); +} + +// Copy of "flagBits" in qmatrix4x4.h. +enum { + Identity = 0x0001, // Identity matrix + General = 0x0002, // General matrix, unknown contents + Translation = 0x0004, // Contains a simple translation + Scale = 0x0008, // Contains a simple scale + Rotation = 0x0010 // Contains a simple rotation +}; + +// Structure that allows direct access to "flagBits" for testing. +struct Matrix4x4 +{ + qrealinner m[4][4]; + int flagBits; +}; + +// Test the inferring of special matrix types. +void tst_QMatrix::inferSpecialType_data() +{ + QTest::addColumn<void *>("mValues"); + QTest::addColumn<int>("flagBits"); + + QTest::newRow("null") + << (void *)nullValues4 << (int)General; + QTest::newRow("identity") + << (void *)identityValues4 << (int)Identity; + QTest::newRow("unique") + << (void *)uniqueValues4 << (int)General; + + static qreal scaleValues[16] = { + 2.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 3.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 4.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("scale") + << (void *)scaleValues << (int)Scale; + + static qreal translateValues[16] = { + 1.0f, 0.0f, 0.0f, 2.0f, + 0.0f, 1.0f, 0.0f, 3.0f, + 0.0f, 0.0f, 1.0f, 4.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("scale") + << (void *)translateValues << (int)Translation; + + static qreal bothValues[16] = { + 1.0f, 0.0f, 0.0f, 2.0f, + 0.0f, 2.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 4.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("both") + << (void *)bothValues << (int)(Scale | Translation); + + static qreal belowValues[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 4.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("below") + << (void *)belowValues << (int)General; +} +void tst_QMatrix::inferSpecialType() +{ + QFETCH(void *, mValues); + QFETCH(int, flagBits); + + QMatrix4x4 m((const qreal *)mValues); + m.inferSpecialType(); + + QCOMPARE(reinterpret_cast<Matrix4x4 *>(&m)->flagBits, flagBits); +} + +void tst_QMatrix::columnsAndRows() +{ + QMatrix4x4 m1(uniqueValues4); + + QVERIFY(m1.column(0) == QVector4D(1, 5, 9, 13)); + QVERIFY(m1.column(1) == QVector4D(2, 6, 10, 14)); + QVERIFY(m1.column(2) == QVector4D(3, 7, 11, 15)); + QVERIFY(m1.column(3) == QVector4D(4, 8, 12, 16)); + + QVERIFY(m1.row(0) == QVector4D(1, 2, 3, 4)); + QVERIFY(m1.row(1) == QVector4D(5, 6, 7, 8)); + QVERIFY(m1.row(2) == QVector4D(9, 10, 11, 12)); + QVERIFY(m1.row(3) == QVector4D(13, 14, 15, 16)); + + m1.setColumn(0, QVector4D(-1, -5, -9, -13)); + m1.setColumn(1, QVector4D(-2, -6, -10, -14)); + m1.setColumn(2, QVector4D(-3, -7, -11, -15)); + m1.setColumn(3, QVector4D(-4, -8, -12, -16)); + + QVERIFY(m1.column(0) == QVector4D(-1, -5, -9, -13)); + QVERIFY(m1.column(1) == QVector4D(-2, -6, -10, -14)); + QVERIFY(m1.column(2) == QVector4D(-3, -7, -11, -15)); + QVERIFY(m1.column(3) == QVector4D(-4, -8, -12, -16)); + + QVERIFY(m1.row(0) == QVector4D(-1, -2, -3, -4)); + QVERIFY(m1.row(1) == QVector4D(-5, -6, -7, -8)); + QVERIFY(m1.row(2) == QVector4D(-9, -10, -11, -12)); + QVERIFY(m1.row(3) == QVector4D(-13, -14, -15, -16)); + + m1.setRow(0, QVector4D(1, 5, 9, 13)); + m1.setRow(1, QVector4D(2, 6, 10, 14)); + m1.setRow(2, QVector4D(3, 7, 11, 15)); + m1.setRow(3, QVector4D(4, 8, 12, 16)); + + QVERIFY(m1.column(0) == QVector4D(1, 2, 3, 4)); + QVERIFY(m1.column(1) == QVector4D(5, 6, 7, 8)); + QVERIFY(m1.column(2) == QVector4D(9, 10, 11, 12)); + QVERIFY(m1.column(3) == QVector4D(13, 14, 15, 16)); + + QVERIFY(m1.row(0) == QVector4D(1, 5, 9, 13)); + QVERIFY(m1.row(1) == QVector4D(2, 6, 10, 14)); + QVERIFY(m1.row(2) == QVector4D(3, 7, 11, 15)); + QVERIFY(m1.row(3) == QVector4D(4, 8, 12, 16)); +} + +// Test converting QMatrix objects into QMatrix4x4 and then +// checking that transformations in the original perform the +// equivalent transformations in the new matrix. +void tst_QMatrix::convertQMatrix() +{ + QMatrix m1; + m1.translate(-3.5, 2.0); + QPointF p1 = m1.map(QPointF(100.0, 150.0)); + QCOMPARE(p1.x(), 100.0 - 3.5); + QCOMPARE(p1.y(), 150.0 + 2.0); + + QMatrix4x4 m2(m1); + QPointF p2 = m2 * QPointF(100.0, 150.0); + QCOMPARE((double)p2.x(), 100.0 - 3.5); + QCOMPARE((double)p2.y(), 150.0 + 2.0); + QVERIFY(m1 == m2.toAffine()); + + QMatrix m3; + m3.scale(1.5, -2.0); + QPointF p3 = m3.map(QPointF(100.0, 150.0)); + QCOMPARE(p3.x(), 1.5 * 100.0); + QCOMPARE(p3.y(), -2.0 * 150.0); + + QMatrix4x4 m4(m3); + QPointF p4 = m4 * QPointF(100.0, 150.0); + QCOMPARE((double)p4.x(), 1.5 * 100.0); + QCOMPARE((double)p4.y(), -2.0 * 150.0); + QVERIFY(m3 == m4.toAffine()); + + QMatrix m5; + m5.rotate(45.0); + QPointF p5 = m5.map(QPointF(100.0, 150.0)); + + QMatrix4x4 m6(m5); + QPointF p6 = m6 * QPointF(100.0, 150.0); + QVERIFY(fuzzyCompare(p5.x(), p6.x(), 0.005)); + QVERIFY(fuzzyCompare(p5.y(), p6.y(), 0.005)); + + QMatrix m7 = m6.toAffine(); + QVERIFY(fuzzyCompare(m5.m11(), m7.m11())); + QVERIFY(fuzzyCompare(m5.m12(), m7.m12())); + QVERIFY(fuzzyCompare(m5.m21(), m7.m21())); + QVERIFY(fuzzyCompare(m5.m22(), m7.m22())); + QVERIFY(fuzzyCompare(m5.dx(), m7.dx())); + QVERIFY(fuzzyCompare(m5.dy(), m7.dy())); +} + +// Test converting QTransform objects into QMatrix4x4 and then +// checking that transformations in the original perform the +// equivalent transformations in the new matrix. +void tst_QMatrix::convertQTransform() +{ + QTransform m1; + m1.translate(-3.5, 2.0); + QPointF p1 = m1.map(QPointF(100.0, 150.0)); + QCOMPARE(p1.x(), 100.0 - 3.5); + QCOMPARE(p1.y(), 150.0 + 2.0); + + QMatrix4x4 m2(m1); + QPointF p2 = m2 * QPointF(100.0, 150.0); + QCOMPARE((double)p2.x(), 100.0 - 3.5); + QCOMPARE((double)p2.y(), 150.0 + 2.0); + QVERIFY(m1 == m2.toTransform()); + + QTransform m3; + m3.scale(1.5, -2.0); + QPointF p3 = m3.map(QPointF(100.0, 150.0)); + QCOMPARE(p3.x(), 1.5 * 100.0); + QCOMPARE(p3.y(), -2.0 * 150.0); + + QMatrix4x4 m4(m3); + QPointF p4 = m4 * QPointF(100.0, 150.0); + QCOMPARE((double)p4.x(), 1.5 * 100.0); + QCOMPARE((double)p4.y(), -2.0 * 150.0); + QVERIFY(m3 == m4.toTransform()); + + QTransform m5; + m5.rotate(45.0); + QPointF p5 = m5.map(QPointF(100.0, 150.0)); + + QMatrix4x4 m6(m5); + QPointF p6 = m6 * QPointF(100.0, 150.0); + QVERIFY(fuzzyCompare(p5.x(), p6.x(), 0.005)); + QVERIFY(fuzzyCompare(p5.y(), p6.y(), 0.005)); + + QTransform m7 = m6.toTransform(); + QVERIFY(fuzzyCompare(m5.m11(), m7.m11())); + QVERIFY(fuzzyCompare(m5.m12(), m7.m12())); + QVERIFY(fuzzyCompare(m5.m21(), m7.m21())); + QVERIFY(fuzzyCompare(m5.m22(), m7.m22())); + QVERIFY(fuzzyCompare(m5.dx(), m7.dx())); + QVERIFY(fuzzyCompare(m5.dy(), m7.dy())); + QVERIFY(fuzzyCompare(m5.m13(), m7.m13())); + QVERIFY(fuzzyCompare(m5.m23(), m7.m23())); + QVERIFY(fuzzyCompare(m5.m33(), m7.m33())); +} + +// Test filling matrices with specific values. +void tst_QMatrix::fill() +{ + QMatrix4x4 m1; + m1.fill(0.0f); + QVERIFY(isSame(m1, nullValues4)); + + static const qreal fillValues4[] = + {2.5f, 2.5f, 2.5f, 2.5f, + 2.5f, 2.5f, 2.5f, 2.5f, + 2.5f, 2.5f, 2.5f, 2.5f, + 2.5f, 2.5f, 2.5f, 2.5f}; + m1.fill(2.5f); + QVERIFY(isSame(m1, fillValues4)); + + QMatrix4x3 m2; + m2.fill(0.0f); + QVERIFY(isSame(m2, nullValues4x3)); + + static const qreal fillValues4x3[] = + {2.5f, 2.5f, 2.5f, 2.5f, + 2.5f, 2.5f, 2.5f, 2.5f, + 2.5f, 2.5f, 2.5f, 2.5f}; + m2.fill(2.5f); + QVERIFY(isSame(m2, fillValues4x3)); +} + +// Force the fixed-point version of the test case to put a +// different test name on the front of failure reports. +class tst_QMatrixFixed : public tst_QMatrix +{ + Q_OBJECT +public: + tst_QMatrixFixed() {} + ~tst_QMatrixFixed() {} +}; + +#ifdef QT_GL_FIXED_PREFERRED + +QTEST_APPLESS_MAIN(tst_QMatrixFixed) + +#else + +QTEST_APPLESS_MAIN(tst_QMatrix) + +#endif + +#include "tst_qmatrixnxn.moc" diff --git a/tests/auto/math3d/qmatrixnxn_fixed/qmatrixnxn_fixed.pro b/tests/auto/math3d/qmatrixnxn_fixed/qmatrixnxn_fixed.pro new file mode 100644 index 0000000..cd1c8fa --- /dev/null +++ b/tests/auto/math3d/qmatrixnxn_fixed/qmatrixnxn_fixed.pro @@ -0,0 +1,8 @@ +load(qttest_p4) +VPATH += ../shared +VPATH += ../qmatrixnxn +INCLUDEPATH += ../shared +INCLUDEPATH += ../../../../src/gui/math3d +DEFINES += FIXED_POINT_TESTS +HEADERS += math3dincludes.h +SOURCES += tst_qmatrixnxn.cpp math3dincludes.cpp diff --git a/tests/auto/math3d/qquaternion/qquaternion.pro b/tests/auto/math3d/qquaternion/qquaternion.pro new file mode 100644 index 0000000..eea84f0 --- /dev/null +++ b/tests/auto/math3d/qquaternion/qquaternion.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +VPATH += ../shared +INCLUDEPATH += ../shared +HEADERS += math3dincludes.h +SOURCES += tst_qquaternion.cpp diff --git a/tests/auto/math3d/qquaternion/tst_qquaternion.cpp b/tests/auto/math3d/qquaternion/tst_qquaternion.cpp new file mode 100644 index 0000000..325cb40 --- /dev/null +++ b/tests/auto/math3d/qquaternion/tst_qquaternion.cpp @@ -0,0 +1,791 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qmath.h> +#include "math3dincludes.h" + +class tst_QQuaternion : public QObject +{ + Q_OBJECT +public: + tst_QQuaternion() {} + ~tst_QQuaternion() {} + +private slots: + void create(); + + void length_data(); + void length(); + + void normalized_data(); + void normalized(); + + void normalize_data(); + void normalize(); + + void compare(); + + void add_data(); + void add(); + + void subtract_data(); + void subtract(); + + void multiply_data(); + void multiply(); + + void multiplyFactor_data(); + void multiplyFactor(); + + void divide_data(); + void divide(); + + void negate_data(); + void negate(); + + void conjugate_data(); + void conjugate(); + + void fromAxisAndAngle_data(); + void fromAxisAndAngle(); + + void interpolate_data(); + void interpolate(); +}; + +// qFuzzyCompare isn't quite "fuzzy" enough to handle conversion +// to fixed-point and back again. So create "fuzzier" compares. +static bool fuzzyCompare(float x, float y) +{ + float diff = x - y; + if (diff < 0.0f) + diff = -diff; + return (diff < 0.001); +} + +// Test the creation of QQuaternion objects in various ways: +// construct, copy, and modify. +void tst_QQuaternion::create() +{ + QQuaternion identity; + QCOMPARE(identity.x(), (qreal)0.0f); + QCOMPARE(identity.y(), (qreal)0.0f); + QCOMPARE(identity.z(), (qreal)0.0f); + QCOMPARE(identity.scalar(), (qreal)1.0f); + QVERIFY(identity.isIdentity()); + + QQuaternion v1(34.0f, 1.0f, 2.5f, -89.25f); + QCOMPARE(v1.x(), (qreal)1.0f); + QCOMPARE(v1.y(), (qreal)2.5f); + QCOMPARE(v1.z(), (qreal)-89.25f); + QCOMPARE(v1.scalar(), (qreal)34.0f); + QVERIFY(!v1.isNull()); + + QQuaternion v1i(34, 1, 2, -89); + QCOMPARE(v1i.x(), (qreal)1.0f); + QCOMPARE(v1i.y(), (qreal)2.0f); + QCOMPARE(v1i.z(), (qreal)-89.0f); + QCOMPARE(v1i.scalar(), (qreal)34.0f); + QVERIFY(!v1i.isNull()); + + QQuaternion v2(v1); + QCOMPARE(v2.x(), (qreal)1.0f); + QCOMPARE(v2.y(), (qreal)2.5f); + QCOMPARE(v2.z(), (qreal)-89.25f); + QCOMPARE(v2.scalar(), (qreal)34.0f); + QVERIFY(!v2.isNull()); + + QQuaternion v4; + QCOMPARE(v4.x(), (qreal)0.0f); + QCOMPARE(v4.y(), (qreal)0.0f); + QCOMPARE(v4.z(), (qreal)0.0f); + QCOMPARE(v4.scalar(), (qreal)1.0f); + QVERIFY(v4.isIdentity()); + v4 = v1; + QCOMPARE(v4.x(), (qreal)1.0f); + QCOMPARE(v4.y(), (qreal)2.5f); + QCOMPARE(v4.z(), (qreal)-89.25f); + QCOMPARE(v4.scalar(), (qreal)34.0f); + QVERIFY(!v4.isNull()); + + QQuaternion v9(34, QVector3D(1.0f, 2.5f, -89.25f)); + QCOMPARE(v9.x(), (qreal)1.0f); + QCOMPARE(v9.y(), (qreal)2.5f); + QCOMPARE(v9.z(), (qreal)-89.25f); + QCOMPARE(v9.scalar(), (qreal)34.0f); + QVERIFY(!v9.isNull()); + + v1.setX(3.0f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)2.5f); + QCOMPARE(v1.z(), (qreal)-89.25f); + QCOMPARE(v1.scalar(), (qreal)34.0f); + QVERIFY(!v1.isNull()); + + v1.setY(10.5f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)10.5f); + QCOMPARE(v1.z(), (qreal)-89.25f); + QCOMPARE(v1.scalar(), (qreal)34.0f); + QVERIFY(!v1.isNull()); + + v1.setZ(15.5f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)10.5f); + QCOMPARE(v1.z(), (qreal)15.5f); + QCOMPARE(v1.scalar(), (qreal)34.0f); + QVERIFY(!v1.isNull()); + + v1.setScalar(6.0f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)10.5f); + QCOMPARE(v1.z(), (qreal)15.5f); + QCOMPARE(v1.scalar(), (qreal)6.0f); + QVERIFY(!v1.isNull()); + + v1.setVector(2.0f, 6.5f, -1.25f); + QCOMPARE(v1.x(), (qreal)2.0f); + QCOMPARE(v1.y(), (qreal)6.5f); + QCOMPARE(v1.z(), (qreal)-1.25f); + QCOMPARE(v1.scalar(), (qreal)6.0f); + QVERIFY(!v1.isNull()); + QVERIFY(v1.vector() == QVector3D(2.0f, 6.5f, -1.25f)); + + v1.setVector(QVector3D(-2.0f, -6.5f, 1.25f)); + QCOMPARE(v1.x(), (qreal)-2.0f); + QCOMPARE(v1.y(), (qreal)-6.5f); + QCOMPARE(v1.z(), (qreal)1.25f); + QCOMPARE(v1.scalar(), (qreal)6.0f); + QVERIFY(!v1.isNull()); + QVERIFY(v1.vector() == QVector3D(-2.0f, -6.5f, 1.25f)); + + v1.setX(0.0f); + v1.setY(0.0f); + v1.setZ(0.0f); + v1.setScalar(0.0f); + QCOMPARE(v1.x(), (qreal)0.0f); + QCOMPARE(v1.y(), (qreal)0.0f); + QCOMPARE(v1.z(), (qreal)0.0f); + QCOMPARE(v1.scalar(), (qreal)0.0f); + QVERIFY(v1.isNull()); + + QVector4D v10 = v9.toVector4D(); + QCOMPARE(v10.x(), (qreal)1.0f); + QCOMPARE(v10.y(), (qreal)2.5f); + QCOMPARE(v10.z(), (qreal)-89.25f); + QCOMPARE(v10.w(), (qreal)34.0f); +} + +// Test length computation for quaternions. +void tst_QQuaternion::length_data() +{ + QTest::addColumn<qreal>("x"); + QTest::addColumn<qreal>("y"); + QTest::addColumn<qreal>("z"); + QTest::addColumn<qreal>("w"); + QTest::addColumn<qreal>("len"); + + QTest::newRow("null") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + QTest::newRow("1x") << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("1y") << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("1z") << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("1w") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)1.0f; + QTest::newRow("-1x") << (qreal)-1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("-1y") << (qreal)0.0f << (qreal)-1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("-1z") << (qreal)0.0f << (qreal)0.0f << (qreal)-1.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("-1w") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)-1.0f << (qreal)1.0f; + QTest::newRow("two") << (qreal)2.0f << (qreal)-2.0f << (qreal)2.0f << (qreal)2.0f << (qreal)qSqrt(16.0f); +} +void tst_QQuaternion::length() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(qreal, w); + QFETCH(qreal, len); + + QQuaternion v(w, x, y, z); + QCOMPARE((float)(v.length()), (float)len); + QCOMPARE((float)(v.lengthSquared()), (float)(x * x + y * y + z * z + w * w)); +} + +// Test the unit vector conversion for quaternions. +void tst_QQuaternion::normalized_data() +{ + // Use the same test data as the length test. + length_data(); +} +void tst_QQuaternion::normalized() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(qreal, w); + QFETCH(qreal, len); + + QQuaternion v(w, x, y, z); + QQuaternion u = v.normalized(); + if (v.isNull()) + QVERIFY(u.isNull()); + else + QCOMPARE((float)(u.length()), (float)1.0f); + QCOMPARE((float)(u.x() * len), (float)(v.x())); + QCOMPARE((float)(u.y() * len), (float)(v.y())); + QCOMPARE((float)(u.z() * len), (float)(v.z())); + QCOMPARE((float)(u.scalar() * len), (float)(v.scalar())); +} + +// Test the unit vector conversion for quaternions. +void tst_QQuaternion::normalize_data() +{ + // Use the same test data as the length test. + length_data(); +} +void tst_QQuaternion::normalize() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(qreal, w); + + QQuaternion v(w, x, y, z); + bool isNull = v.isNull(); + v.normalize(); + if (isNull) + QVERIFY(v.isNull()); + else + QCOMPARE((float)(v.length()), (float)1.0f); +} + +// Test the comparison operators for quaternions. +void tst_QQuaternion::compare() +{ + QQuaternion v1(8, 1, 2, 4); + QQuaternion v2(8, 1, 2, 4); + QQuaternion v3(8, 3, 2, 4); + QQuaternion v4(8, 1, 3, 4); + QQuaternion v5(8, 1, 2, 3); + QQuaternion v6(3, 1, 2, 4); + + QVERIFY(v1 == v2); + QVERIFY(v1 != v3); + QVERIFY(v1 != v4); + QVERIFY(v1 != v5); + QVERIFY(v1 != v6); +} + +// Test addition for quaternions. +void tst_QQuaternion::add_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("w1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("w2"); + QTest::addColumn<qreal>("x3"); + QTest::addColumn<qreal>("y3"); + QTest::addColumn<qreal>("z3"); + QTest::addColumn<qreal>("w3"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)3.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)3.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("zonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)3.0f << (qreal)0.0f; + + QTest::newRow("wonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)3.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f << (qreal)8.0f + << (qreal)4.0f << (qreal)5.0f << (qreal)-6.0f << (qreal)9.0f + << (qreal)5.0f << (qreal)7.0f << (qreal)-3.0f << (qreal)17.0f; +} +void tst_QQuaternion::add() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + QFETCH(qreal, w3); + + QQuaternion v1(w1, x1, y1, z1); + QQuaternion v2(w2, x2, y2, z2); + QQuaternion v3(w3, x3, y3, z3); + + QVERIFY((v1 + v2) == v3); + + QQuaternion v4(v1); + v4 += v2; + QVERIFY(v4 == v3); + + QCOMPARE(v4.x(), v1.x() + v2.x()); + QCOMPARE(v4.y(), v1.y() + v2.y()); + QCOMPARE(v4.z(), v1.z() + v2.z()); + QCOMPARE(v4.scalar(), v1.scalar() + v2.scalar()); +} + +// Test subtraction for quaternions. +void tst_QQuaternion::subtract_data() +{ + // Use the same test data as the add test. + add_data(); +} +void tst_QQuaternion::subtract() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + QFETCH(qreal, w3); + + QQuaternion v1(w1, x1, y1, z1); + QQuaternion v2(w2, x2, y2, z2); + QQuaternion v3(w3, x3, y3, z3); + + QVERIFY((v3 - v1) == v2); + QVERIFY((v3 - v2) == v1); + + QQuaternion v4(v3); + v4 -= v1; + QVERIFY(v4 == v2); + + QCOMPARE(v4.x(), v3.x() - v1.x()); + QCOMPARE(v4.y(), v3.y() - v1.y()); + QCOMPARE(v4.z(), v3.z() - v1.z()); + QCOMPARE(v4.scalar(), v3.scalar() - v1.scalar()); + + QQuaternion v5(v3); + v5 -= v2; + QVERIFY(v5 == v1); + + QCOMPARE(v5.x(), v3.x() - v2.x()); + QCOMPARE(v5.y(), v3.y() - v2.y()); + QCOMPARE(v5.z(), v3.z() - v2.z()); + QCOMPARE(v5.scalar(), v3.scalar() - v2.scalar()); +} + +// Test quaternion multiplication. +void tst_QQuaternion::multiply_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("w1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("w2"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("unitvec") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)1.0f; + + QTest::newRow("complex") + << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f << (qreal)7.0f + << (qreal)4.0f << (qreal)5.0f << (qreal)6.0f << (qreal)8.0f; +} +void tst_QQuaternion::multiply() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + + QQuaternion q1(w1, x1, y1, z1); + QQuaternion q2(w2, x2, y2, z2); + + // Use the simple algorithm at: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q53 + // to calculate the answer we expect to get. + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + qreal scalar = w1 * w2 - QVector3D::dotProduct(v1, v2); + QVector3D vector = w1 * v2 + w2 * v1 + QVector3D::crossProduct(v1, v2); + QQuaternion result(scalar, vector); + + QVERIFY((q1 * q2) == result); +} + +// Test multiplication by a factor for quaternions. +void tst_QQuaternion::multiplyFactor_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("w1"); + QTest::addColumn<qreal>("factor"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("w2"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)100.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("zonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f; + + QTest::newRow("wonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)4.0f + << (qreal)2.0f + << (qreal)2.0f << (qreal)4.0f << (qreal)-6.0f << (qreal)8.0f; + + QTest::newRow("allzero") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)4.0f + << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; +} +void tst_QQuaternion::multiplyFactor() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, factor); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + + QQuaternion v1(w1, x1, y1, z1); + QQuaternion v2(w2, x2, y2, z2); + + QVERIFY((v1 * factor) == v2); + QVERIFY((factor * v1) == v2); + + QQuaternion v3(v1); + v3 *= factor; + QVERIFY(v3 == v2); + + QCOMPARE(v3.x(), v1.x() * factor); + QCOMPARE(v3.y(), v1.y() * factor); + QCOMPARE(v3.z(), v1.z() * factor); + QCOMPARE(v3.scalar(), v1.scalar() * factor); +} + +// Test division by a factor for quaternions. +void tst_QQuaternion::divide_data() +{ + // Use the same test data as the multiply test. + multiplyFactor_data(); +} +void tst_QQuaternion::divide() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, factor); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + + QQuaternion v1(w1, x1, y1, z1); + QQuaternion v2(w2, x2, y2, z2); + + if (factor == (qreal)0.0f) + return; + + QVERIFY((v2 / factor) == v1); + + QQuaternion v3(v2); + v3 /= factor; + QVERIFY(v3 == v1); + + QCOMPARE(v3.x(), v2.x() / factor); + QCOMPARE(v3.y(), v2.y() / factor); + QCOMPARE(v3.z(), v2.z() / factor); + QCOMPARE(v3.scalar(), v2.scalar() / factor); +} + +// Test negation for quaternions. +void tst_QQuaternion::negate_data() +{ + // Use the same test data as the add test. + add_data(); +} +void tst_QQuaternion::negate() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + + QQuaternion v1(w1, x1, y1, z1); + QQuaternion v2(-w1, -x1, -y1, -z1); + + QVERIFY(-v1 == v2); +} + +// Test quaternion conjugate calculations. +void tst_QQuaternion::conjugate_data() +{ + // Use the same test data as the add test. + add_data(); +} +void tst_QQuaternion::conjugate() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + + QQuaternion v1(w1, x1, y1, z1); + QQuaternion v2(w1, -x1, -y1, -z1); + + QVERIFY(v1.conjugate() == v2); +} + +// Test quaternion creation from an axis and an angle. +void tst_QQuaternion::fromAxisAndAngle_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("angle"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)90.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)180.0f; + + QTest::newRow("zonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)270.0f; + + QTest::newRow("complex") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)45.0f; +} +void tst_QQuaternion::fromAxisAndAngle() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, angle); + + // Use a straight-forward implementation of the algorithm at: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56 + // to calculate the answer we expect to get. + QVector3D vector = QVector3D(x1, y1, z1).normalized(); + qreal sin_a = qSin((angle * M_PI / 180.0) / 2.0); + qreal cos_a = qCos((angle * M_PI / 180.0) / 2.0); + QQuaternion result((qreal)cos_a, + (qreal)(vector.x() * sin_a), + (qreal)(vector.y() * sin_a), + (qreal)(vector.z() * sin_a)); + result = result.normalized(); + + QQuaternion answer = QQuaternion::fromAxisAndAngle(QVector3D(x1, y1, z1), angle); + QVERIFY(fuzzyCompare(answer.x(), result.x())); + QVERIFY(fuzzyCompare(answer.y(), result.y())); + QVERIFY(fuzzyCompare(answer.z(), result.z())); + QVERIFY(fuzzyCompare(answer.scalar(), result.scalar())); + + answer = QQuaternion::fromAxisAndAngle(x1, y1, z1, angle); + QVERIFY(fuzzyCompare(answer.x(), result.x())); + QVERIFY(fuzzyCompare(answer.y(), result.y())); + QVERIFY(fuzzyCompare(answer.z(), result.z())); + QVERIFY(fuzzyCompare(answer.scalar(), result.scalar())); +} + +// Test spherical interpolation of quaternions. +void tst_QQuaternion::interpolate_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("angle1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("angle2"); + QTest::addColumn<qreal>("t"); + QTest::addColumn<qreal>("x3"); + QTest::addColumn<qreal>("y3"); + QTest::addColumn<qreal>("z3"); + QTest::addColumn<qreal>("angle3"); + + QTest::newRow("first") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f + << (qreal)0.0f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f; + QTest::newRow("first2") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f + << (qreal)-0.5f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f; + QTest::newRow("second") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f + << (qreal)1.0f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f; + QTest::newRow("second2") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f + << (qreal)1.5f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f; + QTest::newRow("middle") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f + << (qreal)0.5f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)135.0f; + QTest::newRow("wide angle") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)0.0f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)270.0f + << (qreal)0.5f + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)-45.0f; +} +void tst_QQuaternion::interpolate() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, angle1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, angle2); + QFETCH(qreal, t); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + QFETCH(qreal, angle3); + + QQuaternion q1 = QQuaternion::fromAxisAndAngle(x1, y1, z1, angle1); + QQuaternion q2 = QQuaternion::fromAxisAndAngle(x2, y2, z2, angle2); + QQuaternion q3 = QQuaternion::fromAxisAndAngle(x3, y3, z3, angle3); + + QQuaternion result = QQuaternion::interpolate(q1, q2, t); + + QVERIFY(fuzzyCompare(result.x(), q3.x())); + QVERIFY(fuzzyCompare(result.y(), q3.y())); + QVERIFY(fuzzyCompare(result.z(), q3.z())); + QVERIFY(fuzzyCompare(result.scalar(), q3.scalar())); +} + +// Force the fixed-point version of the test case to put a +// different test name on the front of failure reports. +class tst_QQuaternionFixed : public tst_QQuaternion +{ + Q_OBJECT +public: + tst_QQuaternionFixed() {} + ~tst_QQuaternionFixed() {} +}; + +#ifdef QT_GL_FIXED_PREFERRED + +QTEST_APPLESS_MAIN(tst_QQuaternionFixed) + +#else + +QTEST_APPLESS_MAIN(tst_QQuaternion) + +#endif + +#include "tst_qquaternion.moc" diff --git a/tests/auto/math3d/qquaternion_fixed/qquaternion_fixed.pro b/tests/auto/math3d/qquaternion_fixed/qquaternion_fixed.pro new file mode 100644 index 0000000..a0d5401 --- /dev/null +++ b/tests/auto/math3d/qquaternion_fixed/qquaternion_fixed.pro @@ -0,0 +1,8 @@ +load(qttest_p4) +VPATH += ../shared +VPATH += ../qquaternion +INCLUDEPATH += ../shared +INCLUDEPATH += ../../../../src/gui/math3d +DEFINES += FIXED_POINT_TESTS +HEADERS += math3dincludes.h +SOURCES += tst_qquaternion.cpp math3dincludes.cpp diff --git a/tests/auto/math3d/qvectornd/qvectornd.pro b/tests/auto/math3d/qvectornd/qvectornd.pro new file mode 100644 index 0000000..0981637 --- /dev/null +++ b/tests/auto/math3d/qvectornd/qvectornd.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +VPATH += ../shared +INCLUDEPATH += ../shared +HEADERS += math3dincludes.h +SOURCES += tst_qvectornd.cpp diff --git a/tests/auto/math3d/qvectornd/tst_qvectornd.cpp b/tests/auto/math3d/qvectornd/tst_qvectornd.cpp new file mode 100644 index 0000000..83bacdf --- /dev/null +++ b/tests/auto/math3d/qvectornd/tst_qvectornd.cpp @@ -0,0 +1,2063 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qmath.h> +#include "math3dincludes.h" + +class tst_QVector : public QObject +{ + Q_OBJECT +public: + tst_QVector() {} + ~tst_QVector() {} + +private slots: + void create2(); + void create3(); + void create4(); + + void length2_data(); + void length2(); + void length3_data(); + void length3(); + void length4_data(); + void length4(); + + void normalized2_data(); + void normalized2(); + void normalized3_data(); + void normalized3(); + void normalized4_data(); + void normalized4(); + + void normalize2_data(); + void normalize2(); + void normalize3_data(); + void normalize3(); + void normalize4_data(); + void normalize4(); + + void compare2(); + void compare3(); + void compare4(); + + void add2_data(); + void add2(); + void add3_data(); + void add3(); + void add4_data(); + void add4(); + + void subtract2_data(); + void subtract2(); + void subtract3_data(); + void subtract3(); + void subtract4_data(); + void subtract4(); + + void multiply2_data(); + void multiply2(); + void multiply3_data(); + void multiply3(); + void multiply4_data(); + void multiply4(); + + void multiplyFactor2_data(); + void multiplyFactor2(); + void multiplyFactor3_data(); + void multiplyFactor3(); + void multiplyFactor4_data(); + void multiplyFactor4(); + + void divide2_data(); + void divide2(); + void divide3_data(); + void divide3(); + void divide4_data(); + void divide4(); + + void negate2_data(); + void negate2(); + void negate3_data(); + void negate3(); + void negate4_data(); + void negate4(); + + void crossProduct_data(); + void crossProduct(); + void normal_data(); + void normal(); + void distanceToPlane_data(); + void distanceToPlane(); + void distanceToLine_data(); + void distanceToLine(); + + void dotProduct2_data(); + void dotProduct2(); + void dotProduct3_data(); + void dotProduct3(); + void dotProduct4_data(); + void dotProduct4(); +}; + +// qFuzzyCompare isn't quite "fuzzy" enough to handle conversion +// to fixed-point and back again. So create "fuzzier" compares. +static bool fuzzyCompare(float x, float y) +{ + float diff = x - y; + if (diff < 0.0f) + diff = -diff; + return (diff < 0.001); +} + +// Test the creation of QVector2D objects in various ways: +// construct, copy, and modify. +void tst_QVector::create2() +{ + QVector2D null; + QCOMPARE(null.x(), (qreal)0.0f); + QCOMPARE(null.y(), (qreal)0.0f); + QVERIFY(null.isNull()); + + QVector2D v1(1.0f, 2.5f); + QCOMPARE(v1.x(), (qreal)1.0f); + QCOMPARE(v1.y(), (qreal)2.5f); + QVERIFY(!v1.isNull()); + + QVector2D v1i(1, 2); + QCOMPARE(v1i.x(), (qreal)1.0f); + QCOMPARE(v1i.y(), (qreal)2.0f); + QVERIFY(!v1i.isNull()); + + QVector2D v2(v1); + QCOMPARE(v2.x(), (qreal)1.0f); + QCOMPARE(v2.y(), (qreal)2.5f); + QVERIFY(!v2.isNull()); + + QVector2D v4; + QCOMPARE(v4.x(), (qreal)0.0f); + QCOMPARE(v4.y(), (qreal)0.0f); + QVERIFY(v4.isNull()); + v4 = v1; + QCOMPARE(v4.x(), (qreal)1.0f); + QCOMPARE(v4.y(), (qreal)2.5f); + QVERIFY(!v4.isNull()); + + QVector2D v5(QPoint(1, 2)); + QCOMPARE(v5.x(), (qreal)1.0f); + QCOMPARE(v5.y(), (qreal)2.0f); + QVERIFY(!v5.isNull()); + + QVector2D v6(QPointF(1, 2.5)); + QCOMPARE(v6.x(), (qreal)1.0f); + QCOMPARE(v6.y(), (qreal)2.5f); + QVERIFY(!v6.isNull()); + + QVector2D v7(QVector3D(1.0f, 2.5f, 54.25f)); + QCOMPARE(v7.x(), (qreal)1.0f); + QCOMPARE(v7.y(), (qreal)2.5f); + QVERIFY(!v6.isNull()); + + QVector2D v8(QVector4D(1.0f, 2.5f, 54.25f, 34.0f)); + QCOMPARE(v8.x(), (qreal)1.0f); + QCOMPARE(v8.y(), (qreal)2.5f); + QVERIFY(!v6.isNull()); + + v1.setX(3.0f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)2.5f); + QVERIFY(!v1.isNull()); + + v1.setY(10.5f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)10.5f); + QVERIFY(!v1.isNull()); + + v1.setX(0.0f); + v1.setY(0.0f); + QCOMPARE(v1.x(), (qreal)0.0f); + QCOMPARE(v1.y(), (qreal)0.0f); + QVERIFY(v1.isNull()); + + QPoint p1 = v8.toPoint(); + QCOMPARE(p1.x(), 1); + QCOMPARE(p1.y(), 3); + + QPointF p2 = v8.toPointF(); + QCOMPARE((qreal)p2.x(), (qreal)1.0f); + QCOMPARE((qreal)p2.y(), (qreal)2.5f); + + QVector3D v9 = v8.toVector3D(); + QCOMPARE(v9.x(), (qreal)1.0f); + QCOMPARE(v9.y(), (qreal)2.5f); + QCOMPARE(v9.z(), (qreal)0.0f); + + QVector4D v10 = v8.toVector4D(); + QCOMPARE(v10.x(), (qreal)1.0f); + QCOMPARE(v10.y(), (qreal)2.5f); + QCOMPARE(v10.z(), (qreal)0.0f); + QCOMPARE(v10.w(), (qreal)0.0f); +} + +// Test the creation of QVector3D objects in various ways: +// construct, copy, and modify. +void tst_QVector::create3() +{ + QVector3D null; + QCOMPARE(null.x(), (qreal)0.0f); + QCOMPARE(null.y(), (qreal)0.0f); + QCOMPARE(null.z(), (qreal)0.0f); + QVERIFY(null.isNull()); + + QVector3D v1(1.0f, 2.5f, -89.25f); + QCOMPARE(v1.x(), (qreal)1.0f); + QCOMPARE(v1.y(), (qreal)2.5f); + QCOMPARE(v1.z(), (qreal)-89.25f); + QVERIFY(!v1.isNull()); + + QVector3D v1i(1, 2, -89); + QCOMPARE(v1i.x(), (qreal)1.0f); + QCOMPARE(v1i.y(), (qreal)2.0f); + QCOMPARE(v1i.z(), (qreal)-89.0f); + QVERIFY(!v1i.isNull()); + + QVector3D v2(v1); + QCOMPARE(v2.x(), (qreal)1.0f); + QCOMPARE(v2.y(), (qreal)2.5f); + QCOMPARE(v2.z(), (qreal)-89.25f); + QVERIFY(!v2.isNull()); + + QVector3D v3(1.0f, 2.5f, 0.0f); + QCOMPARE(v3.x(), (qreal)1.0f); + QCOMPARE(v3.y(), (qreal)2.5f); + QCOMPARE(v3.z(), (qreal)0.0f); + QVERIFY(!v3.isNull()); + + QVector3D v3i(1, 2, 0); + QCOMPARE(v3i.x(), (qreal)1.0f); + QCOMPARE(v3i.y(), (qreal)2.0f); + QCOMPARE(v3i.z(), (qreal)0.0f); + QVERIFY(!v3i.isNull()); + + QVector3D v4; + QCOMPARE(v4.x(), (qreal)0.0f); + QCOMPARE(v4.y(), (qreal)0.0f); + QCOMPARE(v4.z(), (qreal)0.0f); + QVERIFY(v4.isNull()); + v4 = v1; + QCOMPARE(v4.x(), (qreal)1.0f); + QCOMPARE(v4.y(), (qreal)2.5f); + QCOMPARE(v4.z(), (qreal)-89.25f); + QVERIFY(!v4.isNull()); + + QVector3D v5(QPoint(1, 2)); + QCOMPARE(v5.x(), (qreal)1.0f); + QCOMPARE(v5.y(), (qreal)2.0f); + QCOMPARE(v5.z(), (qreal)0.0f); + QVERIFY(!v5.isNull()); + + QVector3D v6(QPointF(1, 2.5)); + QCOMPARE(v6.x(), (qreal)1.0f); + QCOMPARE(v6.y(), (qreal)2.5f); + QCOMPARE(v6.z(), (qreal)0.0f); + QVERIFY(!v6.isNull()); + + QVector3D v7(QVector2D(1.0f, 2.5f)); + QCOMPARE(v7.x(), (qreal)1.0f); + QCOMPARE(v7.y(), (qreal)2.5f); + QCOMPARE(v7.z(), (qreal)0.0f); + QVERIFY(!v7.isNull()); + + QVector3D v8(QVector2D(1.0f, 2.5f), 54.25f); + QCOMPARE(v8.x(), (qreal)1.0f); + QCOMPARE(v8.y(), (qreal)2.5f); + QCOMPARE(v8.z(), (qreal)54.25f); + QVERIFY(!v8.isNull()); + + QVector3D v9(QVector4D(1.0f, 2.5f, 54.25f, 34.0f)); + QCOMPARE(v9.x(), (qreal)1.0f); + QCOMPARE(v9.y(), (qreal)2.5f); + QCOMPARE(v9.z(), (qreal)54.25f); + QVERIFY(!v9.isNull()); + + v1.setX(3.0f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)2.5f); + QCOMPARE(v1.z(), (qreal)-89.25f); + QVERIFY(!v1.isNull()); + + v1.setY(10.5f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)10.5f); + QCOMPARE(v1.z(), (qreal)-89.25f); + QVERIFY(!v1.isNull()); + + v1.setZ(15.5f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)10.5f); + QCOMPARE(v1.z(), (qreal)15.5f); + QVERIFY(!v1.isNull()); + + v1.setX(0.0f); + v1.setY(0.0f); + v1.setZ(0.0f); + QCOMPARE(v1.x(), (qreal)0.0f); + QCOMPARE(v1.y(), (qreal)0.0f); + QCOMPARE(v1.z(), (qreal)0.0f); + QVERIFY(v1.isNull()); + + QPoint p1 = v8.toPoint(); + QCOMPARE(p1.x(), 1); + QCOMPARE(p1.y(), 3); + + QPointF p2 = v8.toPointF(); + QCOMPARE((qreal)p2.x(), (qreal)1.0f); + QCOMPARE((qreal)p2.y(), (qreal)2.5f); + + QVector2D v10 = v8.toVector2D(); + QCOMPARE(v10.x(), (qreal)1.0f); + QCOMPARE(v10.y(), (qreal)2.5f); + + QVector4D v11 = v8.toVector4D(); + QCOMPARE(v11.x(), (qreal)1.0f); + QCOMPARE(v11.y(), (qreal)2.5f); + QCOMPARE(v11.z(), (qreal)54.25f); + QCOMPARE(v11.w(), (qreal)0.0f); +} + +// Test the creation of QVector4D objects in various ways: +// construct, copy, and modify. +void tst_QVector::create4() +{ + QVector4D null; + QCOMPARE(null.x(), (qreal)0.0f); + QCOMPARE(null.y(), (qreal)0.0f); + QCOMPARE(null.z(), (qreal)0.0f); + QCOMPARE(null.w(), (qreal)0.0f); + QVERIFY(null.isNull()); + + QVector4D v1(1.0f, 2.5f, -89.25f, 34.0f); + QCOMPARE(v1.x(), (qreal)1.0f); + QCOMPARE(v1.y(), (qreal)2.5f); + QCOMPARE(v1.z(), (qreal)-89.25f); + QCOMPARE(v1.w(), (qreal)34.0f); + QVERIFY(!v1.isNull()); + + QVector4D v1i(1, 2, -89, 34); + QCOMPARE(v1i.x(), (qreal)1.0f); + QCOMPARE(v1i.y(), (qreal)2.0f); + QCOMPARE(v1i.z(), (qreal)-89.0f); + QCOMPARE(v1i.w(), (qreal)34.0f); + QVERIFY(!v1i.isNull()); + + QVector4D v2(v1); + QCOMPARE(v2.x(), (qreal)1.0f); + QCOMPARE(v2.y(), (qreal)2.5f); + QCOMPARE(v2.z(), (qreal)-89.25f); + QCOMPARE(v2.w(), (qreal)34.0f); + QVERIFY(!v2.isNull()); + + QVector4D v3(1.0f, 2.5f, 0.0f, 0.0f); + QCOMPARE(v3.x(), (qreal)1.0f); + QCOMPARE(v3.y(), (qreal)2.5f); + QCOMPARE(v3.z(), (qreal)0.0f); + QCOMPARE(v3.w(), (qreal)0.0f); + QVERIFY(!v3.isNull()); + + QVector4D v3i(1, 2, 0, 0); + QCOMPARE(v3i.x(), (qreal)1.0f); + QCOMPARE(v3i.y(), (qreal)2.0f); + QCOMPARE(v3i.z(), (qreal)0.0f); + QCOMPARE(v3i.w(), (qreal)0.0f); + QVERIFY(!v3i.isNull()); + + QVector4D v3b(1.0f, 2.5f, -89.25f, 0.0f); + QCOMPARE(v3b.x(), (qreal)1.0f); + QCOMPARE(v3b.y(), (qreal)2.5f); + QCOMPARE(v3b.z(), (qreal)-89.25f); + QCOMPARE(v3b.w(), (qreal)0.0f); + QVERIFY(!v3b.isNull()); + + QVector4D v3bi(1, 2, -89, 0); + QCOMPARE(v3bi.x(), (qreal)1.0f); + QCOMPARE(v3bi.y(), (qreal)2.0f); + QCOMPARE(v3bi.z(), (qreal)-89.0f); + QCOMPARE(v3bi.w(), (qreal)0.0f); + QVERIFY(!v3bi.isNull()); + + QVector4D v4; + QCOMPARE(v4.x(), (qreal)0.0f); + QCOMPARE(v4.y(), (qreal)0.0f); + QCOMPARE(v4.z(), (qreal)0.0f); + QCOMPARE(v4.w(), (qreal)0.0f); + QVERIFY(v4.isNull()); + v4 = v1; + QCOMPARE(v4.x(), (qreal)1.0f); + QCOMPARE(v4.y(), (qreal)2.5f); + QCOMPARE(v4.z(), (qreal)-89.25f); + QCOMPARE(v4.w(), (qreal)34.0f); + QVERIFY(!v4.isNull()); + + QVector4D v5(QPoint(1, 2)); + QCOMPARE(v5.x(), (qreal)1.0f); + QCOMPARE(v5.y(), (qreal)2.0f); + QCOMPARE(v5.z(), (qreal)0.0f); + QCOMPARE(v5.w(), (qreal)0.0f); + QVERIFY(!v5.isNull()); + + QVector4D v6(QPointF(1, 2.5)); + QCOMPARE(v6.x(), (qreal)1.0f); + QCOMPARE(v6.y(), (qreal)2.5f); + QCOMPARE(v6.z(), (qreal)0.0f); + QCOMPARE(v6.w(), (qreal)0.0f); + QVERIFY(!v6.isNull()); + + QVector4D v7(QVector2D(1.0f, 2.5f)); + QCOMPARE(v7.x(), (qreal)1.0f); + QCOMPARE(v7.y(), (qreal)2.5f); + QCOMPARE(v7.z(), (qreal)0.0f); + QCOMPARE(v7.w(), (qreal)0.0f); + QVERIFY(!v7.isNull()); + + QVector4D v8(QVector3D(1.0f, 2.5f, -89.25f)); + QCOMPARE(v8.x(), (qreal)1.0f); + QCOMPARE(v8.y(), (qreal)2.5f); + QCOMPARE(v8.z(), (qreal)-89.25f); + QCOMPARE(v8.w(), (qreal)0.0f); + QVERIFY(!v8.isNull()); + + QVector4D v9(QVector3D(1.0f, 2.5f, -89.25f), 34); + QCOMPARE(v9.x(), (qreal)1.0f); + QCOMPARE(v9.y(), (qreal)2.5f); + QCOMPARE(v9.z(), (qreal)-89.25f); + QCOMPARE(v9.w(), (qreal)34.0f); + QVERIFY(!v9.isNull()); + + QVector4D v10(QVector2D(1.0f, 2.5f), 23.5f, -8); + QCOMPARE(v10.x(), (qreal)1.0f); + QCOMPARE(v10.y(), (qreal)2.5f); + QCOMPARE(v10.z(), (qreal)23.5f); + QCOMPARE(v10.w(), (qreal)-8.0f); + QVERIFY(!v10.isNull()); + + v1.setX(3.0f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)2.5f); + QCOMPARE(v1.z(), (qreal)-89.25f); + QCOMPARE(v1.w(), (qreal)34.0f); + QVERIFY(!v1.isNull()); + + v1.setY(10.5f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)10.5f); + QCOMPARE(v1.z(), (qreal)-89.25f); + QCOMPARE(v1.w(), (qreal)34.0f); + QVERIFY(!v1.isNull()); + + v1.setZ(15.5f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)10.5f); + QCOMPARE(v1.z(), (qreal)15.5f); + QCOMPARE(v1.w(), (qreal)34.0f); + QVERIFY(!v1.isNull()); + + v1.setW(6.0f); + QCOMPARE(v1.x(), (qreal)3.0f); + QCOMPARE(v1.y(), (qreal)10.5f); + QCOMPARE(v1.z(), (qreal)15.5f); + QCOMPARE(v1.w(), (qreal)6.0f); + QVERIFY(!v1.isNull()); + + v1.setX(0.0f); + v1.setY(0.0f); + v1.setZ(0.0f); + v1.setW(0.0f); + QCOMPARE(v1.x(), (qreal)0.0f); + QCOMPARE(v1.y(), (qreal)0.0f); + QCOMPARE(v1.z(), (qreal)0.0f); + QCOMPARE(v1.w(), (qreal)0.0f); + QVERIFY(v1.isNull()); + + QPoint p1 = v8.toPoint(); + QCOMPARE(p1.x(), 1); + QCOMPARE(p1.y(), 3); + + QPointF p2 = v8.toPointF(); + QCOMPARE((qreal)p2.x(), (qreal)1.0f); + QCOMPARE((qreal)p2.y(), (qreal)2.5f); + + QVector2D v11 = v8.toVector2D(); + QCOMPARE(v11.x(), (qreal)1.0f); + QCOMPARE(v11.y(), (qreal)2.5f); + + QVector3D v12 = v8.toVector3D(); + QCOMPARE(v12.x(), (qreal)1.0f); + QCOMPARE(v12.y(), (qreal)2.5f); + QCOMPARE(v12.z(), (qreal)-89.25f); + + QVector2D v13 = v9.toVector2DAffine(); + QVERIFY(fuzzyCompare(v13.x(), (qreal)(1.0f / 34.0f))); + QVERIFY(fuzzyCompare(v13.y(), (qreal)(2.5f / 34.0f))); + + QVector4D zerow(1.0f, 2.0f, 3.0f, 0.0f); + v13 = zerow.toVector2DAffine(); + QVERIFY(v13.isNull()); + + QVector3D v14 = v9.toVector3DAffine(); + QVERIFY(fuzzyCompare(v14.x(), (qreal)(1.0f / 34.0f))); + QVERIFY(fuzzyCompare(v14.y(), (qreal)(2.5f / 34.0f))); + QVERIFY(fuzzyCompare(v14.z(), (qreal)(-89.25f / 34.0f))); + + v14 = zerow.toVector3DAffine(); + QVERIFY(v14.isNull()); +} + +// Test vector length computation for 2D vectors. +void tst_QVector::length2_data() +{ + QTest::addColumn<qreal>("x"); + QTest::addColumn<qreal>("y"); + QTest::addColumn<qreal>("len"); + + QTest::newRow("null") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + QTest::newRow("1x") << (qreal)1.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("1y") << (qreal)0.0f << (qreal)1.0f << (qreal)1.0f; + QTest::newRow("-1x") << (qreal)-1.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("-1y") << (qreal)0.0f << (qreal)-1.0f << (qreal)1.0f; + QTest::newRow("two") << (qreal)2.0f << (qreal)-2.0f << (qreal)qSqrt(8.0f); +} +void tst_QVector::length2() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, len); + + QVector2D v(x, y); + QCOMPARE((float)(v.length()), (float)len); + QCOMPARE((float)(v.lengthSquared()), (float)(x * x + y * y)); +} + +// Test vector length computation for 3D vectors. +void tst_QVector::length3_data() +{ + QTest::addColumn<qreal>("x"); + QTest::addColumn<qreal>("y"); + QTest::addColumn<qreal>("z"); + QTest::addColumn<qreal>("len"); + + QTest::newRow("null") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + QTest::newRow("1x") << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("1y") << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("1z") << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)1.0f; + QTest::newRow("-1x") << (qreal)-1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("-1y") << (qreal)0.0f << (qreal)-1.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("-1z") << (qreal)0.0f << (qreal)0.0f << (qreal)-1.0f << (qreal)1.0f; + QTest::newRow("two") << (qreal)2.0f << (qreal)-2.0f << (qreal)2.0f << (qreal)qSqrt(12.0f); +} +void tst_QVector::length3() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(qreal, len); + + QVector3D v(x, y, z); + QCOMPARE((float)(v.length()), (float)len); + QCOMPARE((float)(v.lengthSquared()), (float)(x * x + y * y + z * z)); +} + +// Test vector length computation for 4D vectors. +void tst_QVector::length4_data() +{ + QTest::addColumn<qreal>("x"); + QTest::addColumn<qreal>("y"); + QTest::addColumn<qreal>("z"); + QTest::addColumn<qreal>("w"); + QTest::addColumn<qreal>("len"); + + QTest::newRow("null") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + QTest::newRow("1x") << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("1y") << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("1z") << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("1w") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)1.0f; + QTest::newRow("-1x") << (qreal)-1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("-1y") << (qreal)0.0f << (qreal)-1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("-1z") << (qreal)0.0f << (qreal)0.0f << (qreal)-1.0f << (qreal)0.0f << (qreal)1.0f; + QTest::newRow("-1w") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)-1.0f << (qreal)1.0f; + QTest::newRow("two") << (qreal)2.0f << (qreal)-2.0f << (qreal)2.0f << (qreal)2.0f << (qreal)qSqrt(16.0f); +} +void tst_QVector::length4() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(qreal, w); + QFETCH(qreal, len); + + QVector4D v(x, y, z, w); + QCOMPARE((float)(v.length()), (float)len); + QCOMPARE((float)(v.lengthSquared()), (float)(x * x + y * y + z * z + w * w)); +} + +// Test the unit vector conversion for 2D vectors. +void tst_QVector::normalized2_data() +{ + // Use the same test data as the length test. + length2_data(); +} +void tst_QVector::normalized2() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, len); + + QVector2D v(x, y); + QVector2D u = v.normalized(); + if (v.isNull()) + QVERIFY(u.isNull()); + else + QCOMPARE((float)(u.length()), (float)1.0f); + QCOMPARE((float)(u.x() * len), (float)(v.x())); + QCOMPARE((float)(u.y() * len), (float)(v.y())); +} + +// Test the unit vector conversion for 3D vectors. +void tst_QVector::normalized3_data() +{ + // Use the same test data as the length test. + length3_data(); +} +void tst_QVector::normalized3() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(qreal, len); + + QVector3D v(x, y, z); + QVector3D u = v.normalized(); + if (v.isNull()) + QVERIFY(u.isNull()); + else + QCOMPARE((float)(u.length()), (float)1.0f); + QCOMPARE((float)(u.x() * len), (float)(v.x())); + QCOMPARE((float)(u.y() * len), (float)(v.y())); + QCOMPARE((float)(u.z() * len), (float)(v.z())); +} + +// Test the unit vector conversion for 4D vectors. +void tst_QVector::normalized4_data() +{ + // Use the same test data as the length test. + length4_data(); +} +void tst_QVector::normalized4() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(qreal, w); + QFETCH(qreal, len); + + QVector4D v(x, y, z, w); + QVector4D u = v.normalized(); + if (v.isNull()) + QVERIFY(u.isNull()); + else + QCOMPARE((float)(u.length()), (float)1.0f); + QCOMPARE((float)(u.x() * len), (float)(v.x())); + QCOMPARE((float)(u.y() * len), (float)(v.y())); + QCOMPARE((float)(u.z() * len), (float)(v.z())); + QCOMPARE((float)(u.w() * len), (float)(v.w())); +} + +// Test the unit vector conversion for 2D vectors. +void tst_QVector::normalize2_data() +{ + // Use the same test data as the length test. + length2_data(); +} +void tst_QVector::normalize2() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + + QVector2D v(x, y); + bool isNull = v.isNull(); + v.normalize(); + if (isNull) + QVERIFY(v.isNull()); + else + QCOMPARE((float)(v.length()), (float)1.0f); +} + +// Test the unit vector conversion for 3D vectors. +void tst_QVector::normalize3_data() +{ + // Use the same test data as the length test. + length3_data(); +} +void tst_QVector::normalize3() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + + QVector3D v(x, y, z); + bool isNull = v.isNull(); + v.normalize(); + if (isNull) + QVERIFY(v.isNull()); + else + QCOMPARE((float)(v.length()), (float)1.0f); +} + +// Test the unit vector conversion for 4D vectors. +void tst_QVector::normalize4_data() +{ + // Use the same test data as the length test. + length4_data(); +} +void tst_QVector::normalize4() +{ + QFETCH(qreal, x); + QFETCH(qreal, y); + QFETCH(qreal, z); + QFETCH(qreal, w); + + QVector4D v(x, y, z, w); + bool isNull = v.isNull(); + v.normalize(); + if (isNull) + QVERIFY(v.isNull()); + else + QCOMPARE((float)(v.length()), (float)1.0f); +} + +// Test the comparison operators for 2D vectors. +void tst_QVector::compare2() +{ + QVector2D v1(1, 2); + QVector2D v2(1, 2); + QVector2D v3(3, 2); + QVector2D v4(1, 3); + + QVERIFY(v1 == v2); + QVERIFY(v1 != v3); + QVERIFY(v1 != v4); +} + +// Test the comparison operators for 3D vectors. +void tst_QVector::compare3() +{ + QVector3D v1(1, 2, 4); + QVector3D v2(1, 2, 4); + QVector3D v3(3, 2, 4); + QVector3D v4(1, 3, 4); + QVector3D v5(1, 2, 3); + + QVERIFY(v1 == v2); + QVERIFY(v1 != v3); + QVERIFY(v1 != v4); + QVERIFY(v1 != v5); +} + +// Test the comparison operators for 4D vectors. +void tst_QVector::compare4() +{ + QVector4D v1(1, 2, 4, 8); + QVector4D v2(1, 2, 4, 8); + QVector4D v3(3, 2, 4, 8); + QVector4D v4(1, 3, 4, 8); + QVector4D v5(1, 2, 3, 8); + QVector4D v6(1, 2, 4, 3); + + QVERIFY(v1 == v2); + QVERIFY(v1 != v3); + QVERIFY(v1 != v4); + QVERIFY(v1 != v5); + QVERIFY(v1 != v6); +} + +// Test vector addition for 2D vectors. +void tst_QVector::add2_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("x3"); + QTest::addColumn<qreal>("y3"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f + << (qreal)3.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)2.0f + << (qreal)0.0f << (qreal)3.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f + << (qreal)4.0f << (qreal)5.0f + << (qreal)5.0f << (qreal)7.0f; +} +void tst_QVector::add2() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + + QVector2D v1(x1, y1); + QVector2D v2(x2, y2); + QVector2D v3(x3, y3); + + QVERIFY((v1 + v2) == v3); + + QVector2D v4(v1); + v4 += v2; + QVERIFY(v4 == v3); + + QCOMPARE(v4.x(), v1.x() + v2.x()); + QCOMPARE(v4.y(), v1.y() + v2.y()); +} + +// Test vector addition for 3D vectors. +void tst_QVector::add3_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("x3"); + QTest::addColumn<qreal>("y3"); + QTest::addColumn<qreal>("z3"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)3.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)3.0f << (qreal)0.0f; + + QTest::newRow("zonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)3.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f + << (qreal)4.0f << (qreal)5.0f << (qreal)-6.0f + << (qreal)5.0f << (qreal)7.0f << (qreal)-3.0f; +} +void tst_QVector::add3() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + QVector3D v3(x3, y3, z3); + + QVERIFY((v1 + v2) == v3); + + QVector3D v4(v1); + v4 += v2; + QVERIFY(v4 == v3); + + QCOMPARE(v4.x(), v1.x() + v2.x()); + QCOMPARE(v4.y(), v1.y() + v2.y()); + QCOMPARE(v4.z(), v1.z() + v2.z()); +} + +// Test vector addition for 4D vectors. +void tst_QVector::add4_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("w1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("w2"); + QTest::addColumn<qreal>("x3"); + QTest::addColumn<qreal>("y3"); + QTest::addColumn<qreal>("z3"); + QTest::addColumn<qreal>("w3"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)3.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)3.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("zonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)3.0f << (qreal)0.0f; + + QTest::newRow("wonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)3.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f << (qreal)8.0f + << (qreal)4.0f << (qreal)5.0f << (qreal)-6.0f << (qreal)9.0f + << (qreal)5.0f << (qreal)7.0f << (qreal)-3.0f << (qreal)17.0f; +} +void tst_QVector::add4() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + QFETCH(qreal, w3); + + QVector4D v1(x1, y1, z1, w1); + QVector4D v2(x2, y2, z2, w2); + QVector4D v3(x3, y3, z3, w3); + + QVERIFY((v1 + v2) == v3); + + QVector4D v4(v1); + v4 += v2; + QVERIFY(v4 == v3); + + QCOMPARE(v4.x(), v1.x() + v2.x()); + QCOMPARE(v4.y(), v1.y() + v2.y()); + QCOMPARE(v4.z(), v1.z() + v2.z()); + QCOMPARE(v4.w(), v1.w() + v2.w()); +} + +// Test vector subtraction for 2D vectors. +void tst_QVector::subtract2_data() +{ + // Use the same test data as the add test. + add2_data(); +} +void tst_QVector::subtract2() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + + QVector2D v1(x1, y1); + QVector2D v2(x2, y2); + QVector2D v3(x3, y3); + + QVERIFY((v3 - v1) == v2); + QVERIFY((v3 - v2) == v1); + + QVector2D v4(v3); + v4 -= v1; + QVERIFY(v4 == v2); + + QCOMPARE(v4.x(), v3.x() - v1.x()); + QCOMPARE(v4.y(), v3.y() - v1.y()); + + QVector2D v5(v3); + v5 -= v2; + QVERIFY(v5 == v1); + + QCOMPARE(v5.x(), v3.x() - v2.x()); + QCOMPARE(v5.y(), v3.y() - v2.y()); +} + +// Test vector subtraction for 3D vectors. +void tst_QVector::subtract3_data() +{ + // Use the same test data as the add test. + add3_data(); +} +void tst_QVector::subtract3() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + QVector3D v3(x3, y3, z3); + + QVERIFY((v3 - v1) == v2); + QVERIFY((v3 - v2) == v1); + + QVector3D v4(v3); + v4 -= v1; + QVERIFY(v4 == v2); + + QCOMPARE(v4.x(), v3.x() - v1.x()); + QCOMPARE(v4.y(), v3.y() - v1.y()); + QCOMPARE(v4.z(), v3.z() - v1.z()); + + QVector3D v5(v3); + v5 -= v2; + QVERIFY(v5 == v1); + + QCOMPARE(v5.x(), v3.x() - v2.x()); + QCOMPARE(v5.y(), v3.y() - v2.y()); + QCOMPARE(v5.z(), v3.z() - v2.z()); +} + +// Test vector subtraction for 4D vectors. +void tst_QVector::subtract4_data() +{ + // Use the same test data as the add test. + add4_data(); +} +void tst_QVector::subtract4() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + QFETCH(qreal, w3); + + QVector4D v1(x1, y1, z1, w1); + QVector4D v2(x2, y2, z2, w2); + QVector4D v3(x3, y3, z3, w3); + + QVERIFY((v3 - v1) == v2); + QVERIFY((v3 - v2) == v1); + + QVector4D v4(v3); + v4 -= v1; + QVERIFY(v4 == v2); + + QCOMPARE(v4.x(), v3.x() - v1.x()); + QCOMPARE(v4.y(), v3.y() - v1.y()); + QCOMPARE(v4.z(), v3.z() - v1.z()); + QCOMPARE(v4.w(), v3.w() - v1.w()); + + QVector4D v5(v3); + v5 -= v2; + QVERIFY(v5 == v1); + + QCOMPARE(v5.x(), v3.x() - v2.x()); + QCOMPARE(v5.y(), v3.y() - v2.y()); + QCOMPARE(v5.z(), v3.z() - v2.z()); + QCOMPARE(v5.w(), v3.w() - v2.w()); +} + +// Test component-wise vector multiplication for 2D vectors. +void tst_QVector::multiply2_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("x3"); + QTest::addColumn<qreal>("y3"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)2.0f + << (qreal)0.0f << (qreal)2.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f + << (qreal)4.0f << (qreal)5.0f + << (qreal)4.0f << (qreal)10.0f; +} +void tst_QVector::multiply2() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + + QVector2D v1(x1, y1); + QVector2D v2(x2, y2); + QVector2D v3(x3, y3); + + QVERIFY((v1 * v2) == v3); + + QVector2D v4(v1); + v4 *= v2; + QVERIFY(v4 == v3); + + QCOMPARE(v4.x(), v1.x() * v2.x()); + QCOMPARE(v4.y(), v1.y() * v2.y()); +} + +// Test component-wise vector multiplication for 3D vectors. +void tst_QVector::multiply3_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("x3"); + QTest::addColumn<qreal>("y3"); + QTest::addColumn<qreal>("z3"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f; + + QTest::newRow("zonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f + << (qreal)4.0f << (qreal)5.0f << (qreal)-6.0f + << (qreal)4.0f << (qreal)10.0f << (qreal)-18.0f; +} +void tst_QVector::multiply3() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + QVector3D v3(x3, y3, z3); + + QVERIFY((v1 * v2) == v3); + + QVector3D v4(v1); + v4 *= v2; + QVERIFY(v4 == v3); + + QCOMPARE(v4.x(), v1.x() * v2.x()); + QCOMPARE(v4.y(), v1.y() * v2.y()); + QCOMPARE(v4.z(), v1.z() * v2.z()); +} + +// Test component-wise vector multiplication for 4D vectors. +void tst_QVector::multiply4_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("w1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("w2"); + QTest::addColumn<qreal>("x3"); + QTest::addColumn<qreal>("y3"); + QTest::addColumn<qreal>("z3"); + QTest::addColumn<qreal>("w3"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("zonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f; + + QTest::newRow("wonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f << (qreal)8.0f + << (qreal)4.0f << (qreal)5.0f << (qreal)-6.0f << (qreal)9.0f + << (qreal)4.0f << (qreal)10.0f << (qreal)-18.0f << (qreal)72.0f; +} +void tst_QVector::multiply4() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + QFETCH(qreal, w3); + + QVector4D v1(x1, y1, z1, w1); + QVector4D v2(x2, y2, z2, w2); + QVector4D v3(x3, y3, z3, w3); + + QVERIFY((v1 * v2) == v3); + + QVector4D v4(v1); + v4 *= v2; + QVERIFY(v4 == v3); + + QCOMPARE(v4.x(), v1.x() * v2.x()); + QCOMPARE(v4.y(), v1.y() * v2.y()); + QCOMPARE(v4.z(), v1.z() * v2.z()); + QCOMPARE(v4.w(), v1.w() * v2.w()); +} + +// Test vector multiplication by a factor for 2D vectors. +void tst_QVector::multiplyFactor2_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("factor"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f + << (qreal)100.0f + << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f + << (qreal)2.0f + << (qreal)2.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f + << (qreal)2.0f + << (qreal)0.0f << (qreal)2.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f + << (qreal)2.0f + << (qreal)2.0f << (qreal)4.0f; + + QTest::newRow("allzero") + << (qreal)1.0f << (qreal)2.0f + << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f; +} +void tst_QVector::multiplyFactor2() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, factor); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + + QVector2D v1(x1, y1); + QVector2D v2(x2, y2); + + QVERIFY((v1 * factor) == v2); + QVERIFY((factor * v1) == v2); + + QVector2D v3(v1); + v3 *= factor; + QVERIFY(v3 == v2); + + QCOMPARE(v3.x(), v1.x() * factor); + QCOMPARE(v3.y(), v1.y() * factor); +} + +// Test vector multiplication by a factor for 3D vectors. +void tst_QVector::multiplyFactor3_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("factor"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)100.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (qreal)2.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f; + + QTest::newRow("zonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f + << (qreal)2.0f + << (qreal)2.0f << (qreal)4.0f << (qreal)-6.0f; + + QTest::newRow("allzero") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f + << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; +} +void tst_QVector::multiplyFactor3() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, factor); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + + QVERIFY((v1 * factor) == v2); + QVERIFY((factor * v1) == v2); + + QVector3D v3(v1); + v3 *= factor; + QVERIFY(v3 == v2); + + QCOMPARE(v3.x(), v1.x() * factor); + QCOMPARE(v3.y(), v1.y() * factor); + QCOMPARE(v3.z(), v1.z() * factor); +} + +// Test vector multiplication by a factor for 4D vectors. +void tst_QVector::multiplyFactor4_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("w1"); + QTest::addColumn<qreal>("factor"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("w2"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)100.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("xonly") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f + << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("yonly") + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)2.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f; + + QTest::newRow("zonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f; + + QTest::newRow("wonly") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)2.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f; + + QTest::newRow("all") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)4.0f + << (qreal)2.0f + << (qreal)2.0f << (qreal)4.0f << (qreal)-6.0f << (qreal)8.0f; + + QTest::newRow("allzero") + << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)4.0f + << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f; +} +void tst_QVector::multiplyFactor4() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, factor); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + + QVector4D v1(x1, y1, z1, w1); + QVector4D v2(x2, y2, z2, w2); + + QVERIFY((v1 * factor) == v2); + QVERIFY((factor * v1) == v2); + + QVector4D v3(v1); + v3 *= factor; + QVERIFY(v3 == v2); + + QCOMPARE(v3.x(), v1.x() * factor); + QCOMPARE(v3.y(), v1.y() * factor); + QCOMPARE(v3.z(), v1.z() * factor); + QCOMPARE(v3.w(), v1.w() * factor); +} + +// Test vector division by a factor for 2D vectors. +void tst_QVector::divide2_data() +{ + // Use the same test data as the multiply test. + multiplyFactor2_data(); +} +void tst_QVector::divide2() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, factor); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + + QVector2D v1(x1, y1); + QVector2D v2(x2, y2); + + if (factor == (qreal)0.0f) + return; + + QVERIFY((v2 / factor) == v1); + + QVector2D v3(v2); + v3 /= factor; + QVERIFY(v3 == v1); + + QCOMPARE(v3.x(), v2.x() / factor); + QCOMPARE(v3.y(), v2.y() / factor); +} + +// Test vector division by a factor for 3D vectors. +void tst_QVector::divide3_data() +{ + // Use the same test data as the multiply test. + multiplyFactor3_data(); +} +void tst_QVector::divide3() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, factor); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + + if (factor == (qreal)0.0f) + return; + + QVERIFY((v2 / factor) == v1); + + QVector3D v3(v2); + v3 /= factor; + QVERIFY(v3 == v1); + + QCOMPARE(v3.x(), v2.x() / factor); + QCOMPARE(v3.y(), v2.y() / factor); + QCOMPARE(v3.z(), v2.z() / factor); +} + +// Test vector division by a factor for 4D vectors. +void tst_QVector::divide4_data() +{ + // Use the same test data as the multiply test. + multiplyFactor4_data(); +} +void tst_QVector::divide4() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, factor); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + + QVector4D v1(x1, y1, z1, w1); + QVector4D v2(x2, y2, z2, w2); + + if (factor == (qreal)0.0f) + return; + + QVERIFY((v2 / factor) == v1); + + QVector4D v3(v2); + v3 /= factor; + QVERIFY(v3 == v1); + + QCOMPARE(v3.x(), v2.x() / factor); + QCOMPARE(v3.y(), v2.y() / factor); + QCOMPARE(v3.z(), v2.z() / factor); + QCOMPARE(v3.w(), v2.w() / factor); +} + +// Test vector negation for 2D vectors. +void tst_QVector::negate2_data() +{ + // Use the same test data as the add test. + add2_data(); +} +void tst_QVector::negate2() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + + QVector2D v1(x1, y1); + QVector2D v2(-x1, -y1); + + QVERIFY(-v1 == v2); +} + +// Test vector negation for 3D vectors. +void tst_QVector::negate3_data() +{ + // Use the same test data as the add test. + add3_data(); +} +void tst_QVector::negate3() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + + QVector3D v1(x1, y1, z1); + QVector3D v2(-x1, -y1, -z1); + + QVERIFY(-v1 == v2); +} + +// Test vector negation for 4D vectors. +void tst_QVector::negate4_data() +{ + // Use the same test data as the add test. + add4_data(); +} +void tst_QVector::negate4() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + + QVector4D v1(x1, y1, z1, w1); + QVector4D v2(-x1, -y1, -z1, -w1); + + QVERIFY(-v1 == v2); +} + +// Test the computation of vector cross-products. +void tst_QVector::crossProduct_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("x3"); + QTest::addColumn<qreal>("y3"); + QTest::addColumn<qreal>("z3"); + QTest::addColumn<qreal>("dot"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f; + + QTest::newRow("unitvec") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f; + + QTest::newRow("complex") + << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f + << (qreal)4.0f << (qreal)5.0f << (qreal)6.0f + << (qreal)-3.0f << (qreal)6.0f << (qreal)-3.0f + << (qreal)32.0f; +} +void tst_QVector::crossProduct() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + QVector3D v3(x3, y3, z3); + + QVector3D v4 = QVector3D::crossProduct(v1, v2); + QVERIFY(v4 == v3); + + // Compute the cross-product long-hand and check again. + qreal xres = y1 * z2 - z1 * y2; + qreal yres = z1 * x2 - x1 * z2; + qreal zres = x1 * y2 - y1 * x2; + + QCOMPARE(v4.x(), xres); + QCOMPARE(v4.y(), yres); + QCOMPARE(v4.z(), zres); +} + +// Test the computation of normals. +void tst_QVector::normal_data() +{ + // Use the same test data as the crossProduct test. + crossProduct_data(); +} +void tst_QVector::normal() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + QVector3D v3(x3, y3, z3); + + QVERIFY(QVector3D::normal(v1, v2) == v3.normalized()); + QVERIFY(QVector3D::normal(QVector3D(), v1, v2) == v3.normalized()); + + QVector3D point(1.0f, 2.0f, 3.0f); + QVERIFY(QVector3D::normal(point, v1 + point, v2 + point) == v3.normalized()); +} + +// Test distance to plane calculations. +void tst_QVector::distanceToPlane_data() +{ + QTest::addColumn<qreal>("x1"); // Point on plane + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("x2"); // Normal to plane + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("x3"); // Point to test for distance + QTest::addColumn<qreal>("y3"); + QTest::addColumn<qreal>("z3"); + QTest::addColumn<qreal>("x4"); // Second point on plane + QTest::addColumn<qreal>("y4"); + QTest::addColumn<qreal>("z4"); + QTest::addColumn<qreal>("x5"); // Third point on plane + QTest::addColumn<qreal>("y5"); + QTest::addColumn<qreal>("z5"); + QTest::addColumn<qreal>("distance"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f + << (qreal)0.0f; + + QTest::newRow("above") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f + << (qreal)2.0f; + + QTest::newRow("below") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)-1.0f << (qreal)1.0f << (qreal)-2.0f + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f + << (qreal)-2.0f; +} +void tst_QVector::distanceToPlane() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + QFETCH(qreal, x4); + QFETCH(qreal, y4); + QFETCH(qreal, z4); + QFETCH(qreal, x5); + QFETCH(qreal, y5); + QFETCH(qreal, z5); + QFETCH(qreal, distance); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + QVector3D v3(x3, y3, z3); + QVector3D v4(x4, y4, z4); + QVector3D v5(x5, y5, z5); + + QCOMPARE(v3.distanceToPlane(v1, v2), distance); + QCOMPARE(v3.distanceToPlane(v1, v4, v5), distance); +} + +// Test distance to line calculations. +void tst_QVector::distanceToLine_data() +{ + QTest::addColumn<qreal>("x1"); // Point on line + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("x2"); // Direction of the line + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("x3"); // Point to test for distance + QTest::addColumn<qreal>("y3"); + QTest::addColumn<qreal>("z3"); + QTest::addColumn<qreal>("distance"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f; + + QTest::newRow("on line") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)5.0f + << (qreal)0.0f; + + QTest::newRow("off line") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)1.0f; + + QTest::newRow("off line 2") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f << (qreal)-2.0f << (qreal)0.0f + << (qreal)2.0f; + + QTest::newRow("points") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)5.0f << (qreal)0.0f + << (qreal)5.0f; +} +void tst_QVector::distanceToLine() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + QFETCH(qreal, distance); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + QVector3D v3(x3, y3, z3); + + QCOMPARE(v3.distanceToLine(v1, v2), distance); +} + +// Test the computation of dot products for 2D vectors. +void tst_QVector::dotProduct2_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("dot"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f; + + QTest::newRow("unitvec") + << (qreal)1.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)1.0f + << (qreal)0.0f; + + QTest::newRow("complex") + << (qreal)1.0f << (qreal)2.0f + << (qreal)4.0f << (qreal)5.0f + << (qreal)14.0f; +} +void tst_QVector::dotProduct2() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, dot); + + QVector2D v1(x1, y1); + QVector2D v2(x2, y2); + + QVERIFY(QVector2D::dotProduct(v1, v2) == dot); + + // Compute the dot-product long-hand and check again. + qreal d = x1 * x2 + y1 * y2; + + QCOMPARE(QVector2D::dotProduct(v1, v2), d); +} + +// Test the computation of dot products for 3D vectors. +void tst_QVector::dotProduct3_data() +{ + // Use the same test data as the crossProduct test. + crossProduct_data(); +} +void tst_QVector::dotProduct3() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, x3); + QFETCH(qreal, y3); + QFETCH(qreal, z3); + QFETCH(qreal, dot); + + Q_UNUSED(x3); + Q_UNUSED(y3); + Q_UNUSED(z3); + + QVector3D v1(x1, y1, z1); + QVector3D v2(x2, y2, z2); + + QVERIFY(QVector3D::dotProduct(v1, v2) == dot); + + // Compute the dot-product long-hand and check again. + qreal d = x1 * x2 + y1 * y2 + z1 * z2; + + QCOMPARE(QVector3D::dotProduct(v1, v2), d); +} + +// Test the computation of dot products for 4D vectors. +void tst_QVector::dotProduct4_data() +{ + QTest::addColumn<qreal>("x1"); + QTest::addColumn<qreal>("y1"); + QTest::addColumn<qreal>("z1"); + QTest::addColumn<qreal>("w1"); + QTest::addColumn<qreal>("x2"); + QTest::addColumn<qreal>("y2"); + QTest::addColumn<qreal>("z2"); + QTest::addColumn<qreal>("w2"); + QTest::addColumn<qreal>("dot"); + + QTest::newRow("null") + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f; + + QTest::newRow("unitvec") + << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f + << (qreal)0.0f; + + QTest::newRow("complex") + << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f << (qreal)4.0f + << (qreal)4.0f << (qreal)5.0f << (qreal)6.0f << (qreal)7.0f + << (qreal)60.0f; +} +void tst_QVector::dotProduct4() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, w1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, w2); + QFETCH(qreal, dot); + + QVector4D v1(x1, y1, z1, w1); + QVector4D v2(x2, y2, z2, w2); + + QVERIFY(QVector4D::dotProduct(v1, v2) == dot); + + // Compute the dot-product long-hand and check again. + qreal d = x1 * x2 + y1 * y2 + z1 * z2 + w1 * w2; + + QCOMPARE(QVector4D::dotProduct(v1, v2), d); +} + +// Force the fixed-point version of the test case to put a +// different test name on the front of failure reports. +class tst_QVectorFixed : public tst_QVector +{ + Q_OBJECT +public: + tst_QVectorFixed() {} + ~tst_QVectorFixed() {} +}; + +#ifdef QT_GL_FIXED_PREFERRED + +QTEST_APPLESS_MAIN(tst_QVectorFixed) + +#else + +QTEST_APPLESS_MAIN(tst_QVector) + +#endif + +#include "tst_qvectornd.moc" diff --git a/tests/auto/math3d/qvectornd_fixed/qvectornd_fixed.pro b/tests/auto/math3d/qvectornd_fixed/qvectornd_fixed.pro new file mode 100644 index 0000000..a667fcc --- /dev/null +++ b/tests/auto/math3d/qvectornd_fixed/qvectornd_fixed.pro @@ -0,0 +1,8 @@ +load(qttest_p4) +VPATH += ../shared +VPATH += ../qvectornd +INCLUDEPATH += ../shared +INCLUDEPATH += ../../../../src/gui/math3d +DEFINES += FIXED_POINT_TESTS +HEADERS += math3dincludes.h +SOURCES += tst_qvectornd.cpp math3dincludes.cpp diff --git a/src/plugins/kbddrivers/linuxis/linuxiskbddriverplugin.h b/tests/auto/math3d/shared/math3dincludes.cpp index b5f599f..725079d 100644 --- a/src/plugins/kbddrivers/linuxis/linuxiskbddriverplugin.h +++ b/tests/auto/math3d/shared/math3dincludes.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** -** This file is part of the plugins of the Qt Toolkit. +** This file is part of the $MODULE$ of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,20 +39,16 @@ ** ****************************************************************************/ -#ifndef LINUXISKBDDRIVERPLUGIN_H -#define LINUXISKBDDRIVERPLUGIN_H +#include "math3dincludes.h" -#include <QtGui/QWSKeyboardHandlerFactoryInterface> +#if defined(FIXED_POINT_TESTS) -class LinuxInputSubsystemKbdDriverPlugin : public QKbdDriverPlugin { - Q_OBJECT -public: - LinuxInputSubsystemKbdDriverPlugin( QObject *parent = 0 ); - ~LinuxInputSubsystemKbdDriverPlugin(); +#include "qmatrix4x4.cpp" +#include "qgenericmatrix.cpp" +#include "qvector2d.cpp" +#include "qvector3d.cpp" +#include "qvector4d.cpp" +#include "qquaternion.cpp" +#include "qmath3dutil.cpp" - QWSKeyboardHandler* create(const QString& driver, const QString& device); - QWSKeyboardHandler* create(const QString& driver); - QStringList keys()const; -}; - -#endif // LINUXISKBDDRIVERPLUGIN_H +#endif diff --git a/src/plugins/kbddrivers/linuxis/linuxiskbddriverplugin.cpp b/tests/auto/math3d/shared/math3dincludes.h index 79cd298..23b0f16 100644 --- a/src/plugins/kbddrivers/linuxis/linuxiskbddriverplugin.cpp +++ b/tests/auto/math3d/shared/math3dincludes.h @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** -** This file is part of the plugins of the Qt Toolkit. +** This file is part of the $MODULE$ of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,49 +39,49 @@ ** ****************************************************************************/ -#include "linuxiskbddriverplugin.h" -#include "linuxiskbdhandler.h" +#ifndef MATH3DINCLUDES_H +#define MATH3DINCLUDES_H -#include <qdebug.h> -#if 1 -#define qLog(x) qDebug() -#else -#define qLog(x) while (0) qDebug() -#endif - -LinuxInputSubsystemKbdDriverPlugin::LinuxInputSubsystemKbdDriverPlugin( QObject *parent ) - : QKbdDriverPlugin( parent ) -{ -} +#if defined(FIXED_POINT_TESTS) -LinuxInputSubsystemKbdDriverPlugin::~LinuxInputSubsystemKbdDriverPlugin() -{ -} +// Rename the classes we want to test in fixed-point mode so that +// they don't conflict with the ones that are built into Qt. +#define QT_NO_GL_FLOAT 1 +#define QVector2D tst_QVector2D +#define QVector3D tst_QVector3D +#define QVector4D tst_QVector4D +#define QQuaternion tst_QQuaternionX +#define QMatrix2x2 tst_QMatrix2x2 +#define QMatrix3x3 tst_QMatrix3x3 +#define QMatrix4x4 tst_QMatrix4x4 +#define QMatrix2x3 tst_QMatrix2x3 +#define QMatrix2x4 tst_QMatrix2x4 +#define QMatrix3x2 tst_QMatrix3x2 +#define QMatrix3x4 tst_QMatrix3x4 +#define QMatrix4x2 tst_QMatrix4x2 +#define QMatrix4x3 tst_QMatrix4x3 +#define QGenericMatrix tst_QGenericMatrix +#define qt_math3d_sincos tst_qt_math3d_sincos +#define qt_math3d_convert tst_qt_math3d_convert +#define qrealinner tst_qrealinner -QWSKeyboardHandler* LinuxInputSubsystemKbdDriverPlugin::create(const QString &driver, const QString &device) -{ - if (device.isEmpty()) { - return create( driver ); - } - if( driver.toLower() == "linuxis" || driver.toLower() == "linuxiskbdhandler" ) { - qLog(Input) << "Before call LinuxInputSubsystemKbdHandler(" << device << ")"; - return new LinuxInputSubsystemKbdHandler(device); - } - return 0; -} +// We need to re-include the headers with the changed class names. +#undef QGENERICMATRIX_H +#undef QMATH3DGLOBAL_H +#undef QMATH3DUTIL_P_H +#undef QMATRIX4X4_H +#undef QQUATERNION_H +#undef QVECTOR2D_H +#undef QVECTOR3D_H +#undef QVECTOR4D_H -QWSKeyboardHandler* LinuxInputSubsystemKbdDriverPlugin::create( const QString &driver) -{ - if( driver.toLower() == "linuxis" || driver.toLower() == "linuxiskbdhandler" ) { - qLog(Input) << "Before call LinuxInputSubsystemKbdHandler()"; - return new LinuxInputSubsystemKbdHandler(); - } - return 0; -} +#endif -QStringList LinuxInputSubsystemKbdDriverPlugin::keys() const -{ - return QStringList() << "linuxis" << "linuxiskbdhandler"; -} +#include <QtGui/qmatrix4x4.h> +#include <QtGui/qgenericmatrix.h> +#include <QtGui/qvector2d.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qquaternion.h> -Q_EXPORT_PLUGIN2(qwslinuxiskbdhandler, LinuxInputSubsystemKbdDriverPlugin) +#endif diff --git a/tests/auto/q3filedialog/tst_q3filedialog.cpp b/tests/auto/q3filedialog/tst_q3filedialog.cpp index 74e2894..03e1c7e 100644 --- a/tests/auto/q3filedialog/tst_q3filedialog.cpp +++ b/tests/auto/q3filedialog/tst_q3filedialog.cpp @@ -115,13 +115,15 @@ void tst_Q3FileDialog::getSetCheck() obj1.setPreviewMode(Q3FileDialog::PreviewMode(Q3FileDialog::NoPreview)); QCOMPARE(obj1.previewMode(), Q3FileDialog::PreviewMode(Q3FileDialog::NoPreview)); - // Note: Q3FileDialog does not update the previewMode read-state until the - // user has actually started navigating to a file that has a functioning - // preview. + obj1.setContentsPreviewEnabled(true); + obj1.setInfoPreviewEnabled(false); obj1.setPreviewMode(Q3FileDialog::PreviewMode(Q3FileDialog::Contents)); - QCOMPARE(obj1.previewMode(), Q3FileDialog::PreviewMode(Q3FileDialog::NoPreview)); + QCOMPARE(obj1.previewMode(), Q3FileDialog::PreviewMode(Q3FileDialog::Contents)); + + obj1.setInfoPreviewEnabled(true); + obj1.setContentsPreviewEnabled(false); obj1.setPreviewMode(Q3FileDialog::PreviewMode(Q3FileDialog::Info)); - QCOMPARE(obj1.previewMode(), Q3FileDialog::PreviewMode(Q3FileDialog::NoPreview)); + QCOMPARE(obj1.previewMode(), Q3FileDialog::PreviewMode(Q3FileDialog::Info)); } QTEST_MAIN(tst_Q3FileDialog) diff --git a/tests/auto/q3tabdialog/tst_q3tabdialog.cpp b/tests/auto/q3tabdialog/tst_q3tabdialog.cpp index 6de68fa..214cb11 100644 --- a/tests/auto/q3tabdialog/tst_q3tabdialog.cpp +++ b/tests/auto/q3tabdialog/tst_q3tabdialog.cpp @@ -60,6 +60,7 @@ public: private slots: void getSetCheck(); + void task245918_show(); }; tst_Q3TabDialog::tst_Q3TabDialog() @@ -95,5 +96,32 @@ void tst_Q3TabDialog::getSetCheck() delete var1; } +class task245918_Dialog : public Q3TabDialog +{ + Q_OBJECT +public: + task245918_Dialog() + { + QTimer::singleShot(100, this, SLOT(closeWhenVisible())); + } + + private slots: + void closeWhenVisible() + { + if (isVisible()) + accept(); + else + QTimer::singleShot(100, this, SLOT(closeWhenVisible())); + } +}; + +void tst_Q3TabDialog::task245918_show() +{ + task245918_Dialog dialog; + QSignalSpy spy(&dialog, SIGNAL(aboutToShow())); + dialog.exec(); + QCOMPARE(spy.count(), 1); +} + QTEST_MAIN(tst_Q3TabDialog) #include "tst_q3tabdialog.moc" diff --git a/tests/auto/qaction/tst_qaction.cpp b/tests/auto/qaction/tst_qaction.cpp index 34f2dfd..1ec21f2 100644 --- a/tests/auto/qaction/tst_qaction.cpp +++ b/tests/auto/qaction/tst_qaction.cpp @@ -75,6 +75,8 @@ private slots: void alternateShortcuts(); void enabledVisibleInteraction(); void task200823_tooltip(); + void task229128TriggeredSignalWithoutActiongroup(); + void task229128TriggeredSignalWhenInActiongroup(); private: int m_lastEventType; @@ -109,7 +111,7 @@ class MyWidget : public QWidget { Q_OBJECT public: - MyWidget(tst_QAction *tst, QWidget *parent=0) : QWidget(parent) { this->tst = tst; } + MyWidget(tst_QAction *tst, QWidget *parent = 0) : QWidget(parent) { this->tst = tst; } protected: virtual void actionEvent(QActionEvent *e) { tst->updateState(e); } @@ -141,7 +143,7 @@ void tst_QAction::initTestCase() void tst_QAction::cleanupTestCase() { QWidget *testWidget = m_tstWidget; - if ( testWidget ) { + if (testWidget) { testWidget->hide(); delete testWidget; } @@ -189,7 +191,7 @@ void tst_QAction::updateState(QActionEvent *e) { if (!e) { m_lastEventType = 0; - m_lastAction = 0; + m_lastAction = 0; } else { m_lastEventType = (int)e->type(); m_lastAction = e->action(); @@ -249,12 +251,12 @@ void tst_QAction::alternateShortcuts() { //test the alternate shortcuts (by adding more than 1 shortcut) - QWidget *wid=m_tstWidget; + QWidget *wid = m_tstWidget; { QAction act(wid); wid->addAction(&act); - QList<QKeySequence> shlist= QList<QKeySequence>() << QKeySequence("CTRL+P") <<QKeySequence("CTRL+A"); + QList<QKeySequence> shlist = QList<QKeySequence>() << QKeySequence("CTRL+P") << QKeySequence("CTRL+A"); act.setShortcuts(shlist); QSignalSpy spy(&act, SIGNAL(triggered())); @@ -322,5 +324,46 @@ void tst_QAction::task200823_tooltip() QCOMPARE(action->toolTip(), ref); } +void tst_QAction::task229128TriggeredSignalWithoutActiongroup() +{ + // test without a group + QAction *actionWithoutGroup = new QAction("Test", qApp); + QSignalSpy spyWithoutGroup(actionWithoutGroup, SIGNAL(triggered(bool))); + QCOMPARE(spyWithoutGroup.count(), 0); + actionWithoutGroup->trigger(); + // signal should be emitted + QCOMPARE(spyWithoutGroup.count(), 1); + + // it is now a checkable checked action + actionWithoutGroup->setCheckable(true); + actionWithoutGroup->setChecked(true); + spyWithoutGroup.clear(); + QCOMPARE(spyWithoutGroup.count(), 0); + actionWithoutGroup->trigger(); + // signal should be emitted + QCOMPARE(spyWithoutGroup.count(), 1); +} + +void tst_QAction::task229128TriggeredSignalWhenInActiongroup() +{ + QActionGroup ag(0); + QAction *action = new QAction("Test", &ag); + QAction *checkedAction = new QAction("Test 2", &ag); + ag.addAction(action); + action->setCheckable(true); + ag.addAction(checkedAction); + checkedAction->setCheckable(true); + checkedAction->setChecked(true); + + QSignalSpy actionSpy(checkedAction, SIGNAL(triggered(bool))); + QSignalSpy actionGroupSpy(&ag, SIGNAL(triggered(QAction *))); + QCOMPARE(actionGroupSpy.count(), 0); + QCOMPARE(actionSpy.count(), 0); + checkedAction->trigger(); + // check that both the group and the action have emitted the signal + QCOMPARE(actionGroupSpy.count(), 1); + QCOMPARE(actionSpy.count(), 1); +} + QTEST_MAIN(tst_QAction) #include "tst_qaction.moc" diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index 0f2d671..af40e67 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -126,6 +126,7 @@ private slots: void construction(); void constructionWithParent(); void destruction(); + void deleteChildItem(); void scene(); void parentItem(); void setParentItem(); @@ -511,6 +512,29 @@ void tst_QGraphicsItem::destruction() QCOMPARE(itemDeleted, 59); } QCOMPARE(itemDeleted, 109); + { + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsRectItem *parent = new QGraphicsRectItem; + Item *child = new Item; + child->setParentItem(parent); + parent->setVisible(false); + scene->addItem(parent); + QCOMPARE(child->parentItem(), parent); + delete scene; + QCOMPARE(itemDeleted, 110); + } +} + +void tst_QGraphicsItem::deleteChildItem() +{ + QGraphicsScene scene; + QGraphicsItem *rect = scene.addRect(QRectF()); + QGraphicsItem *child1 = new QGraphicsRectItem(rect); + QGraphicsItem *child2 = new QGraphicsRectItem(rect); + QGraphicsItem *child3 = new QGraphicsRectItem(rect); + delete child1; + child2->setParentItem(0); + delete child2; } void tst_QGraphicsItem::scene() @@ -3013,150 +3037,134 @@ protected: void mouseReleaseEvent(QGraphicsSceneMouseEvent *) { ++counter; } }; + void tst_QGraphicsItem::handlesChildEvents() { - ChildEventTester *item2_level2 = new ChildEventTester(QRectF(0, 0, 25, 25)); - ChildEventTester *item1_level1 = new ChildEventTester(QRectF(0, 0, 50, 50)); - ChildEventTester *item_level0 = new ChildEventTester(QRectF(0, 0, 100, 100)); - ChildEventTester *item1_level2 = new ChildEventTester(QRectF(0, 0, 25, 25)); - ChildEventTester *item2_level1 = new ChildEventTester(QRectF(0, 0, 50, 50)); - - item_level0->setBrush(QBrush(Qt::blue)); - item1_level1->setBrush(QBrush(Qt::red)); - item2_level1->setBrush(QBrush(Qt::yellow)); - item1_level2->setBrush(QBrush(Qt::green)); - item2_level2->setBrush(QBrush(Qt::gray)); - item1_level1->setPos(50, 0); - item2_level1->setPos(50, 50); - item1_level2->setPos(25, 0); - item2_level2->setPos(25, 25); - item1_level1->setParentItem(item_level0); - item2_level1->setParentItem(item_level0); - item1_level2->setParentItem(item1_level1); - item2_level2->setParentItem(item1_level1); + ChildEventTester *blue = new ChildEventTester(QRectF(0, 0, 100, 100)); + ChildEventTester *red = new ChildEventTester(QRectF(0, 0, 50, 50)); + ChildEventTester *green = new ChildEventTester(QRectF(0, 0, 25, 25)); + ChildEventTester *gray = new ChildEventTester(QRectF(0, 0, 25, 25)); + ChildEventTester *yellow = new ChildEventTester(QRectF(0, 0, 50, 50)); + + blue->setBrush(QBrush(Qt::blue)); + red->setBrush(QBrush(Qt::red)); + yellow->setBrush(QBrush(Qt::yellow)); + green->setBrush(QBrush(Qt::green)); + gray->setBrush(QBrush(Qt::gray)); + red->setPos(50, 0); + yellow->setPos(50, 50); + green->setPos(25, 0); + gray->setPos(25, 25); + red->setParentItem(blue); + yellow->setParentItem(blue); + green->setParentItem(red); + gray->setParentItem(red); QGraphicsScene scene; - scene.addItem(item_level0); - - // Pull out the items, closest item first - QList<QGraphicsItem *> items = scene.items(scene.itemsBoundingRect()); - QCOMPARE(items.at(4), (QGraphicsItem *)item_level0); - if (item1_level1 < item2_level1) { - QCOMPARE(items.at(3), (QGraphicsItem *)item1_level1); - if (item1_level2 < item2_level2) { - QCOMPARE(items.at(2), (QGraphicsItem *)item1_level2); - QCOMPARE(items.at(1), (QGraphicsItem *)item2_level2); - } else { - QCOMPARE(items.at(2), (QGraphicsItem *)item2_level2); - QCOMPARE(items.at(1), (QGraphicsItem *)item1_level2); - } - QCOMPARE(items.at(0), (QGraphicsItem *)item2_level1); - } else { - QCOMPARE(items.at(3), (QGraphicsItem *)item2_level1); - QCOMPARE(items.at(2), (QGraphicsItem *)item1_level1); - if (item1_level2 < item2_level2) { - QCOMPARE(items.at(1), (QGraphicsItem *)item1_level2); - QCOMPARE(items.at(0), (QGraphicsItem *)item2_level2); - } else { - QCOMPARE(items.at(1), (QGraphicsItem *)item2_level2); - QCOMPARE(items.at(0), (QGraphicsItem *)item1_level2); - } - } + scene.addItem(blue); QGraphicsView view(&scene); view.show(); QTest::qWait(1000); - QCOMPARE(item_level0->counter, 0); + // Pull out the items, closest item first + QList<QGraphicsItem *> items = scene.items(scene.itemsBoundingRect()); + QCOMPARE(items.at(0), (QGraphicsItem *)yellow); + QCOMPARE(items.at(1), (QGraphicsItem *)gray); + QCOMPARE(items.at(2), (QGraphicsItem *)green); + QCOMPARE(items.at(3), (QGraphicsItem *)red); + QCOMPARE(items.at(4), (QGraphicsItem *)blue); + + QCOMPARE(blue->counter, 0); // Send events to the toplevel item QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); pressEvent.setButton(Qt::LeftButton); - pressEvent.setScenePos(item_level0->mapToScene(5, 5)); + pressEvent.setScenePos(blue->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); releaseEvent.setButton(Qt::LeftButton); - releaseEvent.setScenePos(item_level0->mapToScene(5, 5)); + releaseEvent.setScenePos(blue->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 2); + QCOMPARE(blue->counter, 2); // Send events to a level1 item - pressEvent.setScenePos(item1_level1->mapToScene(5, 5)); + pressEvent.setScenePos(red->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level1->mapToScene(5, 5)); + releaseEvent.setScenePos(red->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 2); - QCOMPARE(item1_level1->counter, 2); + QCOMPARE(blue->counter, 2); + QCOMPARE(red->counter, 2); // Send events to a level2 item - pressEvent.setScenePos(item1_level2->mapToScene(5, 5)); + pressEvent.setScenePos(green->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level2->mapToScene(5, 5)); + releaseEvent.setScenePos(green->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 2); - QCOMPARE(item1_level1->counter, 2); - QCOMPARE(item1_level2->counter, 2); + QCOMPARE(blue->counter, 2); + QCOMPARE(red->counter, 2); + QCOMPARE(green->counter, 2); - item_level0->setHandlesChildEvents(true); + blue->setHandlesChildEvents(true); // Send events to a level1 item - pressEvent.setScenePos(item1_level1->mapToScene(5, 5)); + pressEvent.setScenePos(red->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level1->mapToScene(5, 5)); + releaseEvent.setScenePos(red->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 4); - QCOMPARE(item1_level1->counter, 2); + QCOMPARE(blue->counter, 4); + QCOMPARE(red->counter, 2); // Send events to a level2 item - pressEvent.setScenePos(item1_level2->mapToScene(5, 5)); + pressEvent.setScenePos(green->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level2->mapToScene(5, 5)); + releaseEvent.setScenePos(green->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 6); - QCOMPARE(item1_level1->counter, 2); - QCOMPARE(item1_level2->counter, 2); + QCOMPARE(blue->counter, 6); + QCOMPARE(red->counter, 2); + QCOMPARE(green->counter, 2); - item_level0->setHandlesChildEvents(false); + blue->setHandlesChildEvents(false); // Send events to a level1 item - pressEvent.setScenePos(item1_level1->mapToScene(5, 5)); + pressEvent.setScenePos(red->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level1->mapToScene(5, 5)); + releaseEvent.setScenePos(red->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 6); - QCOMPARE(item1_level1->counter, 4); + QCOMPARE(blue->counter, 6); + QCOMPARE(red->counter, 4); // Send events to a level2 item - pressEvent.setScenePos(item1_level2->mapToScene(5, 5)); + pressEvent.setScenePos(green->mapToScene(5, 5)); pressEvent.setScreenPos(view.mapFromScene(pressEvent.scenePos())); - releaseEvent.setScenePos(item1_level2->mapToScene(5, 5)); + releaseEvent.setScenePos(green->mapToScene(5, 5)); releaseEvent.setScreenPos(view.mapFromScene(releaseEvent.scenePos())); QApplication::sendEvent(&scene, &pressEvent); QApplication::sendEvent(&scene, &releaseEvent); - QCOMPARE(item_level0->counter, 6); - QCOMPARE(item1_level1->counter, 4); - QCOMPARE(item1_level2->counter, 4); + QCOMPARE(blue->counter, 6); + QCOMPARE(red->counter, 4); + QCOMPARE(green->counter, 4); } void tst_QGraphicsItem::handlesChildEvents2() @@ -3750,6 +3758,20 @@ void tst_QGraphicsItem::itemChange() QCOMPARE(tester.pos(), QPointF(42, 0)); } { + // ItemZValueChange / ItemZValueHasChanged + tester.itemChangeReturnValue = qreal(2.0); + tester.setZValue(1.0); + ++changeCount; // notification sent too + ++changeCount; + QCOMPARE(tester.changes.size(), changeCount); + QCOMPARE(tester.changes.at(tester.changes.size() - 2), QGraphicsItem::ItemZValueChange); + QCOMPARE(tester.changes.at(tester.changes.size() - 1), QGraphicsItem::ItemZValueHasChanged); + QCOMPARE(tester.values.at(tester.changes.size() - 2), QVariant(qreal(1.0))); + QCOMPARE(tester.values.at(tester.changes.size() - 1), QVariant(qreal(2.0))); + QCOMPARE(tester.oldValues.last(), QVariant(qreal(0.0))); + QCOMPARE(tester.zValue(), qreal(2.0)); + } + { // ItemFlagsChange tester.itemChangeReturnValue = QGraphicsItem::ItemIsSelectable; tester.setFlag(QGraphicsItem::ItemIsSelectable, false); @@ -4488,7 +4510,7 @@ void tst_QGraphicsItem::itemClipsChildrenToShape2() QPainter painter(&image); scene.render(&painter); painter.end(); - + QCOMPARE(image.pixel(5, 5), QColor(0, 0, 255).rgba()); QCOMPARE(image.pixel(5, 10), QRgb(0)); QCOMPARE(image.pixel(10, 5), QRgb(0)); @@ -5145,7 +5167,7 @@ void tst_QGraphicsItem::task240400_clickOnTextItem() QVERIFY(selectable ? item->isSelected() : !item->isSelected()); - // + // if (textFlags & Qt::TextEditorInteraction) QVERIFY(item->textCursor().columnNumber() > column); else @@ -5158,7 +5180,7 @@ void tst_QGraphicsItem::task243707_addChildBeforeParent() // inconsistent internal state that can cause a crash. This test shows // one such crash. QGraphicsScene scene; - QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsWidget *widget = new QGraphicsWidget; QGraphicsWidget *widget2 = new QGraphicsWidget(widget); scene.addItem(widget2); QVERIFY(!widget2->parentItem()); @@ -5274,7 +5296,7 @@ void tst_QGraphicsItem::itemTransform_unrelated() QCOMPARE(stranger1->itemTransform(stranger2).map(QPointF(10, 10)), QPointF(10, 10)); QCOMPARE(stranger2->itemTransform(stranger1).map(QPointF(10, 10)), QPointF(10, 10)); } - + void tst_QGraphicsItem::opacity_data() { QTest::addColumn<qreal>("p_opacity"); @@ -5483,7 +5505,7 @@ void tst_QGraphicsItem::nestedClipping() l1->setData(0, "l1"); l2->setData(0, "l2"); l3->setData(0, "l3"); - + QGraphicsView view(&scene); view.show(); #ifdef Q_WS_X11 @@ -5511,7 +5533,7 @@ void tst_QGraphicsItem::nestedClipping() QCOMPARE(image.pixel(80, 130), qRgba(255, 255, 0, 255)); QCOMPARE(image.pixel(92, 105), qRgba(0, 255, 0, 255)); QCOMPARE(image.pixel(105, 105), qRgba(0, 0, 255, 255)); -#if 0 +#if 0 // Enable this to compare if the test starts failing. image.save("nestedClipping_reference.png"); #endif @@ -5653,7 +5675,7 @@ void tst_QGraphicsItem::tabChangesFocus_data() void tst_QGraphicsItem::tabChangesFocus() { QFETCH(bool, tabChangesFocus); - + QGraphicsScene scene; QGraphicsTextItem *item = scene.addText("Hello"); item->setTabChangesFocus(tabChangesFocus); diff --git a/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp b/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp index f25a079..dee3f54 100644 --- a/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp +++ b/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp @@ -1732,6 +1732,10 @@ void tst_QGraphicsWidget::task236127_bspTreeIndexFails() widget->resize(10, 10); widget2->resize(10, 10); widget2->setZValue(1); + QCOMPARE(widget2->zValue(), qreal(1)); + QCOMPARE(widget->zValue(), qreal(0)); + widget->setData(0, "widget"); + widget2->setData(0, "widget2"); QGraphicsScene scene; scene.addItem(widget); @@ -1742,7 +1746,7 @@ void tst_QGraphicsWidget::task236127_bspTreeIndexFails() #ifdef Q_WS_X11 qt_x11_wait_for_window_manager(&view); #endif - QTest::qWait(50); + QTest::qWait(100); QVERIFY(!scene.itemAt(25, 25)); widget->setGeometry(0, 112, 360, 528); diff --git a/tests/auto/qlineedit/tst_qlineedit.cpp b/tests/auto/qlineedit/tst_qlineedit.cpp index 87e966f..8779831 100644 --- a/tests/auto/qlineedit/tst_qlineedit.cpp +++ b/tests/auto/qlineedit/tst_qlineedit.cpp @@ -257,6 +257,7 @@ private slots: void task233101_cursorPosAfterInputMethod_data(); void task233101_cursorPosAfterInputMethod(); void task241436_passwordEchoOnEditRestoreEchoMode(); + void task248948_redoRemovedSelection(); protected slots: #ifdef QT3_SUPPORT @@ -3487,5 +3488,17 @@ void tst_QLineEdit::task241436_passwordEchoOnEditRestoreEchoMode() testWidget->setEchoMode(QLineEdit::Normal); } +void tst_QLineEdit::task248948_redoRemovedSelection() +{ + testWidget->setText("a"); + testWidget->selectAll(); + QTest::keyPress(testWidget, Qt::Key_Delete); + testWidget->undo(); + testWidget->redo(); + QTest::keyPress(testWidget, 'a'); + QTest::keyPress(testWidget, 'b'); + QCOMPARE(testWidget->text(), QLatin1String("ab")); +} + QTEST_MAIN(tst_QLineEdit) #include "tst_qlineedit.moc" diff --git a/tests/auto/qpainterpath/tst_qpainterpath.cpp b/tests/auto/qpainterpath/tst_qpainterpath.cpp index cff84e9..873fe59 100644 --- a/tests/auto/qpainterpath/tst_qpainterpath.cpp +++ b/tests/auto/qpainterpath/tst_qpainterpath.cpp @@ -109,6 +109,8 @@ private slots: void operators(); void connectPathDuplicatePoint(); + + void translate(); }; // Testing get/set functions @@ -819,7 +821,6 @@ void tst_QPainterPath::closing() } } -#if QT_VERSION >= 0x040200 void tst_QPainterPath::testArcMoveTo_data() { QTest::addColumn<QRectF>("rect"); @@ -1170,7 +1171,43 @@ void tst_QPainterPath::connectPathDuplicatePoint() QCOMPARE(c, a); } -#endif +void tst_QPainterPath::translate() +{ + QPainterPath path; + + // Path with no elements. + QCOMPARE(path.currentPosition(), QPointF()); + path.translate(50.5, 50.5); + QCOMPARE(path.currentPosition(), QPointF()); + QCOMPARE(path.translated(50.5, 50.5).currentPosition(), QPointF()); + + // path.isEmpty(), but we have one MoveTo element that should be translated. + path.moveTo(50, 50); + QCOMPARE(path.currentPosition(), QPointF(50, 50)); + path.translate(99.9, 99.9); + QCOMPARE(path.currentPosition(), QPointF(149.9, 149.9)); + path.translate(-99.9, -99.9); + QCOMPARE(path.currentPosition(), QPointF(50, 50)); + QCOMPARE(path.translated(-50, -50).currentPosition(), QPointF(0, 0)); + + // Complex path. + QRegion shape(100, 100, 300, 200, QRegion::Ellipse); + shape -= QRect(225, 175, 50, 50); + QPainterPath complexPath; + complexPath.addRegion(shape); + QVector<QPointF> untranslatedElements; + for (int i = 0; i < complexPath.elementCount(); ++i) + untranslatedElements.append(QPointF(complexPath.elementAt(i))); + + const QPainterPath untranslatedComplexPath(complexPath); + const QPointF offset(100, 100); + complexPath.translate(offset); + + for (int i = 0; i < complexPath.elementCount(); ++i) + QCOMPARE(QPointF(complexPath.elementAt(i)) - offset, untranslatedElements.at(i)); + + QCOMPARE(complexPath.translated(-offset), untranslatedComplexPath); +} QTEST_APPLESS_MAIN(tst_QPainterPath) diff --git a/tests/auto/qpicture/tst_qpicture.cpp b/tests/auto/qpicture/tst_qpicture.cpp index 43559e5..ddf37f7 100644 --- a/tests/auto/qpicture/tst_qpicture.cpp +++ b/tests/auto/qpicture/tst_qpicture.cpp @@ -47,6 +47,7 @@ #include <qimage.h> #include <qdesktopwidget.h> #include <qapplication.h> +#include <limits.h> //TESTED_CLASS= //TESTED_FILES= @@ -66,6 +67,9 @@ private slots: void operator_lt_lt(); void save_restore(); + + void boundaryValues_data(); + void boundaryValues(); }; // Testing get/set functions @@ -236,5 +240,39 @@ void tst_QPicture::save_restore() QVERIFY( pix1.toImage() == pix2.toImage() ); } +void tst_QPicture::boundaryValues_data() +{ + QTest::addColumn<int>("x"); + QTest::addColumn<int>("y"); + QTest::newRow("max x") << INT_MAX << 50; + QTest::newRow("max y") << 50 << INT_MAX; + QTest::newRow("max x and y") << INT_MAX << INT_MAX; + + QTest::newRow("min x") << INT_MIN << 50; + QTest::newRow("min y") << 50 << INT_MIN; + QTest::newRow("min x and y") << INT_MIN << INT_MIN; + + QTest::newRow("min x, max y") << INT_MIN << INT_MAX; + QTest::newRow("max x, min y") << INT_MAX << INT_MIN; + +} + +void tst_QPicture::boundaryValues() +{ + QPicture picture; + + QPainter painter; + painter.begin(&picture); + + QFETCH(int, x); + QFETCH(int, y); + painter.drawPoint(QPoint(x, y)); + + painter.end(); + + +} + + QTEST_MAIN(tst_QPicture) #include "tst_qpicture.moc" diff --git a/tests/auto/qpixmap/images/designer.png b/tests/auto/qpixmap/images/designer.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/designer.png diff --git a/tests/auto/qpixmap/images/dx_-10_dy_-10_50_50_100_100.png b/tests/auto/qpixmap/images/dx_-10_dy_-10_50_50_100_100.png Binary files differnew file mode 100644 index 0000000..a4a1924 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_-10_dy_-10_50_50_100_100.png diff --git a/tests/auto/qpixmap/images/dx_-10_dy_-10_x_y_w_h.png b/tests/auto/qpixmap/images/dx_-10_dy_-10_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..1506af5 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_-10_dy_-10_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_-10_dy_0_50_50_100_100.png b/tests/auto/qpixmap/images/dx_-10_dy_0_50_50_100_100.png Binary files differnew file mode 100644 index 0000000..8500ab1 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_-10_dy_0_50_50_100_100.png diff --git a/tests/auto/qpixmap/images/dx_-10_dy_0_x_y_w_h.png b/tests/auto/qpixmap/images/dx_-10_dy_0_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..2145c61 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_-10_dy_0_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_-128_dy_-128_x_y_w_h.png b/tests/auto/qpixmap/images/dx_-128_dy_-128_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_-128_dy_-128_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_-128_dy_0_x_y_w_h.png b/tests/auto/qpixmap/images/dx_-128_dy_0_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_-128_dy_0_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_-10_50_50_100_100.png b/tests/auto/qpixmap/images/dx_0_dy_-10_50_50_100_100.png Binary files differnew file mode 100644 index 0000000..728ee79 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_-10_50_50_100_100.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_-10_x_y_w_h.png b/tests/auto/qpixmap/images/dx_0_dy_-10_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..e9d5850 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_-10_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_-128_x_y_w_h.png b/tests/auto/qpixmap/images/dx_0_dy_-128_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_-128_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_0_50_50_100_100.png b/tests/auto/qpixmap/images/dx_0_dy_0_50_50_100_100.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_0_50_50_100_100.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_0_null.png b/tests/auto/qpixmap/images/dx_0_dy_0_null.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_0_null.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_0_x_y_w_h.png b/tests/auto/qpixmap/images/dx_0_dy_0_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_0_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_10_50_50_100_100.png b/tests/auto/qpixmap/images/dx_0_dy_10_50_50_100_100.png Binary files differnew file mode 100644 index 0000000..7c09b17 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_10_50_50_100_100.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_10_x_y_w_h.png b/tests/auto/qpixmap/images/dx_0_dy_10_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..70a6340 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_10_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_128_x_y_w_h.png b/tests/auto/qpixmap/images/dx_0_dy_128_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_128_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_0_dy_1_null.png b/tests/auto/qpixmap/images/dx_0_dy_1_null.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_0_dy_1_null.png diff --git a/tests/auto/qpixmap/images/dx_10_dy_0_50_50_100_100.png b/tests/auto/qpixmap/images/dx_10_dy_0_50_50_100_100.png Binary files differnew file mode 100644 index 0000000..85abada --- /dev/null +++ b/tests/auto/qpixmap/images/dx_10_dy_0_50_50_100_100.png diff --git a/tests/auto/qpixmap/images/dx_10_dy_0_x_y_w_h.png b/tests/auto/qpixmap/images/dx_10_dy_0_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..3e03450 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_10_dy_0_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_10_dy_10_50_50_100_100.png b/tests/auto/qpixmap/images/dx_10_dy_10_50_50_100_100.png Binary files differnew file mode 100644 index 0000000..315fbe0 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_10_dy_10_50_50_100_100.png diff --git a/tests/auto/qpixmap/images/dx_10_dy_10_x_y_w_h.png b/tests/auto/qpixmap/images/dx_10_dy_10_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..d91dc71 --- /dev/null +++ b/tests/auto/qpixmap/images/dx_10_dy_10_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_128_dy_0_x_y_w_h.png b/tests/auto/qpixmap/images/dx_128_dy_0_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_128_dy_0_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_128_dy_128_64_64_128_128.png b/tests/auto/qpixmap/images/dx_128_dy_128_64_64_128_128.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_128_dy_128_64_64_128_128.png diff --git a/tests/auto/qpixmap/images/dx_128_dy_128_x_y_w_h.png b/tests/auto/qpixmap/images/dx_128_dy_128_x_y_w_h.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_128_dy_128_x_y_w_h.png diff --git a/tests/auto/qpixmap/images/dx_1_dy_0_null.png b/tests/auto/qpixmap/images/dx_1_dy_0_null.png Binary files differnew file mode 100644 index 0000000..bca471d --- /dev/null +++ b/tests/auto/qpixmap/images/dx_1_dy_0_null.png diff --git a/tests/auto/qpixmap/qpixmap.pro b/tests/auto/qpixmap/qpixmap.pro index e5dace4..70be4be 100644 --- a/tests/auto/qpixmap/qpixmap.pro +++ b/tests/auto/qpixmap/qpixmap.pro @@ -13,5 +13,4 @@ wince*: { win32:LIBS += -lgdi32 -luser32 } - - +RESOURCES += qpixmap.qrc diff --git a/tests/auto/qpixmap/qpixmap.qrc b/tests/auto/qpixmap/qpixmap.qrc new file mode 100644 index 0000000..99fde61 --- /dev/null +++ b/tests/auto/qpixmap/qpixmap.qrc @@ -0,0 +1,29 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>images/designer.png</file> + <file>images/dx_0_dy_0_50_50_100_100.png</file> + <file>images/dx_0_dy_0_null.png</file> + <file>images/dx_0_dy_0_x_y_w_h.png</file> + <file>images/dx_0_dy_-10_50_50_100_100.png</file> + <file>images/dx_0_dy_10_50_50_100_100.png</file> + <file>images/dx_0_dy_-10_x_y_w_h.png</file> + <file>images/dx_0_dy_10_x_y_w_h.png</file> + <file>images/dx_0_dy_-128_x_y_w_h.png</file> + <file>images/dx_0_dy_128_x_y_w_h.png</file> + <file>images/dx_0_dy_1_null.png</file> + <file>images/dx_-10_dy_0_50_50_100_100.png</file> + <file>images/dx_10_dy_0_50_50_100_100.png</file> + <file>images/dx_-10_dy_0_x_y_w_h.png</file> + <file>images/dx_10_dy_0_x_y_w_h.png</file> + <file>images/dx_-10_dy_-10_50_50_100_100.png</file> + <file>images/dx_10_dy_10_50_50_100_100.png</file> + <file>images/dx_-10_dy_-10_x_y_w_h.png</file> + <file>images/dx_10_dy_10_x_y_w_h.png</file> + <file>images/dx_-128_dy_0_x_y_w_h.png</file> + <file>images/dx_128_dy_0_x_y_w_h.png</file> + <file>images/dx_128_dy_128_64_64_128_128.png</file> + <file>images/dx_-128_dy_-128_x_y_w_h.png</file> + <file>images/dx_128_dy_128_x_y_w_h.png</file> + <file>images/dx_1_dy_0_null.png</file> +</qresource> +</RCC> diff --git a/tests/auto/qpixmap/tst_qpixmap.cpp b/tests/auto/qpixmap/tst_qpixmap.cpp index ba117d8..8a6b63b 100644 --- a/tests/auto/qpixmap/tst_qpixmap.cpp +++ b/tests/auto/qpixmap/tst_qpixmap.cpp @@ -88,6 +88,9 @@ private slots: void testMetrics(); + void scroll_data(); + void scroll(); + void fill_data(); void fill(); void fill_transparent(); @@ -302,6 +305,99 @@ void tst_QPixmap::convertFromImage() QVERIFY( pixmapsAreEqual(&pix, &res) ); } +void tst_QPixmap::scroll_data() +{ + QTest::addColumn<QImage>("input"); + QTest::addColumn<int>("dx"); + QTest::addColumn<int>("dy"); + QTest::addColumn<QRect>("rect"); + QTest::addColumn<QRegion>("exposed"); + QTest::addColumn<bool>("newPix"); + + QImage input(":/images/designer.png"); + + // Noop tests + QTest::newRow("null") << QImage() << 0 << 0 << QRect() << QRegion() << false; + QTest::newRow("dx_0_dy_0_null") << input << 0 << 0 << QRect() << QRegion() << false; + QTest::newRow("dx_1_dy_0_null") << input << 1 << 0 << QRect() << QRegion() << false; + QTest::newRow("dx_0_dy_1_null") << input << 0 << 1 << QRect() << QRegion() << false; + QTest::newRow("dx_0_dy_0_x_y_w_h") << input << 0 << 0 << input.rect() << QRegion() << false; + + QRegion r; + // Scroll whole pixmap + r = QRegion(); r += QRect(0, 0, 128, 10); + QTest::newRow("dx_0_dy_10_x_y_w_h") << input << 0 << 10 << input.rect() << r << true; + r = QRegion(); r += QRect(0, 0, 10, 128); + QTest::newRow("dx_10_dy_0_x_y_w_h") << input << 10 << 0 << input.rect() << r << true; + r = QRegion(); r += QRect(0, 0, 128, 10); r += QRect(0, 10, 10, 118); + QTest::newRow("dx_10_dy_10_x_y_w_h") << input << 10 << 10 << input.rect() << r << true; + r = QRegion(); r += QRect(118, 0, 10, 128); + QTest::newRow("dx_-10_dy_0_x_y_w_h") << input << -10 << 0 << input.rect() << r << true; + r = QRegion(); r += QRect(0, 118, 128, 10); + QTest::newRow("dx_0_dy_-10_x_y_w_h") << input << 0 << -10 << input.rect() << r << true; + r = QRegion(); r += QRect(118, 0, 10, 118); r += QRect(0, 118, 128, 10); + QTest::newRow("dx_-10_dy_-10_x_y_w_h") << input << -10 << -10 << input.rect() << r << true; + + // Scroll part of pixmap + QTest::newRow("dx_0_dy_0_50_50_100_100") << input << 0 << 0 << QRect(50, 50, 100, 100) << QRegion() << false; + r = QRegion(); r += QRect(50, 50, 10, 78); + QTest::newRow("dx_10_dy_0_50_50_100_100") << input << 10 << 0 << QRect(50, 50, 100, 100) << r << true; + r = QRegion(); r += QRect(50, 50, 78, 10); + QTest::newRow("dx_0_dy_10_50_50_100_100") << input << 0 << 10 << QRect(50, 50, 100, 100) << r << true; + r = QRegion(); r += QRect(50, 50, 78, 10); r += QRect(50, 60, 10, 68); + QTest::newRow("dx_10_dy_10_50_50_100_100") << input << 10 << 10 << QRect(50, 50, 100, 100) << r << true; + r = QRegion(); r += QRect(118, 50, 10, 78); + QTest::newRow("dx_-10_dy_0_50_50_100_100") << input << -10 << 0 << QRect(50, 50, 100, 100) << r << true; + r = QRegion(); r += QRect(50, 118, 78, 10); + QTest::newRow("dx_0_dy_-10_50_50_100_100") << input << 0 << -10 << QRect(50, 50, 100, 100) << r << true; + r = QRegion(); r += QRect(118, 50, 10, 68); r += QRect(50, 118, 78, 10); + QTest::newRow("dx_-10_dy_-10_50_50_100_100") << input << -10 << -10 << QRect(50, 50, 100, 100) << r << true; + + // Scroll away the whole pixmap + r = input.rect(); + QTest::newRow("dx_128_dy_0_x_y_w_h") << input << 128 << 0 << input.rect() << r << false; + QTest::newRow("dx_0_dy_128_x_y_w_h") << input << 0 << 128 << input.rect() << r << false; + QTest::newRow("dx_128_dy_128_x_y_w_h") << input << 128 << 128 << input.rect() << r << false; + QTest::newRow("dx_-128_dy_0_x_y_w_h") << input << -128 << 0 << input.rect() << r << false; + QTest::newRow("dx_0_dy_-128_x_y_w_h") << input << 0 << -128 << input.rect() << r << false; + QTest::newRow("dx_-128_dy_-128_x_y_w_h") << input << -128 << -128 << input.rect() << r << false; + + // Scroll away part of the pixmap + r = QRegion(); r += QRect(64, 64, 64, 64); + QTest::newRow("dx_128_dy_128_64_64_128_128") << input << 128 << 128 << QRect(64, 64, 128, 128) << r << false; +} + +void tst_QPixmap::scroll() +{ + QFETCH(QImage, input); + QFETCH(int, dx); + QFETCH(int, dy); + QFETCH(QRect, rect); + QFETCH(QRegion, exposed); + QFETCH(bool, newPix); + + QPixmap pixmap(input); + QRegion exp; + qint64 oldKey = pixmap.cacheKey(); + pixmap.scroll(dx, dy, rect, &exp); + if (!newPix) + QCOMPARE(pixmap.cacheKey(), oldKey); + else + QVERIFY(pixmap.cacheKey() != oldKey); + +#if 0 + // Remember to add to resources. + QString fileName = QString("images/%1.png").arg(QTest::currentDataTag()); + pixmap.toImage().save(fileName); +#else + QString fileName = QString(":/images/%1.png").arg(QTest::currentDataTag()); +#endif + QImage output(fileName); + QVERIFY(input.isNull() == output.isNull()); + QCOMPARE(pixmap.toImage(), output); + QCOMPARE(exp, exposed); +} + void tst_QPixmap::fill_data() { QTest::addColumn<uint>("pixel"); diff --git a/tests/auto/qprocess/qprocess.pro b/tests/auto/qprocess/qprocess.pro index 4600f02..ed59f10 100644 --- a/tests/auto/qprocess/qprocess.pro +++ b/tests/auto/qprocess/qprocess.pro @@ -3,6 +3,7 @@ SUBDIRS = testProcessCrash \ testProcessEcho \ testProcessEcho2 \ testProcessEcho3 \ + testProcessEnvironment \ testProcessLoopback \ testProcessNormal \ testProcessOutput \ diff --git a/tests/auto/qprocess/testProcessEnvironment/main.cpp b/tests/auto/qprocess/testProcessEnvironment/main.cpp new file mode 100644 index 0000000..098d13e --- /dev/null +++ b/tests/auto/qprocess/testProcessEnvironment/main.cpp @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char **argv) +{ +#if defined(_WIN32_WCE) + // no environment in Windows CE + return 0; +#else + if (argc == 1) + return 1; + + char *env = getenv(argv[1]); + if (env) { + printf("%s", env); + return 0; + } + return 1; +#endif +} diff --git a/tests/auto/qprocess/testProcessEnvironment/testProcessEnvironment.pro b/tests/auto/qprocess/testProcessEnvironment/testProcessEnvironment.pro new file mode 100644 index 0000000..14ddae5 --- /dev/null +++ b/tests/auto/qprocess/testProcessEnvironment/testProcessEnvironment.pro @@ -0,0 +1,12 @@ +SOURCES = main.cpp +CONFIG -= qt +CONFIG += console +DESTDIR = ./ + +mac { + CONFIG -= app_bundle +} + +# no install rule for application used by test +INSTALLS = + diff --git a/tests/auto/qprocess/tst_qprocess.cpp b/tests/auto/qprocess/tst_qprocess.cpp index cb2b1bf..ef78c2e 100644 --- a/tests/auto/qprocess/tst_qprocess.cpp +++ b/tests/auto/qprocess/tst_qprocess.cpp @@ -124,6 +124,8 @@ private slots: void spaceArgsTest_data(); void spaceArgsTest(); void exitCodeTest(); + void setEnvironment_data(); + void setEnvironment(); void systemEnvironment(); void spaceInName(); void lockupsInStartDetached(); @@ -1617,13 +1619,117 @@ void tst_QProcess::removeFileWhileProcessIsRunning() } //----------------------------------------------------------------------------- +void tst_QProcess::setEnvironment_data() +{ + QTest::addColumn<QString>("name"); + QTest::addColumn<QString>("value"); + + QTest::newRow("setting-empty") << "tst_QProcess" << ""; + QTest::newRow("setting") << "tst_QProcess" << "value"; + +#ifdef Q_OS_WIN + QTest::newRow("unsetting") << "PROMPT" << QString(); + QTest::newRow("overriding") << "PROMPT" << "value"; +#else + QTest::newRow("unsetting") << "PATH" << QString(); + QTest::newRow("overriding") << "PATH" << "value"; +#endif +} + +void tst_QProcess::setEnvironment() +{ +#if !defined (Q_OS_WINCE) + // there is no concept of system variables on Windows CE as there is no console + + // make sure our environment variables are correct + QVERIFY(qgetenv("tst_QProcess").isEmpty()); + QVERIFY(!qgetenv("PATH").isEmpty()); +#ifdef Q_OS_WIN + QVERIFY(!qgetenv("PROMPT").isEmpty()); +#endif + + QFETCH(QString, name); + QFETCH(QString, value); + QString executable = QDir::currentPath() + "/testProcessEnvironment/testProcessEnvironment"; + + { + QProcess process; + QStringList environment = QProcess::systemEnvironment(); + if (value.isNull()) { + int pos; + QRegExp rx(name + "=.*"); +#ifdef Q_OS_WIN + rx.setCaseSensitivity(Qt::CaseInsensitive); +#endif + while ((pos = environment.indexOf(rx)) != -1) + environment.removeAt(pos); + } else { + environment.append(name + '=' + value); + } + process.setEnvironment(environment); + process.start(executable, QStringList() << name); + + QVERIFY(process.waitForFinished()); + if (value.isNull()) + QCOMPARE(process.exitCode(), 1); + else if (!value.isEmpty()) + QCOMPARE(process.exitCode(), 0); + + QCOMPARE(process.readAll(), value.toLocal8Bit()); + } + + // re-do the test but set the environment twice, to make sure + // that the latter addition overrides + // this test doesn't make sense in unsetting + if (!value.isNull()) { + QProcess process; + QStringList environment = QProcess::systemEnvironment(); + environment.prepend(name + "=This is not the right value"); + environment.append(name + '=' + value); + process.setEnvironment(environment); + process.start(executable, QStringList() << name); + + QVERIFY(process.waitForFinished()); + if (!value.isEmpty()) + QCOMPARE(process.exitCode(), 0); + + QCOMPARE(process.readAll(), value.toLocal8Bit()); + } + + // use the hash variant now + { + QProcess process; + QHash<QString, QString> environment = QProcess::systemEnvironmentHash(); + if (value.isNull()) + environment.remove(name); + else + environment.insert(name, value); + process.setEnvironmentHash(environment); + process.start(executable, QStringList() << name); + + QVERIFY(process.waitForFinished()); + if (value.isNull()) + QCOMPARE(process.exitCode(), 1); + else if (!value.isEmpty()) + QCOMPARE(process.exitCode(), 0); + + QCOMPARE(process.readAll(), value.toLocal8Bit()); + } +#endif +} +//----------------------------------------------------------------------------- void tst_QProcess::systemEnvironment() { #if defined (Q_OS_WINCE) // there is no concept of system variables on Windows CE as there is no console QVERIFY(QProcess::systemEnvironment().isEmpty()); + QVERIFY(QProcess::systemEnvironmentHash().isEmpty()); #else QVERIFY(!QProcess::systemEnvironment().isEmpty()); + QVERIFY(!QProcess::systemEnvironmentHash().isEmpty()); + + QVERIFY(QProcess::systemEnvironmentHash().contains("PATH")); + QVERIFY(!QProcess::systemEnvironment().filter(QRegExp("^PATH=", Qt::CaseInsensitive)).isEmpty()); #endif } diff --git a/tests/auto/qset/tst_qset.cpp b/tests/auto/qset/tst_qset.cpp index 7afcd84..f4b0e93 100644 --- a/tests/auto/qset/tst_qset.cpp +++ b/tests/auto/qset/tst_qset.cpp @@ -74,6 +74,7 @@ private slots: void clear(); void remove(); void contains(); + void containsSet(); void begin(); void end(); void insert(); @@ -347,6 +348,40 @@ void tst_QSet::contains() } } +void tst_QSet::containsSet() +{ + QSet<QString> set1; + QSet<QString> set2; + + // empty set contains the empty set + QVERIFY(set1.contains(set2)); + + for (int i = 0; i < 500; ++i) { + set1.insert(QString::number(i)); + set2.insert(QString::number(i)); + } + QVERIFY(set1.contains(set2)); + + set2.remove(QString::number(19)); + set2.remove(QString::number(82)); + set2.remove(QString::number(7)); + QVERIFY(set1.contains(set2)); + + set1.remove(QString::number(23)); + QVERIFY(!set1.contains(set2)); + + // filled set contains the empty set as well + QSet<QString> set3; + QVERIFY(set1.contains(set3)); + + // the empty set doesn't contain a filled set + QVERIFY(!set3.contains(set1)); + + // verify const signature + const QSet<QString> set4; + QVERIFY(set3.contains(set4)); +} + void tst_QSet::begin() { QSet<int> set1; diff --git a/tests/auto/qsplitter/qsplitter.pro b/tests/auto/qsplitter/qsplitter.pro index 99f27b4..a7ff0a6 100644 --- a/tests/auto/qsplitter/qsplitter.pro +++ b/tests/auto/qsplitter/qsplitter.pro @@ -8,4 +8,7 @@ wince*: { addFiles.sources = extradata.txt setSizes3.dat addFiles.path = . DEPLOYMENT += addFiles + DEFINES += SRCDIR=\\\"./\\\" +} else { + DEFINES += SRCDIR=\\\"$$PWD/\\\" } diff --git a/tests/auto/qsplitter/tst_qsplitter.cpp b/tests/auto/qsplitter/tst_qsplitter.cpp index b463f7f..33404e4 100644 --- a/tests/auto/qsplitter/tst_qsplitter.cpp +++ b/tests/auto/qsplitter/tst_qsplitter.cpp @@ -213,7 +213,7 @@ void tst_QSplitter::setSizes3_data() QTest::addColumn<IntList>("collapsibleStates"); QTest::addColumn<bool>("childrenCollapse"); - QFile file("setSizes3.dat"); + QFile file(SRCDIR "setSizes3.dat"); if (!file.open(QIODevice::ReadOnly)) { qDebug() << "Can't open file, reason:" << file.errorString(); return; diff --git a/tests/auto/qsslsocket/tst_qsslsocket.cpp b/tests/auto/qsslsocket/tst_qsslsocket.cpp index 4f98624..b9725fd 100644 --- a/tests/auto/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/qsslsocket/tst_qsslsocket.cpp @@ -133,6 +133,7 @@ private slots: void addCaCertificates2(); void ciphers(); void connectToHostEncrypted(); + void connectToHostEncryptedWithVerificationPeerName(); void sessionCipher(); void flush(); void isEncrypted(); @@ -597,6 +598,32 @@ void tst_QSslSocket::connectToHostEncrypted() QVERIFY(socket->waitForDisconnected()); } +void tst_QSslSocket::connectToHostEncryptedWithVerificationPeerName() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + + socket->addCaCertificates(QLatin1String("certs/qt-test-server-cacert.pem")); +#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), + this, SLOT(untrustedWorkaroundSlot(QList<QSslError>))); +#endif + + // connect to the server with its local name, but use the full name for verification. + socket->connectToHostEncrypted(QtNetworkSettings::serverLocalName(), 443, QtNetworkSettings::serverName()); + + // This should pass unconditionally when using fluke's CA certificate. + QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString())); + + socket->disconnectFromHost(); + QVERIFY(socket->waitForDisconnected()); + + QCOMPARE(socket->mode(), QSslSocket::SslClientMode); +} + void tst_QSslSocket::sessionCipher() { if (!QSslSocket::supportsSsl()) diff --git a/tests/auto/qtoolbutton/tst_qtoolbutton.cpp b/tests/auto/qtoolbutton/tst_qtoolbutton.cpp index 2c0a0cb..1dbf99b 100644 --- a/tests/auto/qtoolbutton/tst_qtoolbutton.cpp +++ b/tests/auto/qtoolbutton/tst_qtoolbutton.cpp @@ -65,6 +65,7 @@ private slots: void getSetCheck(); void triggered(); void task230994_iconSize(); + void task176137_autoRepeatOfAction(); protected slots: void sendMouseClick(); @@ -179,6 +180,25 @@ void tst_QToolButton::task230994_iconSize() QVERIFY(option.iconSize.isValid()); } +void tst_QToolButton::task176137_autoRepeatOfAction() +{ + QAction action(0); + QToolButton tb; + tb.setDefaultAction (&action); + tb.show(); + QSignalSpy spy(&action,SIGNAL(triggered())); + QTest::mousePress ( &tb, Qt::LeftButton); + QTest::mouseRelease ( &tb, Qt::LeftButton, 0, QPoint (), 2000); + QCOMPARE(spy.count(),1); + + // try again with auto repeat + tb.setAutoRepeat (true); + QSignalSpy repeatSpy(&action,SIGNAL(triggered())); // new spy + QTest::mousePress ( &tb, Qt::LeftButton); + QTest::mouseRelease ( &tb, Qt::LeftButton, 0, QPoint (), 2000); + QCOMPARE (repeatSpy.count(), (2000 - tb.autoRepeatDelay()) / tb.autoRepeatInterval() + 1); +} + void tst_QToolButton::sendMouseClick() { diff --git a/tests/auto/qwidget/tst_qwidget.cpp b/tests/auto/qwidget/tst_qwidget.cpp index dfd0792..9197144 100644 --- a/tests/auto/qwidget/tst_qwidget.cpp +++ b/tests/auto/qwidget/tst_qwidget.cpp @@ -1544,6 +1544,10 @@ void tst_QWidget::focusChainOnReparent() QCOMPARE(w, expectedOriginalChain[i]); w = w->nextInFocusChain(); } + for (int i = 7; i >= 0; --i) { + w = w->previousInFocusChain(); + QCOMPARE(w, expectedOriginalChain[i]); + } QWidget window2; child2->setParent(&window2); @@ -1554,6 +1558,10 @@ void tst_QWidget::focusChainOnReparent() QCOMPARE(w, expectedNewChain[i]); w = w->nextInFocusChain(); } + for (int i = 4; i >= 0; --i) { + w = w->previousInFocusChain(); + QCOMPARE(w, expectedNewChain[i]); + } QWidget *expectedOldChain[5] = {&window, child1, child3, child4, &window}; w = &window; @@ -1561,6 +1569,10 @@ void tst_QWidget::focusChainOnReparent() QCOMPARE(w, expectedOldChain[i]); w = w->nextInFocusChain(); } + for (int i = 4; i >= 0; --i) { + w = w->previousInFocusChain(); + QCOMPARE(w, expectedOldChain[i]); + } } diff --git a/tests/auto/selftests/badxml/badxml.pro b/tests/auto/selftests/badxml/badxml.pro new file mode 100644 index 0000000..323791a --- /dev/null +++ b/tests/auto/selftests/badxml/badxml.pro @@ -0,0 +1,11 @@ +load(qttest_p4) + +SOURCES += tst_badxml.cpp +QT = core + +mac:CONFIG -= app_bundle +CONFIG -= debug_and_release_target +!win32:CONFIG += debug + +TARGET = badxml + diff --git a/tests/auto/selftests/badxml/tst_badxml.cpp b/tests/auto/selftests/badxml/tst_badxml.cpp new file mode 100644 index 0000000..6b2e4c4 --- /dev/null +++ b/tests/auto/selftests/badxml/tst_badxml.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtCore> +#include <QtTest/QtTest> + +/* + This test makes a testlog containing lots of characters which have a special meaning in + XML, with the purpose of exposing bugs in testlib's XML output code. +*/ +class tst_BadXml : public QObject +{ + Q_OBJECT + +private slots: + void badDataTag() const; + void badDataTag_data() const; + + void badMessage() const; + void badMessage_data() const; + +public: + static QList<QByteArray> const& badStrings(); +}; + +/* + Custom metaobject to make it possible to change class name at runtime. +*/ +class EmptyClass : public tst_BadXml +{ Q_OBJECT }; + +class tst_BadXmlSub : public tst_BadXml +{ +public: + const QMetaObject* metaObject() const; + + static char const* className; +}; +char const* tst_BadXmlSub::className = "tst_BadXml"; + +const QMetaObject* tst_BadXmlSub::metaObject() const +{ + const QMetaObject& empty = EmptyClass::staticMetaObject; + static QMetaObject mo = { + { empty.d.superdata, empty.d.stringdata, empty.d.data, empty.d.extradata } + }; + static char currentClassName[1024]; + qstrcpy(currentClassName, className); + int len = qstrlen(className); + currentClassName[len] = 0; + currentClassName[len+1] = 0; + + mo.d.stringdata = currentClassName; + + return &mo; +} + +/* + Outputs incidents and benchmark results with the current data tag set to a bad string. +*/ +void tst_BadXml::badDataTag() const +{ + qDebug("a message"); + + QBENCHMARK { + } + + QFAIL("a failure"); +} + +void tst_BadXml::badDataTag_data() const +{ + QTest::addColumn<int>("dummy"); + + foreach (char const* str, badStrings()) { + QTest::newRow(str) << 0; + } +} + +/* + Outputs a message containing a bad string. +*/ +void tst_BadXml::badMessage() const +{ + QFETCH(QByteArray, message); + qDebug("%s", message.constData()); +} + +void tst_BadXml::badMessage_data() const +{ + QTest::addColumn<QByteArray>("message"); + + int i = 0; + foreach (QByteArray const& str, badStrings()) { + QTest::newRow(qPrintable(QString::fromLatin1("string %1").arg(i++))) << str; + } +} + +/* + Returns a list of strings likely to expose bugs in XML output code. +*/ +QList<QByteArray> const& tst_BadXml::badStrings() +{ + static QList<QByteArray> out; + if (out.isEmpty()) { + out << "end cdata ]]> text ]]> more text"; + out << "quotes \" text\" more text"; + out << "xml close > open < tags < text"; + out << "all > \" mixed ]]> up > \" in < the ]]> hopes < of triggering \"< ]]> bugs"; + } + return out; +} + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + + /* + tst_selftests can't handle multiple XML documents in a single testrun, so we'll + decide before we begin which of our "bad strings" we want to use for our testcase + name. + */ + int badstring = -1; + QVector<char const*> args; + for (int i = 0; i < argc; ++i) { + if (!strcmp(argv[i], "-badstring")) { + bool ok = false; + if (i < argc-1) { + badstring = QByteArray(argv[i+1]).toInt(&ok); + ++i; + } + if (!ok) { + qFatal("Bad `-badstring' option"); + } + } + else { + args << argv[i]; + } + } + /* + We just want testlib to output a benchmark result, we don't actually care about the value, + so just do one iteration to save time. + */ + args << "-iterations" << "1"; + + if (badstring == -1) { + tst_BadXml test; + return QTest::qExec(&test, args.count(), const_cast<char**>(args.data())); + } + + QList<QByteArray> badstrings = tst_BadXml::badStrings(); + if (badstring >= badstrings.count()) + qFatal("`-badstring %d' is out of range", badstring); + + tst_BadXmlSub test; + test.className = badstrings[badstring].constData(); + return QTest::qExec(&test, args.count(), const_cast<char**>(args.data())); +} + +#include "tst_badxml.moc" diff --git a/tests/auto/selftests/expected_skip.txt b/tests/auto/selftests/expected_skip.txt index ba41e67..88c0426 100644 --- a/tests/auto/selftests/expected_skip.txt +++ b/tests/auto/selftests/expected_skip.txt @@ -1,13 +1,13 @@ ********* Start testing of tst_Skip ********* -Config: Using QTest library 4.3.0, Qt 4.3.0 +Config: Using QTest library 4.6.0, Qt 4.6.0 PASS : tst_Skip::initTestCase() SKIP : tst_Skip::test() skipping all - Loc: [/home/fenglich/dev/qt-4.3/tests/auto/selftests/skip/tst_skip.cpp(35)] + Loc: [/home/rmcgover/depot/qt-git/mainline/tests/auto/selftests/skip/tst_skip.cpp(68)] SKIP : tst_Skip::emptytest() skipping all - Loc: [/home/fenglich/dev/qt-4.3/tests/auto/selftests/skip/tst_skip.cpp(45)] + Loc: [/home/rmcgover/depot/qt-git/mainline/tests/auto/selftests/skip/tst_skip.cpp(78)] SKIP : tst_Skip::singleSkip(local 1) skipping one - Loc: [/home/fenglich/dev/qt-4.3/tests/auto/selftests/skip/tst_skip.cpp(64)] -this line should only be reached once (true) + Loc: [/home/rmcgover/depot/qt-git/mainline/tests/auto/selftests/skip/tst_skip.cpp(97)] +QDEBUG : tst_Skip::singleSkip(local 2) this line should only be reached once (true) PASS : tst_Skip::singleSkip() PASS : tst_Skip::cleanupTestCase() Totals: 3 passed, 0 failed, 3 skipped diff --git a/tests/auto/selftests/expected_xunit.txt b/tests/auto/selftests/expected_xunit.txt new file mode 100644 index 0000000..0f7e70a --- /dev/null +++ b/tests/auto/selftests/expected_xunit.txt @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<testsuite errors="3" failures="2" tests="6" name="tst_Xunit"> + <properties> + <property value="4.6.0" name="QTestVersion"/> + <property value="4.6.0" name="QtVersion"/> + </properties> + <testcase result="pass" name="initTestCase"/> + <testcase result="pass" name="testFunc1"> + <error message="just a QWARN() !" type="warn"/> + </testcase> + <testcase result="fail" name="testFunc2"> + <error message="a qDebug() call!" type="qdebug"/> + <failure message="Compared values are not the same + Actual (2): 2 + Expected (3): 3" result="fail"/> + </testcase> + <testcase name="testFunc3"> + <error message="skipping this function!" type="skip"/> + </testcase> + <testcase result="fail" name="testFunc4"> + <failure message="a forced failure!" result="fail"/> + </testcase> + <testcase result="pass" name="cleanupTestCase"/> +</testsuite> diff --git a/tests/auto/selftests/selftests.pro b/tests/auto/selftests/selftests.pro index 2fa1d50..ca69afa 100644 --- a/tests/auto/selftests/selftests.pro +++ b/tests/auto/selftests/selftests.pro @@ -5,7 +5,7 @@ SUBDIRS = subtest test warnings maxwarnings cmptest globaldata skipglobal skip \ skipinit skipinitdata datetime singleskip assert waitwithoutgui differentexec \ exception qexecstringlist datatable commandlinedata\ benchlibwalltime benchlibcallgrind benchlibeventcounter benchlibtickcounter \ - benchliboptions + benchliboptions xunit badxml INSTALLS = diff --git a/tests/auto/selftests/selftests.qrc b/tests/auto/selftests/selftests.qrc index 125619e..d57ff29 100644 --- a/tests/auto/selftests/selftests.qrc +++ b/tests/auto/selftests/selftests.qrc @@ -34,5 +34,6 @@ <file>expected_benchlibeventcounter.txt</file> <file>expected_benchliboptions.txt</file> <file>expected_benchlibtickcounter.txt</file> + <file>expected_xunit.txt</file> </qresource> </RCC> diff --git a/tests/auto/selftests/skip/tst_skip.cpp b/tests/auto/selftests/skip/tst_skip.cpp index b1a3936..437cf62 100644 --- a/tests/auto/selftests/skip/tst_skip.cpp +++ b/tests/auto/selftests/skip/tst_skip.cpp @@ -70,7 +70,7 @@ void tst_Skip::test_data() void tst_Skip::test() { - printf("this line should never be reached, since we skip in the _data function\n"); + qDebug("this line should never be reached, since we skip in the _data function"); } void tst_Skip::emptytest_data() @@ -80,7 +80,7 @@ void tst_Skip::emptytest_data() void tst_Skip::emptytest() { - printf("this line should never be reached, since we skip in the _data function\n"); + qDebug("this line should never be reached, since we skip in the _data function"); } void tst_Skip::singleSkip_data() @@ -95,7 +95,7 @@ void tst_Skip::singleSkip() QFETCH(bool, booll); if (!booll) QSKIP("skipping one", SkipSingle); - printf("this line should only be reached once (%s)\n", booll ? "true" : "false"); + qDebug("this line should only be reached once (%s)", booll ? "true" : "false"); } QTEST_MAIN(tst_Skip) diff --git a/tests/auto/selftests/tst_selftests.cpp b/tests/auto/selftests/tst_selftests.cpp index 103fd79..aa1048b 100644 --- a/tests/auto/selftests/tst_selftests.cpp +++ b/tests/auto/selftests/tst_selftests.cpp @@ -54,9 +54,13 @@ private slots: void runSubTest(); void checkXML() const; void checkXML_data(); + void checkXunitxml() const; + void checkXunitxml_data(); private: QStringList m_checkXMLBlacklist; + QStringList m_checkXunitBlacklist; + void doRunSubTest(QString &subdir, QStringList &arguments ); }; struct BenchmarkResult @@ -186,17 +190,16 @@ void tst_Selftests::runSubTest_data() QTest::newRow("benchlibtickcounter") << "benchlibtickcounter" << QStringList("-tickcounter"); #endif + QTest::newRow("xunit") << "xunit" << QStringList("-xunitxml"); + } -void tst_Selftests::runSubTest() +void tst_Selftests::doRunSubTest(QString &subdir, QStringList &arguments ) { - QFETCH(QString, subdir); - QFETCH(QStringList, arguments); - QProcess proc; proc.setEnvironment(QStringList("")); proc.start(subdir + "/" + subdir, arguments); - QVERIFY(proc.waitForFinished()); + QVERIFY2(proc.waitForFinished(), qPrintable(proc.errorString())); const QByteArray out(proc.readAllStandardOutput()); const QByteArray err(proc.readAllStandardError()); @@ -206,8 +209,8 @@ void tst_Selftests::runSubTest() #if defined(Q_OS_WIN) if(subdir != QLatin1String("exception") && subdir != QLatin1String("fetchbogus")) #endif - QVERIFY2(err.isEmpty(), err.constData()); - + if(subdir != QLatin1String("xunit")) + QVERIFY2(err.isEmpty(), err.constData()); QList<QByteArray> res = splitLines(out); QList<QByteArray> exp = expectedResult(subdir); @@ -255,8 +258,8 @@ void tst_Selftests::runSubTest() { if(output != expected && qstrcmp(QTest::currentDataTag(), "subtest") == 0) { - /* The floating point formatting differs between platforms, so let's just skip it. */ - continue; + /* The floating point formatting differs between platforms, so let's just skip it. */ + continue; } else { /* @@ -284,6 +287,14 @@ void tst_Selftests::runSubTest() } } +void tst_Selftests::runSubTest() +{ + QFETCH(QString, subdir); + QFETCH(QStringList, arguments); + + doRunSubTest(subdir, arguments); +} + void tst_Selftests::initTestCase() { m_checkXMLBlacklist.append("crashes"); // This test crashes @@ -295,6 +306,14 @@ void tst_Selftests::initTestCase() m_checkXMLBlacklist.append("differentexec"); m_checkXMLBlacklist.append("qexecstringlist"); m_checkXMLBlacklist.append("benchliboptions"); + + m_checkXunitBlacklist = m_checkXMLBlacklist; + m_checkXunitBlacklist.append("benchlibwalltime"); + m_checkXunitBlacklist.append("benchlibeventcounter"); + m_checkXunitBlacklist.append("benchlibcallgrind"); + m_checkXunitBlacklist.append("subtest"); + m_checkXunitBlacklist.append("globaldata"); + m_checkXunitBlacklist.append("warnings"); } void tst_Selftests::checkXML() const @@ -306,6 +325,45 @@ void tst_Selftests::checkXML() const return; arguments.prepend("-xml"); + arguments.prepend("-flush"); + + QProcess proc; + proc.setEnvironment(QStringList("")); + proc.start(subdir + "/" + subdir, arguments); + QVERIFY(proc.waitForFinished()); + + QByteArray out(proc.readAllStandardOutput()); + QByteArray err(proc.readAllStandardError()); + + /* Some platforms decides to output a message for uncaught exceptions. For instance, + * this is what windows platforms says: + * "This application has requested the Runtime to terminate it in an unusual way. + * Please contact the application's support team for more information." */ + if(subdir != QLatin1String("exception") && subdir != QLatin1String("fetchbogus")) + QVERIFY2(err.isEmpty(), err.constData()); + + QXmlStreamReader reader(out); + + while(!reader.atEnd()) + reader.readNext(); + + QVERIFY2(!reader.error(), qPrintable(QString("line %1, col %2: %3") + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString()) + )); +} + +void tst_Selftests::checkXunitxml() const +{ + QFETCH(QString, subdir); + QFETCH(QStringList, arguments); + + if(m_checkXunitBlacklist.contains(subdir)) + return; + + arguments.prepend("-xunitxml"); + arguments.prepend("-flush"); QProcess proc; proc.setEnvironment(QStringList("")); @@ -315,6 +373,8 @@ void tst_Selftests::checkXML() const QByteArray out(proc.readAllStandardOutput()); QByteArray err(proc.readAllStandardError()); +// qDebug()<<out; + /* Some platforms decides to output a message for uncaught exceptions. For instance, * this is what windows platforms says: * "This application has requested the Runtime to terminate it in an unusual way. @@ -327,12 +387,26 @@ void tst_Selftests::checkXML() const while(!reader.atEnd()) reader.readNext(); - QVERIFY(!reader.error()); + QVERIFY2(!reader.error(), qPrintable(QString("line %1, col %2: %3") + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString()) + )); +} + +void tst_Selftests::checkXunitxml_data() +{ + checkXML_data(); } void tst_Selftests::checkXML_data() { runSubTest_data(); + QTest::newRow("badxml 1") << "badxml" << QStringList(); + QTest::newRow("badxml 2") << "badxml" << (QStringList() << "-badstring" << "0"); + QTest::newRow("badxml 3") << "badxml" << (QStringList() << "-badstring" << "1"); + QTest::newRow("badxml 4") << "badxml" << (QStringList() << "-badstring" << "2"); + QTest::newRow("badxml 5") << "badxml" << (QStringList() << "-badstring" << "3"); } /* Parse line into the BenchmarkResult it represents. */ diff --git a/tests/auto/selftests/xunit/tst_xunit b/tests/auto/selftests/xunit/tst_xunit Binary files differnew file mode 100755 index 0000000..31d03a8 --- /dev/null +++ b/tests/auto/selftests/xunit/tst_xunit diff --git a/tests/auto/selftests/xunit/tst_xunit.cpp b/tests/auto/selftests/xunit/tst_xunit.cpp new file mode 100644 index 0000000..b42582e --- /dev/null +++ b/tests/auto/selftests/xunit/tst_xunit.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +****************************************************************************/ + +#include <QtTest/QtTest> + +class tst_Xunit : public QObject +{ + Q_OBJECT + +public: + tst_Xunit(); + +private slots: + void testFunc1(); + void testFunc2(); + void testFunc3(); + void testFunc4(); +}; + +tst_Xunit::tst_Xunit() +{ +} + +void tst_Xunit::testFunc1() +{ + QWARN("just a QWARN() !"); + QCOMPARE(1,1); +} + +void tst_Xunit::testFunc2() +{ + qDebug("a qDebug() call!"); + QCOMPARE(2, 3); +} + +void tst_Xunit::testFunc3() +{ + QSKIP("skipping this function!", SkipAll); +} + +void tst_Xunit::testFunc4() +{ + QFAIL("a forced failure!"); +} + + +QTEST_APPLESS_MAIN(tst_Xunit) +#include "tst_xunit.moc" diff --git a/tests/auto/selftests/xunit/xunit.pro b/tests/auto/selftests/xunit/xunit.pro new file mode 100644 index 0000000..81ca157 --- /dev/null +++ b/tests/auto/selftests/xunit/xunit.pro @@ -0,0 +1,15 @@ +load(qttest_p4) +SOURCES += tst_xunit.cpp + +wince*: { + addImages.sources = images/* + addImages.path = images + DEPLOYMENT += addImages + DEFINES += SRCDIR=\\\".\\\" +} else { + contains(QT_CONFIG, qt3support): QT += qt3support + DEFINES += SRCDIR=\\\"$$PWD\\\" +} + +TARGET = xunit + diff --git a/tests/auto/uiloader/baseline/css_splitter.ui b/tests/auto/uiloader/baseline/css_splitter.ui new file mode 100644 index 0000000..99dbc18 --- /dev/null +++ b/tests/auto/uiloader/baseline/css_splitter.ui @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Form</class> + <widget class="QWidget" name="Form"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>424</width> + <height>364</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <property name="styleSheet"> + <string notr="true"> QSplitter::handle:vertical { + image: url(images/splitter_horizontal.png); + } + + QSplitter::handle:horizontal { + image: url(images/splitter_vertical.png); + } + +#big_splitter::handle { background-color: blue; border: 3px dashed green; height:50px; } + + + QSplitter::handle:hover { + background-color: qlineargradient(spread:repeat, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 255), stop:0.17 rgba(255, 0, 0, 255), stop:0.18 rgba(255, 255, 255, 255), stop:0.210212 rgba(255, 255, 255, 255), stop:0.220212 rgba(0, 16, 255, 255), stop:0.279897 rgba(0, 16, 255, 255), stop:0.289897 rgba(255, 255, 255, 255), stop:0.32 rgba(255, 255, 255, 255), stop:0.33 rgba(255, 0, 0, 255), stop:1 rgba(255, 0, 0, 255)) + } + + QSplitter::handle:pressed { + background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(9, 41, 4, 255), stop:0.085 rgba(2, 79, 0, 255), stop:0.19 rgba(50, 147, 22, 255), stop:0.275 rgba(236, 191, 49, 255), stop:0.39 rgba(243, 61, 34, 255), stop:0.555 rgba(135, 81, 60, 255), stop:0.667 rgba(121, 75, 255, 255), stop:0.825 rgba(164, 255, 244, 255), stop:0.885 rgba(104, 222, 71, 255), stop:1 rgba(93, 128, 0, 255)); + }</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QSplitter" name="splitter_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QSplitter" name="big_splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QTextEdit" name="textEdit"/> + <widget class="QTextEdit" name="textEdit_5"/> + <widget class="QTextEdit" name="textEdit_4"/> + </widget> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QTextEdit" name="textEdit_2"/> + <widget class="QTextEdit" name="textEdit_3"/> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tools/designer/data/ui4.xsd b/tools/designer/data/ui4.xsd index 703e497..de4253c 100644 --- a/tools/designer/data/ui4.xsd +++ b/tools/designer/data/ui4.xsd @@ -143,6 +143,7 @@ <xs:element name="script" type="Script" minOccurs="0" /> <xs:element name="properties" type="Properties" minOccurs="0" /> <xs:element name="slots" type="Slots" minOccurs="0" /> + <xs:element name="propertyspecifications" type="PropertySpecifications" minOccurs="0" /> </xs:all> </xs:complexType> @@ -571,4 +572,16 @@ </xs:sequence> </xs:complexType> + <xs:complexType name="PropertySpecifications"> + <xs:sequence maxOccurs="unbounded"> + <xs:element name="stringpropertyspecification" type="StringPropertySpecification" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="StringPropertySpecification"> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="type" type="xs:string" use="required" /> + <xs:attribute name="notr" type="xs:string"/> + </xs:complexType> + </xs:schema> diff --git a/tools/designer/src/components/buddyeditor/buddyeditor.cpp b/tools/designer/src/components/buddyeditor/buddyeditor.cpp index f5c93fa..43348d2 100644 --- a/tools/designer/src/components/buddyeditor/buddyeditor.cpp +++ b/tools/designer/src/components/buddyeditor/buddyeditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::BuddyEditor -*/ - #include "buddyeditor.h" #include <QtDesigner/QDesignerFormWindowInterface> diff --git a/tools/designer/src/components/buddyeditor/buddyeditor_plugin.cpp b/tools/designer/src/components/buddyeditor/buddyeditor_plugin.cpp index 98fff8c..9319916 100644 --- a/tools/designer/src/components/buddyeditor/buddyeditor_plugin.cpp +++ b/tools/designer/src/components/buddyeditor/buddyeditor_plugin.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::BuddyEditorPlugin -*/ - #include <QtGui/QAction> #include "buddyeditor_plugin.h" diff --git a/tools/designer/src/components/buddyeditor/buddyeditor_tool.cpp b/tools/designer/src/components/buddyeditor/buddyeditor_tool.cpp index 68a6030..cf7f250 100644 --- a/tools/designer/src/components/buddyeditor/buddyeditor_tool.cpp +++ b/tools/designer/src/components/buddyeditor/buddyeditor_tool.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::BuddyEditorTool -*/ - #include "buddyeditor_tool.h" #include "buddyeditor.h" diff --git a/tools/designer/src/components/formeditor/embeddedoptionspage.cpp b/tools/designer/src/components/formeditor/embeddedoptionspage.cpp index bf3f3f1..1a41985 100644 --- a/tools/designer/src/components/formeditor/embeddedoptionspage.cpp +++ b/tools/designer/src/components/formeditor/embeddedoptionspage.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::EmbeddedOptionsControl -*/ - #include "embeddedoptionspage.h" #include "deviceprofiledialog.h" #include "widgetfactory_p.h" diff --git a/tools/designer/src/components/formeditor/formwindow.cpp b/tools/designer/src/components/formeditor/formwindow.cpp index 48efcde..07d785a 100644 --- a/tools/designer/src/components/formeditor/formwindow.cpp +++ b/tools/designer/src/components/formeditor/formwindow.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::FormWindow -*/ - #include "formwindow.h" #include "formeditor.h" #include "formwindow_dnditem.h" diff --git a/tools/designer/src/components/formeditor/formwindowcursor.cpp b/tools/designer/src/components/formeditor/formwindowcursor.cpp index 5b4aee1..fb30885 100644 --- a/tools/designer/src/components/formeditor/formwindowcursor.cpp +++ b/tools/designer/src/components/formeditor/formwindowcursor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::FormWindowCursor -*/ - #include "formwindowcursor.h" #include "formwindow.h" diff --git a/tools/designer/src/components/formeditor/formwindowmanager.cpp b/tools/designer/src/components/formeditor/formwindowmanager.cpp index 69fb4a3..ea8d128 100644 --- a/tools/designer/src/components/formeditor/formwindowmanager.cpp +++ b/tools/designer/src/components/formeditor/formwindowmanager.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::FormWindowManager -*/ - // components/formeditor #include "formwindowmanager.h" #include "formwindow_dnditem.h" diff --git a/tools/designer/src/components/formeditor/tool_widgeteditor.cpp b/tools/designer/src/components/formeditor/tool_widgeteditor.cpp index 3a59543..aed7e80 100644 --- a/tools/designer/src/components/formeditor/tool_widgeteditor.cpp +++ b/tools/designer/src/components/formeditor/tool_widgeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::WidgetEditorTool -*/ - #include "tool_widgeteditor.h" #include "formwindow.h" diff --git a/tools/designer/src/components/objectinspector/objectinspector.cpp b/tools/designer/src/components/objectinspector/objectinspector.cpp index 4e515be..cb90d2a 100644 --- a/tools/designer/src/components/objectinspector/objectinspector.cpp +++ b/tools/designer/src/components/objectinspector/objectinspector.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ObjectInspector -*/ - #include "objectinspector.h" #include "objectinspectormodel_p.h" #include "formwindow.h" diff --git a/tools/designer/src/components/objectinspector/objectinspectormodel.cpp b/tools/designer/src/components/objectinspector/objectinspectormodel.cpp index bc1ac0c..1e20ec3 100644 --- a/tools/designer/src/components/objectinspector/objectinspectormodel.cpp +++ b/tools/designer/src/components/objectinspector/objectinspectormodel.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ObjectInspector -*/ - #include "objectinspectormodel_p.h" #include <qlayout_widget_p.h> diff --git a/tools/designer/src/components/propertyeditor/designerpropertymanager.cpp b/tools/designer/src/components/propertyeditor/designerpropertymanager.cpp index 1b267aa..2460ee8 100644 --- a/tools/designer/src/components/propertyeditor/designerpropertymanager.cpp +++ b/tools/designer/src/components/propertyeditor/designerpropertymanager.cpp @@ -2455,6 +2455,9 @@ void DesignerEditorFactory::slotStringTextChanged(const QString &value) if (val.userType() == DesignerPropertyManager::designerStringTypeId()) { PropertySheetStringValue strVal = qVariantValue<PropertySheetStringValue>(val); strVal.setValue(value); + // Disable translation if no translation subproperties exist. + if (varProp->subProperties().empty()) + strVal.setTranslatable(false); val = qVariantFromValue(strVal); } else { val = QVariant(value); diff --git a/tools/designer/src/components/propertyeditor/paletteeditor.cpp b/tools/designer/src/components/propertyeditor/paletteeditor.cpp index b0dc82b..4c60941 100644 --- a/tools/designer/src/components/propertyeditor/paletteeditor.cpp +++ b/tools/designer/src/components/propertyeditor/paletteeditor.cpp @@ -39,13 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PaletteEditor -*/ -/* -TRANSLATOR qdesigner_internal::PaletteModel -*/ - #include "paletteeditor.h" #include <iconloader_p.h> diff --git a/tools/designer/src/components/propertyeditor/paletteeditorbutton.cpp b/tools/designer/src/components/propertyeditor/paletteeditorbutton.cpp index d7a76b9..c425e18 100644 --- a/tools/designer/src/components/propertyeditor/paletteeditorbutton.cpp +++ b/tools/designer/src/components/propertyeditor/paletteeditorbutton.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PaletteEditorButton -*/ - #include "paletteeditorbutton.h" #include "paletteeditor.h" diff --git a/tools/designer/src/components/propertyeditor/previewframe.cpp b/tools/designer/src/components/propertyeditor/previewframe.cpp index 95d533d..e94ace0 100644 --- a/tools/designer/src/components/propertyeditor/previewframe.cpp +++ b/tools/designer/src/components/propertyeditor/previewframe.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PreviewWorkspace -*/ - #include "previewframe.h" #include "previewwidget.h" diff --git a/tools/designer/src/components/propertyeditor/propertyeditor.cpp b/tools/designer/src/components/propertyeditor/propertyeditor.cpp index 63b57d8..ead404b 100644 --- a/tools/designer/src/components/propertyeditor/propertyeditor.cpp +++ b/tools/designer/src/components/propertyeditor/propertyeditor.cpp @@ -137,13 +137,8 @@ void PropertyEditor::setupStringProperty(QtVariantProperty *property, bool isMai const bool hasComment = params.second; property->setAttribute(m_strings.m_validationModeAttribute, params.first); // assuming comment cannot appear or disappear for the same property in different object instance - if (!hasComment) { - QList<QtProperty *> commentProperties = property->subProperties(); - if (commentProperties.count() > 0) - delete commentProperties.at(0); - if (commentProperties.count() > 1) - delete commentProperties.at(1); - } + if (!hasComment) + qDeleteAll(property->subProperties()); } void PropertyEditor::setupPaletteProperty(QtVariantProperty *property) diff --git a/tools/designer/src/components/propertyeditor/stringlisteditorbutton.cpp b/tools/designer/src/components/propertyeditor/stringlisteditorbutton.cpp index 440015c..32a8c78 100644 --- a/tools/designer/src/components/propertyeditor/stringlisteditorbutton.cpp +++ b/tools/designer/src/components/propertyeditor/stringlisteditorbutton.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::StringListEditorButton -*/ - #include "stringlisteditorbutton.h" #include "stringlisteditor.h" diff --git a/tools/designer/src/components/signalsloteditor/signalsloteditor_plugin.cpp b/tools/designer/src/components/signalsloteditor/signalsloteditor_plugin.cpp index 311c843..a6f4969 100644 --- a/tools/designer/src/components/signalsloteditor/signalsloteditor_plugin.cpp +++ b/tools/designer/src/components/signalsloteditor/signalsloteditor_plugin.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::SignalSlotEditorPlugin -*/ - #include "signalsloteditor_plugin.h" #include "signalsloteditor_tool.h" diff --git a/tools/designer/src/components/signalsloteditor/signalsloteditor_tool.cpp b/tools/designer/src/components/signalsloteditor/signalsloteditor_tool.cpp index 973105c..311ec36 100644 --- a/tools/designer/src/components/signalsloteditor/signalsloteditor_tool.cpp +++ b/tools/designer/src/components/signalsloteditor/signalsloteditor_tool.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::SignalSlotEditorTool -*/ - #include "signalsloteditor_tool.h" #include "signalsloteditor.h" #include "ui4_p.h" diff --git a/tools/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp b/tools/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp index dc6b3c3..c916af8 100644 --- a/tools/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp +++ b/tools/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ConnectionModel -*/ - #include "signalsloteditorwindow.h" #include "signalsloteditor_p.h" #include "signalsloteditor.h" diff --git a/tools/designer/src/components/tabordereditor/tabordereditor_plugin.cpp b/tools/designer/src/components/tabordereditor/tabordereditor_plugin.cpp index 46bf378..abe882b 100644 --- a/tools/designer/src/components/tabordereditor/tabordereditor_plugin.cpp +++ b/tools/designer/src/components/tabordereditor/tabordereditor_plugin.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TabOrderEditorPlugin -*/ - #include <QtGui/QAction> #include "tabordereditor_plugin.h" diff --git a/tools/designer/src/components/tabordereditor/tabordereditor_tool.cpp b/tools/designer/src/components/tabordereditor/tabordereditor_tool.cpp index 4f291a1..75304ee 100644 --- a/tools/designer/src/components/tabordereditor/tabordereditor_tool.cpp +++ b/tools/designer/src/components/tabordereditor/tabordereditor_tool.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TabOrderEditorTool -*/ - #include "tabordereditor_tool.h" #include "tabordereditor.h" diff --git a/tools/designer/src/components/taskmenu/button_taskmenu.cpp b/tools/designer/src/components/taskmenu/button_taskmenu.cpp index ced32ce..c2816a2 100644 --- a/tools/designer/src/components/taskmenu/button_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/button_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ButtonTaskMenu -*/ - #include "button_taskmenu.h" #include "inplace_editor.h" #include <qdesigner_formwindowcommand_p.h> diff --git a/tools/designer/src/components/taskmenu/combobox_taskmenu.cpp b/tools/designer/src/components/taskmenu/combobox_taskmenu.cpp index e3364cc..908cb61 100644 --- a/tools/designer/src/components/taskmenu/combobox_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/combobox_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ComboBoxTaskMenu -*/ - #include "combobox_taskmenu.h" #include "listwidgeteditor.h" #include "qdesigner_utils_p.h" diff --git a/tools/designer/src/components/taskmenu/containerwidget_taskmenu.cpp b/tools/designer/src/components/taskmenu/containerwidget_taskmenu.cpp index b182ddf..c6673e3 100644 --- a/tools/designer/src/components/taskmenu/containerwidget_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/containerwidget_taskmenu.cpp @@ -39,13 +39,8 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ContainerWidgetTaskMenu -*/ - #include "containerwidget_taskmenu.h" - #include <QtDesigner/QDesignerFormEditorInterface> #include <QtDesigner/QDesignerFormWindowInterface> #include <QtDesigner/QExtensionManager> diff --git a/tools/designer/src/components/taskmenu/groupbox_taskmenu.cpp b/tools/designer/src/components/taskmenu/groupbox_taskmenu.cpp index bb97342..18b6612 100644 --- a/tools/designer/src/components/taskmenu/groupbox_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/groupbox_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::GroupBoxTaskMenu -*/ - #include "groupbox_taskmenu.h" #include "inplace_editor.h" diff --git a/tools/designer/src/components/taskmenu/label_taskmenu.cpp b/tools/designer/src/components/taskmenu/label_taskmenu.cpp index 77e5ed9..f85df33 100644 --- a/tools/designer/src/components/taskmenu/label_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/label_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::LabelTaskMenu -*/ - #include "label_taskmenu.h" #include "inplace_editor.h" diff --git a/tools/designer/src/components/taskmenu/layouttaskmenu.cpp b/tools/designer/src/components/taskmenu/layouttaskmenu.cpp index 4a3aeec..0bfe607 100644 --- a/tools/designer/src/components/taskmenu/layouttaskmenu.cpp +++ b/tools/designer/src/components/taskmenu/layouttaskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::LayoutWidgetTaskMenu -*/ - #include "layouttaskmenu.h" #include <formlayoutmenu_p.h> #include <morphmenu_p.h> diff --git a/tools/designer/src/components/taskmenu/lineedit_taskmenu.cpp b/tools/designer/src/components/taskmenu/lineedit_taskmenu.cpp index baf4785..a88246a 100644 --- a/tools/designer/src/components/taskmenu/lineedit_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/lineedit_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::LineEditTaskMenu -*/ - #include "lineedit_taskmenu.h" #include "inplace_editor.h" diff --git a/tools/designer/src/components/taskmenu/listwidget_taskmenu.cpp b/tools/designer/src/components/taskmenu/listwidget_taskmenu.cpp index 1a9ba36..10e004d 100644 --- a/tools/designer/src/components/taskmenu/listwidget_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/listwidget_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ListWidgetTaskMenu -*/ - #include "listwidget_taskmenu.h" #include "listwidgeteditor.h" #include "qdesigner_utils_p.h" diff --git a/tools/designer/src/components/taskmenu/listwidgeteditor.cpp b/tools/designer/src/components/taskmenu/listwidgeteditor.cpp index 2a8de52..4c3ede9 100644 --- a/tools/designer/src/components/taskmenu/listwidgeteditor.cpp +++ b/tools/designer/src/components/taskmenu/listwidgeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ListWidgetEditor -*/ - #include "listwidgeteditor.h" #include <designerpropertymanager.h> #include <abstractformbuilder.h> diff --git a/tools/designer/src/components/taskmenu/menutaskmenu.cpp b/tools/designer/src/components/taskmenu/menutaskmenu.cpp index 52320ac..093a1fe 100644 --- a/tools/designer/src/components/taskmenu/menutaskmenu.cpp +++ b/tools/designer/src/components/taskmenu/menutaskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::MenuTaskMenu -*/ - #include "menutaskmenu.h" #include <promotiontaskmenu_p.h> diff --git a/tools/designer/src/components/taskmenu/tablewidget_taskmenu.cpp b/tools/designer/src/components/taskmenu/tablewidget_taskmenu.cpp index 5ef1f9c..dce9f97 100644 --- a/tools/designer/src/components/taskmenu/tablewidget_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/tablewidget_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TableWidgetTaskMenu -*/ - #include "tablewidget_taskmenu.h" #include "tablewidgeteditor.h" diff --git a/tools/designer/src/components/taskmenu/tablewidgeteditor.cpp b/tools/designer/src/components/taskmenu/tablewidgeteditor.cpp index 0863847..be1f89a 100644 --- a/tools/designer/src/components/taskmenu/tablewidgeteditor.cpp +++ b/tools/designer/src/components/taskmenu/tablewidgeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TableWidgetEditor -*/ - #include "tablewidgeteditor.h" #include <abstractformbuilder.h> #include <iconloader_p.h> diff --git a/tools/designer/src/components/taskmenu/textedit_taskmenu.cpp b/tools/designer/src/components/taskmenu/textedit_taskmenu.cpp index feaec2c..a9a2a96 100644 --- a/tools/designer/src/components/taskmenu/textedit_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/textedit_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TextEditTaskMenu -*/ - #include "textedit_taskmenu.h" #include <QtDesigner/QDesignerFormWindowInterface> diff --git a/tools/designer/src/components/taskmenu/toolbar_taskmenu.cpp b/tools/designer/src/components/taskmenu/toolbar_taskmenu.cpp index a15cd8a..d841b87 100644 --- a/tools/designer/src/components/taskmenu/toolbar_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/toolbar_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ToolBarTaskMenu -*/ - #include "toolbar_taskmenu.h" #include "qdesigner_toolbar_p.h" diff --git a/tools/designer/src/components/taskmenu/treewidget_taskmenu.cpp b/tools/designer/src/components/taskmenu/treewidget_taskmenu.cpp index b1c8c28..34d883a 100644 --- a/tools/designer/src/components/taskmenu/treewidget_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/treewidget_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TreeWidgetTaskMenu -*/ - #include "treewidget_taskmenu.h" #include "treewidgeteditor.h" diff --git a/tools/designer/src/components/taskmenu/treewidgeteditor.cpp b/tools/designer/src/components/taskmenu/treewidgeteditor.cpp index faf00f2..e97ebde 100644 --- a/tools/designer/src/components/taskmenu/treewidgeteditor.cpp +++ b/tools/designer/src/components/taskmenu/treewidgeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TreeWidgetEditor -*/ - #include "treewidgeteditor.h" #include <formwindowbase_p.h> #include <iconloader_p.h> diff --git a/tools/designer/src/lib/sdk/abstractformeditor.cpp b/tools/designer/src/lib/sdk/abstractformeditor.cpp index 17e93e7..e6debd5 100644 --- a/tools/designer/src/lib/sdk/abstractformeditor.cpp +++ b/tools/designer/src/lib/sdk/abstractformeditor.cpp @@ -66,9 +66,22 @@ #include <grid_p.h> #include <QtDesigner/QDesignerPromotionInterface> +// Must be done outside of the Qt namespace static void initResources() { Q_INIT_RESOURCE(shared); + Q_INIT_RESOURCE(ClamshellPhone); + Q_INIT_RESOURCE(PDAPhone); + Q_INIT_RESOURCE(pda); + Q_INIT_RESOURCE(PortableMedia); + Q_INIT_RESOURCE(S60_nHD_Touchscreen); + Q_INIT_RESOURCE(S60_QVGA_Candybar); + Q_INIT_RESOURCE(SmartPhone2); + Q_INIT_RESOURCE(SmartPhone); + Q_INIT_RESOURCE(SmartPhoneWithButtons); + Q_INIT_RESOURCE(TouchscreenPhone); + Q_INIT_RESOURCE(Trolltech_Keypad); + Q_INIT_RESOURCE(Trolltech_Touchscreen); } QT_BEGIN_NAMESPACE diff --git a/tools/designer/src/lib/shared/actioneditor.cpp b/tools/designer/src/lib/shared/actioneditor.cpp index 6a66442..d29643d 100644 --- a/tools/designer/src/lib/shared/actioneditor.cpp +++ b/tools/designer/src/lib/shared/actioneditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ActionEditor -*/ - #include "actioneditor_p.h" #include "filterwidget_p.h" #include "actionrepository_p.h" diff --git a/tools/designer/src/lib/shared/codedialog.cpp b/tools/designer/src/lib/shared/codedialog.cpp index 5a2db33..0b18f5e 100644 --- a/tools/designer/src/lib/shared/codedialog.cpp +++ b/tools/designer/src/lib/shared/codedialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::CodeDialog -*/ - #include "codedialog_p.h" #include "qdesigner_utils_p.h" #include "iconloader_p.h" diff --git a/tools/designer/src/lib/shared/csshighlighter.cpp b/tools/designer/src/lib/shared/csshighlighter.cpp index d5e045b..f7144c5 100644 --- a/tools/designer/src/lib/shared/csshighlighter.cpp +++ b/tools/designer/src/lib/shared/csshighlighter.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::StyleSheetEditorDialog -*/ - #include "csshighlighter_p.h" QT_BEGIN_NAMESPACE diff --git a/tools/designer/src/lib/shared/formwindowbase.cpp b/tools/designer/src/lib/shared/formwindowbase.cpp index 3e7e17b..5be907b 100644 --- a/tools/designer/src/lib/shared/formwindowbase.cpp +++ b/tools/designer/src/lib/shared/formwindowbase.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::FormWindowBase -*/ - #include "formwindowbase_p.h" #include "connectionedit_p.h" #include "qdesigner_command_p.h" diff --git a/tools/designer/src/lib/shared/orderdialog.cpp b/tools/designer/src/lib/shared/orderdialog.cpp index 3f14ca6..0df3866 100644 --- a/tools/designer/src/lib/shared/orderdialog.cpp +++ b/tools/designer/src/lib/shared/orderdialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::OrderDialog -*/ - #include "orderdialog_p.h" #include "iconloader_p.h" #include "ui_orderdialog.h" diff --git a/tools/designer/src/lib/shared/plaintexteditor.cpp b/tools/designer/src/lib/shared/plaintexteditor.cpp index ce5cd5b..f30df3d 100644 --- a/tools/designer/src/lib/shared/plaintexteditor.cpp +++ b/tools/designer/src/lib/shared/plaintexteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PlainTextEditorDialog -*/ - #include "plaintexteditor_p.h" #include "abstractsettings_p.h" diff --git a/tools/designer/src/lib/shared/pluginmanager.cpp b/tools/designer/src/lib/shared/pluginmanager.cpp index 9dc8c7b..100a088 100644 --- a/tools/designer/src/lib/shared/pluginmanager.cpp +++ b/tools/designer/src/lib/shared/pluginmanager.cpp @@ -16,6 +16,8 @@ ** 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 +What usually causes this mess is specifying a Z-order (raise/lower) widget or changing the tab order and then deleting the widget. Designer did not delete the widget from those settings, causing uic to report this when applying them. I believe we fixed that 4.5. + ** 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. @@ -72,6 +74,11 @@ static const char *classAttributeC = "class"; static const char *customwidgetElementC = "customwidget"; static const char *extendsElementC = "extends"; static const char *addPageMethodC = "addpagemethod"; +static const char *propertySpecsC = "propertyspecifications"; +static const char *stringPropertySpecC = "stringpropertyspecification"; +static const char *stringPropertyNameAttrC = "name"; +static const char *stringPropertyTypeAttrC = "type"; +static const char *stringPropertyNoTrAttrC = "notr"; static const char *jambiLanguageC = "jambi"; enum { debugPluginManager = 0 }; @@ -141,6 +148,10 @@ static inline QString getDesignerLanguage(QDesignerFormEditorInterface *core) class QDesignerCustomWidgetSharedData : public QSharedData { public: + // Type of a string property + typedef QPair<qdesigner_internal::TextPropertyValidationMode, bool> StringPropertyType; + typedef QHash<QString, StringPropertyType> StringPropertyTypeMap; + explicit QDesignerCustomWidgetSharedData(const QString &thePluginPath) : pluginPath(thePluginPath) {} void clearXML(); @@ -152,6 +163,7 @@ public: QString xmlAddPageMethod; QString xmlExtends; + StringPropertyTypeMap xmlStringPropertyTypeMap; }; void QDesignerCustomWidgetSharedData::clearXML() @@ -161,6 +173,7 @@ void QDesignerCustomWidgetSharedData::clearXML() xmlLanguage.clear(); xmlAddPageMethod.clear(); xmlExtends.clear(); + xmlStringPropertyTypeMap.clear(); } // ---------------- QDesignerCustomWidgetData @@ -220,6 +233,17 @@ QString QDesignerCustomWidgetData::pluginPath() const return m_d->pluginPath; } +bool QDesignerCustomWidgetData::xmlStringPropertyType(const QString &name, StringPropertyType *type) const +{ + QDesignerCustomWidgetSharedData::StringPropertyTypeMap::const_iterator it = m_d->xmlStringPropertyTypeMap.constFind(name); + if (it == m_d->xmlStringPropertyTypeMap.constEnd()) { + *type = StringPropertyType(qdesigner_internal::ValidationRichText, true); + return false; + } + *type = it.value(); + return true; +} + // Wind a QXmlStreamReader until it finds an element. Returns index or one of FindResult enum FindResult { FindError = -2, ElementNotFound = -1 }; @@ -249,6 +273,82 @@ static inline QString msgXmlError(const QString &name, const QString &errorMessa return QDesignerPluginManager::tr("An XML error was encountered when parsing the XML of the custom widget %1: %2").arg(name, errorMessage); } +static inline QString msgAttributeMissing(const QString &name) +{ + return QDesignerPluginManager::tr("A required attribute ('%1') is missing.").arg(name); +} + +static qdesigner_internal::TextPropertyValidationMode typeStringToType(const QString &v, bool *ok) +{ + *ok = true; + if (v == QLatin1String("multiline")) + return qdesigner_internal::ValidationMultiLine; + if (v == QLatin1String("richtext")) + return qdesigner_internal::ValidationRichText; + if (v == QLatin1String("stylesheet")) + return qdesigner_internal::ValidationStyleSheet; + if (v == QLatin1String("singleline")) + return qdesigner_internal::ValidationSingleLine; + if (v == QLatin1String("objectname")) + return qdesigner_internal::ValidationObjectName; + if (v == QLatin1String("objectnamescope")) + return qdesigner_internal::ValidationObjectNameScope; + if (v == QLatin1String("url")) + return qdesigner_internal::ValidationURL; + *ok = false; + return qdesigner_internal::ValidationRichText; +} + +static bool parsePropertySpecs(QXmlStreamReader &sr, + QDesignerCustomWidgetSharedData::StringPropertyTypeMap *rc, + QString *errorMessage) +{ + const QString propertySpecs = QLatin1String(propertySpecsC); + const QString stringPropertySpec = QLatin1String(stringPropertySpecC); + const QString stringPropertyTypeAttr = QLatin1String(stringPropertyTypeAttrC); + const QString stringPropertyNoTrAttr = QLatin1String(stringPropertyNoTrAttrC); + const QString stringPropertyNameAttr = QLatin1String(stringPropertyNameAttrC); + + while (!sr.atEnd()) { + switch(sr.readNext()) { + case QXmlStreamReader::StartElement: { + if (sr.name() != stringPropertySpec) { + *errorMessage = QDesignerPluginManager::tr("An invalid property specification ('%1') was encountered. Supported types: %2").arg(sr.name().toString(), stringPropertySpec); + return false; + } + const QXmlStreamAttributes atts = sr.attributes(); + const QString name = atts.value(stringPropertyNameAttr).toString(); + const QString type = atts.value(stringPropertyTypeAttr).toString(); + const QString notrS = atts.value(stringPropertyNoTrAttr).toString(); //Optional + + if (type.isEmpty()) { + *errorMessage = msgAttributeMissing(stringPropertyTypeAttr); + return false; + } + if (name.isEmpty()) { + *errorMessage = msgAttributeMissing(stringPropertyNameAttr); + return false; + } + bool typeOk; + const bool noTr = notrS == QLatin1String("true") || notrS == QLatin1String("1"); + QDesignerCustomWidgetSharedData::StringPropertyType v(typeStringToType(type, &typeOk), !noTr); + if (!typeOk) { + *errorMessage = QDesignerPluginManager::tr("'%1' is not a valid string property specification!").arg(type); + return false; + } + rc->insert(name, v); + } + break; + case QXmlStreamReader::EndElement: // Outer </stringproperties> + if (sr.name() == propertySpecs) + return true; + default: + break; + } + } + return true; +} + QDesignerCustomWidgetData::ParseResult QDesignerCustomWidgetData::parseXml(const QString &xml, const QString &name, QString *errorMessage) { @@ -311,10 +411,11 @@ QDesignerCustomWidgetData::ParseResult default: break; } - // Find <extends>, <addPageMethod> + // Find <extends>, <addPageMethod>, <stringproperties> elements.clear(); elements.push_back(QLatin1String(extendsElementC)); elements.push_back(QLatin1String(addPageMethodC)); + elements.push_back(QLatin1String(propertySpecsC)); while (true) { switch (findElement(elements, sr)) { case FindError: @@ -336,6 +437,12 @@ QDesignerCustomWidgetData::ParseResult return ParseError; } break; + case 2: // <stringproperties> + if (!parsePropertySpecs(sr, &m_d->xmlStringPropertyTypeMap, errorMessage)) { + *errorMessage = msgXmlError(name, *errorMessage); + return ParseError; + } + break; } } return rc; @@ -345,6 +452,8 @@ QDesignerCustomWidgetData::ParseResult class QDesignerPluginManagerPrivate { public: + typedef QPair<QString, QString> ClassNamePropertyNameKey; + QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core); void clearCustomWidgets(); @@ -562,7 +671,7 @@ bool QDesignerPluginManager::registerNewPlugins() const int before = m_d->m_registeredPlugins.size(); foreach (const QString &path, m_d->m_pluginPaths) - registerPath(path); + registerPath(path); const bool newPluginsFound = m_d->m_registeredPlugins.size() > before; // We force a re-initialize as Jambi collection might return // different widget lists when switching projects. @@ -654,6 +763,15 @@ QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(QDesignerCust return m_d->m_customWidgetData.at(index); } +QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(const QString &name) const +{ + const int count = m_d->m_customWidgets.size(); + for (int i = 0; i < count; i++) + if (m_d->m_customWidgets.at(i)->name() == name) + return m_d->m_customWidgetData.at(i); + return QDesignerCustomWidgetData(); +} + QObjectList QDesignerPluginManager::instances() const { QStringList plugins = registeredPlugins(); diff --git a/tools/designer/src/lib/shared/pluginmanager_p.h b/tools/designer/src/lib/shared/pluginmanager_p.h index e374f42..f73bc74 100644 --- a/tools/designer/src/lib/shared/pluginmanager_p.h +++ b/tools/designer/src/lib/shared/pluginmanager_p.h @@ -54,9 +54,11 @@ #define PLUGINMANAGER_H #include "shared_global_p.h" +#include "shared_enums_p.h" #include <QtCore/QSharedDataPointer> #include <QtCore/QMap> +#include <QtCore/QPair> #include <QtCore/QStringList> QT_BEGIN_NAMESPACE @@ -70,6 +72,9 @@ class QDesignerCustomWidgetSharedData; /* Information contained in the Dom XML of a custom widget. */ class QDESIGNER_SHARED_EXPORT QDesignerCustomWidgetData { public: + // StringPropertyType: validation mode and translatable flag. + typedef QPair<qdesigner_internal::TextPropertyValidationMode, bool> StringPropertyType; + explicit QDesignerCustomWidgetData(const QString &pluginPath = QString()); enum ParseResult { ParseOk, ParseWarning, ParseError }; @@ -93,6 +98,8 @@ public: QString xmlExtends() const; // Optional. The name to be used in the widget box. QString xmlDisplayName() const; + // Type of a string property + bool xmlStringPropertyType(const QString &name, StringPropertyType *type) const; private: QSharedDataPointer<QDesignerCustomWidgetSharedData> m_d; @@ -128,6 +135,7 @@ public: CustomWidgetList registeredCustomWidgets() const; QDesignerCustomWidgetData customWidgetData(QDesignerCustomWidgetInterface *w) const; + QDesignerCustomWidgetData customWidgetData(const QString &className) const; bool registerNewPlugins(); diff --git a/tools/designer/src/lib/shared/previewconfigurationwidget.cpp b/tools/designer/src/lib/shared/previewconfigurationwidget.cpp index 2cf362f..2b45759 100644 --- a/tools/designer/src/lib/shared/previewconfigurationwidget.cpp +++ b/tools/designer/src/lib/shared/previewconfigurationwidget.cpp @@ -65,43 +65,27 @@ #include <QtCore/QFileInfo> #include <QtCore/QSharedData> -// #define DEFAULT_SKINS_FROM_RESOURCE -#ifdef DEFAULT_SKINS_FROM_RESOURCE -QT_BEGIN_NAMESPACE + static const char *skinResourcePathC = ":/skins/"; -QT_END_NAMESPACE -#else -# include <QtCore/QLibraryInfo> -#endif QT_BEGIN_NAMESPACE static const char *skinExtensionC = "skin"; -namespace { - // Pair of skin name, path - typedef QPair<QString, QString> SkinNamePath; - typedef QList<SkinNamePath> Skins; - enum { SkinComboNoneIndex = 0 }; -} +// Pair of skin name, path +typedef QPair<QString, QString> SkinNamePath; +typedef QList<SkinNamePath> Skins; +enum { SkinComboNoneIndex = 0 }; // find default skins (resources) static const Skins &defaultSkins() { static Skins rc; if (rc.empty()) { -#ifdef DEFAULT_SKINS_FROM_RESOURCE const QString skinPath = QLatin1String(skinResourcePathC); -#else - QString skinPath = QLibraryInfo::location(QLibraryInfo::PrefixPath); - skinPath += QDir::separator(); - skinPath += QLatin1String("tools"); - skinPath += QDir::separator(); - skinPath += QLatin1String("qvfb"); -#endif QString pattern = QLatin1String("*."); pattern += QLatin1String(skinExtensionC); const QDir dir(skinPath, pattern); - const QFileInfoList list = dir.entryInfoList(); + const QFileInfoList list = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot, QDir::Name); if (list.empty()) return rc; const QFileInfoList::const_iterator lcend = list.constEnd(); diff --git a/tools/designer/src/lib/shared/previewmanager.cpp b/tools/designer/src/lib/shared/previewmanager.cpp index 8ed1772..ac16741 100644 --- a/tools/designer/src/lib/shared/previewmanager.cpp +++ b/tools/designer/src/lib/shared/previewmanager.cpp @@ -67,6 +67,7 @@ #include <QtGui/QAction> #include <QtGui/QActionGroup> #include <QtGui/QCursor> +#include <QtGui/QMatrix> #include <QtCore/QMap> #include <QtCore/QDebug> @@ -149,11 +150,16 @@ QGraphicsProxyWidget *DesignerZoomWidget::createProxyWidget(QGraphicsItem *paren return new DesignerZoomProxyWidget(parent, wFlags); } -// --------- Widget Preview skin: Forward the key events to the window +// PreviewDeviceSkin: Forwards the key events to the window and +// provides context menu with rotation options. Derived class +// can apply additional transformations to the skin. + class PreviewDeviceSkin : public DeviceSkin { Q_OBJECT public: + enum Direction { DirectionUp, DirectionLeft, DirectionRight }; + explicit PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent); virtual void setPreview(QWidget *w); QSize screenSize() const { return m_screenSize; } @@ -164,15 +170,36 @@ private slots: void slotPopupMenu(); protected: - virtual void populateContextMenu(QMenu *m); + virtual void populateContextMenu(QMenu *) {} + +private slots: + void slotDirection(QAction *); + +protected: + // Fit the widget in case the orientation changes (transposing screensize) + virtual void fitWidget(const QSize &size); + // Calculate the complete transformation for the skin + // (base class implementation provides rotation). + virtual QMatrix skinTransform() const; private: const QSize m_screenSize; + Direction m_direction; + + QAction *m_directionUpAction; + QAction *m_directionLeftAction; + QAction *m_directionRightAction; + QAction *m_closeAction; }; PreviewDeviceSkin::PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent) : - DeviceSkin(parameters, parent), - m_screenSize(parameters.screenSize()) + DeviceSkin(parameters, parent), + m_screenSize(parameters.screenSize()), + m_direction(DirectionUp), + m_directionUpAction(0), + m_directionLeftAction(0), + m_directionRightAction(0), + m_closeAction(0) { connect(this, SIGNAL(skinKeyPressEvent(int,QString,bool)), this, SLOT(slotSkinKeyPressEvent(int,QString,bool))); @@ -195,7 +222,6 @@ void PreviewDeviceSkin::slotSkinKeyPressEvent(int code, const QString& text, boo QKeyEvent e(QEvent::KeyPress,code,0,text,autorep); QApplication::sendEvent(focusWidget, &e); } - } void PreviewDeviceSkin::slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep) @@ -206,16 +232,83 @@ void PreviewDeviceSkin::slotSkinKeyReleaseEvent(int code, const QString& text, b } } +// Create a checkable action with integer data and +// set it checked if it matches the currentState. +static inline QAction + *createCheckableActionIntData(const QString &label, + int actionValue, int currentState, + QActionGroup *ag, QObject *parent) +{ + QAction *a = new QAction(label, parent); + a->setData(actionValue); + a->setCheckable(true); + if (actionValue == currentState) + a->setChecked(true); + ag->addAction(a); + return a; +} + void PreviewDeviceSkin::slotPopupMenu() { QMenu menu(this); + // Create actions + if (!m_directionUpAction) { + QActionGroup *directionGroup = new QActionGroup(this); + connect(directionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotDirection(QAction*))); + directionGroup->setExclusive(true); + m_directionUpAction = createCheckableActionIntData(tr("&Portrait"), DirectionUp, m_direction, directionGroup, this); + m_directionLeftAction = createCheckableActionIntData(tr("Landscape (&CCW)"), DirectionLeft, m_direction, directionGroup, this); + m_directionRightAction = createCheckableActionIntData(tr("&Landscape (CW)"), DirectionRight, m_direction, directionGroup, this); + m_closeAction = new QAction(tr("&Close"), this); + connect(m_closeAction, SIGNAL(triggered()), parentWidget(), SLOT(close())); + } + menu.addAction(m_directionUpAction); + menu.addAction(m_directionLeftAction); + menu.addAction(m_directionRightAction); + menu.addSeparator(); populateContextMenu(&menu); + menu.addAction(m_closeAction); menu.exec(QCursor::pos()); } -void PreviewDeviceSkin::populateContextMenu(QMenu *menu) +void PreviewDeviceSkin::slotDirection(QAction *a) +{ + const Direction newDirection = static_cast<Direction>(a->data().toInt()); + if (m_direction == newDirection) + return; + const Qt::Orientation newOrientation = newDirection == DirectionUp ? Qt::Vertical : Qt::Horizontal; + const Qt::Orientation oldOrientation = m_direction == DirectionUp ? Qt::Vertical : Qt::Horizontal; + m_direction = newDirection; + QApplication::setOverrideCursor(Qt::WaitCursor); + if (oldOrientation != newOrientation) { + QSize size = screenSize(); + if (newOrientation == Qt::Horizontal) + size.transpose(); + fitWidget(size); + } + setTransform(skinTransform()); + QApplication::restoreOverrideCursor(); +} + +void PreviewDeviceSkin::fitWidget(const QSize &size) +{ + view()->setFixedSize(size); +} + +QMatrix PreviewDeviceSkin::skinTransform() const { - connect(menu->addAction(tr("&Close")), SIGNAL(triggered()), parentWidget(), SLOT(close())); + QMatrix newTransform; + switch (m_direction) { + case DirectionUp: + break; + case DirectionLeft: + newTransform.rotate(270.0); + break; + case DirectionRight: + newTransform.rotate(90.0); + break; + } + return newTransform; } // ------------ PreviewConfigurationPrivate @@ -257,16 +350,20 @@ signals: void zoomPercentChanged(int); protected: - virtual void populateContextMenu(QMenu *m); + virtual void populateContextMenu(QMenu *m); + virtual QMatrix skinTransform() const; + virtual void fitWidget(const QSize &size); private: ZoomMenu *m_zoomMenu; + QAction *m_zoomSubMenuAction; ZoomWidget *m_zoomWidget; }; ZoomablePreviewDeviceSkin::ZoomablePreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent) : PreviewDeviceSkin(parameters, parent), m_zoomMenu(new ZoomMenu(this)), + m_zoomSubMenuAction(0), m_zoomWidget(new DesignerZoomWidget) { connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoomPercent(int))); @@ -279,10 +376,20 @@ ZoomablePreviewDeviceSkin::ZoomablePreviewDeviceSkin(const DeviceSkinParameters setView(m_zoomWidget); } -void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget) +static inline qreal zoomFactor(int percent) { - formWidget->setFixedSize(screenSize()); + return qreal(percent) / 100.0; +} + +static inline QSize scaleSize(int zoomPercent, const QSize &size) +{ + return zoomPercent == 100 ? size : (QSizeF(size) * zoomFactor(zoomPercent)).toSize(); +} + +void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget) +{ m_zoomWidget->setWidget(formWidget); + m_zoomWidget->resize(scaleSize(zoomPercent(), screenSize())); } int ZoomablePreviewDeviceSkin::zoomPercent() const @@ -290,32 +397,50 @@ int ZoomablePreviewDeviceSkin::zoomPercent() const return m_zoomWidget->zoom(); } -void ZoomablePreviewDeviceSkin::setZoomPercent(int z) +void ZoomablePreviewDeviceSkin::setZoomPercent(int zp) { - if (z == zoomPercent()) + if (zp == zoomPercent()) return; // If not triggered by the menu itself: Update it - if (m_zoomMenu->zoom() != z) - m_zoomMenu->setZoom(z); + if (m_zoomMenu->zoom() != zp) + m_zoomMenu->setZoom(zp); - const QCursor oldCursor = cursor(); - QApplication::setOverrideCursor(Qt::WaitCursor); - // DeviceSkin has double, not qreal. - const double hundred = 100.0; - setZoom(static_cast<double>(z) / hundred); - m_zoomWidget->setZoom(z); + QApplication::setOverrideCursor(Qt::WaitCursor); + m_zoomWidget->setZoom(zp); + setTransform(skinTransform()); QApplication::restoreOverrideCursor(); } void ZoomablePreviewDeviceSkin::populateContextMenu(QMenu *menu) { - m_zoomMenu->addActions(menu); - menu->addSeparator(); - PreviewDeviceSkin::populateContextMenu(menu); + if (!m_zoomSubMenuAction) { + m_zoomSubMenuAction = new QAction(tr("&Zoom"), this); + QMenu *zoomSubMenu = new QMenu; + m_zoomSubMenuAction->setMenu(zoomSubMenu); + m_zoomMenu->addActions(zoomSubMenu); + } + menu->addAction(m_zoomSubMenuAction); menu->addSeparator(); } +QMatrix ZoomablePreviewDeviceSkin::skinTransform() const +{ + // Complete transformation consisting of base class rotation and zoom. + QMatrix rc = PreviewDeviceSkin::skinTransform(); + const int zp = zoomPercent(); + if (zp != 100) { + const qreal factor = zoomFactor(zp); + rc.scale(factor, factor); + } + return rc; +} + +void ZoomablePreviewDeviceSkin::fitWidget(const QSize &size) +{ + m_zoomWidget->resize(scaleSize(zoomPercent(), size)); +} + // ------------- PreviewConfiguration static const char *styleKey = "Style"; diff --git a/tools/designer/src/lib/shared/promotionmodel.cpp b/tools/designer/src/lib/shared/promotionmodel.cpp index 77d0bbb..1df9cbe 100644 --- a/tools/designer/src/lib/shared/promotionmodel.cpp +++ b/tools/designer/src/lib/shared/promotionmodel.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PromotionModel -*/ - #include "promotionmodel_p.h" #include "widgetdatabase_p.h" diff --git a/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp b/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp index 8453c16..a1c47d2 100644 --- a/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp +++ b/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QDesignerPromotionDialog -*/ - #include "qdesigner_promotiondialog_p.h" #include "promotionmodel_p.h" #include "iconloader_p.h" diff --git a/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp b/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp index 7c7d9d7..3875d0e 100644 --- a/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp +++ b/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp @@ -40,16 +40,56 @@ ****************************************************************************/ #include "qdesigner_propertyeditor_p.h" -#ifdef Q_OS_WIN -# include <widgetfactory_p.h> -#endif -#include <QAction> -#include <QLineEdit> -#include <QAbstractButton> +#include "pluginmanager_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <widgetfactory_p.h> +#include <QtGui/QAction> +#include <QtGui/QLineEdit> +#include <QtGui/QAbstractButton> QT_BEGIN_NAMESPACE namespace qdesigner_internal { +typedef QDesignerPropertyEditor::StringPropertyParameters StringPropertyParameters; +// A map of property name to type +typedef QHash<QString, StringPropertyParameters> PropertyNameTypeMap; + +// Compile a map of hard-coded string property types +static const PropertyNameTypeMap &stringPropertyTypes() +{ + static PropertyNameTypeMap propertyNameTypeMap; + if (propertyNameTypeMap.empty()) { + const StringPropertyParameters richtext(ValidationRichText, true); + // Accessibility. Both are texts the narrator reads + propertyNameTypeMap.insert(QLatin1String("accessibleDescription"), richtext); + propertyNameTypeMap.insert(QLatin1String("accessibleName"), richtext); + // object names + const StringPropertyParameters objectName(ValidationObjectName, false); + propertyNameTypeMap.insert(QLatin1String("buddy"), objectName); + propertyNameTypeMap.insert(QLatin1String("currentItemName"), objectName); + propertyNameTypeMap.insert(QLatin1String("currentPageName"), objectName); + propertyNameTypeMap.insert(QLatin1String("currentTabName"), objectName); + propertyNameTypeMap.insert(QLatin1String("layoutName"), objectName); + propertyNameTypeMap.insert(QLatin1String("spacerName"), objectName); + // Style sheet + propertyNameTypeMap.insert(QLatin1String("styleSheet"), StringPropertyParameters(ValidationStyleSheet, false)); + // Buttons/ QCommandLinkButton + const StringPropertyParameters multiline(ValidationMultiLine, true); + propertyNameTypeMap.insert(QLatin1String("description"), multiline); + propertyNameTypeMap.insert(QLatin1String("iconText"), multiline); + // Tooltips, etc. + propertyNameTypeMap.insert(QLatin1String("toolTip"), richtext); + propertyNameTypeMap.insert(QLatin1String("whatsThis"), richtext); + propertyNameTypeMap.insert(QLatin1String("windowIconText"), richtext); + propertyNameTypeMap.insert(QLatin1String("html"), richtext); + // A QWizard page id + propertyNameTypeMap.insert(QLatin1String("pageId"), StringPropertyParameters(ValidationSingleLine, false)); + // QPlainTextEdit + propertyNameTypeMap.insert(QLatin1String("plainText"), StringPropertyParameters(ValidationMultiLine, true)); + } + return propertyNameTypeMap; +} QDesignerPropertyEditor::QDesignerPropertyEditor(QWidget *parent, Qt::WindowFlags flags) : QDesignerPropertyEditorInterface(parent, flags) @@ -68,33 +108,19 @@ QDesignerPropertyEditor::StringPropertyParameters QDesignerPropertyEditor::textP return StringPropertyParameters(vm, false); } - // Accessibility. Both are texts the narrator reads - if (propertyName == QLatin1String("accessibleDescription") || propertyName == QLatin1String("accessibleName")) - return StringPropertyParameters(ValidationRichText, true); - - // Names - if (propertyName == QLatin1String("buddy") - || propertyName == QLatin1String("currentItemName") - || propertyName == QLatin1String("currentPageName") - || propertyName == QLatin1String("currentTabName") - || propertyName == QLatin1String("layoutName") - || propertyName == QLatin1String("spacerName")) - return StringPropertyParameters(ValidationObjectName, false); - - if (propertyName.endsWith(QLatin1String("Name"))) - return StringPropertyParameters(ValidationSingleLine, true); - - // Multi line? - if (propertyName == QLatin1String("styleSheet")) - return StringPropertyParameters(ValidationStyleSheet, false); - - if (propertyName == QLatin1String("description") || propertyName == QLatin1String("iconText")) // QCommandLinkButton - return StringPropertyParameters(ValidationMultiLine, true); + // Check custom widgets by class. + const QString className = WidgetFactory::classNameOf(core, object); + const QDesignerCustomWidgetData customData = core->pluginManager()->customWidgetData(className); + if (!customData.isNull()) { + StringPropertyParameters customType; + if (customData.xmlStringPropertyType(propertyName, &customType)) + return customType; + } - if (propertyName == QLatin1String("toolTip") || propertyName.endsWith(QLatin1String("ToolTip")) || - propertyName == QLatin1String("whatsThis") || - propertyName == QLatin1String("windowIconText") || propertyName == QLatin1String("html")) - return StringPropertyParameters(ValidationRichText, true); + // Check hardcoded property ames + const PropertyNameTypeMap::const_iterator hit = stringPropertyTypes().constFind(propertyName); + if (hit != stringPropertyTypes().constEnd()) + return hit.value(); // text: Check according to widget type. if (propertyName == QLatin1String("text")) { @@ -104,17 +130,17 @@ QDesignerPropertyEditor::StringPropertyParameters QDesignerPropertyEditor::textP return StringPropertyParameters(ValidationMultiLine, true); return StringPropertyParameters(ValidationRichText, true); } - if (propertyName == QLatin1String("pageId")) // A QWizard page id - return StringPropertyParameters(ValidationSingleLine, false); - if (propertyName == QLatin1String("plainText")) // QPlainTextEdit - return StringPropertyParameters(ValidationMultiLine, true); + // Fuzzy matching + if (propertyName.endsWith(QLatin1String("Name"))) + return StringPropertyParameters(ValidationSingleLine, true); + + if (propertyName.endsWith(QLatin1String("ToolTip"))) + return StringPropertyParameters(ValidationRichText, true); #ifdef Q_OS_WIN // No translation for the active X "control" property - if (propertyName == QLatin1String("control") && WidgetFactory::classNameOf(core, object) == QLatin1String("QAxWidget")) + if (propertyName == QLatin1String("control") && className == QLatin1String("QAxWidget")) return StringPropertyParameters(ValidationSingleLine, false); -#else - Q_UNUSED(core); #endif // default to single diff --git a/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp b/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp index cce0528..d85e18f 100644 --- a/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp +++ b/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QDesignerTaskMenu -*/ - #include "qdesigner_taskmenu_p.h" #include "qdesigner_command_p.h" #include "richtexteditor_p.h" diff --git a/tools/designer/src/lib/shared/qdesigner_toolbar.cpp b/tools/designer/src/lib/shared/qdesigner_toolbar.cpp index cb77801..1c465da 100644 --- a/tools/designer/src/lib/shared/qdesigner_toolbar.cpp +++ b/tools/designer/src/lib/shared/qdesigner_toolbar.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::Sentinel -*/ - #include "qdesigner_toolbar_p.h" #include "qdesigner_command_p.h" #include "actionrepository_p.h" diff --git a/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp b/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp index 5ba8ad7..d94a397 100644 --- a/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp +++ b/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QDesignerWidgetBox -*/ - #include "qdesigner_widgetbox_p.h" #include "qdesigner_utils_p.h" diff --git a/tools/designer/src/lib/shared/qtresourceview.cpp b/tools/designer/src/lib/shared/qtresourceview.cpp index d956491..2b59605 100644 --- a/tools/designer/src/lib/shared/qtresourceview.cpp +++ b/tools/designer/src/lib/shared/qtresourceview.cpp @@ -44,6 +44,7 @@ #include "qtresourcemodel_p.h" #include "qtresourceeditordialog_p.h" #include "iconloader_p.h" +#include "filterwidget_p.h" // For FilterWidget #include <QtDesigner/QDesignerFormEditorInterface> @@ -144,6 +145,7 @@ public: void slotReloadResources(); void slotCopyResourcePath(); void slotListWidgetContextMenuRequested(const QPoint &pos); + void slotFilterChanged(const QString &pattern); void createPaths(); QTreeWidgetItem *createPath(const QString &path, QTreeWidgetItem *parent); void createResources(const QString &path); @@ -152,16 +154,20 @@ public: void restoreSettings(); void saveSettings(); void updateActions(); + void filterOutResources(); QPixmap makeThumbnail(const QPixmap &pix) const; QDesignerFormEditorInterface *m_core; QtResourceModel *m_resourceModel; QToolBar *m_toolBar; + qdesigner_internal::FilterWidget *m_filterWidget; QTreeWidget *m_treeWidget; QListWidget *m_listWidget; QSplitter *m_splitter; - QMap<QString, QStringList> m_pathToContents; // full path to contents file names + QMap<QString, QStringList> m_pathToContents; // full path to contents file names (full path to its resource filenames) + QMap<QString, QString> m_pathToParentPath; // full path to full parent path + QMap<QString, QStringList> m_pathToSubPaths; // full path to full sub paths QMap<QString, QTreeWidgetItem *> m_pathToItem; QMap<QTreeWidgetItem *, QString> m_itemToPath; QMap<QString, QListWidgetItem *> m_resourceToItem; @@ -175,6 +181,7 @@ public: bool m_ignoreGuiSignals; QString m_settingsKey; bool m_resourceEditingEnabled; + QString m_filterPattern; }; QtResourceViewPrivate::QtResourceViewPrivate(QDesignerFormEditorInterface *core) : @@ -251,6 +258,12 @@ void QtResourceViewPrivate::slotListWidgetContextMenuRequested(const QPoint &pos menu.exec(m_listWidget->mapToGlobal(pos)); } +void QtResourceViewPrivate::slotFilterChanged(const QString &pattern) +{ + m_filterPattern = pattern; + filterOutResources(); +} + void QtResourceViewPrivate::storeExpansionState() { QMapIterator<QString, QTreeWidgetItem *> it(m_pathToItem); @@ -294,6 +307,7 @@ void QtResourceViewPrivate::updateActions() m_editResourcesAction->setVisible(m_resourceEditingEnabled); m_editResourcesAction->setEnabled(resourceActive); m_reloadResourcesAction->setEnabled(resourceActive); + m_filterWidget->setEnabled(resourceActive); } void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet) @@ -307,6 +321,8 @@ void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet) const QString currentResource = m_itemToResource.value(m_listWidget->currentItem()); m_treeWidget->clear(); m_pathToContents.clear(); + m_pathToParentPath.clear(); + m_pathToSubPaths.clear(); m_pathToItem.clear(); m_itemToPath.clear(); m_listWidget->clear(); @@ -320,6 +336,7 @@ void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet) q_ptr->selectResource(currentResource); else if (!currentPath.isEmpty()) q_ptr->selectResource(currentPath); + filterOutResources(); } void QtResourceViewPrivate::slotCurrentPathChanged(QTreeWidgetItem *item) @@ -362,9 +379,6 @@ void QtResourceViewPrivate::createPaths() const QString root(QLatin1Char(':')); - QMap<QString, QString> pathToParentPath; // full path to full parent path - QMap<QString, QStringList> pathToSubPaths; // full path to full sub paths - QMap<QString, QString> contents = m_resourceModel->contents(); QMapIterator<QString, QString> itContents(contents); while (itContents.hasNext()) { @@ -372,11 +386,11 @@ void QtResourceViewPrivate::createPaths() const QFileInfo fi(filePath); QString dirPath = fi.absolutePath(); m_pathToContents[dirPath].append(fi.fileName()); - while (!pathToParentPath.contains(dirPath) && dirPath != root) { + while (!m_pathToParentPath.contains(dirPath) && dirPath != root) { // create all parent paths const QFileInfo fd(dirPath); const QString parentDirPath = fd.absolutePath(); - pathToParentPath[dirPath] = parentDirPath; - pathToSubPaths[parentDirPath].append(dirPath); + m_pathToParentPath[dirPath] = parentDirPath; + m_pathToSubPaths[parentDirPath].append(dirPath); dirPath = parentDirPath; } } @@ -387,13 +401,126 @@ void QtResourceViewPrivate::createPaths() QPair<QString, QTreeWidgetItem *> pathToParentItem = pathToParentItemQueue.dequeue(); const QString path = pathToParentItem.first; QTreeWidgetItem *item = createPath(path, pathToParentItem.second); - QStringList subPaths = pathToSubPaths.value(path); + QStringList subPaths = m_pathToSubPaths.value(path); QStringListIterator itSubPaths(subPaths); while (itSubPaths.hasNext()) pathToParentItemQueue.enqueue(qMakePair(itSubPaths.next(), item)); } } +void QtResourceViewPrivate::filterOutResources() +{ + QMap<QString, bool> pathToMatchingContents; // true means the path has any matching contents + QMap<QString, bool> pathToVisible; // true means the path has to be shown + + // 1) we go from root path recursively. + // 2) we check every path if it contains at least one matching resource - if empty we add it + // to pathToMatchingContents and pathToVisible with false, if non empty + // we add it with true and change every parent path in pathToVisible to true. + // 3) we hide these items which has pathToVisible value false. + + const bool matchAll = m_filterPattern.isEmpty(); + const QString root(QLatin1Char(':')); + + QQueue<QString> pathQueue; + pathQueue.enqueue(root); + while (!pathQueue.isEmpty()) { + const QString path = pathQueue.dequeue(); + + QStringList fileNames = m_pathToContents.value(path); + QStringListIterator it(fileNames); + bool hasContents = matchAll; + if (!matchAll) { // the case filter is not empty - we check if the path contains anything + while (it.hasNext()) { + QString fileName = it.next(); + hasContents = fileName.contains(m_filterPattern, Qt::CaseInsensitive); + if (hasContents) // the path contains at least one resource which matches the filter + break; + } + } + + pathToMatchingContents[path] = hasContents; + pathToVisible[path] = hasContents; + + if (hasContents) { // if the path is going to be shown we need to show all its parent paths + QString parentPath = m_pathToParentPath.value(path); + while (!parentPath.isEmpty()) { + QString p = parentPath; + if (pathToVisible.value(p)) // parent path is already shown, we break the loop + break; + pathToVisible[p] = true; + parentPath = m_pathToParentPath.value(p); + } + } + + QStringList subPaths = m_pathToSubPaths.value(path); // we do the same for children paths + QStringListIterator itSubPaths(subPaths); + while (itSubPaths.hasNext()) + pathQueue.enqueue(itSubPaths.next()); + } + + // we setup here new path and resource to be activated + const QString currentPath = m_itemToPath.value(m_treeWidget->currentItem()); + QString newCurrentPath = currentPath; + QString currentResource = m_itemToResource.value(m_listWidget->currentItem()); + if (!matchAll) { + bool searchForNewPathWithContents = true; + + if (!currentPath.isEmpty()) { // if the currentPath is empty we will search for a new path too + QMap<QString, bool>::ConstIterator it = pathToMatchingContents.constFind(currentPath); + if (it != pathToMatchingContents.constEnd() && it.value()) // the current item has contents, we don't need to search for another path + searchForNewPathWithContents = false; + } + + if (searchForNewPathWithContents) { + // we find the first path with the matching contents + QMap<QString, bool>::ConstIterator itContents = pathToMatchingContents.constBegin(); + while (itContents != pathToMatchingContents.constEnd()) { + if (itContents.value()) { + newCurrentPath = itContents.key(); // the new path will be activated + break; + } + + itContents++; + } + } + + QFileInfo fi(currentResource); + if (!fi.fileName().contains(m_filterPattern, Qt::CaseInsensitive)) { // the case when the current resource is filtered out + const QStringList fileNames = m_pathToContents.value(newCurrentPath); + QStringListIterator it(fileNames); + while (it.hasNext()) { // we try to select the first matching resource from the newCurrentPath + QString fileName = it.next(); + if (fileName.contains(m_filterPattern, Qt::CaseInsensitive)) { + QDir dirPath(newCurrentPath); + currentResource = dirPath.absoluteFilePath(fileName); // the new resource inside newCurrentPath will be activated + break; + } + } + } + } + + QTreeWidgetItem *newCurrentItem = m_pathToItem.value(newCurrentPath); + if (currentPath != newCurrentPath) + m_treeWidget->setCurrentItem(newCurrentItem); + else + slotCurrentPathChanged(newCurrentItem); // trigger filtering on the current path + + QListWidgetItem *currentResourceItem = m_resourceToItem.value(currentResource); + if (currentResourceItem) { + m_listWidget->setCurrentItem(currentResourceItem); + m_listWidget->scrollToItem(currentResourceItem); + } + + QMapIterator<QString, bool> it(pathToVisible); // hide all paths filtered out + while (it.hasNext()) { + const QString path = it.next().key(); + QTreeWidgetItem *item = m_pathToItem.value(path); + if (item) + item->setHidden(!it.value()); + } +} + QTreeWidgetItem *QtResourceViewPrivate::createPath(const QString &path, QTreeWidgetItem *parent) { QTreeWidgetItem *item = 0; @@ -417,27 +544,32 @@ QTreeWidgetItem *QtResourceViewPrivate::createPath(const QString &path, QTreeWid void QtResourceViewPrivate::createResources(const QString &path) { + const bool matchAll = m_filterPattern.isEmpty(); + QDir dir(path); - QStringList files = m_pathToContents.value(path); - QStringListIterator it(files); + QStringList fileNames = m_pathToContents.value(path); + QStringListIterator it(fileNames); while (it.hasNext()) { - QString file = it.next(); - QString filePath = dir.absoluteFilePath(file); - QFileInfo fi(filePath); - if (fi.isFile()) { - QListWidgetItem *item = new QListWidgetItem(fi.fileName(), m_listWidget); - const QPixmap pix = QPixmap(filePath); - if (pix.isNull()) { - item->setToolTip(filePath); - } else { - item->setIcon(QIcon(makeThumbnail(pix))); - const QSize size = pix.size(); - item->setToolTip(QtResourceView::tr("Size: %1 x %2\n%3").arg(size.width()).arg(size.height()).arg(filePath)); + QString fileName = it.next(); + const bool showProperty = matchAll || fileName.contains(m_filterPattern, Qt::CaseInsensitive); + if (showProperty) { + QString filePath = dir.absoluteFilePath(fileName); + QFileInfo fi(filePath); + if (fi.isFile()) { + QListWidgetItem *item = new QListWidgetItem(fi.fileName(), m_listWidget); + const QPixmap pix = QPixmap(filePath); + if (pix.isNull()) { + item->setToolTip(filePath); + } else { + item->setIcon(QIcon(makeThumbnail(pix))); + const QSize size = pix.size(); + item->setToolTip(QtResourceView::tr("Size: %1 x %2\n%3").arg(size.width()).arg(size.height()).arg(filePath)); + } + item->setFlags(item->flags() | Qt::ItemIsDragEnabled); + item->setData(Qt::UserRole, filePath); + m_itemToResource[item] = filePath; + m_resourceToItem[filePath] = item; } - item->setFlags(item->flags() | Qt::ItemIsDragEnabled); - item->setData(Qt::UserRole, filePath); - m_itemToResource[item] = filePath; - m_resourceToItem[filePath] = item; } } } @@ -464,6 +596,11 @@ QtResourceView::QtResourceView(QDesignerFormEditorInterface *core, QWidget *pare connect(d_ptr->m_copyResourcePathAction, SIGNAL(triggered()), this, SLOT(slotCopyResourcePath())); d_ptr->m_copyResourcePathAction->setEnabled(false); + //d_ptr->m_filterWidget = new qdesigner_internal::FilterWidget(0, qdesigner_internal::FilterWidget::LayoutAlignNone); + d_ptr->m_filterWidget = new qdesigner_internal::FilterWidget(d_ptr->m_toolBar); + d_ptr->m_toolBar->addWidget(d_ptr->m_filterWidget); + connect(d_ptr->m_filterWidget, SIGNAL(filterChanged(QString)), this, SLOT(slotFilterChanged(QString))); + d_ptr->m_splitter = new QSplitter; d_ptr->m_splitter->setChildrenCollapsible(false); d_ptr->m_splitter->addWidget(d_ptr->m_treeWidget); diff --git a/tools/designer/src/lib/shared/qtresourceview_p.h b/tools/designer/src/lib/shared/qtresourceview_p.h index 33162c5..f78c5af 100644 --- a/tools/designer/src/lib/shared/qtresourceview_p.h +++ b/tools/designer/src/lib/shared/qtresourceview_p.h @@ -113,6 +113,7 @@ private: Q_PRIVATE_SLOT(d_func(), void slotReloadResources()) Q_PRIVATE_SLOT(d_func(), void slotCopyResourcePath()) Q_PRIVATE_SLOT(d_func(), void slotListWidgetContextMenuRequested(const QPoint &pos)) + Q_PRIVATE_SLOT(d_func(), void slotFilterChanged(const QString &pattern)) }; class QDESIGNER_SHARED_EXPORT QtResourceViewDialog : public QDialog diff --git a/tools/designer/src/lib/shared/richtexteditor.cpp b/tools/designer/src/lib/shared/richtexteditor.cpp index 8aa036b..42cf449 100644 --- a/tools/designer/src/lib/shared/richtexteditor.cpp +++ b/tools/designer/src/lib/shared/richtexteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::RichTextEditorDialog -*/ - #include "richtexteditor_p.h" #include "htmlhighlighter_p.h" #include "iconselector_p.h" diff --git a/tools/designer/src/lib/shared/scriptdialog.cpp b/tools/designer/src/lib/shared/scriptdialog.cpp index 616f0c9..ef4d08f 100644 --- a/tools/designer/src/lib/shared/scriptdialog.cpp +++ b/tools/designer/src/lib/shared/scriptdialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ScriptDialog -*/ - #include "scriptdialog_p.h" #include "qscripthighlighter_p.h" diff --git a/tools/designer/src/lib/shared/scripterrordialog.cpp b/tools/designer/src/lib/shared/scripterrordialog.cpp index c4a7e07..326cba2 100644 --- a/tools/designer/src/lib/shared/scripterrordialog.cpp +++ b/tools/designer/src/lib/shared/scripterrordialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ScriptErrorDialog -*/ - #include "scripterrordialog_p.h" #include <QtGui/QTextEdit> diff --git a/tools/designer/src/lib/shared/shared.pri b/tools/designer/src/lib/shared/shared.pri index 8ed051a..0424a41 100644 --- a/tools/designer/src/lib/shared/shared.pri +++ b/tools/designer/src/lib/shared/shared.pri @@ -186,4 +186,17 @@ SOURCES += \ $$PWD/filterwidget.cpp \ $$PWD/plugindialog.cpp -RESOURCES += $$PWD/shared.qrc +RESOURCES += $$PWD/shared.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/ClamshellPhone.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/PDAPhone.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/pda.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/PortableMedia.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/qvfb.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/S60-nHD-Touchscreen.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/S60-QVGA-Candybar.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/SmartPhone2.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/SmartPhone.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/SmartPhoneWithButtons.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/TouchscreenPhone.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/Trolltech-Keypad.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/Trolltech-Touchscreen.qrc diff --git a/tools/designer/src/lib/shared/stylesheeteditor.cpp b/tools/designer/src/lib/shared/stylesheeteditor.cpp index 2056fcf..de64135 100644 --- a/tools/designer/src/lib/shared/stylesheeteditor.cpp +++ b/tools/designer/src/lib/shared/stylesheeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::StyleSheetEditorDialog -*/ - #include "stylesheeteditor_p.h" #include "csshighlighter_p.h" #include "iconselector_p.h" diff --git a/tools/designer/src/lib/shared/widgetfactory.cpp b/tools/designer/src/lib/shared/widgetfactory.cpp index 3822fec..56a1744 100644 --- a/tools/designer/src/lib/shared/widgetfactory.cpp +++ b/tools/designer/src/lib/shared/widgetfactory.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::WidgetFactory -*/ - #include "widgetfactory_p.h" #include "widgetdatabase_p.h" #include "metadatabase_p.h" diff --git a/tools/designer/src/lib/shared/zoomwidget.cpp b/tools/designer/src/lib/shared/zoomwidget.cpp index f4ab48e..a404dcb 100644 --- a/tools/designer/src/lib/shared/zoomwidget.cpp +++ b/tools/designer/src/lib/shared/zoomwidget.cpp @@ -143,9 +143,10 @@ ZoomView::ZoomView(QWidget *parent) : m_zoom(100), m_zoomFactor(1.0), m_zoomContextMenuEnabled(false), - m_autoScrollSuppressed(true), m_zoomMenu(0) { + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setFrameShape(QFrame::NoFrame); setScene(m_scene); if (debugZoomWidget) @@ -187,8 +188,6 @@ void ZoomView::setZoom(int percent) resetTransform(); scale(m_zoomFactor, m_zoomFactor); - if (m_autoScrollSuppressed) - scrollToOrigin(); } void ZoomView::applyZoom() @@ -210,16 +209,6 @@ void ZoomView::setZoomContextMenuEnabled(bool e) m_zoomContextMenuEnabled = e; } -bool ZoomView::isAutoScrollSuppressed() const -{ - return m_autoScrollSuppressed; -} - -void ZoomView::setAutoScrollSuppressed(bool s) -{ - m_autoScrollSuppressed = s; -} - ZoomMenu *ZoomView::zoomMenu() { if (!m_zoomMenu) { @@ -469,13 +458,16 @@ void ZoomWidget::resizeEvent(QResizeEvent *event) * and the use the size ZoomView::resizeEvent(event); */ if (m_proxy && !m_viewResizeBlocked) { if (debugZoomWidget > 1) - qDebug() << ">ZoomWidget (" << size() << ")::resizeEvent from " << event->oldSize() << " to " << event->size(); + qDebug() << '>' << Q_FUNC_INFO << size() << ")::resizeEvent from " << event->oldSize() << " to " << event->size(); const QSizeF newViewPortSize = size() - viewPortMargin(); const QSizeF widgetSizeF = newViewPortSize / zoomFactor() - widgetDecorationSizeF(); m_widgetResizeBlocked = true; m_proxy->widget()->resize(widgetSizeF.toSize()); + setSceneRect(QRectF(QPointF(0, 0), widgetSizeF)); scrollToOrigin(); m_widgetResizeBlocked = false; + if (debugZoomWidget > 1) + qDebug() << '<' << Q_FUNC_INFO << widgetSizeF << m_proxy->widget()->size() << m_proxy->size(); } } @@ -559,7 +551,7 @@ QGraphicsProxyWidget *ZoomWidget::createProxyWidget(QGraphicsItem *parent, Qt::W void ZoomWidget::dump() const { - qDebug() << "ZoomWidget " << geometry() << " Viewport " << viewport()->geometry() + qDebug() << "ZoomWidget::dump " << geometry() << " Viewport " << viewport()->geometry() << "Scroll: " << scrollPosition() << "Matrix: " << matrix() << " SceneRect: " << sceneRect(); if (m_proxy) { qDebug() << "Proxy Pos: " << m_proxy->pos() << "Proxy " << m_proxy->size() diff --git a/tools/designer/src/lib/shared/zoomwidget_p.h b/tools/designer/src/lib/shared/zoomwidget_p.h index fe850f7..92c857e 100644 --- a/tools/designer/src/lib/shared/zoomwidget_p.h +++ b/tools/designer/src/lib/shared/zoomwidget_p.h @@ -104,8 +104,6 @@ class QDESIGNER_SHARED_EXPORT ZoomView : public QGraphicsView { Q_PROPERTY(int zoom READ zoom WRITE setZoom DESIGNABLE true SCRIPTABLE true) Q_PROPERTY(bool zoomContextMenuEnabled READ isZoomContextMenuEnabled WRITE setZoomContextMenuEnabled DESIGNABLE true SCRIPTABLE true) - Q_PROPERTY(bool autoScrollSuppressed READ isAutoScrollSuppressed WRITE setAutoScrollSuppressed DESIGNABLE true SCRIPTABLE true) - Q_OBJECT Q_DISABLE_COPY(ZoomView) public: @@ -120,10 +118,6 @@ public: bool isZoomContextMenuEnabled() const; void setZoomContextMenuEnabled(bool e); - // Suppress scrolling when changing zoom. Default: on - bool isAutoScrollSuppressed() const; - void setAutoScrollSuppressed(bool s); - QGraphicsScene &scene() { return *m_scene; } const QGraphicsScene &scene() const { return *m_scene; } @@ -149,8 +143,7 @@ private: int m_zoom; qreal m_zoomFactor; - bool m_zoomContextMenuEnabled; - bool m_autoScrollSuppressed; + bool m_zoomContextMenuEnabled; bool m_resizeBlocked; ZoomMenu *m_zoomMenu; }; diff --git a/tools/designer/src/lib/uilib/ui4.cpp b/tools/designer/src/lib/uilib/ui4.cpp index 2047739..72c7f50 100644 --- a/tools/designer/src/lib/uilib/ui4.cpp +++ b/tools/designer/src/lib/uilib/ui4.cpp @@ -2368,6 +2368,7 @@ void DomCustomWidget::clear(bool clear_all) delete m_script; delete m_properties; delete m_slots; + delete m_propertyspecifications; if (clear_all) { m_text.clear(); @@ -2381,6 +2382,7 @@ void DomCustomWidget::clear(bool clear_all) m_script = 0; m_properties = 0; m_slots = 0; + m_propertyspecifications = 0; } DomCustomWidget::DomCustomWidget() @@ -2393,6 +2395,7 @@ DomCustomWidget::DomCustomWidget() m_script = 0; m_properties = 0; m_slots = 0; + m_propertyspecifications = 0; } DomCustomWidget::~DomCustomWidget() @@ -2403,6 +2406,7 @@ DomCustomWidget::~DomCustomWidget() delete m_script; delete m_properties; delete m_slots; + delete m_propertyspecifications; } void DomCustomWidget::read(QXmlStreamReader &reader) @@ -2468,6 +2472,12 @@ void DomCustomWidget::read(QXmlStreamReader &reader) setElementSlots(v); continue; } + if (tag == QLatin1String("propertyspecifications")) { + DomPropertySpecifications *v = new DomPropertySpecifications(); + v->read(reader); + setElementPropertyspecifications(v); + continue; + } reader.raiseError(QLatin1String("Unexpected element ") + tag); } break; @@ -2548,6 +2558,12 @@ void DomCustomWidget::read(const QDomElement &node) setElementSlots(v); continue; } + if (tag == QLatin1String("propertyspecifications")) { + DomPropertySpecifications *v = new DomPropertySpecifications(); + v->read(e); + setElementPropertyspecifications(v); + continue; + } } m_text.clear(); for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { @@ -2605,6 +2621,10 @@ void DomCustomWidget::write(QXmlStreamWriter &writer, const QString &tagName) co m_slots->write(writer, QLatin1String("slots")); } + if (m_children & Propertyspecifications) { + m_propertyspecifications->write(writer, QLatin1String("propertyspecifications")); + } + if (!m_text.isEmpty()) writer.writeCharacters(m_text); @@ -2731,6 +2751,21 @@ void DomCustomWidget::setElementSlots(DomSlots* a) m_slots = a; } +DomPropertySpecifications* DomCustomWidget::takeElementPropertyspecifications() +{ + DomPropertySpecifications* a = m_propertyspecifications; + m_propertyspecifications = 0; + m_children ^= Propertyspecifications; + return a; +} + +void DomCustomWidget::setElementPropertyspecifications(DomPropertySpecifications* a) +{ + delete m_propertyspecifications; + m_children |= Propertyspecifications; + m_propertyspecifications = a; +} + void DomCustomWidget::clearElementClass() { m_children &= ~Class; @@ -2798,6 +2833,13 @@ void DomCustomWidget::clearElementSlots() m_children &= ~Slots; } +void DomCustomWidget::clearElementPropertyspecifications() +{ + delete m_propertyspecifications; + m_propertyspecifications = 0; + m_children &= ~Propertyspecifications; +} + void DomProperties::clear(bool clear_all) { qDeleteAll(m_property); @@ -10883,5 +10925,208 @@ void DomSlots::setElementSlot(const QStringList& a) m_slot = a; } +void DomPropertySpecifications::clear(bool clear_all) +{ + qDeleteAll(m_stringpropertyspecification); + m_stringpropertyspecification.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomPropertySpecifications::DomPropertySpecifications() +{ + m_children = 0; +} + +DomPropertySpecifications::~DomPropertySpecifications() +{ + qDeleteAll(m_stringpropertyspecification); + m_stringpropertyspecification.clear(); +} + +void DomPropertySpecifications::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("stringpropertyspecification")) { + DomStringPropertySpecification *v = new DomStringPropertySpecification(); + v->read(reader); + m_stringpropertyspecification.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomPropertySpecifications::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("stringpropertyspecification")) { + DomStringPropertySpecification *v = new DomStringPropertySpecification(); + v->read(e); + m_stringpropertyspecification.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomPropertySpecifications::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("propertyspecifications") : tagName.toLower()); + + for (int i = 0; i < m_stringpropertyspecification.size(); ++i) { + DomStringPropertySpecification* v = m_stringpropertyspecification[i]; + v->write(writer, QLatin1String("stringpropertyspecification")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomPropertySpecifications::setElementStringpropertyspecification(const QList<DomStringPropertySpecification*>& a) +{ + m_children |= Stringpropertyspecification; + m_stringpropertyspecification = a; +} + +void DomStringPropertySpecification::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + m_has_attr_type = false; + m_has_attr_notr = false; + } + + m_children = 0; +} + +DomStringPropertySpecification::DomStringPropertySpecification() +{ + m_children = 0; + m_has_attr_name = false; + m_has_attr_type = false; + m_has_attr_notr = false; +} + +DomStringPropertySpecification::~DomStringPropertySpecification() +{ +} + +void DomStringPropertySpecification::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + if (name == QLatin1String("type")) { + setAttributeType(attribute.value().toString()); + continue; + } + if (name == QLatin1String("notr")) { + setAttributeNotr(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomStringPropertySpecification::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + if (node.hasAttribute(QLatin1String("type"))) + setAttributeType(node.attribute(QLatin1String("type"))); + if (node.hasAttribute(QLatin1String("notr"))) + setAttributeNotr(node.attribute(QLatin1String("notr"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomStringPropertySpecification::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("stringpropertyspecification") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (hasAttributeType()) + writer.writeAttribute(QLatin1String("type"), attributeType()); + + if (hasAttributeNotr()) + writer.writeAttribute(QLatin1String("notr"), attributeNotr()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + QT_END_NAMESPACE diff --git a/tools/designer/src/lib/uilib/ui4_p.h b/tools/designer/src/lib/uilib/ui4_p.h index df02a39..fa70573 100644 --- a/tools/designer/src/lib/uilib/ui4_p.h +++ b/tools/designer/src/lib/uilib/ui4_p.h @@ -161,6 +161,8 @@ class DomScript; class DomWidgetData; class DomDesignerData; class DomSlots; +class DomPropertySpecifications; +class DomStringPropertySpecification; /******************************************************************************* ** Declarations @@ -1015,6 +1017,12 @@ public: inline bool hasElementSlots() const { return m_children & Slots; } void clearElementSlots(); + inline DomPropertySpecifications* elementPropertyspecifications() const { return m_propertyspecifications; } + DomPropertySpecifications* takeElementPropertyspecifications(); + void setElementPropertyspecifications(DomPropertySpecifications* a); + inline bool hasElementPropertyspecifications() const { return m_children & Propertyspecifications; } + void clearElementPropertyspecifications(); + private: QString m_text; void clear(bool clear_all = true); @@ -1033,6 +1041,7 @@ private: DomScript* m_script; DomProperties* m_properties; DomSlots* m_slots; + DomPropertySpecifications* m_propertyspecifications; enum Child { Class = 1, Extends = 2, @@ -1044,7 +1053,8 @@ private: Pixmap = 128, Script = 256, Properties = 512, - Slots = 1024 + Slots = 1024, + Propertyspecifications = 2048 }; DomCustomWidget(const DomCustomWidget &other); @@ -3686,6 +3696,91 @@ private: void operator = (const DomSlots&other); }; +class QDESIGNER_UILIB_EXPORT DomPropertySpecifications { +public: + DomPropertySpecifications(); + ~DomPropertySpecifications(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList<DomStringPropertySpecification*> elementStringpropertyspecification() const { return m_stringpropertyspecification; } + void setElementStringpropertyspecification(const QList<DomStringPropertySpecification*>& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList<DomStringPropertySpecification*> m_stringpropertyspecification; + enum Child { + Stringpropertyspecification = 1 + }; + + DomPropertySpecifications(const DomPropertySpecifications &other); + void operator = (const DomPropertySpecifications&other); +}; + +class QDESIGNER_UILIB_EXPORT DomStringPropertySpecification { +public: + DomStringPropertySpecification(); + ~DomStringPropertySpecification(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + inline bool hasAttributeType() const { return m_has_attr_type; } + inline QString attributeType() const { return m_attr_type; } + inline void setAttributeType(const QString& a) { m_attr_type = a; m_has_attr_type = true; } + inline void clearAttributeType() { m_has_attr_type = false; } + + inline bool hasAttributeNotr() const { return m_has_attr_notr; } + inline QString attributeNotr() const { return m_attr_notr; } + inline void setAttributeNotr(const QString& a) { m_attr_notr = a; m_has_attr_notr = true; } + inline void clearAttributeNotr() { m_has_attr_notr = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + QString m_attr_type; + bool m_has_attr_type; + + QString m_attr_notr; + bool m_has_attr_notr; + + // child element data + uint m_children; + + DomStringPropertySpecification(const DomStringPropertySpecification &other); + void operator = (const DomStringPropertySpecification&other); +}; + #ifdef QFORMINTERNAL_NAMESPACE } diff --git a/tools/kmap2qmap/kmap2qmap.pro b/tools/kmap2qmap/kmap2qmap.pro new file mode 100644 index 0000000..cc8200b --- /dev/null +++ b/tools/kmap2qmap/kmap2qmap.pro @@ -0,0 +1,12 @@ + +TEMPLATE = app +DESTDIR = ../../bin +QT = core +CONFIG += console +CONFIG -= app_bundle + +DEPENDPATH += $$QT_SOURCE_TREE/src/gui/embedded +INCLUDEPATH += $$QT_SOURCE_TREE/src/gui/embedded + +# Input +SOURCES += main.cpp diff --git a/tools/kmap2qmap/main.cpp b/tools/kmap2qmap/main.cpp new file mode 100644 index 0000000..5e80b23 --- /dev/null +++ b/tools/kmap2qmap/main.cpp @@ -0,0 +1,948 @@ +#include <cstdio> + +#include <QFile> +#include <QFileInfo> +#include <QDir> +#include <QTextCodec> +#include <QList> +#include <QVector> +#include <QByteArray> +#include <QStringList> +#include <QTextStream> + +#include "qkbd_qws_p.h" + +using namespace std; + + +struct modifier_map_t { + const char *symbol; + quint8 modifier; + quint32 qtmodifier; +}; + +static const struct modifier_map_t modifier_map[] = { + { "plain", QWSKeyboard::ModPlain, Qt::NoModifier }, + { "shift", QWSKeyboard::ModShift, Qt::ShiftModifier }, + { "altgr", QWSKeyboard::ModAltGr, Qt::AltModifier }, + { "control", QWSKeyboard::ModControl, Qt::ControlModifier }, + { "alt", QWSKeyboard::ModAlt, Qt::AltModifier }, + { "meta", QWSKeyboard::ModAlt, Qt::AltModifier }, + { "shiftl", QWSKeyboard::ModShiftL, Qt::ShiftModifier }, + { "shiftr", QWSKeyboard::ModShiftR, Qt::ShiftModifier }, + { "ctrll", QWSKeyboard::ModCtrlL, Qt::ControlModifier }, + { "ctrlr", QWSKeyboard::ModCtrlR, Qt::ControlModifier }, +}; + +static const int modifier_map_size = sizeof(modifier_map)/sizeof(modifier_map_t); + + +struct symbol_map_t { + const char *symbol; + quint32 qtcode; +}; + +static const struct symbol_map_t symbol_map[] = { + { "space", Qt::Key_Space }, + { "exclam", Qt::Key_Exclam }, + { "quotedbl", Qt::Key_QuoteDbl }, + { "numbersign", Qt::Key_NumberSign }, + { "dollar", Qt::Key_Dollar }, + { "percent", Qt::Key_Percent }, + { "ampersand", Qt::Key_Ampersand }, + { "apostrophe", Qt::Key_Apostrophe }, + { "parenleft", Qt::Key_ParenLeft }, + { "parenright", Qt::Key_ParenRight }, + { "asterisk", Qt::Key_Asterisk }, + { "plus", Qt::Key_Plus }, + { "comma", Qt::Key_Comma }, + { "minus", Qt::Key_Minus }, + { "period", Qt::Key_Period }, + { "slash", Qt::Key_Slash }, + { "zero", Qt::Key_0 }, + { "one", Qt::Key_1 }, + { "two", Qt::Key_2 }, + { "three", Qt::Key_3 }, + { "four", Qt::Key_4 }, + { "five", Qt::Key_5 }, + { "six", Qt::Key_6 }, + { "seven", Qt::Key_7 }, + { "eight", Qt::Key_8 }, + { "nine", Qt::Key_9 }, + { "colon", Qt::Key_Colon }, + { "semicolon", Qt::Key_Semicolon }, + { "less", Qt::Key_Less }, + { "equal", Qt::Key_Equal }, + { "greater", Qt::Key_Greater }, + { "question", Qt::Key_Question }, + { "at", Qt::Key_At }, + { "bracketleft", Qt::Key_BracketLeft }, + { "backslash", Qt::Key_Backslash }, + { "bracketright", Qt::Key_BracketRight }, + { "asciicircum", Qt::Key_AsciiCircum }, + { "underscore", Qt::Key_Underscore }, + { "grave", Qt::Key_QuoteLeft }, + { "braceleft", Qt::Key_BraceLeft }, + { "bar", Qt::Key_Bar }, + { "braceright", Qt::Key_BraceRight }, + { "asciitilde", Qt::Key_AsciiTilde }, + { "nobreakspace", Qt::Key_nobreakspace }, + { "exclamdown", Qt::Key_exclamdown }, + { "cent", Qt::Key_cent }, + { "sterling", Qt::Key_sterling }, + { "currency", Qt::Key_currency }, + { "yen", Qt::Key_yen }, + { "brokenbar", Qt::Key_brokenbar }, + { "section", Qt::Key_section }, + { "diaeresis", Qt::Key_diaeresis }, + { "copyright", Qt::Key_copyright }, + { "ordfeminine", Qt::Key_ordfeminine }, + { "guillemotleft", Qt::Key_guillemotleft }, + { "notsign", Qt::Key_notsign }, + { "hyphen", Qt::Key_hyphen }, + { "registered", Qt::Key_registered }, + { "macron", Qt::Key_macron }, + { "degree", Qt::Key_degree }, + { "plusminus", Qt::Key_plusminus }, + { "twosuperior", Qt::Key_twosuperior }, + { "threesuperior", Qt::Key_threesuperior }, + { "acute", Qt::Key_acute }, + { "mu", Qt::Key_mu }, + { "paragraph", Qt::Key_paragraph }, + { "periodcentered", Qt::Key_periodcentered }, + { "cedilla", Qt::Key_cedilla }, + { "onesuperior", Qt::Key_onesuperior }, + { "masculine", Qt::Key_masculine }, + { "guillemotright", Qt::Key_guillemotright }, + { "onequarter", Qt::Key_onequarter }, + { "onehalf", Qt::Key_onehalf }, + { "threequarters", Qt::Key_threequarters }, + { "questiondown", Qt::Key_questiondown }, + { "Agrave", Qt::Key_Agrave }, + { "Aacute", Qt::Key_Aacute }, + { "Acircumflex", Qt::Key_Acircumflex }, + { "Atilde", Qt::Key_Atilde }, + { "Adiaeresis", Qt::Key_Adiaeresis }, + { "Aring", Qt::Key_Aring }, + { "AE", Qt::Key_AE }, + { "Ccedilla", Qt::Key_Ccedilla }, + { "Egrave", Qt::Key_Egrave }, + { "Eacute", Qt::Key_Eacute }, + { "Ecircumflex", Qt::Key_Ecircumflex }, + { "Ediaeresis", Qt::Key_Ediaeresis }, + { "Igrave", Qt::Key_Igrave }, + { "Iacute", Qt::Key_Iacute }, + { "Icircumflex", Qt::Key_Icircumflex }, + { "Idiaeresis", Qt::Key_Idiaeresis }, + { "ETH", Qt::Key_ETH }, + { "Ntilde", Qt::Key_Ntilde }, + { "Ograve", Qt::Key_Ograve }, + { "Oacute", Qt::Key_Oacute }, + { "Ocircumflex", Qt::Key_Ocircumflex }, + { "Otilde", Qt::Key_Otilde }, + { "Odiaeresis", Qt::Key_Odiaeresis }, + { "multiply", Qt::Key_multiply }, + { "Ooblique", Qt::Key_Ooblique }, + { "Ugrave", Qt::Key_Ugrave }, + { "Uacute", Qt::Key_Uacute }, + { "Ucircumflex", Qt::Key_Ucircumflex }, + { "Udiaeresis", Qt::Key_Udiaeresis }, + { "Yacute", Qt::Key_Yacute }, + { "THORN", Qt::Key_THORN }, + { "ssharp", Qt::Key_ssharp }, + + { "agrave", 0xe0 /*Qt::Key_agrave*/ }, + { "aacute", 0xe1 /*Qt::Key_aacute*/ }, + { "acircumflex", 0xe2 /*Qt::Key_acircumflex*/ }, + { "atilde", 0xe3 /*Qt::Key_atilde*/ }, + { "adiaeresis", 0xe4 /*Qt::Key_adiaeresis*/ }, + { "aring", 0xe5 /*Qt::Key_aring*/ }, + { "ae", 0xe6 /*Qt::Key_ae*/ }, + { "ccedilla", 0xe7 /*Qt::Key_ccedilla*/ }, + { "egrave", 0xe8 /*Qt::Key_egrave*/ }, + { "eacute", 0xe9 /*Qt::Key_eacute*/ }, + { "ecircumflex", 0xea /*Qt::Key_ecircumflex*/ }, + { "ediaeresis", 0xeb /*Qt::Key_ediaeresis*/ }, + { "igrave", 0xec /*Qt::Key_igrave*/ }, + { "iacute", 0xed /*Qt::Key_iacute*/ }, + { "icircumflex", 0xee /*Qt::Key_icircumflex*/ }, + { "idiaeresis", 0xef /*Qt::Key_idiaeresis*/ }, + { "eth", 0xf0 /*Qt::Key_eth*/ }, + { "ntilde", 0xf1 /*Qt::Key_ntilde*/ }, + { "ograve", 0xf2 /*Qt::Key_ograve*/ }, + { "oacute", 0xf3 /*Qt::Key_oacute*/ }, + { "ocircumflex", 0xf4 /*Qt::Key_ocircumflex*/ }, + { "otilde", 0xf5 /*Qt::Key_otilde*/ }, + { "odiaeresis", 0xf6 /*Qt::Key_odiaeresis*/ }, + { "division", Qt::Key_division }, + { "oslash", 0xf8 /*Qt::Key_oslash*/ }, + { "ugrave", 0xf9 /*Qt::Key_ugrave*/ }, + { "uacute", 0xfa /*Qt::Key_uacute*/ }, + { "ucircumflex", 0xfb /*Qt::Key_ucircumflex*/ }, + { "udiaeresis", 0xfc /*Qt::Key_udiaeresis*/ }, + { "yacute", 0xfd /*Qt::Key_yacute*/ }, + { "thorn", 0xfe /*Qt::Key_thorn*/ }, + { "ydiaeresis", Qt::Key_ydiaeresis }, + + { "F1", Qt::Key_F1 }, + { "F2", Qt::Key_F2 }, + { "F3", Qt::Key_F3 }, + { "F4", Qt::Key_F4 }, + { "F5", Qt::Key_F5 }, + { "F6", Qt::Key_F6 }, + { "F7", Qt::Key_F7 }, + { "F8", Qt::Key_F8 }, + { "F9", Qt::Key_F9 }, + { "F10", Qt::Key_F10 }, + { "F11", Qt::Key_F11 }, + { "F12", Qt::Key_F12 }, + { "F13", Qt::Key_F13 }, + { "F14", Qt::Key_F14 }, + { "F15", Qt::Key_F15 }, + { "F16", Qt::Key_F16 }, + { "F17", Qt::Key_F17 }, + { "F18", Qt::Key_F18 }, + { "F19", Qt::Key_F19 }, + { "F20", Qt::Key_F20 }, + { "F21", Qt::Key_F21 }, + { "F22", Qt::Key_F22 }, + { "F23", Qt::Key_F23 }, + { "F24", Qt::Key_F24 }, + { "F25", Qt::Key_F25 }, + { "F26", Qt::Key_F26 }, + { "F27", Qt::Key_F27 }, + { "F28", Qt::Key_F28 }, + { "F29", Qt::Key_F29 }, + { "F30", Qt::Key_F30 }, + { "F31", Qt::Key_F31 }, + { "F32", Qt::Key_F32 }, + { "F33", Qt::Key_F33 }, + { "F34", Qt::Key_F34 }, + { "F35", Qt::Key_F35 }, + + { "BackSpace", Qt::Key_Backspace }, + { "Tab", Qt::Key_Tab }, + { "Escape", Qt::Key_Escape }, + { "Delete", Qt::Key_Backspace }, // what's the difference between "Delete" and "BackSpace"?? + { "Return", Qt::Key_Return }, + { "Break", Qt::Key_unknown }, //TODO: why doesn't Qt support the 'Break' key? + { "Caps_Lock", Qt::Key_CapsLock }, + { "Num_Lock", Qt::Key_NumLock }, + { "Scroll_Lock", Qt::Key_ScrollLock }, + { "Caps_On", Qt::Key_CapsLock }, + { "Compose", Qt::Key_Multi_key }, + { "Bare_Num_Lock", Qt::Key_NumLock }, + { "Find", Qt::Key_Home }, + { "Insert", Qt::Key_Insert }, + { "Remove", Qt::Key_Delete }, + { "Select", Qt::Key_End }, + { "Prior", Qt::Key_PageUp }, + { "Next", Qt::Key_PageDown }, + { "Help", Qt::Key_Help }, + { "Pause", Qt::Key_Pause }, + + { "KP_0", Qt::Key_0 | Qt::KeypadModifier }, + { "KP_1", Qt::Key_1 | Qt::KeypadModifier }, + { "KP_2", Qt::Key_2 | Qt::KeypadModifier }, + { "KP_3", Qt::Key_3 | Qt::KeypadModifier }, + { "KP_4", Qt::Key_4 | Qt::KeypadModifier }, + { "KP_5", Qt::Key_5 | Qt::KeypadModifier }, + { "KP_6", Qt::Key_6 | Qt::KeypadModifier }, + { "KP_7", Qt::Key_7 | Qt::KeypadModifier }, + { "KP_8", Qt::Key_8 | Qt::KeypadModifier }, + { "KP_9", Qt::Key_9 | Qt::KeypadModifier }, + { "KP_Add", Qt::Key_Plus | Qt::KeypadModifier }, + { "KP_Subtract", Qt::Key_Minus | Qt::KeypadModifier }, + { "KP_Multiply", Qt::Key_Asterisk | Qt::KeypadModifier }, + { "KP_Divide", Qt::Key_Slash | Qt::KeypadModifier }, + { "KP_Enter", Qt::Key_Enter | Qt::KeypadModifier }, + { "KP_Comma", Qt::Key_Comma | Qt::KeypadModifier }, + { "KP_Period", Qt::Key_Period | Qt::KeypadModifier }, + { "KP_MinPlus", Qt::Key_plusminus | Qt::KeypadModifier }, + + { "dead_grave", Qt::Key_Dead_Grave }, + { "dead_acute", Qt::Key_Dead_Acute }, + { "dead_circumflex", Qt::Key_Dead_Circumflex }, + { "dead_tilde", Qt::Key_Dead_Tilde }, + { "dead_diaeresis", Qt::Key_Dead_Diaeresis }, + { "dead_cedilla", Qt::Key_Dead_Cedilla }, + + { "Down", Qt::Key_Down }, + { "Left", Qt::Key_Left }, + { "Right", Qt::Key_Right }, + { "Up", Qt::Key_Up }, + { "Shift", Qt::Key_Shift }, + { "AltGr", Qt::Key_AltGr }, + { "Control", Qt::Key_Control }, + { "Alt", Qt::Key_Alt }, + { "ShiftL", Qt::Key_Shift }, + { "ShiftR", Qt::Key_Shift }, + { "CtrlL", Qt::Key_Control }, + { "CtrlR", Qt::Key_Control }, +}; + +static const int symbol_map_size = sizeof(symbol_map)/sizeof(symbol_map_t); + + +struct symbol_dead_unicode_t { + quint32 dead; + quint16 unicode; +}; + +static const symbol_dead_unicode_t symbol_dead_unicode[] = { + { Qt::Key_Dead_Grave, '`' }, + { Qt::Key_Dead_Acute, '\'' }, + { Qt::Key_Dead_Circumflex, '^' }, + { Qt::Key_Dead_Tilde, '~' }, + { Qt::Key_Dead_Diaeresis, '"' }, + { Qt::Key_Dead_Cedilla, ',' }, +}; + +static const int symbol_dead_unicode_size = sizeof(symbol_dead_unicode)/sizeof(symbol_dead_unicode_t); + + +struct symbol_synonyms_t { + const char *from; + const char *to; +}; + +static const symbol_synonyms_t symbol_synonyms[] = { + { "Control_h", "BackSpace" }, + { "Control_i", "Tab" }, + { "Control_j", "Linefeed" }, + { "Home", "Find" }, + { "End", "Select" }, + { "PageUp", "Prior" }, + { "PageDown", "Next" }, + { "multiplication", "multiply" }, + { "pound", "sterling" }, + { "pilcrow", "paragraph" }, + { "Oslash", "Ooblique" }, + { "Shift_L", "ShiftL" }, + { "Shift_R", "ShiftR" }, + { "Control_L", "CtrlL" }, + { "Control_R", "CtrlR" }, + { "AltL", "Alt" }, + { "AltR", "AltGr" }, + { "Alt_L", "Alt" }, + { "Alt_R", "AltGr" }, + { "AltGr_L", "Alt" }, + { "AltGr_R", "AltGr" }, + { "tilde", "asciitilde" }, + { "circumflex", "asciicircum" }, + { "dead_ogonek", "dead_cedilla" }, + { "dead_caron", "dead_circumflex" }, + { "dead_breve", "dead_tilde" }, + { "dead_doubleacute", "dead_tilde" }, + { "no-break_space", "nobreakspace" }, + { "paragraph_sign", "section" }, + { "soft_hyphen", "hyphen" }, + { "rightanglequote", "guillemotright" }, +}; + +static const int symbol_synonyms_size = sizeof(symbol_synonyms)/sizeof(symbol_synonyms_t); + +// makes the generated array in --header mode a bit more human readable +static bool operator<(const QWSKeyboard::Mapping &m1, const QWSKeyboard::Mapping &m2) +{ + return m1.keycode != m2.keycode ? m1.keycode < m2.keycode : m1.modifiers < m2.modifiers; +} + +class KeymapParser { +public: + KeymapParser(); + ~KeymapParser(); + + bool parseKmap(QFile *kmap); + bool generateQmap(QFile *qmap); + bool generateHeader(QFile *qmap); + + int parseWarningCount() const { return m_warning_count; } + +private: + bool parseSymbol(const QByteArray &str, const QTextCodec *codec, quint16 &unicode, quint32 &qtcode, quint8 &flags, quint16 &special); + bool parseCompose(const QByteArray &str, const QTextCodec *codec, quint16 &unicode); + bool parseModifier(const QByteArray &str, quint8 &modifier); + + void updateMapping(quint16 keycode = 0, quint8 modifiers = 0, quint16 unicode = 0xffff, quint32 qtcode = Qt::Key_unknown, quint8 flags = 0, quint16 = 0); + + static quint32 toQtModifiers(quint8 modifiers); + static QList<QByteArray> tokenize(const QByteArray &line); + + +private: + QList<QWSKeyboard::Mapping> m_keymap; + QList<QWSKeyboard::Composing> m_keycompose; + + int m_warning_count; +}; + + + +int main(int argc, char **argv) +{ + int header = 0; + if (argc >= 2 && !qstrcmp(argv[1], "--header")) + header = 1; + + if (argc < (3 + header)) { + fprintf(stderr, "Usage: kmap2qmap [--header] <kmap> [<additional kmaps> ...] <qmap>\n"); + fprintf(stderr, " --header can be used to generate Qt's default compiled in qmap.\n"); + return 1; + } + + QVector<QFile *> kmaps(argc - header - 2); + for (int i = 0; i < kmaps.size(); ++i) { + kmaps [i] = new QFile(QString::fromLocal8Bit(argv[i + 1 + header])); + + if (!kmaps[i]->open(QIODevice::ReadOnly)) { + fprintf(stderr, "Could not read from '%s'.\n", argv[i + 1 + header]); + return 2; + } + } + QFile *qmap = new QFile(QString::fromLocal8Bit(argv[argc - 1])); + + if (!qmap->open(QIODevice::WriteOnly)) { + fprintf(stderr, "Could not write to '%s'.\n", argv[argc - 1]); + return 3; + } + + KeymapParser p; + + for (int i = 0; i < kmaps.size(); ++i) { + if (!p.parseKmap(kmaps[i])) { + fprintf(stderr, "Parsing kmap '%s' failed.\n", qPrintable(kmaps[i]->fileName())); + return 4; + } + } + + if (p.parseWarningCount()) { + fprintf(stderr, "\nParsing the specified keymap(s) produced %d warning(s).\n" \ + "Your generated qmap might not be complete.\n", \ + p.parseWarningCount()); + } + if (!(header ? p.generateHeader(qmap) : p.generateQmap(qmap))) { + fprintf(stderr, "Generating the qmap failed.\n"); + return 5; + } + + qDeleteAll(kmaps); + delete qmap; + + return 0; +} + + +KeymapParser::KeymapParser() + : m_warning_count(0) +{ } + + +KeymapParser::~KeymapParser() +{ } + + +bool KeymapParser::generateHeader(QFile *f) +{ + QTextStream ts(f); + + ts << "#ifndef QWSKEYBOARDHANDLER_DEFAULTMAP_H" << endl; + ts << "#define QWSKEYBOARDHANDLER_DEFAULTMAP_H" << endl << endl; + + ts << "const QWSKeyboard::Mapping QWSKbPrivate::s_keymap_default[] = {" << endl; + + for (int i = 0; i < m_keymap.size(); ++i) { + const QWSKeyboard::Mapping &m = m_keymap.at(i); + QString s; + s.sprintf(" { %3d, 0x%04x, 0x%08x, 0x%02x, 0x%02x, 0x%04x },\n", m.keycode, m.unicode, m.qtcode, m.modifiers, m.flags, m.special); + ts << s; + } + + ts << "};" << endl << endl; + + ts << "const QWSKeyboard::Composing QWSKbPrivate::s_keycompose_default[] = {" << endl; + + for (int i = 0; i < m_keycompose.size(); ++i) { + const QWSKeyboard::Composing &c = m_keycompose.at(i); + QString s; + s.sprintf(" { 0x%04x, 0x%04x, 0x%04x },\n", c.first, c.second, c.result); + ts << s; + } + ts << "};" << endl << endl; + + ts << "#endif" << endl; + + return (ts.status() == QTextStream::Ok); +} + + +bool KeymapParser::generateQmap(QFile *f) +{ + QDataStream ds(f); + + ds << quint32(QWSKeyboard::FileMagic) << quint32(1 /* version */) << quint32(m_keymap.size()) << quint32(m_keycompose.size()); + + if (ds.status() != QDataStream::Ok) + return false; + + for (int i = 0; i < m_keymap.size(); ++i) + ds << m_keymap[i]; + + for (int i = 0; i < m_keycompose.size(); ++i) + ds << m_keycompose[i]; + + return (ds.status() == QDataStream::Ok); +} + + +QList<QByteArray> KeymapParser::tokenize(const QByteArray &line) +{ + bool quoted = false, separator = true; + QList<QByteArray> result; + QByteArray token; + + for (int i = 0; i < line.length(); ++i) { + QChar c = line.at(i); + + if (!quoted && c == '#' && separator) + break; + else if (!quoted && c == '"' && separator) + quoted = true; + else if (quoted && c == '"') + quoted = false; + else if (!quoted && c.isSpace()) { + separator = true; + if (!token.isEmpty()) { + result.append(token); + token.truncate(0); + } + } + else { + separator = false; + token.append(c); + } + } + if (!token.isEmpty()) + result.append(token); + return result; +} + + +#define parseWarning(s) do { qWarning("Warning: keymap file '%s', line %d: %s", qPrintable(f->fileName()), lineno, s); ++m_warning_count; } while (false) + +bool KeymapParser::parseKmap(QFile *f) +{ + QByteArray line; + int lineno = 0; + QList<int> keymaps; + QTextCodec *codec = QTextCodec::codecForName("iso8859-1"); + + for (int i = 0; i <= 256; ++i) + keymaps << i; + + while (!f->atEnd() && !f->error()) { + line = f->readLine(); + lineno++; + + QList<QByteArray> tokens = tokenize(line); + + if (tokens.isEmpty()) + continue; + + if (tokens[0] == "keymaps") { + keymaps.clear(); + + if (tokens.count() > 1) { + foreach (const QByteArray §ion, tokens[1].split(',')) { + int dashpos = section.indexOf('-'); + + //qWarning("Section %s", section.constData()); + int end = section.mid(dashpos + 1).toInt(); + int start = end; + if (dashpos > 0) + start = section.left(dashpos).toInt(); + + if (start <= end && start >=0 && end <= 256) { + for (int i = start; i <= end; ++i) { + //qWarning("appending keymap %d", i); + keymaps.append(i); + } + } + else + parseWarning("keymaps has an invalid range"); + } + qSort(keymaps); + } + else + parseWarning("keymaps with more than one argument"); + } + else if (tokens[0] == "alt_is_meta") { + // simply ignore it for now + } + else if (tokens[0] == "include") { + if (tokens.count() == 2) { + QString incname = QString::fromLocal8Bit(tokens[1]); + bool found = false; + QList<QDir> searchpath; + QFileInfo fi(*f); + + if (!incname.endsWith(QLatin1String(".kmap")) && !incname.endsWith(QLatin1String(".inc"))) + incname.append(QLatin1String(".inc")); + + QDir d = fi.dir(); + searchpath << d; + if (d.cdUp() && d.cd(QLatin1String("include"))) + searchpath << d; + searchpath << QDir::current(); + + foreach (const QDir &path, searchpath) { + QFile f2(path.filePath(incname)); + //qWarning(" -- trying to include %s", qPrintable(f2.fileName())); + if (f2.open(QIODevice::ReadOnly)) { + if (!parseKmap(&f2)) + parseWarning("could not parse keymap include"); + found = true; + } + } + + if (!found) + parseWarning("could not locate keymap include"); + } else + parseWarning("include doesn't have exactly one argument"); + } + else if (tokens[0] == "charset") { + if (tokens.count() == 2) { + codec = QTextCodec::codecForName(tokens[1]); + if (!codec) { + parseWarning("could not parse codec definition"); + codec = QTextCodec::codecForName("iso8859-1"); + } + } else + parseWarning("codec doesn't habe exactly one argument"); + } + else if (tokens[0] == "strings") { + // simply ignore those - they have no meaning for QWS + } + else if (tokens[0] == "compose") { + if (tokens.count() == 5 && tokens[3] == "to") { + QWSKeyboard::Composing c = { 0xffff, 0xffff, 0xffff }; + + if (!parseCompose(tokens[1], codec, c.first)) + parseWarning("could not parse first compose symbol"); + if (!parseCompose(tokens[2], codec, c.second)) + parseWarning("could not parse second compose symbol"); + if (!parseCompose(tokens[4], codec, c.result)) + parseWarning("could not parse resulting compose symbol"); + + if (c.first != 0xffff && c.second != 0xffff && c.result != 0xffff) { + m_keycompose << c; + } + } else + parseWarning("non-standard compose line"); + } + else { + int kcpos = tokens.indexOf("keycode"); + + if (kcpos >= 0 && kcpos < (tokens.count()-3) && tokens[kcpos+2] == "=") { + quint16 keycode = tokens[kcpos+1].toInt(); + + if (keycode <= 0 || keycode > 0x1ff /* KEY_MAX */) { + parseWarning("keycode out of range [0..0x1ff]"); + break; + } + + bool line_modifiers = (kcpos > 0); + + quint8 modifiers = 0; //, modifiers_mask = 0xff; + for (int i = 0; i < kcpos; ++i) { + quint8 mod; + if (!parseModifier(tokens[i], mod)) { + parseWarning("unknown modifier prefix for keycode"); + continue; + } + modifiers |= mod; + } + + int kccount = tokens.count() - kcpos - 3; // 3 : 'keycode' 'X' '=' + + if (line_modifiers && kccount > 1) { + parseWarning("line has modifiers, but more than one keycode"); + break; + } + + // only process one symbol when a prefix modifer was specified + for (int i = 0; i < (line_modifiers ? 1 : kccount); ++i) { + if (!line_modifiers) + modifiers = keymaps[i]; + + quint32 qtcode; + quint16 unicode; + quint16 special; + quint8 flags; + if (!parseSymbol(tokens[i + kcpos + 3], codec, unicode, qtcode, flags, special)) { + parseWarning((QByteArray("symbol could not be parsed: ") + tokens[i + kcpos + 3]).constData()); + break; + } + + if (qtcode == Qt::Key_unknown && unicode == 0xffff) // VoidSymbol + continue; + + if (!line_modifiers && kccount == 1) { + if ((unicode >= 'A' && unicode <= 'Z') || (unicode >= 'a' && unicode <= 'z')) { + quint16 other_unicode = (unicode >= 'A' && unicode <= 'Z') ? unicode - 'A' + 'a' : unicode - 'a' + 'A'; + quint16 lower_unicode = (unicode >= 'A' && unicode <= 'Z') ? unicode - 'A' + 'a' : unicode; + + // a single a-z|A-Z value results in a very flags mapping: see below + + updateMapping(keycode, QWSKeyboard::ModPlain, unicode, qtcode, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModShift, other_unicode, qtcode, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModAltGr, unicode, qtcode, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAltGr | QWSKeyboard::ModShift, other_unicode, qtcode, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModControl, lower_unicode, qtcode | Qt::ControlModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModControl | QWSKeyboard::ModShift, lower_unicode, qtcode | Qt::ControlModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModControl | QWSKeyboard::ModAltGr, lower_unicode, qtcode | Qt::ControlModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModControl | QWSKeyboard::ModAltGr | QWSKeyboard::ModShift, lower_unicode, qtcode | Qt::ControlModifier, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModAlt, unicode, qtcode | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModShift, unicode, qtcode | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModAltGr, unicode, qtcode | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModAltGr | QWSKeyboard::ModShift, unicode, qtcode | Qt::AltModifier, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModControl, lower_unicode, qtcode | Qt::ControlModifier | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModControl | QWSKeyboard::ModShift, lower_unicode, qtcode | Qt::ControlModifier | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModControl | QWSKeyboard::ModAltGr, lower_unicode, qtcode | Qt::ControlModifier | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModControl | QWSKeyboard::ModAltGr | QWSKeyboard::ModShift, lower_unicode, qtcode | Qt::ControlModifier | Qt::AltModifier, flags, 0); + } + else { + // a single value results in that mapping regardless of the modifier + //for (int mod = 0; mod <= 255; ++mod) + // updateMapping(keycode, quint8(mod), unicode, qtcode | toQtModifiers(mod), flags, special); + + // we can save a lot of space in the qmap, since we do that anyway in the kbd handler: + updateMapping(keycode, QWSKeyboard::ModPlain, unicode, qtcode, flags, special); + } + } + else { + // "normal" mapping + updateMapping(keycode, modifiers, unicode, qtcode, flags, special); + } + } + } + } + } + qSort(m_keymap); + return !m_keymap.isEmpty(); +} + + + +void KeymapParser::updateMapping(quint16 keycode, quint8 modifiers, quint16 unicode, quint32 qtcode, quint8 flags, quint16 special) +{ + for (int i = 0; i < m_keymap.size(); ++i) { + QWSKeyboard::Mapping &m = m_keymap[i]; + + if (m.keycode == keycode && m.modifiers == modifiers) { + m.unicode = unicode; + m.qtcode = qtcode; + m.flags = flags; + m.special = special; + return; + } + } + QWSKeyboard::Mapping m = { keycode, unicode, qtcode, modifiers, flags, special }; + m_keymap << m; +} + + +quint32 KeymapParser::toQtModifiers(quint8 modifiers) +{ + quint32 qtmodifiers = Qt::NoModifier; + + for (int i = 0; i < modifier_map_size; ++i) { + if (modifiers & modifier_map[i].modifier) + qtmodifiers |= modifier_map[i].qtmodifier; + } + return qtmodifiers; +} + + +bool KeymapParser::parseModifier(const QByteArray &str, quint8 &modifier) +{ + QByteArray lstr = str.toLower(); + + for (int i = 0; i < modifier_map_size; ++i) { + if (lstr == modifier_map[i].symbol) { + modifier = modifier_map[i].modifier; + return true; + } + } + return false; +} + + +bool KeymapParser::parseCompose(const QByteArray &str, const QTextCodec *codec, quint16 &unicode) +{ + if (str == "'\\''") { + unicode = '\''; + return true; + } else if (str.length() == 3 && str.startsWith('\'') && str.endsWith('\'')) { + QString temp = codec->toUnicode(str.constData() + 1, str.length() - 2); + if (temp.length() != 1) + return false; + unicode = temp[0].unicode(); + return true; + } else { + quint32 code = str.toUInt(); + if (code > 255) + return false; + char c[2]; + c[0] = char(code); + c[1] = 0; + QString temp = codec->toUnicode(c); + if (temp.length() != 1) + return false; + unicode = temp[0].unicode(); + return true; + } +} + + +bool KeymapParser::parseSymbol(const QByteArray &str, const QTextCodec * /*codec*/, quint16 &unicode, quint32 &qtcode, quint8 &flags, quint16 &special) +{ + flags = (str[0] == '+') ? QWSKeyboard::IsLetter : 0; + QByteArray sym = (flags & QWSKeyboard::IsLetter) ? str.right(str.length() - 1) : str; + + special = 0; + qtcode = Qt::Key_unknown; + unicode = 0xffff; + + if (sym == "VoidSymbol" || sym == "nul") + return true; + + bool try_to_find_qtcode = false; + + if (sym[0] >= '0' && sym[0] <= '9') { // kernel internal action number + return false; + } else if (sym.length() == 6 && sym[1] == '+' && (sym[0] == 'U' || sym[0] == 'u')) { // unicode + bool ok; + unicode = sym.mid(2).toUInt(&ok, 16); + if (!ok) + return false; + try_to_find_qtcode = true; + } else { // symbolic + for (int i = 0; i < symbol_synonyms_size; ++i) { + if (sym == symbol_synonyms[i].from) { + sym = symbol_synonyms[i].to; + break; + } + } + + quint32 qtmod = 0; + + // parse prepended modifiers + forever { + int underpos = sym.indexOf('_'); + + if (underpos <= 0) + break; + QByteArray modsym = sym.left(underpos); + QByteArray nomodsym = sym.mid(underpos + 1); + quint8 modifier = 0; + + if (!parseModifier(modsym, modifier)) + break; + + qtmod |= toQtModifiers(modifier); + sym = nomodsym; + } + + if (qtcode == Qt::Key_unknown) { + quint8 modcode; + // check if symbol is a modifier + if (parseModifier(sym, modcode)) { + special = modcode; + flags |= QWSKeyboard::IsModifier; + } + + // map symbol to Qt key code + for (int i = 0; i < symbol_map_size; ++i) { + if (sym == symbol_map[i].symbol) { + qtcode = symbol_map[i].qtcode; + break; + } + } + + // a-zA-Z is not in the table to save space + if (qtcode == Qt::Key_unknown && sym.length() == 1) { + char letter = sym.at(0); + + if (letter >= 'a' && letter <= 'z') { + qtcode = Qt::Key_A + letter - 'a'; + unicode = letter; + } + else if (letter >= 'A' && letter <= 'Z') { + qtcode = Qt::Key_A + letter - 'A'; + unicode = letter; + } + } + // System keys + if (qtcode == Qt::Key_unknown) { + quint16 sys = 0; + + if (sym == "Decr_Console") { + sys = QWSKeyboard::SystemConsolePrevious; + } else if (sym == "Incr_Console") { + sys = QWSKeyboard::SystemConsoleNext; + } else if (sym.startsWith("Console_")) { + int console = sym.mid(8).toInt() - 1; + if (console >= 0 && console <= (QWSKeyboard::SystemConsoleLast - QWSKeyboard::SystemConsoleFirst)) { + sys = QWSKeyboard::SystemConsoleFirst + console; + } + } else if (sym == "Boot") { + sys = QWSKeyboard::SystemReboot; + } else if (sym == "QtZap") { + sys = QWSKeyboard::SystemZap; + } + + if (sys) { + flags |= QWSKeyboard::IsSystem; + special = sys; + qtcode = Qt::Key_Escape; // just a dummy + } + } + + // map Qt key codes in the iso-8859-1 range to unicode + if (qtcode != Qt::Key_unknown && unicode == 0xffff) { + quint32 qtcode_no_mod = qtcode & ~(Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier); + if (qtcode_no_mod <= 0x000000ff) // iso-8859-1 + unicode = quint16(qtcode_no_mod); + } + + // flag dead keys + if (qtcode >= Qt::Key_Dead_Grave && qtcode <= Qt::Key_Dead_Horn) { + flags = QWSKeyboard::IsDead; + + for (int i = 0; i < symbol_dead_unicode_size; ++i) { + if (symbol_dead_unicode[i].dead == qtcode) { + unicode = symbol_dead_unicode[i].unicode; + break; + } + } + } + } + if ((qtcode == Qt::Key_unknown) && (unicode == 0xffff)) + return false; + + qtcode |= qtmod; + } + + // map unicode in the iso-8859-1 range to Qt key codes + if (unicode >= 0x0020 && unicode <= 0x00ff && qtcode == Qt::Key_unknown) + qtcode = unicode; // iso-8859-1 + + return true; +} + diff --git a/tools/linguist/lconvert/main.cpp b/tools/linguist/lconvert/main.cpp index 9ccc60e..bdc6c9a 100644 --- a/tools/linguist/lconvert/main.cpp +++ b/tools/linguist/lconvert/main.cpp @@ -51,21 +51,16 @@ static int usage(const QStringList &args) Q_UNUSED(args); QString loaders; - QString savers; - QString line = QString(QLatin1String(" %1 - %2\n")); - foreach (Translator::FileFormat format, Translator::registeredFileFormats()) { + QString line(QLatin1String(" %1 - %2\n")); + foreach (Translator::FileFormat format, Translator::registeredFileFormats()) loaders += line.arg(format.extension, -5).arg(format.description); - if (format.fileType != Translator::FileFormat::SourceCode) - savers += line.arg(format.extension, -5).arg(format.description); - } qWarning("%s", qPrintable(QString(QLatin1String("\nUsage:\n" " lconvert [options] <infile> [<infile>...]\n\n" "lconvert is part of Qt's Linguist tool chain. It can be used as a\n" - "stand-alone tool to convert translation data files from one of the\n" - "following input formats\n\n%1\n" - "to one of the following output formats\n\n%2\n" - "If multiple input files are specified the translations are merged with\n" + "stand-alone tool to convert and filter translation data files.\n" + "The following file formats are supported:\n\n%1\n" + "If multiple input files are specified, they are merged with\n" "translations from later files taking precedence.\n\n" "Options:\n" " -h\n" @@ -93,7 +88,7 @@ static int usage(const QStringList &args) " Note: this implies --no-obsolete.\n\n" " --source-language <language>[_<region>]\n" " Specify/override the language of the source strings. Defaults to\n" - " POSIX if not specified and the file does not name it yet.\n" + " POSIX if not specified and the file does not name it yet.\n\n" " --target-language <language>[_<region>]\n" " Specify/override the language of the translation.\n" " The target language is guessed from the file name if this option\n" @@ -109,7 +104,7 @@ static int usage(const QStringList &args) " 0 on success\n" " 1 on command line parse failures\n" " 2 on read failures\n" - " 3 on write failures\n")).arg(loaders).arg(savers))); + " 3 on write failures\n")).arg(loaders))); return 1; } diff --git a/tools/linguist/linguist/main.cpp b/tools/linguist/linguist/main.cpp index 018fbc5..a6a0d27 100644 --- a/tools/linguist/linguist/main.cpp +++ b/tools/linguist/linguist/main.cpp @@ -80,12 +80,15 @@ int main(int argc, char **argv) } QTranslator translator; - translator.load(QLatin1String("linguist_") + QLocale::system().name(), resourceDir); - app.installTranslator(&translator); - QTranslator qtTranslator; - qtTranslator.load(QLatin1String("qt_") + QLocale::system().name(), resourceDir); - app.installTranslator(&qtTranslator); + QString sysLocale = QLocale::system().name(); + if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir)) { + app.installTranslator(&translator); + if (qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) + app.installTranslator(&qtTranslator); + else + app.removeTranslator(&translator); + } app.setOrganizationName(QLatin1String("Trolltech")); app.setApplicationName(QLatin1String("Linguist")); diff --git a/tools/linguist/linguist/messageeditor.cpp b/tools/linguist/linguist/messageeditor.cpp index dc8b8e4..3fc91e3 100644 --- a/tools/linguist/linguist/messageeditor.cpp +++ b/tools/linguist/linguist/messageeditor.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -/* TRANSLATOR MsgEdit +/* TRANSLATOR MessageEditor This is the right panel of the main window. */ diff --git a/tools/linguist/linguist/phrasebookbox.cpp b/tools/linguist/linguist/phrasebookbox.cpp index 50749d7..d3bb937 100644 --- a/tools/linguist/linguist/phrasebookbox.cpp +++ b/tools/linguist/linguist/phrasebookbox.cpp @@ -56,13 +56,15 @@ QT_BEGIN_NAMESPACE -#define NewPhrase tr("(New Entry)") - PhraseBookBox::PhraseBookBox(PhraseBook *phraseBook, QWidget *parent) : QDialog(parent), m_phraseBook(phraseBook), m_translationSettingsDialog(0) { + +// This definition needs to be within class context for lupdate to find it +#define NewPhrase tr("(New Entry)") + setupUi(this); setWindowTitle(tr("%1[*] - Qt Linguist").arg(m_phraseBook->friendlyPhraseBookName())); setWindowModified(m_phraseBook->isModified()); diff --git a/tools/linguist/lrelease/main.cpp b/tools/linguist/lrelease/main.cpp index 3bcc998..7d0452f 100644 --- a/tools/linguist/lrelease/main.cpp +++ b/tools/linguist/lrelease/main.cpp @@ -40,7 +40,6 @@ ****************************************************************************/ #include "translator.h" -#include "translatortools.h" #include "profileevaluator.h" #include <QtCore/QCoreApplication> diff --git a/tools/linguist/lupdate/cpp.cpp b/tools/linguist/lupdate/cpp.cpp new file mode 100644 index 0000000..42aa2f0 --- /dev/null +++ b/tools/linguist/lupdate/cpp.cpp @@ -0,0 +1,1818 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Linguist 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdate.h" + +#include <translator.h> + +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QStack> +#include <QtCore/QString> +#include <QtCore/QTextCodec> +#include <QtCore/QTextStream> + +#include <ctype.h> // for isXXX() + +QT_BEGIN_NAMESPACE + +/* qmake ignore Q_OBJECT */ + +static const char MagicComment[] = "TRANSLATOR "; + +static const int yyIdentMaxLen = 128; +static const int yyCommentMaxLen = 65536; +static const int yyStringMaxLen = 65536; + +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) +#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s))) + +//#define DIAGNOSE_RETRANSLATABILITY // FIXME: should make a runtime option of this + +uint qHash(const QStringList &qsl) +{ + uint hash = 0; + foreach (const QString &qs, qsl) { + hash ^= qHash(qs) ^ 0xa09df22f; + hash = (hash << 13) | (hash >> 19); + } + return hash; +} + +struct Namespace { + + Namespace() : + isClass(false), + hasTrFunctions(false), needsTrFunctions(false), complained(false) + {} + + QString name; + QMap<QString, Namespace *> children; + QMap<QString, QStringList> aliases; + QSet<QStringList> usings; + + int fileId; + + bool isClass; + + bool hasTrFunctions; + bool needsTrFunctions; + bool complained; // ... that tr functions are missing. +}; + +typedef QList<Namespace *> NamespaceList; + +struct ParseResults { + + ParseResults() + { + static int nextFileId; + rootNamespace.fileId = nextFileId++; + tor = 0; + } + bool detachNamespace(Namespace **that); + Namespace *include(Namespace *that, const Namespace *other); + void unite(const ParseResults *other); + + Namespace rootNamespace; + Translator *tor; + QSet<QString> allIncludes; +}; + +typedef QHash<QString, const ParseResults *> ParseResultHash; + +class CppFiles { + +public: + static const ParseResults *getResults(const QString &cleanFile); + static void setResults(const QString &cleanFile, const ParseResults *results); + static bool isBlacklisted(const QString &cleanFile); + static void setBlacklisted(const QString &cleanFile); + +private: + static ParseResultHash &parsedFiles(); + static QSet<QString> &blacklistedFiles(); +}; + +class CppParser { + +public: + CppParser(ParseResults *results = 0); + void setInput(const QString &in); + void setInput(QTextStream &ts, const QString &fileName); + void setTranslator(Translator *tor) { results->tor = tor; } + void parse(const QString &initialContext, ConversionData &cd, QSet<QString> &inclusions); + void parseInternal(ConversionData &cd, QSet<QString> &inclusions); + const ParseResults *getResults() const { return results; } + void deleteResults() { delete results; } + +private: + struct SavedState { + QStringList namespaces; + QStack<int> namespaceDepths; + QStringList functionContext; + QString functionContextUnresolved; + QString pendingContext; + }; + + struct IfdefState { + IfdefState() {} + IfdefState(int _braceDepth, int _parenDepth) : + braceDepth(_braceDepth), + parenDepth(_parenDepth), + elseLine(-1) + {} + + SavedState state; + int braceDepth, braceDepth1st; + int parenDepth, parenDepth1st; + int elseLine; + }; + + uint getChar(); + uint getToken(); + bool match(uint t); + bool matchString(QString *s); + bool matchEncoding(bool *utf8); + bool matchInteger(qlonglong *number); + bool matchStringOrNull(QString *s); + bool matchExpression(); + + QString transcode(const QString &str, bool utf8); + void recordMessage( + int line, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, bool utf8, bool plural); + + void processInclude(const QString &file, ConversionData &cd, + QSet<QString> &inclusions); + + void saveState(SavedState *state); + void loadState(const SavedState *state); + + static QString stringifyNamespace(const NamespaceList &namespaces); + static QStringList stringListifyNamespace(const NamespaceList &namespaces); + void modifyNamespace(NamespaceList *namespaces); + NamespaceList resolveNamespaces(const QStringList &segments); + bool qualifyOne(const NamespaceList &namespaces, int nsIdx, const QString &segment, + NamespaceList *resolved); + bool fullyQualify(const NamespaceList &namespaces, const QStringList &segments, + bool isDeclaration, + NamespaceList *resolved, QStringList *unresolved); + void enterNamespace(NamespaceList *namespaces, const QString &name); + void truncateNamespaces(NamespaceList *namespaces, int lenght); + + enum { + Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return, + Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8, + Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS, + Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon, + Tok_Equals, + Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon, + Tok_Integer = 40, + Tok_QuotedInclude = 50, Tok_AngledInclude, + Tok_Other = 99 + }; + + // Tokenizer state + QString yyFileName; + int yyCh; + bool yyAtNewline; + bool yyCodecIsUtf8; + bool yyForceUtf8; + QString yyIdent; + QString yyComment; + QString yyString; + qlonglong yyInteger; + QStack<IfdefState> yyIfdefStack; + int yyBraceDepth; + int yyParenDepth; + int yyLineNo; + int yyCurLineNo; + int yyBraceLineNo; + int yyParenLineNo; + + // the string to read from and current position in the string + QTextCodec *yySourceCodec; + bool yySourceIsUnicode; + QString yyInStr; + int yyInPos; + + // Parser state + uint yyTok; + + NamespaceList namespaces; + QStack<int> namespaceDepths; + NamespaceList functionContext; + QString functionContextUnresolved; + QString prospectiveContext; + QString pendingContext; + ParseResults *results; + bool directInclude; + + SavedState savedState; + int yyMinBraceDepth; + bool inDefine; +}; + +CppParser::CppParser(ParseResults *_results) +{ + if (_results) { + results = _results; + directInclude = true; + } else { + results = new ParseResults; + directInclude = false; + } + yyInPos = 0; + yyBraceDepth = 0; + yyParenDepth = 0; + yyCurLineNo = 1; + yyBraceLineNo = 1; + yyParenLineNo = 1; + yyAtNewline = true; + yyMinBraceDepth = 0; + inDefine = false; +} + +void CppParser::setInput(const QString &in) +{ + yyInStr = in; + yyFileName = QString(); + yySourceCodec = 0; + yySourceIsUnicode = true; + yyForceUtf8 = true; +} + +void CppParser::setInput(QTextStream &ts, const QString &fileName) +{ + yyInStr = ts.readAll(); + yyFileName = fileName; + yySourceCodec = ts.codec(); + yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-"); + yyForceUtf8 = false; +} + +/* + The first part of this source file is the C++ tokenizer. We skip + most of C++; the only tokens that interest us are defined here. + Thus, the code fragment + + int main() + { + printf("Hello, world!\n"); + return 0; + } + + is broken down into the following tokens (Tok_ omitted): + + Ident Ident LeftParen RightParen + LeftBrace + Ident LeftParen String RightParen Semicolon + return Semicolon + RightBrace. + + The 0 doesn't produce any token. +*/ + +uint CppParser::getChar() +{ + forever { + if (yyInPos >= yyInStr.size()) + return EOF; + uint c = yyInStr[yyInPos++].unicode(); + if (c == '\\' && yyInPos < yyInStr.size() && yyInStr[yyInPos].unicode() == '\n') { + ++yyCurLineNo; + ++yyInPos; + continue; + } + if (c == '\n') { + ++yyCurLineNo; + yyAtNewline = true; + } else if (c != ' ' && c != '\t' && c != '#') { + yyAtNewline = false; + } + return c; + } +} + +uint CppParser::getToken() +{ + restart: + yyIdent.clear(); + yyComment.clear(); + yyString.clear(); + + while (yyCh != EOF) { + yyLineNo = yyCurLineNo; + + if (yyCh == '#' && yyAtNewline) { + /* + Early versions of lupdate complained about + unbalanced braces in the following code: + + #ifdef ALPHA + while (beta) { + #else + while (gamma) { + #endif + delta; + } + + The code contains, indeed, two opening braces for + one closing brace; yet there's no reason to panic. + + The solution is to remember yyBraceDepth as it was + when #if, #ifdef or #ifndef was met, and to set + yyBraceDepth to that value when meeting #elif or + #else. + */ + do { + yyCh = getChar(); + } while (isspace(yyCh) && yyCh != '\n'); + + switch (yyCh) { + case 'd': // define + // Skip over the name of the define to avoid it being interpreted as c++ code + do { // Rest of "define" + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (!isspace(yyCh)); + do { // Space beween "define" and macro name + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (isspace(yyCh)); + do { // Macro name + if (yyCh == '(') { + // Argument list. Follows the name without a space, and no + // paren nesting is possible. + do { + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (yyCh != ')'); + break; + } + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (!isspace(yyCh)); + do { // Shortcut the immediate newline case if no comments follow. + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (isspace(yyCh)); + + saveState(&savedState); + yyMinBraceDepth = yyBraceDepth; + inDefine = true; + goto restart; + case 'i': + yyCh = getChar(); + if (yyCh == 'f') { + // if, ifdef, ifndef + yyIfdefStack.push(IfdefState(yyBraceDepth, yyParenDepth)); + yyCh = getChar(); + } else if (yyCh == 'n') { + // include + do { + yyCh = getChar(); + } while (yyCh != EOF && !isspace(yyCh)); + do { + yyCh = getChar(); + } while (isspace(yyCh)); + int tChar; + if (yyCh == '"') + tChar = '"'; + else if (yyCh == '<') + tChar = '>'; + else + break; + forever { + yyCh = getChar(); + if (yyCh == EOF || yyCh == '\n') + break; + if (yyCh == tChar) { + yyCh = getChar(); + break; + } + yyString += yyCh; + } + return (tChar == '"') ? Tok_QuotedInclude : Tok_AngledInclude; + } + break; + case 'e': + yyCh = getChar(); + if (yyCh == 'l') { + // elif, else + if (!yyIfdefStack.isEmpty()) { + IfdefState &is = yyIfdefStack.top(); + if (is.elseLine != -1) { + if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) + qWarning("%s:%d: Parenthesis/brace mismatch between " + "#if and #else branches; using #if branch\n", + qPrintable(yyFileName), is.elseLine); + } else { + is.braceDepth1st = yyBraceDepth; + is.parenDepth1st = yyParenDepth; + saveState(&is.state); + } + is.elseLine = yyLineNo; + yyBraceDepth = is.braceDepth; + yyParenDepth = is.parenDepth; + } + yyCh = getChar(); + } else if (yyCh == 'n') { + // endif + if (!yyIfdefStack.isEmpty()) { + IfdefState is = yyIfdefStack.pop(); + if (is.elseLine != -1) { + if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) + qWarning("%s:%d: Parenthesis/brace mismatch between " + "#if and #else branches; using #if branch\n", + qPrintable(yyFileName), is.elseLine); + yyBraceDepth = is.braceDepth1st; + yyParenDepth = is.parenDepth1st; + loadState(&is.state); + } + } + yyCh = getChar(); + } + break; + } + // Optimization: skip over rest of preprocessor directive + do { + if (yyCh == '/') { + yyCh = getChar(); + if (yyCh == '/') { + do { + yyCh = getChar(); + } while (yyCh != EOF && yyCh != '\n'); + break; + } else if (yyCh == '*') { + bool metAster = false; + + forever { + yyCh = getChar(); + if (yyCh == EOF) { + qWarning("%s:%d: Unterminated C++ comment\n", + qPrintable(yyFileName), yyLineNo); + break; + } + + if (yyCh == '*') { + metAster = true; + } else if (metAster && yyCh == '/') { + yyCh = getChar(); + break; + } else { + metAster = false; + } + } + } + } else { + yyCh = getChar(); + } + } while (yyCh != '\n' && yyCh != EOF); + yyCh = getChar(); + } else if (isalpha(yyCh) || yyCh == '_') { + do { + yyIdent += yyCh; + yyCh = getChar(); + } while (isalnum(yyCh) || yyCh == '_'); + + //qDebug() << "IDENT: " << yyIdent; + + switch (yyIdent.at(0).unicode()) { + case 'Q': + if (yyIdent == QLatin1String("Q_OBJECT")) + return Tok_Q_OBJECT; + if (yyIdent == QLatin1String("Q_DECLARE_TR_FUNCTIONS")) + return Tok_Q_DECLARE_TR_FUNCTIONS; + if (yyIdent == QLatin1String("QT_TR_NOOP")) + return Tok_tr; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP")) + return Tok_translate; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3")) + return Tok_translate; + if (yyIdent == QLatin1String("QT_TR_NOOP_UTF8")) + return Tok_trUtf8; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP_UTF8")) + return Tok_translateUtf8; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3_UTF8")) + return Tok_translateUtf8; + break; + case 'T': + // TR() for when all else fails + if (yyIdent.compare(QLatin1String("TR"), Qt::CaseInsensitive) == 0) { + return Tok_tr; + } + break; + case 'c': + if (yyIdent == QLatin1String("class")) + return Tok_class; + break; + case 'f': + /* + QTranslator::findMessage() has the same parameters as + QApplication::translate(). + */ + if (yyIdent == QLatin1String("findMessage")) + return Tok_translate; + if (yyIdent == QLatin1String("friend")) + return Tok_friend; + break; + case 'n': + if (yyIdent == QLatin1String("namespace")) + return Tok_namespace; + break; + case 'r': + if (yyIdent == QLatin1String("return")) + return Tok_return; + break; + case 's': + if (yyIdent == QLatin1String("struct")) + return Tok_class; + break; + case 't': + if (yyIdent == QLatin1String("tr")) { + return Tok_tr; + } + if (yyIdent == QLatin1String("trUtf8")) { + return Tok_trUtf8; + } + if (yyIdent == QLatin1String("translate")) { + return Tok_translate; + } + break; + case 'u': + if (yyIdent == QLatin1String("using")) + return Tok_using; + break; + } + return Tok_Ident; + } else { + switch (yyCh) { + case '\n': + if (inDefine) { + loadState(&savedState); + prospectiveContext.clear(); + yyBraceDepth = yyMinBraceDepth; + yyMinBraceDepth = 0; + inDefine = false; + } + yyCh = getChar(); + break; + case '/': + yyCh = getChar(); + if (yyCh == '/') { + do { + yyCh = getChar(); + if (yyCh == EOF) + break; + yyComment.append(yyCh); + } while (yyCh != '\n'); + } else if (yyCh == '*') { + bool metAster = false; + + forever { + yyCh = getChar(); + if (yyCh == EOF) { + qWarning("%s:%d: Unterminated C++ comment\n", + qPrintable(yyFileName), yyLineNo); + return Tok_Comment; + } + yyComment.append(yyCh); + + if (yyCh == '*') + metAster = true; + else if (metAster && yyCh == '/') + break; + else + metAster = false; + } + yyCh = getChar(); + yyComment.chop(2); + } + return Tok_Comment; + case '"': + yyCh = getChar(); + while (yyCh != EOF && yyCh != '\n' && yyCh != '"') { + if (yyCh == '\\') { + yyCh = getChar(); + if (yyCh == EOF || yyCh == '\n') + break; + if (yyString.size() < yyStringMaxLen) { + yyString.append(QLatin1Char('\\')); + yyString.append(yyCh); + } + } else { + if (yyString.size() < yyStringMaxLen) + yyString.append(yyCh); + } + yyCh = getChar(); + } + + if (yyCh != '"') + qWarning("%s:%d: Unterminated C++ string\n", + qPrintable(yyFileName), yyLineNo); + else + yyCh = getChar(); + return Tok_String; + case '-': + yyCh = getChar(); + if (yyCh == '>') { + yyCh = getChar(); + return Tok_Arrow; + } + break; + case ':': + yyCh = getChar(); + if (yyCh == ':') { + yyCh = getChar(); + return Tok_ColonColon; + } + return Tok_Colon; + // Incomplete: '<' might be part of '<=' or of template syntax. + // The main intent of not completely ignoring it is to break + // parsing of things like std::cout << QObject::tr() as + // context std::cout::QObject (see Task 161106) + case '=': + yyCh = getChar(); + return Tok_Equals; + case '>': + case '<': + yyCh = getChar(); + return Tok_Other; + case '\'': + yyCh = getChar(); + if (yyCh == '\\') + yyCh = getChar(); + + forever { + if (yyCh == EOF || yyCh == '\n') { + qWarning("%s:%d: Unterminated C++ character\n", + qPrintable(yyFileName), yyLineNo); + break; + } + yyCh = getChar(); + if (yyCh == '\'') { + yyCh = getChar(); + break; + } + } + break; + case '{': + if (yyBraceDepth == 0) + yyBraceLineNo = yyCurLineNo; + yyBraceDepth++; + yyCh = getChar(); + return Tok_LeftBrace; + case '}': + if (yyBraceDepth == yyMinBraceDepth) { + if (!inDefine) + qWarning("%s:%d: Excess closing brace in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyCurLineNo); + // Avoid things getting messed up even more + yyCh = getChar(); + return Tok_Semicolon; + } + yyBraceDepth--; + yyCh = getChar(); + return Tok_RightBrace; + case '(': + if (yyParenDepth == 0) + yyParenLineNo = yyCurLineNo; + yyParenDepth++; + yyCh = getChar(); + return Tok_LeftParen; + case ')': + if (yyParenDepth == 0) + qWarning("%s:%d: Excess closing parenthesis in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyCurLineNo); + else + yyParenDepth--; + yyCh = getChar(); + return Tok_RightParen; + case ',': + yyCh = getChar(); + return Tok_Comma; + case ';': + yyCh = getChar(); + return Tok_Semicolon; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + QByteArray ba; + ba += yyCh; + yyCh = getChar(); + bool hex = yyCh == 'x'; + if (hex) { + ba += yyCh; + yyCh = getChar(); + } + while (hex ? isxdigit(yyCh) : isdigit(yyCh)) { + ba += yyCh; + yyCh = getChar(); + } + bool ok; + yyInteger = ba.toLongLong(&ok); + if (ok) + return Tok_Integer; + break; + } + default: + yyCh = getChar(); + break; + } + } + } + return Tok_Eof; +} + +/* + The second part of this source file are namespace/class related + utilities for the third part. +*/ + +void CppParser::saveState(SavedState *state) +{ + state->namespaces = stringListifyNamespace(namespaces); + state->namespaceDepths = namespaceDepths; + state->functionContext = stringListifyNamespace(functionContext); + state->functionContextUnresolved = functionContextUnresolved; + state->pendingContext = pendingContext; +} + +void CppParser::loadState(const SavedState *state) +{ + namespaces = resolveNamespaces(state->namespaces); + namespaceDepths = state->namespaceDepths; + functionContext = resolveNamespaces(state->functionContext); + functionContextUnresolved = state->functionContextUnresolved; + pendingContext = state->pendingContext; +} + +bool ParseResults::detachNamespace(Namespace **that) +{ + if ((*that)->fileId != rootNamespace.fileId) { + Namespace *newThat = new Namespace; + *newThat = **that; + newThat->fileId = rootNamespace.fileId; + *that = newThat; + return true; + } + return false; +} + +Namespace *ParseResults::include(Namespace *that, const Namespace *other) +{ + Namespace *origThat = that; + foreach (Namespace *otherSub, other->children) { + if (Namespace *thisSub = that->children.value(otherSub->name)) { + // Don't make these cause a detach - it's best + // (though not necessary) if they are shared + thisSub->isClass |= otherSub->isClass; + thisSub->hasTrFunctions |= otherSub->hasTrFunctions; + thisSub->needsTrFunctions |= otherSub->needsTrFunctions; + thisSub->complained |= otherSub->complained; + + if (Namespace *newSub = include(thisSub, otherSub)) { + thisSub = newSub; + detachNamespace(&that); + that->children[thisSub->name] = thisSub; + } + } else { + detachNamespace(&that); + that->children[otherSub->name] = otherSub; + } + } + if ((that->aliases != other->aliases && !other->aliases.isEmpty()) + || (that->usings != other->usings && !other->usings.isEmpty())) { + detachNamespace(&that); + that->aliases.unite(other->aliases); + that->usings.unite(other->usings); + } + return (that != origThat) ? that : 0; +} + +void ParseResults::unite(const ParseResults *other) +{ + allIncludes.unite(other->allIncludes); + include(&rootNamespace, &other->rootNamespace); +} + +void CppParser::modifyNamespace(NamespaceList *namespaces) +{ + Namespace *pns = 0; + int i = namespaces->count(); + forever { + --i; + Namespace *ns = namespaces->at(i); + bool detached = results->detachNamespace(&ns); + if (pns) + ns->children[pns->name] = pns; + if (!detached) // Known to be true for root namespace + return; + pns = ns; + namespaces->replace(i, ns); + } +} + +QString CppParser::stringifyNamespace(const NamespaceList &namespaces) +{ + QString ret; + for (int i = 1; i < namespaces.count(); ++i) { + if (i > 1) + ret += QLatin1String("::"); + ret += namespaces.at(i)->name; + } + return ret; +} + +QStringList CppParser::stringListifyNamespace(const NamespaceList &namespaces) +{ + QStringList ret; + for (int i = 1; i < namespaces.count(); ++i) + ret << namespaces.at(i)->name; + return ret; +} + +// This function is called only with known-existing namespaces +NamespaceList CppParser::resolveNamespaces(const QStringList &segments) +{ + NamespaceList ret; + Namespace *ns = &results->rootNamespace; + ret << ns; + foreach (const QString &seg, segments) { + ns = ns->children.value(seg); + ret << ns; + } + return ret; +} + +bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsIdx, const QString &segment, + NamespaceList *resolved) +{ + const Namespace *ns = namespaces.at(nsIdx); + QMap<QString, Namespace *>::ConstIterator cnsi = ns->children.constFind(segment); + if (cnsi != ns->children.constEnd()) { + *resolved = namespaces.mid(0, nsIdx + 1); + *resolved << *cnsi; + return true; + } + QMap<QString, QStringList>::ConstIterator nsai = ns->aliases.constFind(segment); + if (nsai != ns->aliases.constEnd()) { + *resolved = resolveNamespaces(*nsai); + return true; + } + foreach (const QStringList &use, ns->usings) { + NamespaceList usedNs = resolveNamespaces(use); + if (qualifyOne(usedNs, usedNs.count() - 1, segment, resolved)) + return true; + } + return false; +} + +bool CppParser::fullyQualify(const NamespaceList &namespaces, const QStringList &segments, + bool isDeclaration, + NamespaceList *resolved, QStringList *unresolved) +{ + int nsIdx; + int initSegIdx; + + if (segments.first().isEmpty()) { + // fully qualified + if (segments.count() == 1) { + resolved->clear(); + *resolved << &results->rootNamespace; + return true; + } + initSegIdx = 1; + nsIdx = 0; + } else { + initSegIdx = 0; + nsIdx = namespaces.count() - 1; + } + + do { + if (qualifyOne(namespaces, nsIdx, segments[initSegIdx], resolved)) { + int segIdx = initSegIdx; + while (++segIdx < segments.count()) { + if (!qualifyOne(*resolved, resolved->count() - 1, segments[segIdx], resolved)) { + if (unresolved) + *unresolved = segments.mid(segIdx); + return false; + } + } + return true; + } + } while (!isDeclaration && --nsIdx >= 0); + resolved->clear(); + *resolved << &results->rootNamespace; + if (unresolved) + *unresolved = segments.mid(initSegIdx); + return false; +} + +void CppParser::enterNamespace(NamespaceList *namespaces, const QString &name) +{ + Namespace *ns = namespaces->last()->children.value(name); + if (!ns) { + ns = new Namespace; + ns->fileId = results->rootNamespace.fileId; + ns->name = name; + modifyNamespace(namespaces); + namespaces->last()->children[name] = ns; + } + *namespaces << ns; +} + +void CppParser::truncateNamespaces(NamespaceList *namespaces, int length) +{ + if (namespaces->count() > length) + namespaces->erase(namespaces->begin() + length, namespaces->end()); +} + +/* + Functions for processing include files. +*/ + +ParseResultHash &CppFiles::parsedFiles() +{ + static ParseResultHash parsed; + + return parsed; +} + +QSet<QString> &CppFiles::blacklistedFiles() +{ + static QSet<QString> blacklisted; + + return blacklisted; +} + +const ParseResults *CppFiles::getResults(const QString &cleanFile) +{ + ParseResultHash::ConstIterator it = parsedFiles().find(cleanFile); + if (it == parsedFiles().constEnd()) + return 0; + return *it; +} + +void CppFiles::setResults(const QString &cleanFile, const ParseResults *results) +{ + parsedFiles().insert(cleanFile, results); +} + +bool CppFiles::isBlacklisted(const QString &cleanFile) +{ + return blacklistedFiles().contains(cleanFile); +} + +void CppFiles::setBlacklisted(const QString &cleanFile) +{ + blacklistedFiles().insert(cleanFile); +} + +void CppParser::processInclude(const QString &file, ConversionData &cd, + QSet<QString> &inclusions) +{ + QString cleanFile = QDir::cleanPath(file); + + if (inclusions.contains(cleanFile)) { + qWarning("%s:%d: circular inclusion of %s\n", + qPrintable(yyFileName), yyLineNo, qPrintable(cleanFile)); + return; + } + + // If the #include is in any kind of namespace, has been blacklisted previously, + // or is not a header file (stdc++ extensionless or *.h*), then really include + // it. Otherwise it is safe to process it stand-alone and re-use the parsed + // namespace data for inclusion into other files. + bool isIndirect = false; + if (namespaces.count() == 1 && functionContext.count() == 1 + && functionContextUnresolved.isEmpty() && pendingContext.isEmpty() + && !CppFiles::isBlacklisted(cleanFile)) { + QString fileExt = QFileInfo(cleanFile).suffix(); + if (fileExt.isEmpty() || fileExt.startsWith(QLatin1Char('h'), Qt::CaseInsensitive)) { + + if (results->allIncludes.contains(cleanFile)) + return; + results->allIncludes.insert(cleanFile); + + if (const ParseResults *res = CppFiles::getResults(cleanFile)) { + results->unite(res); + return; + } + + isIndirect = true; + } + } + + QFile f(cleanFile); + if (!f.open(QIODevice::ReadOnly)) { + qWarning("%s:%d: Cannot open %s: %s\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(cleanFile), qPrintable(f.errorString())); + return; + } + + QTextStream ts(&f); + ts.setCodec(yySourceCodec); + ts.setAutoDetectUnicode(true); + + inclusions.insert(cleanFile); + if (isIndirect) { + CppParser parser; + foreach (const QString &projectRoot, cd.m_projectRoots) + if (cleanFile.startsWith(projectRoot)) { + parser.setTranslator(new Translator); + break; + } + parser.setInput(ts, cleanFile); + parser.parse(cd.m_defaultContext, cd, inclusions); + CppFiles::setResults(cleanFile, parser.getResults()); + results->unite(parser.results); + } else { + CppParser parser(results); + parser.namespaces = namespaces; + parser.functionContext = functionContext; + parser.functionContextUnresolved = functionContextUnresolved; + parser.pendingContext = pendingContext; + parser.setInput(ts, cleanFile); + parser.parseInternal(cd, inclusions); + // Don't wreak havoc if not enough braces were found. + truncateNamespaces(&parser.namespaces, namespaces.count()); + truncateNamespaces(&parser.functionContext, functionContext.count()); + // Copy them back - the pointers might have changed. + namespaces = parser.namespaces; + functionContext = parser.functionContext; + // Avoid that messages obtained by direct scanning are used + CppFiles::setBlacklisted(cleanFile); + } + inclusions.remove(cleanFile); +} + +/* + The third part of this source file is the parser. It accomplishes + a very easy task: It finds all strings inside a tr() or translate() + call, and possibly finds out the context of the call. It supports + three cases: (1) the context is specified, as in + FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello"); + (2) the call appears within an inlined function; (3) the call + appears within a function defined outside the class definition. +*/ + +bool CppParser::match(uint t) +{ + bool matches = (yyTok == t); + if (matches) + yyTok = getToken(); + return matches; +} + +bool CppParser::matchString(QString *s) +{ + bool matches = (yyTok == Tok_String); + s->clear(); + while (yyTok == Tok_String) { + *s += yyString; + yyTok = getToken(); + } + return matches; +} + +bool CppParser::matchEncoding(bool *utf8) +{ + STRING(QApplication); + STRING(QCoreApplication); + STRING(UnicodeUTF8); + STRING(DefaultCodec); + STRING(CodecForTr); + + if (yyTok != Tok_Ident) + return false; + if (yyIdent == strQApplication || yyIdent == strQCoreApplication) { + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + yyTok = getToken(); + } + if (yyIdent == strUnicodeUTF8) { + *utf8 = true; + yyTok = getToken(); + return true; + } + if (yyIdent == strDefaultCodec || yyIdent == strCodecForTr) { + *utf8 = false; + yyTok = getToken(); + return true; + } + return false; +} + +bool CppParser::matchInteger(qlonglong *number) +{ + bool matches = (yyTok == Tok_Integer); + if (matches) { + yyTok = getToken(); + *number = yyInteger; + } + return matches; +} + +bool CppParser::matchStringOrNull(QString *s) +{ + bool matches = matchString(s); + qlonglong num = 0; + if (!matches) + matches = matchInteger(&num); + return matches && num == 0; +} + +/* + * match any expression that can return a number, which can be + * 1. Literal number (e.g. '11') + * 2. simple identifier (e.g. 'm_count') + * 3. simple function call (e.g. 'size()' ) + * 4. function call on an object (e.g. 'list.size()') + * 5. function call on an object (e.g. 'list->size()') + * + * Other cases: + * size(2,4) + * list().size() + * list(a,b).size(2,4) + * etc... + */ +bool CppParser::matchExpression() +{ + if (match(Tok_Integer)) + return true; + + int parenlevel = 0; + while (match(Tok_Ident) || parenlevel > 0) { + if (yyTok == Tok_RightParen) { + if (parenlevel == 0) break; + --parenlevel; + yyTok = getToken(); + } else if (yyTok == Tok_LeftParen) { + yyTok = getToken(); + if (yyTok == Tok_RightParen) { + yyTok = getToken(); + } else { + ++parenlevel; + } + } else if (yyTok == Tok_Ident) { + continue; + } else if (yyTok == Tok_Arrow) { + yyTok = getToken(); + } else if (parenlevel == 0) { + return false; + } + } + return true; +} + +QString CppParser::transcode(const QString &str, bool utf8) +{ + static const char tab[] = "abfnrtv"; + static const char backTab[] = "\a\b\f\n\r\t\v"; + const QString in = (!utf8 || yySourceIsUnicode) + ? str : QString::fromUtf8(yySourceCodec->fromUnicode(str).data()); + QString out; + + out.reserve(in.length()); + for (int i = 0; i < in.length();) { + ushort c = in[i++].unicode(); + if (c == '\\') { + if (i >= in.length()) + break; + c = in[i++].unicode(); + + if (c == '\n') + continue; + + if (c == 'x') { + QByteArray hex; + while (i < in.length() && isxdigit((c = in[i].unicode()))) { + hex += c; + i++; + } + out += hex.toUInt(0, 16); + } else if (c >= '0' && c < '8') { + QByteArray oct; + int n = 0; + oct += c; + while (n < 2 && i < in.length() && (c = in[i].unicode()) >= '0' && c < '8') { + i++; + n++; + oct += c; + } + out += oct.toUInt(0, 8); + } else { + const char *p = strchr(tab, c); + out += QChar(QLatin1Char(!p ? c : backTab[p - tab])); + } + } else { + out += c; + } + } + return out; +} + +void CppParser::recordMessage( + int line, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, bool utf8, bool plural) +{ + TranslatorMessage msg( + transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(), + yyFileName, line, QStringList(), + TranslatorMessage::Unfinished, plural); + msg.setExtraComment(transcode(extracomment.simplified(), utf8)); + if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit()) + msg.setUtf8(true); + results->tor->append(msg); +} + +void CppParser::parse(const QString &initialContext, ConversionData &cd, + QSet<QString> &inclusions) +{ + if (results->tor) + yyCodecIsUtf8 = (results->tor->codecName() == "UTF-8"); + + namespaces << &results->rootNamespace; + functionContext = namespaces; + functionContextUnresolved = initialContext; + + parseInternal(cd, inclusions); +} + +void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions) +{ + static QString strColons(QLatin1String("::")); + + QString context; + QString text; + QString comment; + QString extracomment; + QString prefix; +#ifdef DIAGNOSE_RETRANSLATABILITY + QString functionName; +#endif + int line; + bool utf8; + bool yyTokColonSeen = false; // Start of c'tor's initializer list + + yyCh = getChar(); + yyTok = getToken(); + while (yyTok != Tok_Eof) { + //qDebug() << "TOKEN: " << yyTok; + switch (yyTok) { + case Tok_QuotedInclude: { + text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyString); + if (QFileInfo(text).isFile()) { + processInclude(text, cd, inclusions); + yyTok = getToken(); + break; + } + } + /* fall through */ + case Tok_AngledInclude: { + QStringList cSources = cd.m_allCSources.values(yyString); + if (!cSources.isEmpty()) { + foreach (const QString &cSource, cSources) + processInclude(cSource, cd, inclusions); + goto incOk; + } + foreach (const QString &incPath, cd.m_includePath) { + text = QDir(incPath).absoluteFilePath(yyString); + if (QFileInfo(text).isFile()) { + processInclude(text, cd, inclusions); + goto incOk; + } + } + incOk: + yyTok = getToken(); + break; + } + case Tok_friend: + yyTok = getToken(); + // Ensure that these don't end up being interpreted as forward declarations + // (they are forwards, but with different namespacing). + if (yyTok == Tok_class) + yyTok = getToken(); + break; + case Tok_class: + yyTokColonSeen = false; + /* + Partial support for inlined functions. + */ + yyTok = getToken(); + if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) { + QStringList fct; + do { + /* + This code should execute only once, but we play + safe with impure definitions such as + 'class Q_EXPORT QMessageBox', in which case + 'QMessageBox' is the class name, not 'Q_EXPORT'. + */ + fct = QStringList(yyIdent); + yyTok = getToken(); + } while (yyTok == Tok_Ident); + while (yyTok == Tok_ColonColon) { + yyTok = getToken(); + if (yyTok != Tok_Ident) + break; // Oops ... + fct += yyIdent; + yyTok = getToken(); + } + if (fct.count() > 1) { + // Forward-declared class definitions can be namespaced + NamespaceList nsl; + if (!fullyQualify(namespaces, fct, true, &nsl, 0)) { + qWarning("%s:%d: Ignoring definition of undeclared qualified class\n", + qPrintable(yyFileName), yyLineNo); + break; + } + namespaceDepths.push(namespaces.count()); + namespaces = nsl; + } else { + namespaceDepths.push(namespaces.count()); + enterNamespace(&namespaces, fct.first()); + } + namespaces.last()->isClass = true; + + while (yyTok == Tok_Comment) + yyTok = getToken(); + if (yyTok == Tok_Colon) { + // Skip any token until '{' since lupdate might do things wrong if it finds + // a '::' token here. + do { + yyTok = getToken(); + } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof); + } else { + if (yyTok != Tok_LeftBrace) { + // Obviously a forward decl + truncateNamespaces(&namespaces, namespaceDepths.pop()); + break; + } + } + + functionContext = namespaces; + functionContextUnresolved.clear(); // Pointless + prospectiveContext.clear(); + pendingContext.clear(); + } + break; + case Tok_namespace: + yyTokColonSeen = false; + yyTok = getToken(); + if (yyTok == Tok_Ident) { + QString ns = yyIdent; + yyTok = getToken(); + if (yyTok == Tok_LeftBrace) { + namespaceDepths.push(namespaces.count()); + enterNamespace(&namespaces, ns); + yyTok = getToken(); + } else if (yyTok == Tok_Equals) { + // e.g. namespace Is = OuterSpace::InnerSpace; + QStringList fullName; + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + if (fullName.isEmpty()) + break; + NamespaceList nsl; + if (fullyQualify(namespaces, fullName, false, &nsl, 0)) { + modifyNamespace(&namespaces); + namespaces.last()->aliases.insert(ns, stringListifyNamespace(nsl)); + } + } + } else if (yyTok == Tok_LeftBrace) { + // Anonymous namespace + namespaceDepths.push(namespaces.count()); + yyTok = getToken(); + } + break; + case Tok_using: + yyTok = getToken(); + if (yyTok == Tok_namespace) { + QStringList fullName; + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + NamespaceList nsl; + QStringList unresolved; + if (fullyQualify(namespaces, fullName, false, &nsl, &unresolved)) { + modifyNamespace(&namespaces); + namespaces.last()->usings.insert(stringListifyNamespace(nsl)); + } + } else { + QStringList fullName; + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + if (fullName.isEmpty()) + break; + NamespaceList nsl; + if (fullyQualify(namespaces, fullName, false, &nsl, 0)) { + modifyNamespace(&namespaces); + namespaces.last()->aliases.insert(nsl.last()->name, stringListifyNamespace(nsl)); + } + } + break; + case Tok_tr: + case Tok_trUtf8: + if (!results->tor) + goto case_default; + utf8 = (yyTok == Tok_trUtf8); + line = yyLineNo; + yyTok = getToken(); + if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) { + comment.clear(); + bool plural = false; + + if (match(Tok_RightParen)) { + // no comment + } else if (match(Tok_Comma) && matchStringOrNull(&comment)) { //comment + if (match(Tok_RightParen)) { + // ok, + } else if (match(Tok_Comma)) { + plural = true; + } + } + if (!pendingContext.isEmpty()) { + QStringList unresolved; + if (!fullyQualify(namespaces, pendingContext.split(strColons), true, + &functionContext, &unresolved)) { + functionContextUnresolved = unresolved.join(strColons); + qWarning("%s:%d: Qualifying with unknown namespace/class %s::%s\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(functionContext)), + qPrintable(unresolved.first())); + } + pendingContext.clear(); + } + if (prefix.isEmpty()) { + if (functionContextUnresolved.isEmpty()) { + int idx = functionContext.length(); + if (idx < 2) { + qWarning("%s:%d: tr() cannot be called without context\n", + qPrintable(yyFileName), yyLineNo); + break; + } + while (!functionContext.at(idx - 1)->hasTrFunctions) { + if (idx == 1 || !functionContext.at(idx - 2)->isClass) { + idx = functionContext.length(); + if (!functionContext.last()->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(functionContext))); + functionContext.last()->complained = true; + } + break; + } + --idx; + } + context.clear(); + for (int i = 1;;) { + context += functionContext.at(i)->name; + if (++i == idx) + break; + context += strColons; + } + } else { + context = (stringListifyNamespace(functionContext) + << functionContextUnresolved).join(strColons); + } + } else { +#ifdef DIAGNOSE_RETRANSLATABILITY + int last = prefix.lastIndexOf(strColons); + QString className = prefix.mid(last == -1 ? 0 : last + 2); + if (!className.isEmpty() && className == functionName) { + qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ", + qPrintable(yyFileName), yyLineNo, + className.constData(), functionName.constData()); + } +#endif + prefix.chop(2); + NamespaceList nsl; + QStringList unresolved; + if (fullyQualify(functionContext, prefix.split(strColons), false, &nsl, &unresolved)) { + if (!nsl.last()->hasTrFunctions && !nsl.last()->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(nsl))); + nsl.last()->complained = true; + } + context = stringifyNamespace(nsl); + } else { + context = (stringListifyNamespace(nsl) + unresolved).join(strColons); + } + prefix.clear(); + } + + recordMessage(line, context, text, comment, extracomment, utf8, plural); + } + extracomment.clear(); + break; + case Tok_translateUtf8: + case Tok_translate: + if (!results->tor) + goto case_default; + utf8 = (yyTok == Tok_translateUtf8); + line = yyLineNo; + yyTok = getToken(); + if (match(Tok_LeftParen) + && matchString(&context) + && match(Tok_Comma) + && matchString(&text) && !text.isEmpty()) + { + comment.clear(); + bool plural = false; + if (!match(Tok_RightParen)) { + // look for comment + if (match(Tok_Comma) && matchStringOrNull(&comment)) { + if (!match(Tok_RightParen)) { + // look for encoding + if (match(Tok_Comma)) { + if (matchEncoding(&utf8)) { + if (!match(Tok_RightParen)) { + // look for the plural quantifier, + // this can be a number, an identifier or + // a function call, + // so for simplicity we mark it as plural if + // we know we have a comma instead of an + // right parentheses. + plural = match(Tok_Comma); + } + } else { + // This can be a QTranslator::translate("context", + // "source", "comment", n) plural translation + if (matchExpression() && match(Tok_RightParen)) { + plural = true; + } else { + break; + } + } + } else { + break; + } + } + } else { + break; + } + } + recordMessage(line, context, text, comment, extracomment, utf8, plural); + } + extracomment.clear(); + break; + case Tok_Q_DECLARE_TR_FUNCTIONS: + case Tok_Q_OBJECT: + namespaces.last()->hasTrFunctions = true; + yyTok = getToken(); + break; + case Tok_Ident: + prefix += yyIdent; + yyTok = getToken(); + if (yyTok != Tok_ColonColon) { + prefix.clear(); + if (yyTok == Tok_Ident && !yyParenDepth) + prospectiveContext.clear(); + } + break; + case Tok_Comment: + if (!results->tor) + goto case_default; + if (yyComment.startsWith(QLatin1Char(':'))) { + yyComment.remove(0, 1); + extracomment.append(yyComment); + } else { + comment = yyComment.simplified(); + if (comment.startsWith(QLatin1String(MagicComment))) { + comment.remove(0, sizeof(MagicComment) - 1); + int k = comment.indexOf(QLatin1Char(' ')); + if (k == -1) { + context = comment; + } else { + context = comment.left(k); + comment.remove(0, k + 1); + recordMessage(yyLineNo, context, QString(), comment, extracomment, false, false); + } + } + } + yyTok = getToken(); + break; + case Tok_Arrow: + yyTok = getToken(); + if (yyTok == Tok_tr || yyTok == Tok_trUtf8) + qWarning("%s:%d: Cannot invoke tr() like this\n", + qPrintable(yyFileName), yyLineNo); + break; + case Tok_ColonColon: + if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0 && !yyTokColonSeen) + prospectiveContext = prefix; + prefix += strColons; + yyTok = getToken(); +#ifdef DIAGNOSE_RETRANSLATABILITY + if (yyTok == Tok_Ident && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) + functionName = yyIdent; +#endif + break; + case Tok_RightBrace: + if (yyBraceDepth + 1 == namespaceDepths.count()) { + // class or namespace + Namespace *ns = namespaces.last(); + if (ns->needsTrFunctions && !ns->hasTrFunctions && !ns->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(namespaces))); + ns->complained = true; + } + truncateNamespaces(&namespaces, namespaceDepths.pop()); + } + if (yyBraceDepth == namespaceDepths.count()) { + // function, class or namespace + if (!yyBraceDepth && !directInclude) { + truncateNamespaces(&functionContext, 1); + functionContextUnresolved = cd.m_defaultContext; + } else { + functionContext = namespaces; + functionContextUnresolved.clear(); + } + pendingContext.clear(); + } + // fallthrough + case Tok_Semicolon: + prospectiveContext.clear(); + prefix.clear(); + extracomment.clear(); + yyTokColonSeen = false; + yyTok = getToken(); + break; + case Tok_Colon: + if (!prospectiveContext.isEmpty() + && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) + pendingContext = prospectiveContext; + yyTokColonSeen = true; + yyTok = getToken(); + break; + case Tok_LeftBrace: + if (!prospectiveContext.isEmpty() + && yyBraceDepth == namespaceDepths.count() + 1 && yyParenDepth == 0) + pendingContext = prospectiveContext; + // fallthrough + case Tok_LeftParen: + case Tok_RightParen: + yyTokColonSeen = false; + yyTok = getToken(); + break; + default: + if (!yyParenDepth) + prospectiveContext.clear(); + case_default: + yyTok = getToken(); + break; + } + } + + if (yyBraceDepth != 0) + qWarning("%s:%d: Unbalanced opening brace in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyBraceLineNo); + else if (yyParenDepth != 0) + qWarning("%s:%d: Unbalanced opening parenthesis in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyParenLineNo); +} + +/* + Fetches tr() calls in C++ code in UI files (inside "<function>" + tag). This mechanism is obsolete. +*/ +void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context) +{ + CppParser parser; + parser.setInput(in); + ConversionData cd; + QSet<QString> inclusions; + parser.setTranslator(&translator); + parser.parse(context, cd, inclusions); + parser.deleteResults(); +} + +void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd) +{ + QByteArray codecName = cd.m_codecForSource.isEmpty() + ? translator.codecName() : cd.m_codecForSource; + QTextCodec *codec = QTextCodec::codecForName(codecName); + + foreach (const QString filename, filenames) { + if (CppFiles::getResults(filename) || CppFiles::isBlacklisted(filename)) + continue; + + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + continue; + } + + CppParser parser; + QTextStream ts(&file); + ts.setCodec(codec); + ts.setAutoDetectUnicode(true); + if (ts.codec()->name() == "UTF-16") + translator.setCodecName("System"); + parser.setInput(ts, filename); + Translator *tor = new Translator; + tor->setCodecName(translator.codecName()); + parser.setTranslator(tor); + QSet<QString> inclusions; + parser.parse(cd.m_defaultContext, cd, inclusions); + CppFiles::setResults(filename, parser.getResults()); + } + + foreach (const QString filename, filenames) + if (!CppFiles::isBlacklisted(filename)) + if (Translator *tor = CppFiles::getResults(filename)->tor) + foreach (const TranslatorMessage &msg, tor->messages()) + translator.extend(msg); +} + +QT_END_NAMESPACE diff --git a/tools/linguist/shared/java.cpp b/tools/linguist/lupdate/java.cpp index 912a8d7..c8dbe5b 100644 --- a/tools/linguist/shared/java.cpp +++ b/tools/linguist/lupdate/java.cpp @@ -39,7 +39,9 @@ ** ****************************************************************************/ -#include "translator.h" +#include "lupdate.h" + +#include <translator.h> #include <QtCore/QDebug> #include <QtCore/QFile> @@ -600,14 +602,18 @@ static void parse( Translator *tor ) } -bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd) +bool loadJava(Translator &translator, const QString &filename, ConversionData &cd) { - //void LupdateApplication::fetchtr_java( const QString &fileName, Translator *tor, - //const QString &defaultContext, bool mustExist, const QByteArray &codecForSource ) + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } yyDefaultContext = cd.m_defaultContext; yyInPos = -1; - yyFileName = cd.m_sourceFileName; + yyFileName = filename; yyPackage.clear(); yyScope.clear(); yyTok = -1; @@ -615,7 +621,7 @@ bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd) yyCurLineNo = 0; yyParenLineNo = 1; - QTextStream ts(&dev); + QTextStream ts(&file); QByteArray codecName; if (!cd.m_codecForSource.isEmpty()) codecName = cd.m_codecForSource; @@ -625,7 +631,7 @@ bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd) ts.setAutoDetectUnicode(true); yyInStr = ts.readAll(); yyInPos = 0; - yyFileName = cd.m_sourceFileName; + yyFileName = filename; yyCurLineNo = 1; yyParenLineNo = 1; yyCh = getChar(); @@ -637,19 +643,4 @@ bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd) return true; } -int initJava() -{ - Translator::FileFormat format; - format.extension = QLatin1String("java"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.description = QObject::tr("Java source files"); - format.loader = &loadJava; - format.saver = 0; - Translator::registerFileFormat(format); - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initJava) - QT_END_NAMESPACE diff --git a/tools/linguist/shared/translatortools.h b/tools/linguist/lupdate/lupdate.h index 9eaf024..2f98643 100644 --- a/tools/linguist/shared/translatortools.h +++ b/tools/linguist/lupdate/lupdate.h @@ -48,7 +48,9 @@ QT_BEGIN_NAMESPACE +class ConversionData; class QString; +class QStringList; class Translator; class TranslatorMessage; @@ -72,6 +74,12 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(UpdateOptions) Translator merge(const Translator &tor, const Translator &virginTor, UpdateOptions options, QString &err); +void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context); +void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd); +bool loadJava(Translator &translator, const QString &filename, ConversionData &cd); +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd); +bool loadUI(Translator &translator, const QString &filename, ConversionData &cd); + QT_END_NAMESPACE #endif diff --git a/tools/linguist/lupdate/lupdate.pro b/tools/linguist/lupdate/lupdate.pro index b05a4ef..ccc2d47 100644 --- a/tools/linguist/lupdate/lupdate.pro +++ b/tools/linguist/lupdate/lupdate.pro @@ -14,9 +14,20 @@ build_all:!build_pass { include(../shared/formats.pri) include(../shared/proparser.pri) -include(../shared/translatortools.pri) -SOURCES += main.cpp +SOURCES += \ + main.cpp \ + merge.cpp \ + ../shared/simtexth.cpp \ + \ + cpp.cpp \ + java.cpp \ + qscript.cpp \ + ui.cpp + +HEADERS += \ + lupdate.h \ + ../shared/simtexth.h DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII diff --git a/tools/linguist/lupdate/main.cpp b/tools/linguist/lupdate/main.cpp index b537b6e..8a70b55 100644 --- a/tools/linguist/lupdate/main.cpp +++ b/tools/linguist/lupdate/main.cpp @@ -39,9 +39,10 @@ ** ****************************************************************************/ -#include "translator.h" -#include "translatortools.h" -#include "profileevaluator.h" +#include "lupdate.h" + +#include <translator.h> +#include <profileevaluator.h> #include <QtCore/QCoreApplication> #include <QtCore/QDebug> @@ -83,12 +84,12 @@ static void printUsage() { printOut(QObject::tr( "Usage:\n" - " lupdate [options] [project-file]\n" + " lupdate [options] [project-file]...\n" " lupdate [options] [source-file|path]... -ts ts-files\n\n" - "lupdate is part of Qt's Linguist tool chain. It can be used as a\n" - "stand-alone tool to create XML based translations files in the .ts\n" - "format from translatable messages in C++ and Java source code.\n\n" - "lupdate can also merge such messages into existing .ts files.\n\n" + "lupdate is part of Qt's Linguist tool chain. It extracts translatable\n" + "messages from Qt UI files, C++, Java and JavaScript/QtScript source code.\n" + "Extracted messages are stored in textual translation source files (typically\n" + "Qt TS XML). New and modified messages can be merged into existing TS files.\n\n" "Options:\n" " -help Display this information and exit.\n" " -no-obsolete\n" @@ -106,7 +107,10 @@ static void printUsage() " -no-recursive\n" " Do not recursively scan the following directories.\n" " -recursive\n" - " Recursively scan the following directories.\n" + " Recursively scan the following directories (default).\n" + " -I <includepath> or -I<includepath>\n" + " Additional location to look for include files.\n" + " May be specified multiple times.\n" " -locations {absolute|relative|none}\n" " Specify/override how source code references are saved in ts files.\n" " Default is absolute.\n" @@ -216,7 +220,10 @@ int main(int argc, char **argv) QByteArray codecForSource; QStringList tsFileNames; QStringList proFiles; + QMultiHash<QString, QString> allCSources; + QSet<QString> projectRoots; QStringList sourceFiles; + QStringList includePath; QString targetLanguage; QString sourceLanguage; @@ -343,6 +350,18 @@ int main(int argc, char **argv) proFiles += args[i]; numFiles++; continue; + } else if (arg.startsWith(QLatin1String("-I"))) { + if (arg.length() == 2) { + ++i; + if (i == argc) { + qWarning("The -I option should be followed by a path."); + return 1; + } + includePath += args[i]; + } else { + includePath += args[i].mid(2); + } + continue; } else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) { qWarning("Unrecognized option '%s'", qPrintable(arg)); return 1; @@ -387,33 +406,46 @@ int main(int argc, char **argv) if (options & Verbose) printOut(QObject::tr("Scanning directory '%1'...").arg(arg)); QDir dir = QDir(fi.filePath()); + projectRoots.insert(dir.absolutePath() + QLatin1Char('/')); if (extensionsNameFilters.isEmpty()) { - extensions = extensions.trimmed(); - // Remove the potential dot in front of each extension - if (extensions.startsWith(QLatin1Char('.'))) - extensions.remove(0,1); - extensions.replace(QLatin1String(",."), QLatin1String(",")); - - extensions.insert(0, QLatin1String("*.")); - extensions.replace(QLatin1Char(','), QLatin1String(",*.")); - extensionsNameFilters = extensions.split(QLatin1Char(',')); + foreach (QString ext, extensions.split(QLatin1Char(','))) { + ext = ext.trimmed(); + if (ext.startsWith(QLatin1Char('.'))) + ext.remove(0,1); + ext.insert(0, QLatin1String("*.")); + extensionsNameFilters << ext; + } } QDir::Filters filters = QDir::Files | QDir::NoSymLinks; QFileInfoList fileinfolist; recursiveFileInfoList(dir, extensionsNameFilters, filters, recursiveScan, &fileinfolist); - QFileInfoList::iterator ii; - QString fn; - for (ii = fileinfolist.begin(); ii != fileinfolist.end(); ++ii) { - // Make sure the path separator is stored with '/' in the ts file - sourceFiles << ii->canonicalFilePath().replace(QLatin1Char('\\'), QLatin1Char('/')); + int scanRootLen = dir.absolutePath().length(); + foreach (const QFileInfo &fi, fileinfolist) { + QString fn = QDir::cleanPath(fi.absoluteFilePath()); + sourceFiles << fn; + + if (!fn.endsWith(QLatin1String(".java")) + && !fn.endsWith(QLatin1String(".ui")) + && !fn.endsWith(QLatin1String(".js")) + && !fn.endsWith(QLatin1String(".qs"))) { + int offset = 0; + int depth = 0; + do { + offset = fn.lastIndexOf(QLatin1Char('/'), offset - 1); + QString ffn = fn.mid(offset + 1); + allCSources.insert(ffn, fn); + } while (++depth < 3 && offset > scanRootLen); + } } } else { - sourceFiles << fi.canonicalFilePath().replace(QLatin1Char('\\'), QLatin1Char('/')); + sourceFiles << QDir::cleanPath(fi.absoluteFilePath());; } } } // for args + foreach (const QString &proFile, proFiles) + projectRoots.insert(QDir::cleanPath(QFileInfo(proFile).absolutePath()) + QLatin1Char('/')); bool firstPass = true; bool fail = false; @@ -421,6 +453,9 @@ int main(int argc, char **argv) ConversionData cd; cd.m_defaultContext = defaultContext; cd.m_noUiLines = options & NoUiLines; + cd.m_projectRoots = projectRoots; + cd.m_includePath = includePath; + cd.m_allCSources = allCSources; QStringList tsFiles = tsFileNames; if (proFiles.count() > 0) { @@ -450,6 +485,8 @@ int main(int argc, char **argv) continue; } + cd.m_includePath += visitor.values(QLatin1String("INCLUDEPATH")); + evaluateProFile(visitor, &variables); sourceFiles = variables.value("SOURCES"); @@ -472,27 +509,19 @@ int main(int argc, char **argv) tsFiles += variables.value("TRANSLATIONS"); } + QStringList sourceFilesCpp; for (QStringList::iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) { - if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive)) { - cd.m_sourceFileName = *it; - fetchedTor.load(*it, cd, QLatin1String("java")); - //fetchtr_java(*it, &fetchedTor, defaultContext, true, codecForSource); - } - else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive)) { - fetchedTor.load(*it, cd, QLatin1String("ui")); - //fetchedTor.load(*it + QLatin1String(".h"), cd, QLatin1String("cpp")); - //fetchtr_ui(*it, &fetchedTor, defaultContext, true); - //fetchtr_cpp(*it + QLatin1String(".h"), &fetchedTor, - // defaultContext, false, codecForSource); - } + if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive)) + loadJava(fetchedTor, *it, cd); + else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive)) + loadUI(fetchedTor, *it, cd); else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive) - || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) { - fetchedTor.load(*it, cd, QLatin1String("js")); - } else { - fetchedTor.load(*it, cd, QLatin1String("cpp")); - //fetchtr_cpp(*it, &fetchedTor, defaultContext, true, codecForSource); - } + || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) + loadQScript(fetchedTor, *it, cd); + else + sourceFilesCpp << *it; } + loadCPP(fetchedTor, sourceFilesCpp, cd); if (!cd.error().isEmpty()) printOut(cd.error()); diff --git a/tools/linguist/shared/translatortools.cpp b/tools/linguist/lupdate/merge.cpp index 96301d5..c4f4448 100644 --- a/tools/linguist/shared/translatortools.cpp +++ b/tools/linguist/lupdate/merge.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include "translatortools.h" +#include "lupdate.h" #include "simtexth.h" #include "translator.h" diff --git a/tools/linguist/shared/qscript.cpp b/tools/linguist/lupdate/qscript.cpp index 7377cba..64adde6 100644 --- a/tools/linguist/shared/qscript.cpp +++ b/tools/linguist/lupdate/qscript.cpp @@ -752,7 +752,7 @@ const int QScriptGrammar::action_check [] = { #define Q_SCRIPT_REGEXPLITERAL_RULE2 8 -#include "translator.h" +#include <translator.h> #include <QtCore/qdebug.h> #include <QtCore/qnumeric.h> @@ -2356,9 +2356,15 @@ case 94: { } -bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) { - QTextStream ts(&dev); + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + QTextStream ts(&file); QByteArray codecName; if (!cd.m_codecForSource.isEmpty()) codecName = cd.m_codecForSource; @@ -2371,8 +2377,8 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) QScript::Lexer lexer; lexer.setCode(code, /*lineNumber=*/1); QScriptParser parser; - if (!parser.parse(&lexer, cd.m_sourceFileName, &translator)) { - qWarning("%s:%d: %s", qPrintable(cd.m_sourceFileName), parser.errorLineNumber(), + if (!parser.parse(&lexer, filename, &translator)) { + qWarning("%s:%d: %s", qPrintable(filename), parser.errorLineNumber(), qPrintable(parser.errorMessage())); return false; } @@ -2382,27 +2388,4 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) return true; } -bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd) -{ - Q_UNUSED(dev); - Q_UNUSED(translator); - cd.appendError(QLatin1String("Cannot save .js files")); - return false; -} - -int initQScript() -{ - Translator::FileFormat format; - format.extension = QLatin1String("js"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.description = QObject::tr("Qt Script source files"); - format.loader = &loadQScript; - format.saver = &saveQScript; - Translator::registerFileFormat(format); - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initQScript) - QT_END_NAMESPACE diff --git a/tools/linguist/shared/qscript.g b/tools/linguist/lupdate/qscript.g index 8d33277..563974a 100644 --- a/tools/linguist/shared/qscript.g +++ b/tools/linguist/lupdate/qscript.g @@ -42,6 +42,10 @@ -- ---------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-- Process with "qlalr --no-debug --no-lines qscript.g" to update qscript.cpp -- +-------------------------------------------------------------------------------- + %parser QScriptGrammar %merged_output qscript.cpp %expect 3 @@ -81,7 +85,7 @@ %start Program /. -#include "translator.h" +#include <translator.h> #include <QtCore/qdebug.h> #include <QtCore/qnumeric.h> @@ -1986,9 +1990,15 @@ PropertyNameAndValueListOpt: PropertyNameAndValueList ; } -bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) { - QTextStream ts(&dev); + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + QTextStream ts(&file); QByteArray codecName; if (!cd.m_codecForSource.isEmpty()) codecName = cd.m_codecForSource; @@ -2001,8 +2011,8 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) QScript::Lexer lexer; lexer.setCode(code, /*lineNumber=*/1); QScriptParser parser; - if (!parser.parse(&lexer, cd.m_sourceFileName, &translator)) { - qWarning("%s:%d: %s", qPrintable(cd.m_sourceFileName), parser.errorLineNumber(), + if (!parser.parse(&lexer, filename, &translator)) { + qWarning("%s:%d: %s", qPrintable(filename), parser.errorLineNumber(), qPrintable(parser.errorMessage())); return false; } @@ -2012,28 +2022,5 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) return true; } -bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd) -{ - Q_UNUSED(dev); - Q_UNUSED(translator); - cd.appendError(QLatin1String("Cannot save .js files")); - return false; -} - -int initQScript() -{ - Translator::FileFormat format; - format.extension = QLatin1String("js"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.description = QObject::tr("Qt Script source files"); - format.loader = &loadQScript; - format.saver = &saveQScript; - Translator::registerFileFormat(format); - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initQScript) - QT_END_NAMESPACE ./ diff --git a/tools/linguist/shared/ui.cpp b/tools/linguist/lupdate/ui.cpp index 23a73e7..6b47969 100644 --- a/tools/linguist/shared/ui.cpp +++ b/tools/linguist/lupdate/ui.cpp @@ -39,7 +39,9 @@ ** ****************************************************************************/ -#include "translator.h" +#include "lupdate.h" + +#include <translator.h> #include <QtCore/QDebug> #include <QtCore/QFile> @@ -53,9 +55,6 @@ QT_BEGIN_NAMESPACE -// in cpp.cpp -void fetchtrInlinedCpp(const QString &in, Translator &tor, const QString &context); - class UiReader : public QXmlDefaultHandler { public: @@ -151,7 +150,7 @@ bool UiReader::fatalError(const QXmlParseException &exception) msg.sprintf("XML error: Parse error at line %d, column %d (%s).", exception.lineNumber(), exception.columnNumber(), exception.message().toLatin1().data()); - m_cd.appendError(msg); + m_cd.appendError(msg); return false; } @@ -169,9 +168,16 @@ void UiReader::flush() m_comment.clear(); } -bool loadUI(Translator &translator, QIODevice &dev, ConversionData &cd) +bool loadUI(Translator &translator, const QString &filename, ConversionData &cd) { - QXmlInputSource in(&dev); + cd.m_sourceFileName = filename; + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + QXmlInputSource in(&file); QXmlSimpleReader reader; reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), false); reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), true); @@ -188,39 +194,4 @@ bool loadUI(Translator &translator, QIODevice &dev, ConversionData &cd) return result; } -bool saveUI(const Translator &translator, QIODevice &dev, ConversionData &cd) -{ - Q_UNUSED(dev); - Q_UNUSED(translator); - cd.appendError(QLatin1String("Cannot save .ui files")); - return false; -} - -int initUI() -{ - Translator::FileFormat format; - - // "real" Qt Designer - format.extension = QLatin1String("ui"); - format.description = QObject::tr("Qt Designer form files"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.loader = &loadUI; - format.saver = &saveUI; - Translator::registerFileFormat(format); - - // same for jambi - format.extension = QLatin1String("jui"); - format.description = QObject::tr("Qt Jambi form files"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.loader = &loadUI; - format.saver = &saveUI; - Translator::registerFileFormat(format); - - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initUI) - QT_END_NAMESPACE diff --git a/tools/linguist/shared/cpp.cpp b/tools/linguist/shared/cpp.cpp deleted file mode 100644 index 2e137cf..0000000 --- a/tools/linguist/shared/cpp.cpp +++ /dev/null @@ -1,1081 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the Qt Linguist 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 either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** 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.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "translator.h" - -#include <QtCore/QDebug> -#include <QtCore/QStack> -#include <QtCore/QString> -#include <QtCore/QTextCodec> -#include <QtCore/QTextStream> - -#include <ctype.h> // for isXXX() - -QT_BEGIN_NAMESPACE - -/* qmake ignore Q_OBJECT */ - -static const char MagicComment[] = "TRANSLATOR "; - -static QSet<QString> needs_Q_OBJECT; -static QSet<QString> lacks_Q_OBJECT; - -static const int yyIdentMaxLen = 128; -static const int yyCommentMaxLen = 65536; -static const int yyStringMaxLen = 65536; - -#define STRINGIFY_INTERNAL(x) #x -#define STRINGIFY(x) STRINGIFY_INTERNAL(x) -#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s))) - -//#define DIAGNOSE_RETRANSLATABILITY -/* - The first part of this source file is the C++ tokenizer. We skip - most of C++; the only tokens that interest us are defined here. - Thus, the code fragment - - int main() - { - printf("Hello, world!\n"); - return 0; - } - - is broken down into the following tokens (Tok_ omitted): - - Ident Ident LeftParen RightParen - LeftBrace - Ident LeftParen String RightParen Semicolon - return Semicolon - RightBrace. - - The 0 doesn't produce any token. -*/ - -enum { - Tok_Eof, Tok_class, Tok_namespace, Tok_return, - Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8, - Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS, - Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon, - Tok_Equals, - Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon, - Tok_Integer = 40, - Tok_Other -}; - -/* - The tokenizer maintains the following global variables. The names - should be self-explanatory. -*/ -static QString yyFileName; -static int yyCh; -static bool yyCodecIsUtf8; -static bool yyForceUtf8; -static QString yyIdent; -static QString yyComment; -static QString yyString; -static qlonglong yyInteger; -static QStack<int> yySavedBraceDepth; -static QStack<int> yySavedParenDepth; -static int yyBraceDepth; -static int yyParenDepth; -static int yyLineNo; -static int yyCurLineNo; -static int yyBraceLineNo; -static int yyParenLineNo; -static bool yyTokColonSeen = false; - -// the string to read from and current position in the string -static QTextCodec *yySourceCodec; -static bool yySourceIsUnicode; -static QString yyInStr; -static int yyInPos; - -static uint getChar() -{ - forever { - if (yyInPos >= yyInStr.size()) - return EOF; - uint c = yyInStr[yyInPos++].unicode(); - if (c == '\\' && yyInPos < yyInStr.size() && yyInStr[yyInPos].unicode() == '\n') { - ++yyCurLineNo; - ++yyInPos; - continue; - } - if (c == '\n') - ++yyCurLineNo; - return c; - } -} - -static uint getToken() -{ - yyIdent.clear(); - yyComment.clear(); - yyString.clear(); - - while (yyCh != EOF) { - yyLineNo = yyCurLineNo; - - if (isalpha(yyCh) || yyCh == '_') { - do { - yyIdent += yyCh; - yyCh = getChar(); - } while (isalnum(yyCh) || yyCh == '_'); - - //qDebug() << "IDENT: " << yyIdent; - - switch (yyIdent.at(0).unicode()) { - case 'Q': - if (yyIdent == QLatin1String("Q_OBJECT")) - return Tok_Q_OBJECT; - if (yyIdent == QLatin1String("Q_DECLARE_TR_FUNCTIONS")) - return Tok_Q_DECLARE_TR_FUNCTIONS; - if (yyIdent == QLatin1String("QT_TR_NOOP")) - return Tok_tr; - if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP")) - return Tok_translate; - if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3")) - return Tok_translate; - if (yyIdent == QLatin1String("QT_TR_NOOP_UTF8")) - return Tok_trUtf8; - if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP_UTF8")) - return Tok_translateUtf8; - if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3_UTF8")) - return Tok_translateUtf8; - break; - case 'T': - // TR() for when all else fails - if (yyIdent.compare(QLatin1String("TR"), Qt::CaseInsensitive) == 0) { - return Tok_tr; - } - break; - case 'c': - if (yyIdent == QLatin1String("class")) - return Tok_class; - break; - case 'f': - /* - QTranslator::findMessage() has the same parameters as - QApplication::translate(). - */ - if (yyIdent == QLatin1String("findMessage")) - return Tok_translate; - break; - case 'n': - if (yyIdent == QLatin1String("namespace")) - return Tok_namespace; - break; - case 'r': - if (yyIdent == QLatin1String("return")) - return Tok_return; - break; - case 's': - if (yyIdent == QLatin1String("struct")) - return Tok_class; - break; - case 't': - if (yyIdent == QLatin1String("tr")) { - return Tok_tr; - } - if (yyIdent == QLatin1String("trUtf8")) { - return Tok_trUtf8; - } - if (yyIdent == QLatin1String("translate")) { - return Tok_translate; - } - } - return Tok_Ident; - } else { - switch (yyCh) { - case '#': - /* - Early versions of lupdate complained about - unbalanced braces in the following code: - - #ifdef ALPHA - while (beta) { - #else - while (gamma) { - #endif - delta; - } - - The code contains, indeed, two opening braces for - one closing brace; yet there's no reason to panic. - - The solution is to remember yyBraceDepth as it was - when #if, #ifdef or #ifndef was met, and to set - yyBraceDepth to that value when meeting #elif or - #else. - */ - do { - yyCh = getChar(); - } while (isspace(yyCh) && yyCh != '\n'); - - switch (yyCh) { - case 'i': - yyCh = getChar(); - if (yyCh == 'f') { - // if, ifdef, ifndef - yySavedBraceDepth.push(yyBraceDepth); - yySavedParenDepth.push(yyParenDepth); - } - break; - case 'e': - yyCh = getChar(); - if (yyCh == 'l') { - // elif, else - if (!yySavedBraceDepth.isEmpty()) { - yyBraceDepth = yySavedBraceDepth.top(); - yyParenDepth = yySavedParenDepth.top(); - } - } else if (yyCh == 'n') { - // endif - if (!yySavedBraceDepth.isEmpty()) { - yySavedBraceDepth.pop(); - yySavedParenDepth.pop(); - } - } - } - while (isalnum(yyCh) || yyCh == '_') - yyCh = getChar(); - break; - case '/': - yyCh = getChar(); - if (yyCh == '/') { - do { - yyCh = getChar(); - if (yyCh == EOF) - break; - yyComment.append(yyCh); - } while (yyCh != '\n'); - } else if (yyCh == '*') { - bool metAster = false; - bool metAsterSlash = false; - - while (!metAsterSlash) { - yyCh = getChar(); - if (yyCh == EOF) { - qWarning("%s: Unterminated C++ comment starting at" - " line %d\n", - qPrintable(yyFileName), yyLineNo); - return Tok_Comment; - } - yyComment.append(yyCh); - - if (yyCh == '*') - metAster = true; - else if (metAster && yyCh == '/') - metAsterSlash = true; - else - metAster = false; - } - yyCh = getChar(); - yyComment.chop(2); - } - return Tok_Comment; - case '"': - yyCh = getChar(); - while (yyCh != EOF && yyCh != '\n' && yyCh != '"') { - if (yyCh == '\\') { - yyCh = getChar(); - if (yyString.size() < yyStringMaxLen) { - yyString.append(QLatin1Char('\\')); - yyString.append(yyCh); - } - } else { - if (yyString.size() < yyStringMaxLen) - yyString.append(yyCh); - } - yyCh = getChar(); - } - - if (yyCh != '"') - qWarning("%s:%d: Unterminated C++ string", - qPrintable(yyFileName), yyLineNo); - - if (yyCh == EOF) - return Tok_Eof; - yyCh = getChar(); - return Tok_String; - case '-': - yyCh = getChar(); - if (yyCh == '>') { - yyCh = getChar(); - return Tok_Arrow; - } - break; - case ':': - yyCh = getChar(); - if (yyCh == ':') { - yyCh = getChar(); - return Tok_ColonColon; - } - return Tok_Colon; - // Incomplete: '<' might be part of '<=' or of template syntax. - // The main intent of not completely ignoring it is to break - // parsing of things like std::cout << QObject::tr() as - // context std::cout::QObject (see Task 161106) - case '=': - yyCh = getChar(); - return Tok_Equals; - case '>': - case '<': - yyCh = getChar(); - return Tok_Other; - case '\'': - yyCh = getChar(); - if (yyCh == '\\') - yyCh = getChar(); - - do { - yyCh = getChar(); - } while (yyCh != EOF && yyCh != '\''); - yyCh = getChar(); - break; - case '{': - if (yyBraceDepth == 0) - yyBraceLineNo = yyCurLineNo; - yyBraceDepth++; - yyCh = getChar(); - return Tok_LeftBrace; - case '}': - if (yyBraceDepth == 0) - yyBraceLineNo = yyCurLineNo; - yyBraceDepth--; - yyCh = getChar(); - return Tok_RightBrace; - case '(': - if (yyParenDepth == 0) - yyParenLineNo = yyCurLineNo; - yyParenDepth++; - yyCh = getChar(); - return Tok_LeftParen; - case ')': - if (yyParenDepth == 0) - yyParenLineNo = yyCurLineNo; - yyParenDepth--; - yyCh = getChar(); - return Tok_RightParen; - case ',': - yyCh = getChar(); - return Tok_Comma; - case ';': - yyCh = getChar(); - return Tok_Semicolon; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - QByteArray ba; - ba += yyCh; - yyCh = getChar(); - bool hex = yyCh == 'x'; - if (hex) { - ba += yyCh; - yyCh = getChar(); - } - while (hex ? isxdigit(yyCh) : isdigit(yyCh)) { - ba += yyCh; - yyCh = getChar(); - } - bool ok; - yyInteger = ba.toLongLong(&ok); - if (ok) - return Tok_Integer; - break; - } - default: - yyCh = getChar(); - break; - } - } - } - return Tok_Eof; -} - -/* - The second part of this source file is the parser. It accomplishes - a very easy task: It finds all strings inside a tr() or translate() - call, and possibly finds out the context of the call. It supports - three cases: (1) the context is specified, as in - FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello"); - (2) the call appears within an inlined function; (3) the call - appears within a function defined outside the class definition. -*/ - -static uint yyTok; - -static bool match(uint t) -{ - bool matches = (yyTok == t); - if (matches) - yyTok = getToken(); - return matches; -} - -static bool matchString(QString *s) -{ - bool matches = (yyTok == Tok_String); - s->clear(); - while (yyTok == Tok_String) { - *s += yyString; - yyTok = getToken(); - } - return matches; -} - -static bool matchEncoding(bool *utf8) -{ - STRING(QApplication); - STRING(QCoreApplication); - STRING(UnicodeUTF8); - STRING(DefaultCodec); - STRING(CodecForTr); - - if (yyTok != Tok_Ident) - return false; - if (yyIdent == strQApplication || yyIdent == strQCoreApplication) { - yyTok = getToken(); - if (yyTok == Tok_ColonColon) - yyTok = getToken(); - } - if (yyIdent == strUnicodeUTF8) { - *utf8 = true; - yyTok = getToken(); - return true; - } - if (yyIdent == strDefaultCodec || yyIdent == strCodecForTr) { - *utf8 = false; - yyTok = getToken(); - return true; - } - return false; -} - -static bool matchInteger(qlonglong *number) -{ - bool matches = (yyTok == Tok_Integer); - if (matches) { - yyTok = getToken(); - *number = yyInteger; - } - return matches; -} - -static bool matchStringOrNull(QString *s) -{ - bool matches = matchString(s); - qlonglong num = 0; - if (!matches) - matches = matchInteger(&num); - return matches && num == 0; -} - -/* - * match any expression that can return a number, which can be - * 1. Literal number (e.g. '11') - * 2. simple identifier (e.g. 'm_count') - * 3. simple function call (e.g. 'size()' ) - * 4. function call on an object (e.g. 'list.size()') - * 5. function call on an object (e.g. 'list->size()') - * - * Other cases: - * size(2,4) - * list().size() - * list(a,b).size(2,4) - * etc... - */ -static bool matchExpression() -{ - if (match(Tok_Integer)) - return true; - - int parenlevel = 0; - while (match(Tok_Ident) || parenlevel > 0) { - if (yyTok == Tok_RightParen) { - if (parenlevel == 0) break; - --parenlevel; - yyTok = getToken(); - } else if (yyTok == Tok_LeftParen) { - yyTok = getToken(); - if (yyTok == Tok_RightParen) { - yyTok = getToken(); - } else { - ++parenlevel; - } - } else if (yyTok == Tok_Ident) { - continue; - } else if (yyTok == Tok_Arrow) { - yyTok = getToken(); - } else if (parenlevel == 0) { - return false; - } - } - return true; -} - -static QStringList resolveNamespaces( - const QStringList &namespaces, const QHash<QString, QStringList> &namespaceAliases) -{ - static QString strColons(QLatin1String("::")); - - QStringList ns; - foreach (const QString &cns, namespaces) { - ns << cns; - ns = namespaceAliases.value(ns.join(strColons), ns); - } - return ns; -} - -static QStringList getFullyQualifiedNamespaceName( - const QSet<QString> &allNamespaces, const QStringList &namespaces, - const QHash<QString, QStringList> &namespaceAliases, - const QStringList &segments) -{ - static QString strColons(QLatin1String("::")); - - if (segments.first().isEmpty()) { - // fully qualified - QStringList segs = segments; - segs.removeFirst(); - return resolveNamespaces(segs, namespaceAliases); - } else { - for (int n = namespaces.count(); --n >= -1; ) { - QStringList ns; - for (int i = 0; i <= n; ++i) // Note: n == -1 possible - ns << namespaces[i]; - foreach (const QString &cns, segments) { - ns << cns; - ns = namespaceAliases.value(ns.join(strColons), ns); - } - if (allNamespaces.contains(ns.join(strColons))) - return ns; - } - - // Fallback when the namespace was declared in a header, etc. - QStringList ns = namespaces; - ns += segments; - return ns; - } -} - -static QString getFullyQualifiedClassName( - const QSet<QString> &allClasses, const QStringList &namespaces, - const QHash<QString, QStringList> &namespaceAliases, - const QString &ident, bool hasPrefix) -{ - static QString strColons(QLatin1String("::")); - - QString context = ident; - QStringList segments = context.split(strColons); - if (segments.first().isEmpty()) { - // fully qualified - segments.removeFirst(); - context = resolveNamespaces(segments, namespaceAliases).join(strColons); - } else { - for (int n = namespaces.count(); --n >= -1; ) { - QStringList ns; - for (int i = 0; i <= n; ++i) // Note: n == -1 possible - ns.append(namespaces[i]); - foreach (const QString &cns, segments) { - ns.append(cns); - ns = namespaceAliases.value(ns.join(strColons), ns); - } - QString nctx = ns.join(strColons); - if (allClasses.contains(nctx)) { - context = nctx; - goto gotit; - } - } - - if (!hasPrefix && namespaces.count()) - context = namespaces.join(strColons) + strColons + context; - } -gotit: - //qDebug() << "CLASSES:" << allClasses << "NAMEPACES:" << namespaces - // << "IDENT:" << ident << "CONTEXT:" << context; - return context; -} - - -static QString transcode(const QString &str, bool utf8) -{ - static const char tab[] = "abfnrtv"; - static const char backTab[] = "\a\b\f\n\r\t\v"; - const QString in = (!utf8 || yySourceIsUnicode) - ? str : QString::fromUtf8(yySourceCodec->fromUnicode(str).data()); - QString out; - - out.reserve(in.length()); - for (int i = 0; i < in.length();) { - ushort c = in[i++].unicode(); - if (c == '\\') { - if (i >= in.length()) - break; - c = in[i++].unicode(); - - if (c == '\n') - continue; - - if (c == 'x') { - QByteArray hex; - while (i < in.length() && isxdigit((c = in[i].unicode()))) { - hex += c; - i++; - } - out += hex.toUInt(0, 16); - } else if (c >= '0' && c < '8') { - QByteArray oct; - int n = 0; - oct += c; - while (n < 2 && i < in.length() && (c = in[i].unicode()) >= '0' && c < '8') { - i++; - n++; - oct += c; - } - out += oct.toUInt(0, 8); - } else { - const char *p = strchr(tab, c); - out += QChar(QLatin1Char(!p ? c : backTab[p - tab])); - } - } else { - out += c; - } - } - return out; -} - -static void recordMessage( - Translator *tor, int line, const QString &context, const QString &text, const QString &comment, - const QString &extracomment, bool utf8, bool plural) -{ - TranslatorMessage msg( - transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(), - yyFileName, line, QStringList(), - TranslatorMessage::Unfinished, plural); - msg.setExtraComment(transcode(extracomment.simplified(), utf8)); - if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit()) - msg.setUtf8(true); - tor->extend(msg); -} - -static void parse(Translator *tor, const QString &initialContext, const QString &defaultContext) -{ - static QString strColons(QLatin1String("::")); - - QMap<QString, QString> qualifiedContexts; - QSet<QString> allClasses; - QSet<QString> allNamespaces; - QHash<QString, QStringList> namespaceAliases; - QStringList namespaces; - QString context; - QString text; - QString comment; - QString extracomment; - QString functionContext = initialContext; - QString prefix; -#ifdef DIAGNOSE_RETRANSLATABILITY - QString functionName; -#endif - int line; - bool utf8 = false; - bool missing_Q_OBJECT = false; - - yyTok = getToken(); - while (yyTok != Tok_Eof) { - //qDebug() << "TOKEN: " << yyTok; - switch (yyTok) { - case Tok_class: - yyTokColonSeen = false; - /* - Partial support for inlined functions. - */ - yyTok = getToken(); - if (yyBraceDepth == namespaces.count() && yyParenDepth == 0) { - QStringList fct; - do { - /* - This code should execute only once, but we play - safe with impure definitions such as - 'class Q_EXPORT QMessageBox', in which case - 'QMessageBox' is the class name, not 'Q_EXPORT'. - */ - fct = QStringList(yyIdent); - yyTok = getToken(); - } while (yyTok == Tok_Ident); - while (yyTok == Tok_ColonColon) { - yyTok = getToken(); - if (yyTok != Tok_Ident) - break; // Oops ... - fct += yyIdent; - yyTok = getToken(); - } - functionContext = resolveNamespaces(namespaces + fct, namespaceAliases).join(strColons); - allClasses.insert(functionContext); - - if (yyTok == Tok_Colon) { - missing_Q_OBJECT = true; - // Skip any token until '{' since lupdate might do things wrong if it finds - // a '::' token here. - do { - yyTok = getToken(); - } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof); - } else { - //functionContext = defaultContext; - } - } - break; - case Tok_namespace: - yyTokColonSeen = false; - yyTok = getToken(); - if (yyTok == Tok_Ident) { - QString ns = yyIdent; - yyTok = getToken(); - if (yyTok == Tok_LeftBrace) { - if (yyBraceDepth == namespaces.count() + 1) { - namespaces.append(ns); - allNamespaces.insert(namespaces.join(strColons)); - } - } else if (yyTok == Tok_Equals) { - // e.g. namespace Is = OuterSpace::InnerSpace; - QStringList alias = namespaces; - alias.append(ns); - QStringList fullName; - yyTok = getToken(); - if (yyTok == Tok_ColonColon) - fullName.append(QString()); - while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { - if (yyTok == Tok_Ident) - fullName.append(yyIdent); - yyTok = getToken(); - } - namespaceAliases[alias.join(strColons)] = - getFullyQualifiedNamespaceName(allNamespaces, namespaces, namespaceAliases, fullName); - } - } - break; - case Tok_tr: - case Tok_trUtf8: - utf8 = (yyTok == Tok_trUtf8); - line = yyLineNo; - yyTok = getToken(); - if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) { - comment.clear(); - bool plural = false; - - if (match(Tok_RightParen)) { - // no comment - } else if (match(Tok_Comma) && matchStringOrNull(&comment)) { //comment - if (match(Tok_RightParen)) { - // ok, - } else if (match(Tok_Comma)) { - plural = true; - } - } - if (prefix.isEmpty()) { - context = functionContext; - } else { -#ifdef DIAGNOSE_RETRANSLATABILITY - int last = prefix.lastIndexOf(strColons); - QString className = prefix.mid(last == -1 ? 0 : last + 2); - if (!className.isEmpty() && className == functionName) { - qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ", - qPrintable(yyFileName), yyLineNo, - className.constData(), functionName.constData()); - } -#endif - prefix.chop(2); - context = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, true); - } - prefix.clear(); - if (qualifiedContexts.contains(context)) - context = qualifiedContexts[context]; - - if (!text.isEmpty()) - recordMessage(tor, line, context, text, comment, extracomment, utf8, plural); - - if (lacks_Q_OBJECT.contains(context)) { - qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro", - qPrintable(yyFileName), yyLineNo, - qPrintable(context)); - lacks_Q_OBJECT.remove(context); - } else { - needs_Q_OBJECT.insert(context); - } - } - extracomment.clear(); - break; - case Tok_translateUtf8: - case Tok_translate: - utf8 = (yyTok == Tok_translateUtf8); - line = yyLineNo; - yyTok = getToken(); - if (match(Tok_LeftParen) - && matchString(&context) - && match(Tok_Comma) - && matchString(&text)) - { - comment.clear(); - bool plural = false; - if (!match(Tok_RightParen)) { - // look for comment - if (match(Tok_Comma) && matchStringOrNull(&comment)) { - if (!match(Tok_RightParen)) { - // look for encoding - if (match(Tok_Comma)) { - if (matchEncoding(&utf8)) { - if (!match(Tok_RightParen)) { - // look for the plural quantifier, - // this can be a number, an identifier or - // a function call, - // so for simplicity we mark it as plural if - // we know we have a comma instead of an - // right parentheses. - plural = match(Tok_Comma); - } - } else { - // This can be a QTranslator::translate("context", - // "source", "comment", n) plural translation - if (matchExpression() && match(Tok_RightParen)) { - plural = true; - } else { - break; - } - } - } else { - break; - } - } - } else { - break; - } - } - if (!text.isEmpty()) - recordMessage(tor, line, context, text, comment, extracomment, utf8, plural); - } - extracomment.clear(); - break; - case Tok_Q_DECLARE_TR_FUNCTIONS: - case Tok_Q_OBJECT: - missing_Q_OBJECT = false; - yyTok = getToken(); - break; - case Tok_Ident: - prefix += yyIdent; - yyTok = getToken(); - if (yyTok != Tok_ColonColon) - prefix.clear(); - break; - case Tok_Comment: - if (yyComment.startsWith(QLatin1Char(':'))) { - yyComment.remove(0, 1); - extracomment.append(yyComment); - } else { - comment = yyComment.simplified(); - if (comment.startsWith(QLatin1String(MagicComment))) { - comment.remove(0, sizeof(MagicComment) - 1); - int k = comment.indexOf(QLatin1Char(' ')); - if (k == -1) { - context = comment; - } else { - context = comment.left(k); - comment.remove(0, k + 1); - recordMessage(tor, yyLineNo, context, QString(), comment, extracomment, false, false); - } - - /* - Provide a backdoor for people using "using - namespace". See the manual for details. - */ - k = 0; - while ((k = context.indexOf(strColons, k)) != -1) { - qualifiedContexts.insert(context.mid(k + 2), context); - k++; - } - } - } - yyTok = getToken(); - break; - case Tok_Arrow: - yyTok = getToken(); - if (yyTok == Tok_tr || yyTok == Tok_trUtf8) - qWarning("%s:%d: Cannot invoke tr() like this", - qPrintable(yyFileName), yyLineNo); - break; - case Tok_ColonColon: - if (yyBraceDepth == namespaces.count() && yyParenDepth == 0 && !yyTokColonSeen) - functionContext = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, false); - prefix += strColons; - yyTok = getToken(); -#ifdef DIAGNOSE_RETRANSLATABILITY - if (yyTok == Tok_Ident && yyBraceDepth == namespaces.count() && yyParenDepth == 0) - functionName = yyIdent; -#endif - break; - case Tok_RightBrace: - case Tok_Semicolon: - prefix.clear(); - extracomment.clear(); - yyTokColonSeen = false; - if (yyBraceDepth >= 0 && yyBraceDepth + 1 == namespaces.count()) - namespaces.removeLast(); - if (yyBraceDepth == namespaces.count()) { - if (missing_Q_OBJECT) { - if (needs_Q_OBJECT.contains(functionContext)) { - qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro", - qPrintable(yyFileName), yyLineNo, - qPrintable(functionContext)); - } else { - lacks_Q_OBJECT.insert(functionContext); - } - } - functionContext = defaultContext; - missing_Q_OBJECT = false; - } - yyTok = getToken(); - break; - case Tok_Colon: - yyTokColonSeen = true; - yyTok = getToken(); - break; - case Tok_LeftParen: - case Tok_RightParen: - case Tok_LeftBrace: - yyTokColonSeen = false; - yyTok = getToken(); - break; - default: - yyTok = getToken(); - break; - } - } - - if (yyBraceDepth != 0) - qWarning("%s:%d: Unbalanced braces in C++ code (or abuse of the C++" - " preprocessor)\n", - qPrintable(yyFileName), yyBraceLineNo); - else if (yyParenDepth != 0) - qWarning("%s:%d: Unbalanced parentheses in C++ code (or abuse of the C++" - " preprocessor)\n", - qPrintable(yyFileName), yyParenLineNo); -} - -/* - Fetches tr() calls in C++ code in UI files (inside "<function>" - tag). This mechanism is obsolete. -*/ -void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context) -{ - yyInStr = in; - yyInPos = 0; - yyFileName = QString(); - yyCodecIsUtf8 = (translator.codecName() == "UTF-8"); - yyForceUtf8 = true; - yySourceIsUnicode = true; - yySavedBraceDepth.clear(); - yySavedParenDepth.clear(); - yyBraceDepth = 0; - yyParenDepth = 0; - yyCurLineNo = 1; - yyBraceLineNo = 1; - yyParenLineNo = 1; - yyCh = getChar(); - - parse(&translator, context, QString()); -} - - -bool loadCPP(Translator &translator, QIODevice &dev, ConversionData &cd) -{ - QString defaultContext = cd.m_defaultContext; - - yyCodecIsUtf8 = (translator.codecName() == "UTF-8"); - yyForceUtf8 = false; - QTextStream ts(&dev); - QByteArray codecName = cd.m_codecForSource.isEmpty() - ? translator.codecName() : cd.m_codecForSource; - ts.setCodec(QTextCodec::codecForName(codecName)); - ts.setAutoDetectUnicode(true); - yySourceCodec = ts.codec(); - if (yySourceCodec->name() == "UTF-16") - translator.setCodecName("System"); - yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-"); - yyInStr = ts.readAll(); - yyInPos = 0; - yyFileName = cd.m_sourceFileName; - yySavedBraceDepth.clear(); - yySavedParenDepth.clear(); - yyBraceDepth = 0; - yyParenDepth = 0; - yyCurLineNo = 1; - yyBraceLineNo = 1; - yyParenLineNo = 1; - yyCh = getChar(); - - parse(&translator, defaultContext, defaultContext); - - return true; -} - -int initCPP() -{ - Translator::FileFormat format; - format.extension = QLatin1String("cpp"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.description = QObject::tr("C++ source files"); - format.loader = &loadCPP; - format.saver = 0; - Translator::registerFileFormat(format); - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initCPP) - -QT_END_NAMESPACE diff --git a/tools/linguist/shared/formats.pri b/tools/linguist/shared/formats.pri index 9c8072b..985f6db 100644 --- a/tools/linguist/shared/formats.pri +++ b/tools/linguist/shared/formats.pri @@ -19,8 +19,4 @@ SOURCES += \ $$PWD/qph.cpp \ $$PWD/po.cpp \ $$PWD/ts.cpp \ - $$PWD/ui.cpp \ - $$PWD/cpp.cpp \ - $$PWD/java.cpp \ - $$PWD/qscript.cpp \ $$PWD/xliff.cpp diff --git a/tools/linguist/shared/make-qscript.sh b/tools/linguist/shared/make-qscript.sh deleted file mode 100755 index 42cab7a..0000000 --- a/tools/linguist/shared/make-qscript.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -me=$(dirname $0) -mkdir -p $me/out -(cd $me/out && ${QLALR-qlalr} --no-debug --troll --no-lines ../qscript.g) - -for f in $me/out/*.{h,cpp}; do - n=$(basename $f) - p4 open $me/../$n - cp $f $me/../$n -done - -p4 revert -a $me/../... -p4 diff -du $me/../... diff --git a/tools/linguist/shared/profileevaluator.cpp b/tools/linguist/shared/profileevaluator.cpp index ce53c27..14355b3 100644 --- a/tools/linguist/shared/profileevaluator.cpp +++ b/tools/linguist/shared/profileevaluator.cpp @@ -44,6 +44,7 @@ #include "proitems.h" #include <QtCore/QByteArray> +#include <QtCore/QDateTime> #include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QFile> @@ -56,6 +57,15 @@ #include <QtCore/QStringList> #include <QtCore/QTextStream> +#ifdef Q_OS_UNIX +#include <unistd.h> +#include <sys/utsname.h> +#elif defined(Q_OS_WIN32) +#include <Windows.h> +#endif +#include <stdio.h> +#include <stdlib.h> + #ifdef Q_OS_WIN32 #define QT_POPEN _popen #else @@ -66,6 +76,58 @@ QT_BEGIN_NAMESPACE /////////////////////////////////////////////////////////////////////// // +// Option +// +/////////////////////////////////////////////////////////////////////// + +QString +Option::fixString(QString string, uchar flags) +{ + // XXX Ripped out caching, so this will be slow. Should not matter for current uses. + + //fix the environment variables + if (flags & Option::FixEnvVars) { + int rep; + QRegExp reg_variableName(QLatin1String("\\$\\(.*\\)")); + reg_variableName.setMinimal(true); + while ((rep = reg_variableName.indexIn(string)) != -1) + string.replace(rep, reg_variableName.matchedLength(), + QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_variableName.matchedLength() - 3).toLatin1().constData()).constData())); + } + + //canonicalize it (and treat as a path) + if (flags & Option::FixPathCanonicalize) { +#if 0 + string = QFileInfo(string).canonicalFilePath(); +#endif + string = QDir::cleanPath(string); + } + + if (string.length() > 2 && string[0].isLetter() && string[1] == QLatin1Char(':')) + string[0] = string[0].toLower(); + + //fix separators + Q_ASSERT(!((flags & Option::FixPathToLocalSeparators) && (flags & Option::FixPathToTargetSeparators))); + if (flags & Option::FixPathToLocalSeparators) { +#if defined(Q_OS_WIN32) + string = string.replace(QLatin1Char('/'), QLatin1Char('\\')); +#else + string = string.replace(QLatin1Char('\\'), QLatin1Char('/')); +#endif + } else if (flags & Option::FixPathToTargetSeparators) { + string = string.replace(QLatin1Char('/'), Option::dir_sep) + .replace(QLatin1Char('\\'), Option::dir_sep); + } + + if ((string.startsWith(QLatin1Char('"')) && string.endsWith(QLatin1Char('"'))) || + (string.startsWith(QLatin1Char('\'')) && string.endsWith(QLatin1Char('\'')))) + string = string.mid(1, string.length() - 2); + + return string; +} + +/////////////////////////////////////////////////////////////////////// +// // ProFileEvaluator::Private // /////////////////////////////////////////////////////////////////////// @@ -75,6 +137,12 @@ class ProFileEvaluator::Private : public AbstractProItemVisitor public: Private(ProFileEvaluator *q_); + ProFileEvaluator *q; + int m_lineNo; // Error reporting + bool m_verbose; + + /////////////// Reading pro file + bool read(ProFile *pro); ProBlock *currentBlock(); @@ -87,6 +155,17 @@ public: void leaveScope(); void finalizeBlock(); + QStack<ProBlock *> m_blockstack; + ProBlock *m_block; + + ProItem *m_commentItem; + QString m_proitem; + QString m_pendingComment; + bool m_syntaxError; + bool m_contNextLine; + + /////////////// Evaluating pro file contents + // implementation of AbstractProItemVisitor bool visitBeginProBlock(ProBlock *block); bool visitEndProBlock(ProBlock *block); @@ -102,6 +181,8 @@ public: QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; } QStringList values(const QString &variableName) const; QStringList values(const QString &variableName, const ProFile *pro) const; + QStringList values(const QString &variableName, const QHash<QString, QStringList> &place, + const ProFile *pro) const; QString propertyValue(const QString &val) const; bool isActiveConfig(const QString &config, bool regex = false); @@ -113,7 +194,7 @@ public: QString format(const char *format) const; QString currentFileName() const; - QString getcwd() const; + QString currentDirectory() const; ProFile *currentProFile() const; bool evaluateConditionalFunction(const QString &function, const QString &arguments, bool *result); @@ -122,41 +203,51 @@ public: QStringList qmakeFeaturePaths(); - ProFileEvaluator *q; - - QStack<ProBlock *> m_blockstack; - ProBlock *m_block; - - ProItem *m_commentItem; - QString m_proitem; - QString m_pendingComment; - bool m_syntaxError; - bool m_contNextLine; - bool m_condition; + enum { ConditionTrue, ConditionFalse, ConditionElse }; + int m_condition; + int m_prevCondition; + bool m_updateCondition; bool m_invertNext; + int m_skipLevel; + bool m_cumulative; + bool m_isFirstVariableValue; QString m_lastVarName; ProVariable::VariableOperator m_variableOperator; - int m_lineNo; // Error reporting + QString m_origfile; QString m_oldPath; // To restore the current path to the path QStack<ProFile*> m_profileStack; // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri' + // we need the following two variables for handling + // CONFIG = foo bar $$CONFIG + QHash<QString, QStringList> m_tempValuemap; // used while evaluating (variable operator value1 value2 ...) + QHash<const ProFile*, QHash<QString, QStringList> > m_tempFilevaluemap; // used while evaluating (variable operator value1 value2 ...) + QHash<QString, QStringList> m_valuemap; // VariableName must be us-ascii, the content however can be non-us-ascii. QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file QHash<QString, QString> m_properties; - QString m_origfile; + QString m_outputDir; int m_prevLineNo; // Checking whether we're assigning the same TARGET ProFile *m_prevProFile; // See m_prevLineNo - - bool m_verbose; }; ProFileEvaluator::Private::Private(ProFileEvaluator *q_) : q(q_) { + // Global parser state m_prevLineNo = 0; m_prevProFile = 0; + + // Configuration, more or less m_verbose = true; + m_cumulative = true; + + // Evaluator state + m_updateCondition = false; + m_condition = ConditionFalse; + m_invertNext = false; + m_skipLevel = 0; + m_isFirstVariableValue = true; } bool ProFileEvaluator::Private::read(ProFile *pro) @@ -167,6 +258,7 @@ bool ProFileEvaluator::Private::read(ProFile *pro) return false; } + // Parser state m_block = 0; m_commentItem = 0; m_contNextLine = false; @@ -452,15 +544,35 @@ void ProFileEvaluator::Private::updateItem() bool ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block) { if (block->blockKind() == ProBlock::ScopeKind) { - m_invertNext = false; - m_condition = false; + m_updateCondition = true; + if (!m_skipLevel) { + m_prevCondition = m_condition; + m_condition = ConditionFalse; + } else { + Q_ASSERT(m_condition != ConditionTrue); + } + } else if (block->blockKind() & ProBlock::ScopeContentsKind) { + m_updateCondition = false; + if (m_condition != ConditionTrue) + ++m_skipLevel; + else + Q_ASSERT(!m_skipLevel); } return true; } bool ProFileEvaluator::Private::visitEndProBlock(ProBlock *block) { - Q_UNUSED(block); + if (block->blockKind() & ProBlock::ScopeContentsKind) { + if (m_skipLevel) { + Q_ASSERT(m_condition != ConditionTrue); + --m_skipLevel; + } else { + // Conditionals contained inside this block may have changed the state. + // So we reset it here to make an else following us do the right thing. + m_condition = ConditionTrue; + } + } return true; } @@ -468,12 +580,17 @@ bool ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable) { m_lastVarName = variable->variable(); m_variableOperator = variable->variableOperator(); + m_isFirstVariableValue = true; + m_tempValuemap = m_valuemap; + m_tempFilevaluemap = m_filevaluemap; return true; } bool ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable) { Q_UNUSED(variable); + m_valuemap = m_tempValuemap; + m_filevaluemap = m_tempFilevaluemap; m_lastVarName.clear(); return true; } @@ -486,12 +603,20 @@ bool ProFileEvaluator::Private::visitProOperator(ProOperator *oper) bool ProFileEvaluator::Private::visitProCondition(ProCondition *cond) { - if (!m_condition) { - if (m_invertNext) - m_condition |= !isActiveConfig(cond->text(), true); - else - m_condition |= isActiveConfig(cond->text(), true); + if (!m_skipLevel) { + if (cond->text().toLower() == QLatin1String("else")) { + // The state ConditionElse makes sure that subsequential elses are ignored. + // That's braindead, but qmake is like that. + if (m_prevCondition == ConditionTrue) + m_condition = ConditionElse; + else if (m_prevCondition == ConditionFalse) + m_condition = ConditionTrue; + } else if (m_condition == ConditionFalse) { + if (isActiveConfig(cond->text(), true) ^ m_invertNext) + m_condition = ConditionTrue; + } } + m_invertNext = false; return true; } @@ -500,18 +625,32 @@ bool ProFileEvaluator::Private::visitBeginProFile(ProFile * pro) PRE(pro); bool ok = true; m_lineNo = pro->lineNumber(); + + if (m_origfile.isEmpty()) + m_origfile = pro->fileName(); if (m_oldPath.isEmpty()) { // change the working directory for the initial profile we visit, since // that is *the* profile. All the other times we reach this function will be due to // include(file) or load(file) + m_oldPath = QDir::currentPath(); + m_profileStack.push(pro); + + const QString mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS")); + if (!mkspecDirectory.isEmpty()) { + bool cumulative = m_cumulative; + m_cumulative = false; + // This is what qmake does, everything set in the mkspec is also set + // But this also creates a lot of problems + evaluateFile(mkspecDirectory + QLatin1String("/default/qmake.conf"), &ok); + evaluateFile(mkspecDirectory + QLatin1String("/features/default_pre.prf"), &ok); + m_cumulative = cumulative; + } + ok = QDir::setCurrent(pro->directoryName()); } - if (m_origfile.isEmpty()) - m_origfile = pro->fileName(); - return ok; } @@ -521,12 +660,60 @@ bool ProFileEvaluator::Private::visitEndProFile(ProFile * pro) bool ok = true; m_lineNo = pro->lineNumber(); if (m_profileStack.count() == 1 && !m_oldPath.isEmpty()) { + const QString &mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS")); + if (!mkspecDirectory.isEmpty()) { + bool cumulative = m_cumulative; + m_cumulative = false; + + evaluateFile(mkspecDirectory + QLatin1String("/features/default_post.prf"), &ok); + + QSet<QString> processed; + forever { + bool finished = true; + QStringList configs = valuesDirect(QLatin1String("CONFIG")); + for (int i = configs.size() - 1; i >= 0; --i) { + const QString config = configs[i].toLower(); + if (!processed.contains(config)) { + processed.insert(config); + evaluateFile(mkspecDirectory + QLatin1String("/features/") + + config + QLatin1String(".prf"), &ok); + if (ok) { + finished = false; + break; + } + } + } + if (finished) + break; + } + + m_cumulative = cumulative; + } + m_profileStack.pop(); ok = QDir::setCurrent(m_oldPath); } return ok; } +static void replaceInList(QStringList *varlist, + const QRegExp ®exp, const QString &replace, bool global) +{ + for (QStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) { + if ((*varit).contains(regexp)) { + (*varit).replace(regexp, replace); + if ((*varit).isEmpty()) + varit = varlist->erase(varit); + else + ++varit; + if(!global) + break; + } else { + ++varit; + } + } +} + bool ProFileEvaluator::Private::visitProValue(ProValue *value) { PRE(value); @@ -544,8 +731,8 @@ bool ProFileEvaluator::Private::visitProValue(ProValue *value) if (varName == QLatin1String("TARGET") && m_lineNo == m_prevLineNo && currentProFile() == m_prevProFile) { - QStringList targets = m_valuemap.value(QLatin1String("TARGET")); - m_valuemap.remove(QLatin1String("TARGET")); + QStringList targets = m_tempValuemap.value(QLatin1String("TARGET")); + m_tempValuemap.remove(QLatin1String("TARGET")); QStringList lastTarget(targets.takeLast()); lastTarget << v.join(QLatin1String(" ")); targets.push_back(lastTarget.join(QLatin1String(" "))); @@ -578,37 +765,58 @@ bool ProFileEvaluator::Private::visitProValue(ProValue *value) } switch (m_variableOperator) { - case ProVariable::UniqueAddOperator: // * - insertUnique(&m_valuemap, varName, v, true); - insertUnique(&m_filevaluemap[currentProFile()], varName, v, true); - break; case ProVariable::SetOperator: // = - case ProVariable::AddOperator: // + - insertUnique(&m_valuemap, varName, v, false); - insertUnique(&m_filevaluemap[currentProFile()], varName, v, false); + if (!m_cumulative) { + if (!m_skipLevel) { + if (m_isFirstVariableValue) { + m_tempValuemap[varName] = v; + m_tempFilevaluemap[currentProFile()][varName] = v; + } else { // handle lines "CONFIG = foo bar" + m_tempValuemap[varName] += v; + m_tempFilevaluemap[currentProFile()][varName] += v; + } + } + } else { + // We are greedy for values. + m_tempValuemap[varName] += v; + m_tempFilevaluemap[currentProFile()][varName] += v; + } break; - case ProVariable::RemoveOperator: // - - // fix me: interaction between AddOperator and RemoveOperator - insertUnique(&m_valuemap, varName.prepend(QLatin1Char('-')), v, false); - insertUnique(&m_filevaluemap[currentProFile()], - varName.prepend(QLatin1Char('-')), v, false); + case ProVariable::UniqueAddOperator: // *= + if (!m_skipLevel || m_cumulative) { + insertUnique(&m_tempValuemap, varName, v); + insertUnique(&m_tempFilevaluemap[currentProFile()], varName, v); + } break; - case ProVariable::ReplaceOperator: // ~ + case ProVariable::AddOperator: // += + if (!m_skipLevel || m_cumulative) { + m_tempValuemap[varName] += v; + m_tempFilevaluemap[currentProFile()][varName] += v; + } + break; + case ProVariable::RemoveOperator: // -= + if (!m_cumulative) { + if (!m_skipLevel) { + removeEach(&m_tempValuemap, varName, v); + removeEach(&m_tempFilevaluemap[currentProFile()], varName, v); + } + } else { + // We are stingy with our values, too. + } + break; + case ProVariable::ReplaceOperator: // ~= { // DEFINES ~= s/a/b/?[gqi] -/* Create a superset by executing replacement + adding items that have changed - to original list. We're not sure if this is really the right approach, so for - the time being we will just do nothing ... - + // FIXME: qmake variable-expands val first. + if (val.length() < 4 || val[0] != QLatin1Char('s')) { + q->logMessage(format("the ~= operator can handle only the s/// function.")); + return false; + } QChar sep = val.at(1); QStringList func = val.split(sep); if (func.count() < 3 || func.count() > 4) { - q->logMessage(format("'~= operator '(function s///) expects 3 or 4 arguments.")); - return false; - } - if (func[0] != QLatin1String("s")) { - q->logMessage(format("~= operator can only handle s/// function.")); + q->logMessage(format("the s/// function expects 3 or 4 arguments.")); return false; } @@ -625,40 +833,40 @@ bool ProFileEvaluator::Private::visitProValue(ProValue *value) QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive); - QStringList replaceList = replaceInList(m_valuemap.value(varName), regexp, replace, - global); - // Add changed entries to list - foreach (const QString &entry, replaceList) - if (!m_valuemap.value(varName).contains(entry)) - insertUnique(&m_valuemap, varName, QStringList() << entry, false); - - replaceList = replaceInList(m_filevaluemap[currentProFile()].value(varName), regexp, - replace, global); - foreach (const QString &entry, replaceList) - if (!m_filevaluemap[currentProFile()].value(varName).contains(entry)) - insertUnique(&m_filevaluemap[currentProFile()], varName, - QStringList() << entry, false); */ + if (!m_skipLevel || m_cumulative) { + // We could make a union of modified and unmodified values, + // but this will break just as much as it fixes, so leave it as is. + replaceInList(&m_tempValuemap[varName], regexp, replace, global); + replaceInList(&m_tempFilevaluemap[currentProFile()][varName], regexp, replace, global); + } } break; } + m_isFirstVariableValue = false; return true; } bool ProFileEvaluator::Private::visitProFunction(ProFunction *func) { - m_lineNo = func->lineNumber(); - bool result = true; - bool ok = true; - QString text = func->text(); - int lparen = text.indexOf(QLatin1Char('(')); - int rparen = text.lastIndexOf(QLatin1Char(')')); - Q_ASSERT(lparen < rparen); - - QString arguments = text.mid(lparen + 1, rparen - lparen - 1); - QString funcName = text.left(lparen); - ok &= evaluateConditionalFunction(funcName.trimmed(), arguments, &result); - return ok; + if (!m_updateCondition || m_condition == ConditionFalse) { + QString text = func->text(); + int lparen = text.indexOf(QLatin1Char('(')); + int rparen = text.lastIndexOf(QLatin1Char(')')); + Q_ASSERT(lparen < rparen); + QString arguments = text.mid(lparen + 1, rparen - lparen - 1); + QString funcName = text.left(lparen); + m_lineNo = func->lineNumber(); + bool result; + if (!evaluateConditionalFunction(funcName.trimmed(), arguments, &result)) { + m_invertNext = false; + return false; + } + if (!m_skipLevel && (result ^ m_invertNext)) + m_condition = ConditionTrue; + } + m_invertNext = false; + return true; } @@ -778,7 +986,7 @@ QString ProFileEvaluator::Private::currentFileName() const return QString(); } -QString ProFileEvaluator::Private::getcwd() const +QString ProFileEvaluator::Private::currentDirectory() const { ProFile *cur = m_profileStack.top(); return cur->directoryName(); @@ -1015,29 +1223,29 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun static QHash<QString, int> *expands = 0; if (!expands) { expands = new QHash<QString, int>; - expands->insert(QLatin1String("member"), E_MEMBER); //v (implemented) - expands->insert(QLatin1String("first"), E_FIRST); //v - expands->insert(QLatin1String("last"), E_LAST); //v + expands->insert(QLatin1String("member"), E_MEMBER); + expands->insert(QLatin1String("first"), E_FIRST); + expands->insert(QLatin1String("last"), E_LAST); expands->insert(QLatin1String("cat"), E_CAT); - expands->insert(QLatin1String("fromfile"), E_FROMFILE); + expands->insert(QLatin1String("fromfile"), E_FROMFILE); // implementation disabled (see comment below) expands->insert(QLatin1String("eval"), E_EVAL); expands->insert(QLatin1String("list"), E_LIST); expands->insert(QLatin1String("sprintf"), E_SPRINTF); - expands->insert(QLatin1String("join"), E_JOIN); //v - expands->insert(QLatin1String("split"), E_SPLIT); //v - expands->insert(QLatin1String("basename"), E_BASENAME); //v - expands->insert(QLatin1String("dirname"), E_DIRNAME); //v + expands->insert(QLatin1String("join"), E_JOIN); + expands->insert(QLatin1String("split"), E_SPLIT); + expands->insert(QLatin1String("basename"), E_BASENAME); + expands->insert(QLatin1String("dirname"), E_DIRNAME); expands->insert(QLatin1String("section"), E_SECTION); expands->insert(QLatin1String("find"), E_FIND); - expands->insert(QLatin1String("system"), E_SYSTEM); //v + expands->insert(QLatin1String("system"), E_SYSTEM); expands->insert(QLatin1String("unique"), E_UNIQUE); - expands->insert(QLatin1String("quote"), E_QUOTE); //v + expands->insert(QLatin1String("quote"), E_QUOTE); expands->insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND); expands->insert(QLatin1String("upper"), E_UPPER); expands->insert(QLatin1String("lower"), E_LOWER); expands->insert(QLatin1String("re_escape"), E_RE_ESCAPE); expands->insert(QLatin1String("files"), E_FILES); - expands->insert(QLatin1String("prompt"), E_PROMPT); + expands->insert(QLatin1String("prompt"), E_PROMPT); // interactive, so cannot be implemented expands->insert(QLatin1String("replace"), E_REPLACE); } ExpandFunc func_t = ExpandFunc(expands->value(func.toLower())); @@ -1086,6 +1294,16 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun } break; } + case E_SPRINTF: + if(args.count() < 1) { + q->logMessage(format("sprintf(format, ...) requires at least one argument")); + } else { + QString tmp = args.at(0); + for (int i = 1; i < args.count(); ++i) + tmp = tmp.arg(args.at(i)); + ret = split_value_list(tmp); + } + break; case E_JOIN: { if (args.count() < 1 || args.count() > 4) { q->logMessage(format("join(var, glue, before, after) requires one to four arguments.")); @@ -1105,9 +1323,9 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun } case E_SPLIT: { if (args.count() != 2) { - q->logMessage(format("split(var, sep) requires two arguments")); + q->logMessage(format("split(var, sep) requires one or two arguments")); } else { - QString sep = args.at(1); + const QString &sep = (args.count() == 2) ? args[1] : QString(Option::field_sep); foreach (const QString &var, values(args.first())) foreach (const QString &splt, var.split(sep)) ret.append(splt); @@ -1178,8 +1396,82 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun } break; } - case E_SYSTEM: { - if (m_condition) { + case E_CAT: + if (args.count() < 1 || args.count() > 2) { + q->logMessage(format("cat(file, singleline=true) requires one or two arguments.")); + } else { + QString file = args[0]; + file = Option::fixPathToLocalOS(file); + + bool singleLine = true; + if (args.count() > 1) + singleLine = (args[1].toLower() == QLatin1String("true")); + + QFile qfile(file); + if (qfile.open(QIODevice::ReadOnly)) { + QTextStream stream(&qfile); + while (!stream.atEnd()) { + ret += split_value_list(stream.readLine().trimmed()); + if (!singleLine) + ret += QLatin1String("\n"); + } + qfile.close(); + } + } + break; +#if 0 // Used only by Qt's configure for caching + case E_FROMFILE: + if (args.count() != 2) { + q->logMessage(format("fromfile(file, variable) requires two arguments.")); + } else { + QString file = args[0], seek_variableName = args[1]; + + ProFile pro(Option::fixPathToLocalOS(file)); + + ProFileEvaluator visitor; + visitor.setVerbose(m_verbose); + visitor.setCumulative(m_cumulative); + + if (!visitor.queryProFile(&pro)) + break; + + if (!visitor.accept(&pro)) + break; + + ret = visitor.values(seek_variableName); + } + break; +#endif + case E_EVAL: { + if (args.count() != 1) { + q->logMessage(format("eval(variable) requires one argument")); + + } else { + ret += values(args.at(0)); + } + break; } + case E_LIST: { + static int x = 0; + QString tmp; + tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", x++); + ret = QStringList(tmp); + QStringList lst; + foreach (const QString &arg, args) + lst += split_value_list(arg); + m_valuemap[tmp] = lst; + break; } + case E_FIND: + if (args.count() != 2) { + q->logMessage(format("find(var, str) requires two arguments.")); + } else { + QRegExp regx(args[1]); + foreach (const QString &val, values(args.first())) + if (regx.indexIn(val) != -1) + ret += val; + } + break; + case E_SYSTEM: + if (!m_skipLevel) { if (args.count() < 1 || args.count() > 2) { q->logMessage(format("system(execute) requires one or two arguments.")); } else { @@ -1203,13 +1495,114 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun ret += split_value_list(output); } } - break; } + break; + case E_UNIQUE: + if(args.count() != 1) { + q->logMessage(format("unique(var) requires one argument.")); + } else { + foreach (const QString &var, values(args.first())) + if (!ret.contains(var)) + ret.append(var); + } + break; case E_QUOTE: for (int i = 0; i < args.count(); ++i) ret += QStringList(args.at(i)); break; + case E_ESCAPE_EXPAND: + for (int i = 0; i < args.size(); ++i) { + QChar *i_data = args[i].data(); + int i_len = args[i].length(); + for (int x = 0; x < i_len; ++x) { + if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) { + if (*(i_data+x+1) == QLatin1Char('\\')) { + ++x; + } else { + struct { + char in, out; + } mapped_quotes[] = { + { 'n', '\n' }, + { 't', '\t' }, + { 'r', '\r' }, + { 0, 0 } + }; + for (int i = 0; mapped_quotes[i].in; ++i) { + if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) { + *(i_data+x) = QLatin1Char(mapped_quotes[i].out); + if (x < i_len-2) + memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar)); + --i_len; + break; + } + } + } + } + } + ret.append(QString(i_data, i_len)); + } + break; + case E_RE_ESCAPE: + for (int i = 0; i < args.size(); ++i) + ret += QRegExp::escape(args[i]); + break; + case E_UPPER: + case E_LOWER: + for (int i = 0; i < args.count(); ++i) + if (func_t == E_UPPER) + ret += args[i].toUpper(); + else + ret += args[i].toLower(); + break; + case E_FILES: + if (args.count() != 1 && args.count() != 2) { + q->logMessage(format("files(pattern, recursive=false) requires one or two arguments")); + } else { + bool recursive = false; + if (args.count() == 2) + recursive = (args[1].toLower() == QLatin1String("true") || args[1].toInt()); + QStringList dirs; + QString r = Option::fixPathToLocalOS(args[0]); + int slash = r.lastIndexOf(QDir::separator()); + if (slash != -1) { + dirs.append(r.left(slash)); + r = r.mid(slash+1); + } else { + dirs.append(QString()); + } + + const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard); + for (int d = 0; d < dirs.count(); d++) { + QString dir = dirs[d]; + if (!dir.isEmpty() && !dir.endsWith(Option::dir_sep)) + dir += QLatin1Char('/'); + + QDir qdir(dir); + for (int i = 0; i < (int)qdir.count(); ++i) { + if (qdir[i] == QLatin1String(".") || qdir[i] == QLatin1String("..")) + continue; + QString fname = dir + qdir[i]; + if (QFileInfo(fname).isDir()) { + if (recursive) + dirs.append(fname); + } + if (regex.exactMatch(qdir[i])) + ret += fname; + } + } + } + break; + case E_REPLACE: + if(args.count() != 3 ) { + q->logMessage(format("replace(var, before, after) requires three arguments")); + } else { + const QRegExp before(args[1]); + const QString after(args[2]); + foreach (QString val, values(args.first())) + ret += val.replace(before, after); + } + break; case 0: - q->logMessage(format("'%1' is not a function").arg(func)); + q->logMessage(format("'%1' is not a recognized replace function").arg(func)); break; default: q->logMessage(format("Function '%1' is not implemented").arg(func)); @@ -1230,26 +1623,67 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct for (int i = 0; i < argumentsList.count(); ++i) args += expandVariableReferences(argumentsList[i]).join(sep); - enum ConditionFunc { CF_CONFIG = 1, CF_CONTAINS, CF_COUNT, CF_EXISTS, CF_INCLUDE, - CF_LOAD, CF_ISEMPTY, CF_SYSTEM, CF_MESSAGE}; + enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS, + T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM, + T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE, + T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF }; static QHash<QString, int> *functions = 0; if (!functions) { functions = new QHash<QString, int>; - functions->insert(QLatin1String("load"), CF_LOAD); //v - functions->insert(QLatin1String("include"), CF_INCLUDE); //v - functions->insert(QLatin1String("message"), CF_MESSAGE); //v - functions->insert(QLatin1String("warning"), CF_MESSAGE); //v - functions->insert(QLatin1String("error"), CF_MESSAGE); //v + functions->insert(QLatin1String("requires"), T_REQUIRES); + functions->insert(QLatin1String("greaterThan"), T_GREATERTHAN); + functions->insert(QLatin1String("lessThan"), T_LESSTHAN); + functions->insert(QLatin1String("equals"), T_EQUALS); + functions->insert(QLatin1String("isEqual"), T_EQUALS); + functions->insert(QLatin1String("exists"), T_EXISTS); + functions->insert(QLatin1String("export"), T_EXPORT); + functions->insert(QLatin1String("clear"), T_CLEAR); + functions->insert(QLatin1String("unset"), T_UNSET); + functions->insert(QLatin1String("eval"), T_EVAL); + functions->insert(QLatin1String("CONFIG"), T_CONFIG); + functions->insert(QLatin1String("if"), T_IF); + functions->insert(QLatin1String("isActiveConfig"), T_CONFIG); + functions->insert(QLatin1String("system"), T_SYSTEM); + functions->insert(QLatin1String("return"), T_RETURN); + functions->insert(QLatin1String("break"), T_BREAK); + functions->insert(QLatin1String("next"), T_NEXT); + functions->insert(QLatin1String("defined"), T_DEFINED); + functions->insert(QLatin1String("contains"), T_CONTAINS); + functions->insert(QLatin1String("infile"), T_INFILE); + functions->insert(QLatin1String("count"), T_COUNT); + functions->insert(QLatin1String("isEmpty"), T_ISEMPTY); + functions->insert(QLatin1String("load"), T_LOAD); //v + functions->insert(QLatin1String("include"), T_INCLUDE); //v + functions->insert(QLatin1String("debug"), T_DEBUG); + functions->insert(QLatin1String("message"), T_MESSAGE); //v + functions->insert(QLatin1String("warning"), T_MESSAGE); //v + functions->insert(QLatin1String("error"), T_MESSAGE); //v } bool cond = false; bool ok = true; - ConditionFunc func_t = (ConditionFunc)functions->value(function); + TestFunc func_t = (TestFunc)functions->value(function); switch (func_t) { - case CF_CONFIG: { +#if 0 + case T_INFILE: + case T_REQUIRES: + case T_GREATERTHAN: + case T_LESSTHAN: + case T_EQUALS: + case T_EXPORT: + case T_CLEAR: + case T_UNSET: + case T_EVAL: + case T_IF: + case T_RETURN: + case T_BREAK: + case T_NEXT: + case T_DEFINED: +#endif + case T_CONFIG: { if (args.count() < 1 || args.count() > 2) { q->logMessage(format("CONFIG(config) requires one or two arguments.")); ok = false; @@ -1261,7 +1695,7 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct } const QStringList mutuals = args[1].split(QLatin1Char('|')); const QStringList &configs = valuesDirect(QLatin1String("CONFIG")); - for (int i = configs.size() - 1 && ok; i >= 0; i--) { + for (int i = configs.size() - 1; i >= 0; i--) { for (int mut = 0; mut < mutuals.count(); mut++) { if (configs[i] == mutuals[mut].trimmed()) { cond = (configs[i] == args[0]); @@ -1272,7 +1706,7 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct done_T_CONFIG: break; } - case CF_CONTAINS: { + case T_CONTAINS: { if (args.count() < 2 || args.count() > 3) { q->logMessage(format("contains(var, val) requires two or three arguments.")); ok = false; @@ -1304,9 +1738,9 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct done_T_CONTAINS: break; } - case CF_COUNT: { + case T_COUNT: { if (args.count() != 2 && args.count() != 3) { - q->logMessage(format("count(var, count) requires two or three arguments.")); + q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments.")); ok = false; break; } @@ -1331,7 +1765,9 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct cond = values(args.first()).count() == args[1].toInt(); break; } - case CF_INCLUDE: { + case T_INCLUDE: { + if (m_skipLevel && !m_cumulative) + break; QString parseInto; if (args.count() == 2) { parseInto = args[1]; @@ -1342,12 +1778,14 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct } QString fileName = args.first(); // ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style. - QDir currentProPath(getcwd()); + QDir currentProPath(currentDirectory()); fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName)); ok = evaluateFile(fileName, &ok); break; } - case CF_LOAD: { + case T_LOAD: { + if (m_skipLevel && !m_cumulative) + break; QString parseInto; bool ignore_error = false; if (args.count() == 2) { @@ -1361,13 +1799,16 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct ok = evaluateFeatureFile( args.first(), &cond); break; } - case CF_MESSAGE: { + case T_DEBUG: + // Yup - do nothing. Nothing is going to enable debug output anyway. + break; + case T_MESSAGE: { if (args.count() != 1) { q->logMessage(format("%1(message) requires one argument.").arg(function)); ok = false; break; } - QString msg = args.first(); + QString msg = fixEnvVariables(args.first()); if (function == QLatin1String("error")) { QStringList parents; foreach (ProFile *proFile, m_profileStack) @@ -1384,7 +1825,8 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct } break; } - case CF_SYSTEM: { +#if 0 // Way too dangerous to enable. + case T_SYSTEM: { if (args.count() != 1) { q->logMessage(format("system(exec) requires one argument.")); ok = false; @@ -1393,7 +1835,8 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct ok = system(args.first().toLatin1().constData()) == 0; break; } - case CF_ISEMPTY: { +#endif + case T_ISEMPTY: { if (args.count() != 1) { q->logMessage(format("isEmpty(var) requires one argument.")); ok = false; @@ -1408,31 +1851,38 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct } break; } - case CF_EXISTS: { + case T_EXISTS: { if (args.count() != 1) { q->logMessage(format("exists(file) requires one argument.")); ok = false; break; } QString file = args.first(); - - file = QDir::cleanPath(file); + file = Option::fixPathToLocalOS(file); if (QFile::exists(file)) { cond = true; break; } //regular expression I guess - QString dirstr = getcwd(); + QString dirstr = currentDirectory(); int slsh = file.lastIndexOf(Option::dir_sep); if (slsh != -1) { dirstr = file.left(slsh+1); file = file.right(file.length() - slsh - 1); } - cond = QDir(dirstr).entryList(QStringList(file)).count(); + if (file.contains(QLatin1Char('*')) || file.contains(QLatin1Char('?'))) + cond = QDir(dirstr).entryList(QStringList(file)).count(); break; } + case 0: + // This is too chatty currently (missing defineTest and defineReplace) + //q->logMessage(format("'%1' is not a recognized test function").arg(function)); + break; + default: + q->logMessage(format("Function '%1' is not implemented").arg(function)); + break; } if (result) @@ -1441,32 +1891,131 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct return ok; } -QStringList ProFileEvaluator::Private::values(const QString &variableName) const +QStringList ProFileEvaluator::Private::values(const QString &variableName, + const QHash<QString, QStringList> &place, + const ProFile *pro) const { - if (variableName == QLatin1String("TARGET")) { - QStringList list = m_valuemap.value(variableName); - if (!m_origfile.isEmpty()) - list.append(QFileInfo(m_origfile).baseName()); - return list; + if (variableName == QLatin1String("LITERAL_WHITESPACE")) //a real space in a token + return QStringList(QLatin1String("\t")); + if (variableName == QLatin1String("LITERAL_DOLLAR")) //a real $ + return QStringList(QLatin1String("$")); + if (variableName == QLatin1String("LITERAL_HASH")) //a real # + return QStringList(QLatin1String("#")); + if (variableName == QLatin1String("OUT_PWD")) //the out going dir + return QStringList(m_outputDir); + if (variableName == QLatin1String("PWD") || //current working dir (of _FILE_) + variableName == QLatin1String("IN_PWD")) + return QStringList(currentDirectory()); + if (variableName == QLatin1String("DIR_SEPARATOR")) + return QStringList(Option::dir_sep); + if (variableName == QLatin1String("DIRLIST_SEPARATOR")) + return QStringList(Option::dirlist_sep); + if (variableName == QLatin1String("_LINE_")) //parser line number + return QStringList(QString::number(m_lineNo)); + if (variableName == QLatin1String("_FILE_")) //parser file; qmake is a bit weird here + return QStringList(m_profileStack.size() == 1 ? pro->fileName() : QFileInfo(pro->fileName()).fileName()); + if (variableName == QLatin1String("_DATE_")) //current date/time + return QStringList(QDateTime::currentDateTime().toString()); + if (variableName == QLatin1String("_PRO_FILE_")) + return QStringList(m_origfile); + if (variableName == QLatin1String("_PRO_FILE_PWD_")) + return QStringList(QFileInfo(m_origfile).absolutePath()); + if (variableName == QLatin1String("_QMAKE_CACHE_")) + return QStringList(); // FIXME? + if (variableName.startsWith(QLatin1String("QMAKE_HOST."))) { + QString ret, type = variableName.mid(11); +#if defined(Q_OS_WIN32) + if (type == QLatin1String("os")) { + ret = QLatin1String("Windows"); + } else if (type == QLatin1String("name")) { + DWORD name_length = 1024; + TCHAR name[1024]; + if (GetComputerName(name, &name_length)) + ret = QString::fromUtf16((ushort*)name, name_length); + } else if (type == QLatin1String("version") || type == QLatin1String("version_string")) { + QSysInfo::WinVersion ver = QSysInfo::WindowsVersion; + if (type == QLatin1String("version")) + ret = QString::number(ver); + else if (ver == QSysInfo::WV_Me) + ret = QLatin1String("WinMe"); + else if (ver == QSysInfo::WV_95) + ret = QLatin1String("Win95"); + else if (ver == QSysInfo::WV_98) + ret = QLatin1String("Win98"); + else if (ver == QSysInfo::WV_NT) + ret = QLatin1String("WinNT"); + else if (ver == QSysInfo::WV_2000) + ret = QLatin1String("Win2000"); + else if (ver == QSysInfo::WV_2000) + ret = QLatin1String("Win2003"); + else if (ver == QSysInfo::WV_XP) + ret = QLatin1String("WinXP"); + else if (ver == QSysInfo::WV_VISTA) + ret = QLatin1String("WinVista"); + else + ret = QLatin1String("Unknown"); + } else if (type == QLatin1String("arch")) { + SYSTEM_INFO info; + GetSystemInfo(&info); + switch(info.wProcessorArchitecture) { +#ifdef PROCESSOR_ARCHITECTURE_AMD64 + case PROCESSOR_ARCHITECTURE_AMD64: + ret = QLatin1String("x86_64"); + break; +#endif + case PROCESSOR_ARCHITECTURE_INTEL: + ret = QLatin1String("x86"); + break; + case PROCESSOR_ARCHITECTURE_IA64: +#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 + case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: +#endif + ret = QLatin1String("IA64"); + break; + default: + ret = QLatin1String("Unknown"); + break; + } + } +#elif defined(Q_OS_UNIX) + struct utsname name; + if (!uname(&name)) { + if (type == QLatin1String("os")) + ret = QString::fromLatin1(name.sysname); + else if (type == QLatin1String("name")) + ret = QString::fromLatin1(name.nodename); + else if (type == QLatin1String("version")) + ret = QString::fromLatin1(name.release); + else if (type == QLatin1String("version_string")) + ret = QString::fromLatin1(name.version); + else if (type == QLatin1String("arch")) + ret = QString::fromLatin1(name.machine); + } +#endif + return QStringList(ret); } - if (variableName == QLatin1String("PWD")) { - return QStringList(getcwd()); + + QStringList result = place[variableName]; + if (result.isEmpty()) { + if (variableName == QLatin1String("TARGET")) { + result.append(QFileInfo(m_origfile).baseName()); + } else if (variableName == QLatin1String("TEMPLATE")) { + result.append(QLatin1String("app")); + } else if (variableName == QLatin1String("QMAKE_DIR_SEP")) { + result.append(Option::dirlist_sep); + } } - return m_valuemap.value(variableName); + return result; +} + +QStringList ProFileEvaluator::Private::values(const QString &variableName) const +{ + return values(variableName, m_valuemap, currentProFile()); } QStringList ProFileEvaluator::Private::values(const QString &variableName, const ProFile *pro) const { - if (variableName == QLatin1String("TARGET")) { - QStringList list = m_filevaluemap[pro].value(variableName); - if (!m_origfile.isEmpty()) - list.append(QFileInfo(m_origfile).baseName()); - return list; - } - if (variableName == QLatin1String("PWD")) { - return QStringList(QFileInfo(pro->fileName()).absoluteFilePath()); - } - return m_filevaluemap[pro].value(variableName); + return values(variableName, m_filevaluemap[pro], pro); } ProFile *ProFileEvaluator::parsedProFile(const QString &fileName) @@ -1531,7 +2080,13 @@ bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName, boo break; } } - return fn.isEmpty() ? false : evaluateFile(fn, result); + if (fn.isEmpty()) + return false; + bool cumulative = m_cumulative; + m_cumulative = false; + bool ok = evaluateFile(fn, result); + m_cumulative = cumulative; + return ok; } void ProFileEvaluator::Private::expandPatternHelper(const QString &relName, const QString &absName, @@ -1641,14 +2196,23 @@ bool ProFileEvaluator::contains(const QString &variableName) const return d->m_valuemap.contains(variableName); } +inline QStringList fixEnvVariables(const QStringList &x) +{ + QStringList ret; + foreach (const QString &str, x) + ret << Option::fixString(str, Option::FixEnvVars); + return ret; +} + + QStringList ProFileEvaluator::values(const QString &variableName) const { - return d->values(variableName); + return fixEnvVariables(d->values(variableName)); } QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const { - return d->values(variableName, pro); + return fixEnvVariables(d->values(variableName, pro)); } ProFileEvaluator::TemplateType ProFileEvaluator::templateType() @@ -1660,6 +2224,8 @@ ProFileEvaluator::TemplateType ProFileEvaluator::templateType() return TT_Application; if (t == QLatin1String("lib")) return TT_Library; + if (t == QLatin1String("script")) + return TT_Script; if (t == QLatin1String("subdirs")) return TT_Subdirs; } @@ -1704,18 +2270,20 @@ void ProFileEvaluator::addProperties(const QHash<QString, QString> &properties) void ProFileEvaluator::logMessage(const QString &message) { - if (d->m_verbose) + if (d->m_verbose && !d->m_skipLevel) qWarning("%s", qPrintable(message)); } void ProFileEvaluator::fileMessage(const QString &message) { - qWarning("%s", qPrintable(message)); + if (!d->m_skipLevel) + qWarning("%s", qPrintable(message)); } void ProFileEvaluator::errorMessage(const QString &message) { - qWarning("%s", qPrintable(message)); + if (!d->m_skipLevel) + qWarning("%s", qPrintable(message)); } void ProFileEvaluator::setVerbose(bool on) @@ -1723,6 +2291,16 @@ void ProFileEvaluator::setVerbose(bool on) d->m_verbose = on; } +void ProFileEvaluator::setCumulative(bool on) +{ + d->m_cumulative = on; +} + +void ProFileEvaluator::setOutputDir(const QString &dir) +{ + d->m_outputDir = dir; +} + void evaluateProFile(const ProFileEvaluator &visitor, QHash<QByteArray, QStringList> *varMap) { QStringList sourceFiles; diff --git a/tools/linguist/shared/profileevaluator.h b/tools/linguist/shared/profileevaluator.h index beb16ea..ae1422a 100644 --- a/tools/linguist/shared/profileevaluator.h +++ b/tools/linguist/shared/profileevaluator.h @@ -65,6 +65,7 @@ public: TT_Unknown = 0, TT_Application, TT_Library, + TT_Script, TT_Subdirs }; @@ -73,7 +74,9 @@ public: ProFileEvaluator::TemplateType templateType(); virtual bool contains(const QString &variableName) const; - void setVerbose(bool on); + void setVerbose(bool on); // Default is false + void setCumulative(bool on); // Default is true! + void setOutputDir(const QString &dir); // Default is empty bool queryProFile(ProFile *pro); bool accept(ProFile *pro); diff --git a/tools/linguist/shared/proparserutils.h b/tools/linguist/shared/proparserutils.h index 8914a8e..c27c3c0 100644 --- a/tools/linguist/shared/proparserutils.h +++ b/tools/linguist/shared/proparserutils.h @@ -93,6 +93,25 @@ struct Option Option::qmakespec = QString::fromLatin1(qgetenv("QMAKESPEC").data()); Option::field_sep = QLatin1Char(' '); } + + enum StringFixFlags { + FixNone = 0x00, + FixEnvVars = 0x01, + FixPathCanonicalize = 0x02, + FixPathToLocalSeparators = 0x04, + FixPathToTargetSeparators = 0x08 + }; + static QString fixString(QString string, uchar flags); + + inline static QString fixPathToLocalOS(const QString &in, bool fix_env = true, bool canonical = true) + { + uchar flags = FixPathToLocalSeparators; + if (fix_env) + flags |= FixEnvVars; + if (canonical) + flags |= FixPathCanonicalize; + return fixString(in, flags); + } }; #if defined(Q_OS_WIN32) Option::TARG_MODE Option::target_mode = Option::TARG_WIN_MODE; @@ -110,17 +129,20 @@ QString Option::dir_sep; QChar Option::field_sep; static void insertUnique(QHash<QString, QStringList> *map, - const QString &key, const QStringList &value, bool unique = true) + const QString &key, const QStringList &value) { QStringList &sl = (*map)[key]; - if (!unique) { - sl += value; - } else { - for (int i = 0; i < value.count(); ++i) { - if (!sl.contains(value.at(i))) - sl.append(value.at(i)); - } - } + foreach (const QString &str, value) + if (!sl.contains(str)) + sl.append(str); +} + +static void removeEach(QHash<QString, QStringList> *map, + const QString &key, const QStringList &value) +{ + QStringList &sl = (*map)[key]; + foreach (const QString &str, value) + sl.removeAll(str); } /* @@ -148,7 +170,12 @@ static QStringList replaceInList(const QStringList &varList, const QRegExp ®e } */ -inline QStringList splitPathList(const QString paths) +inline QString fixEnvVariables(const QString &x) +{ + return Option::fixString(x, Option::FixEnvVars); +} + +inline QStringList splitPathList(const QString &paths) { return paths.split(Option::dirlist_sep); } diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h index 6b88b23..4e97000 100644 --- a/tools/linguist/shared/translator.h +++ b/tools/linguist/shared/translator.h @@ -47,7 +47,9 @@ #include <QDir> #include <QList> #include <QLocale> +#include <QMultiHash> #include <QString> +#include <QSet> QT_BEGIN_NAMESPACE @@ -85,7 +87,10 @@ public: QString m_sourceFileName; QString m_targetFileName; QDir m_sourceDir; - QDir m_targetDir; // FIXME: TS spefic + QDir m_targetDir; // FIXME: TS specific + QSet<QString> m_projectRoots; + QMultiHash<QString, QString> m_allCSources; + QStringList m_includePath; QStringList m_dropTags; // tags to be dropped QStringList m_errors; bool m_verbose; @@ -178,7 +183,7 @@ public: QString description; // human-readable description LoadFunction loader; SaveFunction saver; - enum FileType { SourceCode, TranslationSource, TranslationBinary } fileType; + enum FileType { TranslationSource, TranslationBinary } fileType; int priority; // 0 = highest, -1 = invisible }; static void registerFileFormat(const FileFormat &format); diff --git a/tools/linguist/shared/translatortools.pri b/tools/linguist/shared/translatortools.pri deleted file mode 100644 index 2b6de8c..0000000 --- a/tools/linguist/shared/translatortools.pri +++ /dev/null @@ -1,11 +0,0 @@ - - -INCLUDEPATH *= $$PWD - -SOURCES += \ - $$PWD/translatortools.cpp \ - $$PWD/simtexth.cpp - -HEADERS += \ - $$PWD/translatortools.h - diff --git a/tools/macdeployqt/macchangeqt/main.cpp b/tools/macdeployqt/macchangeqt/main.cpp index e94e8a3..ebdfc14 100644 --- a/tools/macdeployqt/macchangeqt/main.cpp +++ b/tools/macdeployqt/macchangeqt/main.cpp @@ -42,13 +42,35 @@ int main(int argc, char **argv) { - if (argc != 3) { - qDebug() << "Changeqt changes witch qt frameworks an application links against."; - qDebug() << "Usage: changeqt app-bundle qt-dir"; + // useDebugLibs should always be false because even if set all Qt + // libraries inside a binary to point to debug versions, as soon as + // one of them loads a Qt plugin, the plugin itself will load the + // release version of Qt, and as such, the app will crash. + bool useDebugLibs = false; + + int optionsSpecified = 0; + for (int i = 2; i < argc; ++i) { + QByteArray argument = QByteArray(argv[i]); + if (argument.startsWith(QByteArray("-verbose="))) { + LogDebug() << "Argument found:" << argument; + optionsSpecified++; + int index = argument.indexOf("="); + bool ok = false; + int number = argument.mid(index+1).toInt(&ok); + if (!ok) + LogError() << "Could not parse verbose level"; + else + logLevel = number; + } + } + + if (argc != (3 + optionsSpecified)) { + qDebug() << "Changeqt: changes witch Qt frameworks an application links against."; + qDebug() << "Usage: changeqt app-bundle qt-dir <-verbose=[0-3]>"; return 0; } - + const QString appPath = QString::fromLocal8Bit(argv[1]); const QString qtPath = QString::fromLocal8Bit(argv[2]); - changeQtFrameworks(appPath, qtPath); + changeQtFrameworks(appPath, qtPath, useDebugLibs); } diff --git a/tools/macdeployqt/macdeployqt/main.cpp b/tools/macdeployqt/macdeployqt/main.cpp index d6f7e5f..5836cd0 100644 --- a/tools/macdeployqt/macdeployqt/main.cpp +++ b/tools/macdeployqt/macdeployqt/main.cpp @@ -51,9 +51,11 @@ int main(int argc, char **argv) qDebug() << "Usage: macdeployqt app-bundle [options]"; qDebug() << ""; qDebug() << "Options:"; - qDebug() << " -no-plugins: Skip plugin deployment"; - qDebug() << " -dmg : Create a .dmg disk image"; - qDebug() << " -no-strip : Don't run 'strip' on the binaries"; + qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug"; + qDebug() << " -no-plugins : Skip plugin deployment"; + qDebug() << " -dmg : Create a .dmg disk image"; + qDebug() << " -no-strip : Don't run 'strip' on the binaries"; + qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)"; qDebug() << ""; qDebug() << "macdeployqt takes an application bundle as input and makes it"; qDebug() << "self-contained by copying in the Qt frameworks and plugins that"; @@ -68,10 +70,10 @@ int main(int argc, char **argv) return 0; } - + if (appBundlePath.endsWith("/")) appBundlePath.chop(1); - + if (QDir().exists(appBundlePath) == false) { qDebug() << "Error: Could not find app bundle" << appBundlePath; return 0; @@ -79,23 +81,40 @@ int main(int argc, char **argv) bool plugins = true; bool dmg = false; + bool useDebugLibs = false; extern bool runStripEnabled; for (int i = 2; i < argc; ++i) { QByteArray argument = QByteArray(argv[i]); if (argument == QByteArray("-no-plugins")) { + LogDebug() << "Argument found:" << argument; plugins = false; } else if (argument == QByteArray("-dmg")) { + LogDebug() << "Argument found:" << argument; dmg = true; } else if (argument == QByteArray("-no-strip")) { + LogDebug() << "Argument found:" << argument; runStripEnabled = false; + } else if (argument == QByteArray("-use-debug-libs")) { + LogDebug() << "Argument found:" << argument; + useDebugLibs = true; + runStripEnabled = false; + } else if (argument.startsWith(QByteArray("-verbose"))) { + LogDebug() << "Argument found:" << argument; + int index = argument.indexOf("="); + bool ok = false; + int number = argument.mid(index+1).toInt(&ok); + if (!ok) + LogError() << "Could not parse verbose level"; + else + logLevel = number; } else if (argument.startsWith("-")) { - qDebug() << "Error: Unknown option" << argument << "\n"; + LogError() << "Unknown argument" << argument << "\n"; return 0; } } - DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath); + DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, useDebugLibs); if (plugins) { if (deploymentInfo.qtPath.isEmpty()) @@ -103,13 +122,13 @@ int main(int argc, char **argv) else deploymentInfo.pluginPath = deploymentInfo.qtPath + "/plugins"; - qDebug() << ""; - qDebug() << "Deploying plugins from" << deploymentInfo.pluginPath; - deployPlugins(appBundlePath, deploymentInfo); + LogNormal(); + deployPlugins(appBundlePath, deploymentInfo, useDebugLibs); createQtConf(appBundlePath); } if (dmg) { + LogNormal(); createDiskImage(appBundlePath); } } diff --git a/tools/macdeployqt/shared/shared.cpp b/tools/macdeployqt/shared/shared.cpp index db76ef2..a10e668 100644 --- a/tools/macdeployqt/shared/shared.cpp +++ b/tools/macdeployqt/shared/shared.cpp @@ -50,6 +50,7 @@ #include "shared.h" bool runStripEnabled = true; +int logLevel = 1; using std::cout; using std::endl; @@ -61,8 +62,8 @@ bool operator==(const FrameworkInfo &a, const FrameworkInfo &b) QDebug operator<<(QDebug debug, const FrameworkInfo &info) { - debug << "Framework directory" << info.frameworkDirectory << "\n"; debug << "Framework name" << info.frameworkName << "\n"; + debug << "Framework directory" << info.frameworkDirectory << "\n"; debug << "Framework path" << info.frameworkPath << "\n"; debug << "Binary directory" << info.binaryDirectory << "\n"; debug << "Binary name" << info.binaryName << "\n"; @@ -71,8 +72,8 @@ QDebug operator<<(QDebug debug, const FrameworkInfo &info) debug << "Install name" << info.installName << "\n"; debug << "Deployed install name" << info.deployedInstallName << "\n"; debug << "Source file Path" << info.sourceFilePath << "\n"; - debug << "Deployed Directtory (relative to bundle)" << info.destinationDirectory << "\n"; - + debug << "Deployed Directory (relative to bundle)" << info.destinationDirectory << "\n"; + return debug; } @@ -89,40 +90,41 @@ inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info) bool copyFilePrintStatus(const QString &from, const QString &to) { if (QFile::copy(from, to)) { - qDebug() << "copied" << from << "to" << to; + LogNormal() << " copied:" << from; + LogNormal() << " to" << to; return true; } else { - qDebug() << "ERROR: file copy failed from" << from << "to" << to; + LogError() << "file copy failed from" << from; + LogError() << " to" << to; return false; } } - -FrameworkInfo parseOtoolLibraryLine(const QString &line) +FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs) { FrameworkInfo info; QString trimmed = line.trimmed(); if (trimmed.isEmpty()) return info; - + // Don't deploy system libraries. if (trimmed.startsWith("/System/Library/") || (trimmed.startsWith("/usr/lib/") && trimmed.contains("libQt") == false) // exception for libQtuitools and libQtlucene || trimmed.startsWith("@executable_path")) return info; - + enum State {QtPath, FrameworkName, DylibName, Version, End}; State state = QtPath; int part = 0; QString name; QString qtPath; + QString suffix = useDebugLibs ? "_debug" : ""; // Split the line into [Qt-path]/lib/qt[Module].framework/Versions/[Version]/ QStringList parts = trimmed.split("/"); while (part < parts.count()) { const QString currentPart = parts.at(part).simplified() ; -// qDebug() << "currentPart" << currentPart; ++part; if (currentPart == "") continue; @@ -148,13 +150,13 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line) info.frameworkDirectory = "/usr/lib/"; state = DylibName; } - + --part; continue; } qtPath += (currentPart + "/"); - - } if (state == FrameworkName) { + + } if (state == FrameworkName) { // remove ".framework" name = currentPart; name.chop(QString(".framework").length()); @@ -163,28 +165,29 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line) ++part; continue; } if (state == DylibName) { - name = currentPart.split(" (compatibility").at(0); + name = currentPart.split(" (compatibility").at(0); info.frameworkName = name; - info.installName += info.frameworkName; - info.deployedInstallName = "@executable_path/../Frameworks/" + info.frameworkName; - info.binaryName = name; - info.frameworkPath = info.frameworkDirectory + info.frameworkName; + info.binaryName = name.left(name.indexOf('.')) + suffix + name.mid(name.indexOf('.')); + info.installName += name; + info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName; + info.frameworkPath = info.frameworkDirectory + info.binaryName; info.sourceFilePath = info.frameworkPath; info.destinationDirectory = bundleFrameworkDirectory + "/"; + info.binaryDirectory = info.frameworkDirectory; + info.binaryPath = info.frameworkPath; state = End; ++part; continue; } else if (state == Version) { info.version = currentPart; - info.binaryDirectory = "Versions/" + info.version; - info.binaryName = name; + info.binaryDirectory = "Versions/" + info.version; + info.binaryName = name + suffix; info.binaryPath = "/" + info.binaryDirectory + "/" + info.binaryName; - info.installName += info.frameworkName + info.binaryPath; + info.installName += info.frameworkName + "/" + info.binaryDirectory + "/" + name; info.deployedInstallName = "@executable_path/../Frameworks/" + info.frameworkName + info.binaryPath; info.frameworkPath = info.frameworkDirectory + info.frameworkName; info.sourceFilePath = info.frameworkPath + info.binaryPath; info.destinationDirectory = bundleFrameworkDirectory + "/" + info.frameworkName + "/" + info.binaryDirectory; - state = End; } else if (state == End) { break; @@ -198,42 +201,46 @@ QString findAppBinary(const QString &appBundlePath) { QString appName = QFileInfo(appBundlePath).completeBaseName(); QString binaryPath = appBundlePath + "/Contents/MacOS/" + appName; - + if (QFile::exists(binaryPath)) return binaryPath; - qDebug() << "Error: Could not find bundle binary for" << appBundlePath; + LogError() << "Could not find bundle binary for" << appBundlePath; return QString(); } -QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines) +QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebugLibs) { - QList<FrameworkInfo> libraries; + QList<FrameworkInfo> libraries; foreach(const QString line, otoolLines) { - FrameworkInfo info = parseOtoolLibraryLine(line); + FrameworkInfo info = parseOtoolLibraryLine(line, useDebugLibs); if (info.frameworkName.isEmpty() == false) { + LogDebug() << "Adding framework:"; + LogDebug() << info; libraries.append(info); } } return libraries; } -QList<FrameworkInfo> getQtFrameworks(const QString &path) +QList<FrameworkInfo> getQtFrameworks(const QString &path, bool useDebugLibs) { + LogDebug() << "Using otool:"; + LogDebug() << " inspecting" << path; QProcess otool; otool.start("otool", QStringList() << "-L" << path); otool.waitForFinished(); - + if (otool.exitCode() != 0) { - qDebug() << otool.readAllStandardError(); + LogError() << otool.readAllStandardError(); } - + QString output = otool.readAllStandardOutput(); QStringList outputLines = output.split("\n"); outputLines.removeFirst(); // remove line containing the binary path if (path.contains(".framework") || path.contains(".dylib")) outputLines.removeFirst(); // frameworks and dylibs lists themselves as a dependency. - return getQtFrameworks(outputLines); + return getQtFrameworks(outputLines, useDebugLibs); } // copies everything _inside_ sourcePath to destinationPath @@ -256,32 +263,31 @@ void recursiveCopy(const QString &sourcePath, const QString &destinationPath) QString copyFramework(const FrameworkInfo &framework, const QString path) { - const QString from = framework.sourceFilePath; - const QString toDir = path + "/" + framework.destinationDirectory; - const QString to = toDir + "/" + framework.binaryName; + QString from = framework.sourceFilePath; + QString toDir = path + "/" + framework.destinationDirectory; + QString to = toDir + "/" + framework.binaryName; if (QFile::exists(from) == false) { - qDebug() << "ERROR: no file at" << from; + LogError() << "no file at" << from; return QString(); } QDir dir; if (dir.mkpath(toDir) == false) { - qDebug() << "ERROR: could not create destination directory" << to; + LogError() << "could not create destination directory" << to; return QString(); } - + if (QFile::exists(to)) { -// qDebug() << framework.frameworkName << "already deployed, skip"; return QString(); } - + copyFilePrintStatus(from, to); - const QString resourcesSourcePath = framework.frameworkPath + "/Resources"; - const QString resourcesDestianationPath = path + "/Contents/Frameworks/" + framework.frameworkName + "/Resources"; + const QString resourcesSourcePath = framework.frameworkPath + "/Resources"; + const QString resourcesDestianationPath = path + "/Contents/Frameworks/" + framework.frameworkName + "/Resources"; recursiveCopy(resourcesSourcePath, resourcesDestianationPath); return to; @@ -293,20 +299,25 @@ void runInstallNameTool(QStringList options) installNametool.start("install_name_tool", options); installNametool.waitForFinished(); if (installNametool.exitCode() != 0) { - qDebug() << installNametool.readAllStandardError(); - qDebug() << installNametool.readAllStandardOutput(); + LogError() << installNametool.readAllStandardError(); + LogError() << installNametool.readAllStandardOutput(); } } void changeIdentification(const QString &id, const QString &binaryPath) { -// qDebug() << "change identification on" << binaryPath << id; + LogDebug() << "Using install_name_tool:"; + LogDebug() << " change identification in" << binaryPath; + LogDebug() << " to" << id; runInstallNameTool(QStringList() << "-id" << id << binaryPath); } void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath) { -// qDebug() << "change install name on" << binaryPath << oldName << newName; + LogDebug() << "Using install_name_tool:"; + LogDebug() << " in" << binaryPath; + LogDebug() << " change reference" << oldName; + LogDebug() << " to" << newName; runInstallNameTool(QStringList() << "-change" << oldName << newName << binaryPath); } @@ -315,14 +326,14 @@ void runStrip(const QString &binaryPath) if (runStripEnabled == false) return; + LogDebug() << "Using strip:"; + LogDebug() << " stripped" << binaryPath; QProcess strip; strip.start("strip", QStringList() << "-x" << binaryPath); strip.waitForFinished(); if (strip.exitCode() != 0) { - qDebug() << strip.readAllStandardError(); - qDebug() << strip.readAllStandardOutput(); - } else { - qDebug() << "stripped" << binaryPath; + LogError() << strip.readAllStandardError(); + LogError() << strip.readAllStandardOutput(); } } @@ -333,31 +344,31 @@ void runStrip(const QString &binaryPath) Returns a DeploymentInfo structure containing the Qt path used and a a list of actually deployed frameworks. */ -DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString &bundlePath, const QString &binaryPath) +DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, + const QString &bundlePath, const QString &binaryPath, bool useDebugLibs) { + LogNormal(); + LogNormal() << "Deploying Qt frameworks found inside:" << binaryPath; QStringList copiedFrameworks; DeploymentInfo deploymenInfo; - + while (frameworks.isEmpty() == false) { const FrameworkInfo framework = frameworks.takeFirst(); copiedFrameworks.append(framework.frameworkName); - + // Get the qt path from one of the Qt frameworks; - if (deploymenInfo.qtPath == QString() && framework.frameworkName.contains("Qt") + if (deploymenInfo.qtPath == QString() && framework.frameworkName.contains("Qt") && framework.frameworkDirectory.contains("/lib")) { deploymenInfo.qtPath = framework.frameworkDirectory; deploymenInfo.qtPath.chop(5); // remove "/lib/" } -// qDebug() << ""; -// qDebug() << "deploy" << framework.frameworkName; - if (framework.installName.startsWith("/@executable_path/")) { - qDebug() << framework.frameworkName << "already deployed, skipping."; + LogError() << framework.frameworkName << "already deployed, skipping."; continue; } - + // Install_name_tool the new id into the binary changeInstallName(framework.installName, framework.deployedInstallName, binaryPath); @@ -366,18 +377,17 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString // Skip the rest if already was deployed. if (deployedBinaryPath == QString()) continue; - + runStrip(deployedBinaryPath); // Install_name_tool it a new id. changeIdentification(framework.deployedInstallName, deployedBinaryPath); // Check for framework dependencies - QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath); + QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, useDebugLibs); foreach (FrameworkInfo dependency, dependencies) { -// qDebug() << "dependent framework" << dependency.installName << deployedBinaryPath; changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath); - + // Deploy framework if neccesary. if (copiedFrameworks.contains(dependency.frameworkName) == false && frameworks.contains(dependency) == false) { frameworks.append(dependency); @@ -388,28 +398,39 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString return deploymenInfo; } -DeploymentInfo deployQtFrameworks(const QString &appBundlePath) +DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLibs) { ApplicationBundleInfo applicationBundle; applicationBundle.path = appBundlePath; applicationBundle.binaryPath = findAppBinary(appBundlePath); - return deployQtFrameworks(getQtFrameworks(applicationBundle.binaryPath), applicationBundle.path, applicationBundle.binaryPath); + QList<FrameworkInfo> frameworks = getQtFrameworks(applicationBundle.binaryPath, useDebugLibs); + if (frameworks.isEmpty()) { + LogWarning(); + LogWarning() << "Could not find any external Qt frameworks to deploy in" << appBundlePath; + LogWarning() << "Perhaps macdeployqt was already used on" << appBundlePath << "?"; + LogWarning() << "If so, you will need to rebuild" << appBundlePath << "before trying again."; + return DeploymentInfo(); + } else { + return deployQtFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, useDebugLibs); + } } -void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pluginSourcePath, const QString pluginDestinationPath, DeploymentInfo deploymentInfo) +void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pluginSourcePath, + const QString pluginDestinationPath, DeploymentInfo deploymentInfo, bool useDebugLibs) { + LogNormal() << "Deploying plugins from" << pluginSourcePath; QStringList plugins = QDir(pluginSourcePath).entryList(QStringList() << "*.dylib"); foreach (QString pluginName, plugins) { - - // Skip some Qt plugins based on what frameworks were deployed: - //qDebug() << pluginSourcePath << deploymentInfo.pluginPath; - if (pluginSourcePath.contains(deploymentInfo.pluginPath)) { QStringList deployedFrameworks = deploymentInfo.deployedFrameworks; - // Skip the debug versions of the plugins - if (pluginName.endsWith("_debug.dylib")) + // Skip the debug versions of the plugins, unless specified otherwise. + if (!useDebugLibs && pluginName.endsWith("_debug.dylib")) + continue; + + // Skip the release versions of the plugins, unless specified otherwise. + if (useDebugLibs && !pluginName.endsWith("_debug.dylib")) continue; // Skip the designer plugins @@ -420,7 +441,7 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl // SKip the opengl graphicssystem plugin when not in use. if (pluginName.contains("libqglgraphicssystem")) continue; -#endif +#endif // Deploy accessibility for Qt3Support only if the Qt3Support.framework is in use if (deployedFrameworks.indexOf("Qt3Support.framework") == -1 && pluginName.contains("accessiblecompatwidgets")) continue; @@ -448,28 +469,25 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl const QString sourcePath = pluginSourcePath + "/" + pluginName; const QString destinationPath = pluginDestinationPath + "/" + pluginName; if (copyFilePrintStatus(sourcePath, destinationPath)) { - - runStrip(destinationPath); - - // Special case for the phonon plugin: CoreVideo is not available as a separate framework - // on panther, link against the QuartzCore framework instead. (QuartzCore contians CoreVideo.) - if (pluginName.contains("libphonon_qt7")) { - changeInstallName("/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo", - "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", - destinationPath); - } -// qDebug() << "deploy plugin depedencies:"; - QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath); -// qDebug() << frameworks; - deployQtFrameworks(frameworks, appBundleInfo.path, destinationPath); -// qDebug() << "deploy plugin depedencies done"; + runStrip(destinationPath); + + // Special case for the phonon plugin: CoreVideo is not available as a separate framework + // on panther, link against the QuartzCore framework instead. (QuartzCore contians CoreVideo.) + if (pluginName.contains("libphonon_qt7")) { + changeInstallName("/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo", + "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", + destinationPath); + } + + QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, useDebugLibs); + deployQtFrameworks(frameworks, appBundleInfo.path, destinationPath, useDebugLibs); } } // foreach plugins QStringList subdirs = QDir(pluginSourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot); foreach (const QString &subdir, subdirs) - deployPlugins(appBundleInfo, pluginSourcePath + "/" + subdir, pluginDestinationPath + "/" + subdir, deploymentInfo); + deployPlugins(appBundleInfo, pluginSourcePath + "/" + subdir, pluginDestinationPath + "/" + subdir, deploymentInfo, useDebugLibs); } void createQtConf(const QString &appBundlePath) @@ -479,63 +497,64 @@ void createQtConf(const QString &appBundlePath) QString fileName = filePath + "qt.conf"; QDir().mkpath(filePath); - + QFile qtconf(fileName); if (qtconf.exists()) { - qDebug() << ""; - qDebug() << "Warning:" << fileName << "already exists, will not overwrite."; - qDebug() << "To make sure the plugins are loaded from the correct location,"; - qDebug() << "please make sure qt.conf contains the following lines:"; - qDebug() << contents; - qDebug() << ""; + LogWarning(); + LogWarning() << fileName << "already exists, will not overwrite."; + LogWarning() << "To make sure the plugins are loaded from the correct location,"; + LogWarning() << "please make sure qt.conf contains the following lines:"; + LogWarning() << "[Paths]"; + LogWarning() << " Plugins = PlugIns"; return; } qtconf.open(QIODevice::WriteOnly); if (qtconf.write(contents) != -1) { - qDebug() << ""; - qDebug() << "Created configuration file:" << fileName; - qDebug() << "This file sets the plugin search path to" << appBundlePath + "/Contents/PlugIns"; - qDebug() << ""; + LogNormal() << "Created configuration file:" << fileName; + LogNormal() << "This file sets the plugin search path to" << appBundlePath + "/Contents/PlugIns"; } } -void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo) +void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs) { ApplicationBundleInfo applicationBundle; applicationBundle.path = appBundlePath; applicationBundle.binaryPath = findAppBinary(appBundlePath); - + const QString pluginDestinationPath = appBundlePath + "/" + "Contents/PlugIns"; - -// qDebug() << ""; -// qDebug() << "recursively copying plugins from" << deploymentInfo.pluginPath << "to" << pluginDestinationPath; - deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo); + deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs); } void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &appBinaryPath, const QString &absoluteQtPath) { - qDebug() << "Changing" << appBinaryPath << "to link against Qt in" << absoluteQtPath; + LogNormal() << "Changing" << appBinaryPath << "to link against"; + LogNormal() << "Qt in" << absoluteQtPath; QString finalQtPath = absoluteQtPath; - if (absoluteQtPath.startsWith("/Library/Frameworks") == false) + if (!absoluteQtPath.startsWith("/Library/Frameworks")) finalQtPath += "/lib/"; foreach (FrameworkInfo framework, frameworks) { const QString oldBinaryId = framework.installName; const QString newBinaryId = finalQtPath + framework.frameworkName + framework.binaryPath; - qDebug() << "Changing" << oldBinaryId << "to" << newBinaryId; changeInstallName(oldBinaryId, newBinaryId, appBinaryPath); } } -void changeQtFrameworks(const QString appPath, const QString &qtPath) +void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDebugLibs) { const QString appBinaryPath = findAppBinary(appPath); - const QList<FrameworkInfo> qtFrameworks = getQtFrameworks(appBinaryPath); - const QString absoluteQtPath = QDir(qtPath).absolutePath(); - changeQtFrameworks(qtFrameworks, appBinaryPath, absoluteQtPath); + const QList<FrameworkInfo> frameworks = getQtFrameworks(appBinaryPath, useDebugLibs); + if (frameworks.isEmpty()) { + LogWarning(); + LogWarning() << "Could not find any _external_ Qt frameworks to change in" << appPath; + return; + } else { + const QString absoluteQtPath = QDir(qtPath).absolutePath(); + changeQtFrameworks(frameworks, appBinaryPath, absoluteQtPath); + } } @@ -543,15 +562,15 @@ void createDiskImage(const QString &appBundlePath) { QString appBaseName = appBundlePath; appBaseName.chop(4); // remove ".app" from end - + QString dmgName = appBaseName + ".dmg"; QFile dmg(dmgName); if (dmg.exists()) { - qDebug() << "Disk image already exists, skipping .dmg creation for" << dmg.fileName(); + LogNormal() << "Disk image already exists, skipping .dmg creation for" << dmg.fileName(); } else { - qDebug() << "Creating disk image (.dmg) for" << appBundlePath; + LogNormal() << "Creating disk image (.dmg) for" << appBundlePath; } // More dmg options can be found in the hdiutil man page. diff --git a/tools/macdeployqt/shared/shared.h b/tools/macdeployqt/shared/shared.h index 5f30dad..637873e 100644 --- a/tools/macdeployqt/shared/shared.h +++ b/tools/macdeployqt/shared/shared.h @@ -45,6 +45,12 @@ #include <QStringList> #include <QDebug> +extern int logLevel; +#define LogError() if (logLevel < 1) {} else qDebug() << "ERROR:" +#define LogWarning() if (logLevel < 1) {} else qDebug() << "WARNING:" +#define LogNormal() if (logLevel < 2) {} else qDebug() << "Log:" +#define LogDebug() if (logLevel < 3) {} else qDebug() << "Log:" + extern bool runStripEnabled; class FrameworkInfo @@ -68,7 +74,7 @@ QDebug operator<<(QDebug debug, const FrameworkInfo &info); class ApplicationBundleInfo { -public: + public: QString path; QString binaryPath; }; @@ -84,18 +90,18 @@ public: inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info); -void changeQtFrameworks(const QString appPath, const QString &qtPath); +void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDebugLibs); void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &appBinaryPath, const QString &qtPath); -FrameworkInfo parseOtoolLibraryLine(const QString &line); +FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs); QString findAppBinary(const QString &appBundlePath); -QList<FrameworkInfo> getQtFrameworks(const QString &path); -QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines); +QList<FrameworkInfo> getQtFrameworks(const QString &path, bool useDebugLibs); +QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebugLibs); QString copyFramework(const FrameworkInfo &framework, const QString path); -DeploymentInfo deployQtFrameworks(const QString &appBundlePath); +DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLibs); DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString &bundlePath, const QString &binaryPath); void createQtConf(const QString &appBundlePath); -void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo); +void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs); void changeIdentification(const QString &id, const QString &binaryPath); void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath); QString findAppBinary(const QString &appBundlePath); diff --git a/tools/qdoc3/test/assistant.qdocconf b/tools/qdoc3/test/assistant.qdocconf index 7bb6bbf..9ba1b14 100644 --- a/tools/qdoc3/test/assistant.qdocconf +++ b/tools/qdoc3/test/assistant.qdocconf @@ -17,7 +17,7 @@ qhp.Assistant.namespace = com.trolltech.assistant.450 qhp.Assistant.virtualFolder = qdoc qhp.Assistant.indexTitle = Qt Assistant Manual qhp.Assistant.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png -qhp.Assistant.filterAttributes = qt 4.5.0 tools assistant +qhp.Assistant.filterAttributes = qt 4.6.0 tools assistant qhp.Assistant.customFilters.Assistant.name = Qt Assistant Manual qhp.Assistant.customFilters.Assistant.filterAttributes = qt tools assistant qhp.Assistant.subprojects = manual examples diff --git a/tools/qdoc3/test/designer.qdocconf b/tools/qdoc3/test/designer.qdocconf index 26636cd..f67cccc 100644 --- a/tools/qdoc3/test/designer.qdocconf +++ b/tools/qdoc3/test/designer.qdocconf @@ -17,7 +17,7 @@ qhp.Designer.namespace = com.trolltech.designer.450 qhp.Designer.virtualFolder = qdoc qhp.Designer.indexTitle = Qt Designer Manual qhp.Designer.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png -qhp.Designer.filterAttributes = qt 4.5.0 tools designer +qhp.Designer.filterAttributes = qt 4.6.0 tools designer qhp.Designer.customFilters.Designer.name = Qt Designer Manual qhp.Designer.customFilters.Designer.filterAttributes = qt tools designer qhp.Designer.subprojects = manual examples diff --git a/tools/qdoc3/test/linguist.qdocconf b/tools/qdoc3/test/linguist.qdocconf index 5e889a8..beff2da 100644 --- a/tools/qdoc3/test/linguist.qdocconf +++ b/tools/qdoc3/test/linguist.qdocconf @@ -17,7 +17,7 @@ qhp.Linguist.namespace = com.trolltech.linguist.450 qhp.Linguist.virtualFolder = qdoc qhp.Linguist.indexTitle = Qt Linguist Manual qhp.Linguist.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png -qhp.Linguist.filterAttributes = qt 4.5.0 tools linguist +qhp.Linguist.filterAttributes = qt 4.6.0 tools linguist qhp.Linguist.customFilters.Linguist.name = Qt Linguist Manual qhp.Linguist.customFilters.Linguist.filterAttributes = qt tools linguist qhp.Linguist.subprojects = manual examples diff --git a/tools/qdoc3/test/qmake.qdocconf b/tools/qdoc3/test/qmake.qdocconf index c357cfb..57299ae 100644 --- a/tools/qdoc3/test/qmake.qdocconf +++ b/tools/qdoc3/test/qmake.qdocconf @@ -17,7 +17,7 @@ qhp.qmake.namespace = com.trolltech.qmake.450 qhp.qmake.virtualFolder = qdoc qhp.qmake.indexTitle = QMake Manual qhp.qmake.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png -qhp.qmake.filterAttributes = qt 4.5.0 tools qmake +qhp.qmake.filterAttributes = qt 4.6.0 tools qmake qhp.qmake.customFilters.qmake.name = qmake Manual qhp.qmake.customFilters.qmake.filterAttributes = qt tools qmake qhp.qmake.subprojects = manual diff --git a/tools/qdoc3/test/qt-build-docs.qdocconf b/tools/qdoc3/test/qt-build-docs.qdocconf index 25cdc5a..a13ea94 100644 --- a/tools/qdoc3/test/qt-build-docs.qdocconf +++ b/tools/qdoc3/test/qt-build-docs.qdocconf @@ -35,9 +35,9 @@ qhp.Qt.extraFiles = classic.css \ images/dynamiclayouts-example.png \ images/stylesheet-coffee-plastique.png -qhp.Qt.filterAttributes = qt 4.5.0 qtrefdoc -qhp.Qt.customFilters.Qt.name = Qt 4.5.0 -qhp.Qt.customFilters.Qt.filterAttributes = qt 4.5.0 +qhp.Qt.filterAttributes = qt 4.6.0 qtrefdoc +qhp.Qt.customFilters.Qt.name = Qt 4.6.0 +qhp.Qt.customFilters.Qt.filterAttributes = qt 4.6.0 qhp.Qt.subprojects = classes overviews examples qhp.Qt.subprojects.classes.title = Classes qhp.Qt.subprojects.classes.indexTitle = Qt's Classes diff --git a/tools/qdoc3/test/qt.qdocconf b/tools/qdoc3/test/qt.qdocconf index 298311e..288da62 100644 --- a/tools/qdoc3/test/qt.qdocconf +++ b/tools/qdoc3/test/qt.qdocconf @@ -37,9 +37,9 @@ qhp.Qt.extraFiles = classic.css \ images/dynamiclayouts-example.png \ images/stylesheet-coffee-plastique.png -qhp.Qt.filterAttributes = qt 4.5.0 qtrefdoc -qhp.Qt.customFilters.Qt.name = Qt 4.5.0 -qhp.Qt.customFilters.Qt.filterAttributes = qt 4.5.0 +qhp.Qt.filterAttributes = qt 4.6.0 qtrefdoc +qhp.Qt.customFilters.Qt.name = Qt 4.6.0 +qhp.Qt.customFilters.Qt.filterAttributes = qt 4.6.0 qhp.Qt.subprojects = classes overviews examples qhp.Qt.subprojects.classes.title = Classes qhp.Qt.subprojects.classes.indexTitle = Qt's Classes diff --git a/tools/qvfb/S60-QVGA-Candybar.qrc b/tools/qvfb/S60-QVGA-Candybar.qrc new file mode 100644 index 0000000..8138484 --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/skins"> + <file>S60-QVGA-Candybar.skin</file> +</qresource> +</RCC> diff --git a/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar-down.png b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar-down.png Binary files differnew file mode 100644 index 0000000..89d40cb --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar-down.png diff --git a/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.png b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.png Binary files differnew file mode 100644 index 0000000..0d0e598 --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.png diff --git a/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.skin b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.skin new file mode 100644 index 0000000..4f8fe5d --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.skin @@ -0,0 +1,15 @@ +[SkinFile] +Up=S60-QVGA-Candybar.png +Down=S60-QVGA-Candybar-down.png +Screen=61 93 240 320 +Areas=7 +HasMouseHover=false + + +"Context1" 0x01100000 54 469 151 469 140 483 88 485 81 496 54 498 +"Back" 0x01000061 211 468 307 467 307 498 278 497 219 486 +"Select" 0x01010000 165 491 196 522 +"Left" 0x1000012 149 474 166 492 163 519 143 538 142 481 +"Down" 0x1000015 164 521 195 522 212 539 204 545 154 544 145 536 +"Right" 0x1000014 214 475 219 487 219 528 212 539 196 522 197 492 +"Up" 0x1000013 150 474 156 467 209 467 213 476 197 489 165 489 diff --git a/tools/qvfb/S60-QVGA-Candybar.skin/defaultbuttons.conf b/tools/qvfb/S60-QVGA-Candybar.skin/defaultbuttons.conf new file mode 100644 index 0000000..e349dbc --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.skin/defaultbuttons.conf @@ -0,0 +1,78 @@ +[Translation] +File=QtopiaDefaults +Context=Buttons +[Menu] +Rows=4 +Columns=3 +Map=123456789*0# +Default=5 +1=Applications/camera.desktop +2=Applications/datebook.desktop +3=Applications +4=Applications/qtmail.desktop +5=Applications/addressbook.desktop +6=Games +7=Settings/Beaming.desktop +8=Applications/simapp.desktop,Applications/calculator.desktop +9=Settings +*=Applications/mediarecorder.desktop +0=Applications/todolist.desktop +#=Documents +Animator=Bounce +AnimatorBackground=Radial +[SoftKeys] +Count=3 +Key0=Context1 +Key1=Select +Key2=Back +[SystemButtons] +Count=5 +Key0=Context1 +Key1=Select +Key2=Back +Key3=Flip +Key4=Backspace +[TextButtons] +Buttons=0123456789*# +Hold0='0 +Hold1='1 +Hold2='2 +Hold3='3 +Hold4='4 +Hold5='5 +Hold6='6 +Hold7='7 +Hold8='8 +Hold9='9 +Hold*=symbol +Hold#=mode +Tap0=space +Tap1="\".,'?!-@:1" +Tap2="\"a\xe4\xe5\xe6\xe0\xe1\xe2\x62\x63\xe7\x32" +Tap3="\"de\xe8\xe9\xea\x66\x33" +Tap4="\"ghi\xec\xed\xee\x34" +Tap5="\"jkl5" +Tap6="\"mn\xf1o\xf6\xf8\xf2\xf3\x36" +Tap7="\"pqrs\xdf\x37" +Tap8="\"tu\xfc\xf9\xfav8" +Tap9="\"wxyz9" +Tap*=modify +Tap#=shift +[LocaleTextButtons] +Buttons=23456789 +Tap2[]='abc +Tap3[]='def +Tap4[]='ghi +Tap5[]='jkl +Tap6[]='mno +Tap7[]='pqrs +Tap8[]='tuv +Tap9[]='wxyz +[PhoneTextButtons] +Buttons=*# +Tap*='*+pw +Hold*=+ +Tap#=# +Hold#=mode +[Device] +PrimaryInput=Keypad diff --git a/tools/qvfb/S60-nHD-Touchscreen.qrc b/tools/qvfb/S60-nHD-Touchscreen.qrc new file mode 100644 index 0000000..daf0cc3 --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/skins"> + <file>S60-nHD-Touchscreen.skin</file> +</qresource> +</RCC> diff --git a/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen-down.png b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen-down.png Binary files differnew file mode 100644 index 0000000..7253e38 --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen-down.png diff --git a/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.png b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.png Binary files differnew file mode 100644 index 0000000..675563e --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.png diff --git a/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.skin b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.skin new file mode 100644 index 0000000..ed25d0e --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.skin @@ -0,0 +1,10 @@ +[SkinFile] +Up=S60-nHD-Touchscreen.png +Down=S60-nHD-Touchscreen-down.png +Screen=53 183 360 640 +Areas=3 +HasMouseHover=false + +"Call" 0x01100004 76 874 171 899 +"Hangup" 0x01100005 300 876 393 899 +"Home" 0x1000010 174 878 298 899 diff --git a/tools/qvfb/S60-nHD-Touchscreen.skin/defaultbuttons.conf b/tools/qvfb/S60-nHD-Touchscreen.skin/defaultbuttons.conf new file mode 100644 index 0000000..6665125 --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.skin/defaultbuttons.conf @@ -0,0 +1,53 @@ +[Translation] +File=QtopiaDefaults +Context=Buttons +[Menu] +Rows=4 +Columns=3 +Map=123456789*0# +Default=5 +1=Applications/camera.desktop +2=Applications/mediaplayer.desktop +3=Applications/simapp.desktop,Applications/calculator.desktop +4=Applications/qtmail.desktop +5=Applications/addressbook.desktop +6=Applications/datebook.desktop +7=Games +8=Settings/beaming.desktop +9=Applications/todolist.desktop +*=Settings +0=Applications +#=Documents +Animator=Bounce +AnimatorBackground=Radial +[SoftKeys] +Count=3 +Key0=Context1 +Key1=Select +Key2=Back +[SystemButtons] +Count=5 +Key0=Context1 +Key1=Back +Key2=Select +Key3=Call +Key4=Hangup +[Device] +PrimaryInput=Touchscreen +[Button] +Count=2 +[Button0] +Name[]=Home Button +Key=Home +PressedActionMappable=0 +PressedActionService=TaskManager +PressedActionMessage=multitask() +HeldActionMappable=0 +HeldActionService=TaskManager +HeldActionMessage=showRunningTasks() +[Button1] +Name=Power Button +Key=Hangup +HeldActionService=Launcher +HeldActionMessage=execute(QString) +HeldActionArgs=@ByteArray(\0\0\0\x1\0\0\0\n\0\0\0\0\x10\0s\0h\0u\0t\0\x64\0o\0w\0n) diff --git a/tools/qvfb/qvfb.pro b/tools/qvfb/qvfb.pro index a3b55ab..85c4d96 100644 --- a/tools/qvfb/qvfb.pro +++ b/tools/qvfb/qvfb.pro @@ -69,5 +69,6 @@ RESOURCES += qvfb.qrc \ TouchscreenPhone.qrc \ Trolltech-Keypad.qrc \ Trolltech-Touchscreen.qrc \ - PortableMedia.qrc - + PortableMedia.qrc \ + S60-QVGA-Candybar.qrc \ + S60-nHD-Touchscreen.qrc diff --git a/tools/shared/qtgradienteditor/qtgradientdialog.cpp b/tools/shared/qtgradienteditor/qtgradientdialog.cpp index 032cb16..950a1d2 100644 --- a/tools/shared/qtgradienteditor/qtgradientdialog.cpp +++ b/tools/shared/qtgradienteditor/qtgradientdialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QtGradientDialog -*/ - #include "qtgradientdialog.h" #include "ui_qtgradientdialog.h" #include <QtGui/QPushButton> diff --git a/tools/shared/qtgradienteditor/qtgradienteditor.cpp b/tools/shared/qtgradienteditor/qtgradienteditor.cpp index 9eca9d8..42d7767 100644 --- a/tools/shared/qtgradienteditor/qtgradienteditor.cpp +++ b/tools/shared/qtgradienteditor/qtgradienteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QtGradientEditor -*/ - #include "qtgradienteditor.h" #include "qtgradientstopscontroller.h" #include "ui_qtgradienteditor.h" diff --git a/tools/shared/qtgradienteditor/qtgradientstopscontroller.cpp b/tools/shared/qtgradienteditor/qtgradientstopscontroller.cpp index cbc53db..c9ef03b 100644 --- a/tools/shared/qtgradienteditor/qtgradientstopscontroller.cpp +++ b/tools/shared/qtgradienteditor/qtgradientstopscontroller.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QtGradientStopsController -*/ - #include "qtgradientstopscontroller.h" #include "ui_qtgradienteditor.h" #include "qtgradientstopsmodel.h" diff --git a/tools/tools.pro b/tools/tools.pro index ffc5d63..c76174c 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -22,6 +22,8 @@ mac { SUBDIRS += macdeployqt } +SUBDIRS += kmap2qmap + contains(QT_CONFIG, dbus):SUBDIRS += qdbus !wince*:contains(QT_CONFIG, xmlpatterns): SUBDIRS += xmlpatterns embedded: SUBDIRS += makeqpf diff --git a/translations/translations.pri b/translations/translations.pri index efefa09..20d4467 100644 --- a/translations/translations.pri +++ b/translations/translations.pri @@ -24,6 +24,7 @@ LRELEASE = $$fixPath($$QT_BUILD_TREE/bin/lrelease) QT_TS = de fr zh_CN untranslated ar es iw ja_JP pl pt ru sk sv uk zh_TW ts-qt.commands = (cd $$QT_SOURCE_TREE/src && $$LUPDATE \ + -I../include -I../include/Qt \ 3rdparty/phonon \ 3rdparty/webkit \ activeqt \ diff --git a/util/qlalr/cppgenerator.cpp b/util/qlalr/cppgenerator.cpp index 9bfc1a9..e85aa4c 100644 --- a/util/qlalr/cppgenerator.cpp +++ b/util/qlalr/cppgenerator.cpp @@ -49,18 +49,47 @@ QString CppGenerator::trollCopyrightHeader() const { return QLatin1String( - "/****************************************************************************\n" - "**\n" - "** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).\n" - "**\n" - "** This file is part of the $MODULE$ of the Qt Toolkit.\n" - "**\n" - "** $TROLLTECH_DUAL_LICENSE$\n" - "**\n" - "** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE\n" - "** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n" - "**\n" - "****************************************************************************/\n" + +"/****************************************************************************\n" +"**\n" +"** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).\n" +"** Contact: Qt Software Information (qt-info@nokia.com)\n" +"**\n" +"** This file is part of the QtCore module of the Qt Toolkit.\n" +"**\n" +"** $QT_BEGIN_LICENSE:LGPL$\n" +"** No Commercial Usage\n" +"** This file contains pre-release code and may not be distributed.\n" +"** You may use this file in accordance with the terms and conditions\n" +"** contained in the either Technology Preview License Agreement or the\n" +"** Beta Release License Agreement.\n" +"**\n" +"** GNU Lesser General Public License Usage\n" +"** Alternatively, this file may be used under the terms of the GNU Lesser\n" +"** General Public License version 2.1 as published by the Free Software\n" +"** Foundation and appearing in the file LICENSE.LGPL included in the\n" +"** packaging of this file. Please review the following information to\n" +"** ensure the GNU Lesser General Public License version 2.1 requirements\n" +"** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\n" +"**\n" +"** In addition, as a special exception, Nokia gives you certain\n" +"** additional rights. These rights are described in the Nokia Qt LGPL\n" +"** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this\n" +"** package.\n" +"**\n" +"** GNU General Public License Usage\n" +"** Alternatively, this file may be used under the terms of the GNU\n" +"** General Public License version 3.0 as published by the Free Software\n" +"** Foundation and appearing in the file LICENSE.GPL included in the\n" +"** packaging of this file. Please review the following information to\n" +"** ensure the GNU General Public License version 3.0 requirements will be\n" +"** met: http://www.gnu.org/copyleft/gpl.html.\n" +"**\n" +"** If you are unsure which license is appropriate for your use, please\n" +"** contact the sales department at qt-sales@nokia.com.\n" +"** $QT_END_LICENSE$\n" +"**\n" +"****************************************************************************/\n" "\n"); } diff --git a/util/scripts/make_qfeatures_dot_h b/util/scripts/make_qfeatures_dot_h index c434649..056c113 100755 --- a/util/scripts/make_qfeatures_dot_h +++ b/util/scripts/make_qfeatures_dot_h @@ -83,23 +83,55 @@ open OUT, ">$ENV{QTDIR}/src/corelib/global/qfeatures.h" or die "Cannot open $ENV{QTDIR}/src/corelib/global/qfeatures.h for writing"; print OUT -"/**************************************************************************** +'/**************************************************************************** ** -** Copyright (C) 1992-\$THISYEAR\$ \$TROLLTECH\$. All rights reserved. +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) ** -** This file is part of the \$MODULE\$ of the Qt Toolkit. +** This file is part of the QtCore module of the Qt Toolkit. ** -** \$TROLLTECH_DUAL_LICENSE\$ +** $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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ ** ****************************************************************************/ /* * All features and their dependencies. * - * This list is generated from \$QTDIR/src/corelib/global/qfeatures.txt + * This list is generated from $QTDIR/src/corelib/global/qfeatures.txt */ -"; +'; + for $macro ( @macros ) { print OUT "// $label{$macro}\n"; diff --git a/util/webkit/mkdist-webkit b/util/webkit/mkdist-webkit index d4e6d9e..701133e 100755 --- a/util/webkit/mkdist-webkit +++ b/util/webkit/mkdist-webkit @@ -5,7 +5,7 @@ die() { exit 1 } -default_tag="origin/qtwebkit-4.5" +default_tag="origin/svn/master" if [ $# -eq 0 ]; then tag="$default_tag" @@ -27,7 +27,7 @@ excluded_directories="$excluded_directories PlanetWebKit" excluded_directories="$excluded_directories SunSpider" excluded_directories="$excluded_directories WebKitExamplePlugins" -excluded_directories="$excluded_directories symbols.filter" +excluded_directories="$excluded_directories autotools" excluded_directories="$excluded_directories JavaScriptCore/Makefile" excluded_directories="$excluded_directories Makefile" excluded_directories="$excluded_directories Makefile.shared" @@ -104,7 +104,6 @@ excluded_directories="$excluded_directories WebCore/platform/image-decoders/ico" excluded_directories="$excluded_directories WebCore/platform/image-decoders/jpeg" excluded_directories="$excluded_directories WebCore/platform/image-decoders/xbm" -excluded_directories="$excluded_directories WebCore/plugins/wx" excluded_directories="$excluded_directories WebCore/plugins/gtk" excluded_directories="$excluded_directories WebCore/platform/symbian WebCore/platform/wx" @@ -132,7 +131,6 @@ files_to_remove="$files_to_remove WebKit/qt/Api/qcookiejar.h" files_to_remove="$files_to_remove WebKit/qt/Api/qcookiejar.cpp" files_to_remove="$files_to_remove WebCore/rendering/RenderThemeMac.mm" -files_to_remove="$files_to_remove acinclude.m4" files_to_remove="$files_to_remove autogen.sh" files_to_remove="$files_to_remove configure.ac" |