Merge branch 'master' of git://scm.dev.nokia.troll.no/qt/qtbase-staging

This commit is contained in:
Jason McDonald 2011-05-13 10:42:41 +10:00
commit dd1a7a6379
33 changed files with 1808 additions and 1635 deletions

View File

@ -1,8 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
** a KDAB Group company, info@kdab.com,
** author Stephen Kelly <stephen.kelly@kdab.com>
** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**

View File

@ -2647,10 +2647,7 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT
QString out_directory_cdin, out_directory_cdout;
MAKE_CD_IN_AND_OUT(out_directory);
//don't need the makefile arg if it isn't changed
QString makefilein;
if(subtarget->makefile != "$(MAKEFILE)")
makefilein = " -f " + subtarget->makefile;
QString makefilein = " -f " + subtarget->makefile;
//write the rule/depends
if(flags & SubTargetOrdered) {

View File

@ -111,8 +111,8 @@ public:
TableSummaryChanged,
TextAttributeChanged,
TextCaretMoved,
TextChanged,
TextColumnChanged,
// TextChanged, deprecated, use TextUpdated
TextColumnChanged = TextCaretMoved + 2,
TextInserted,
TextRemoved,
TextUpdated,

View File

@ -269,12 +269,20 @@ void QGraphicsLayout::activate()
return;
Q_ASSERT(!parentItem->isLayout());
setGeometry(parentItem->contentsRect()); // relayout children
if (QGraphicsLayout::instantInvalidatePropagation()) {
QGraphicsWidget *parentWidget = static_cast<QGraphicsWidget*>(parentItem);
if (!parentWidget->parentLayoutItem()) {
// we've reached the topmost widget, resize it
bool wasResized = parentWidget->testAttribute(Qt::WA_Resized);
parentWidget->resize(parentWidget->size());
parentWidget->setAttribute(Qt::WA_Resized, wasResized);
}
// ### bug, should be parentItem ?
parentLayoutItem()->updateGeometry(); // bubble up; will set activated to false
// ### too many resizes? maybe we should walk up the chain to the
// ### top-level layouted layoutItem and call activate there.
setGeometry(parentItem->contentsRect()); // relayout children
} else {
setGeometry(parentItem->contentsRect()); // relayout children
parentLayoutItem()->updateGeometry();
}
}
/*!
@ -300,32 +308,36 @@ bool QGraphicsLayout::isActivated() const
*/
void QGraphicsLayout::invalidate()
{
// only mark layouts as invalid (activated = false) if we can post a LayoutRequest event.
QGraphicsLayoutItem *layoutItem = this;
while (layoutItem && layoutItem->isLayout()) {
// we could call updateGeometry(), but what if that method
// does not call the base implementation? In addition, updateGeometry()
// does more than we need.
layoutItem->d_func()->sizeHintCacheDirty = true;
layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
layoutItem = layoutItem->parentLayoutItem();
}
if (layoutItem) {
layoutItem->d_func()->sizeHintCacheDirty = true;
layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
}
bool postIt = layoutItem ? !layoutItem->isLayout() : false;
if (postIt) {
layoutItem = this;
while (layoutItem && layoutItem->isLayout()
&& static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated) {
static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated = false;
if (QGraphicsLayout::instantInvalidatePropagation()) {
updateGeometry();
} else {
// only mark layouts as invalid (activated = false) if we can post a LayoutRequest event.
QGraphicsLayoutItem *layoutItem = this;
while (layoutItem && layoutItem->isLayout()) {
// we could call updateGeometry(), but what if that method
// does not call the base implementation? In addition, updateGeometry()
// does more than we need.
layoutItem->d_func()->sizeHintCacheDirty = true;
layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
layoutItem = layoutItem->parentLayoutItem();
}
if (layoutItem && !layoutItem->isLayout()) {
// If a layout has a parent that is not a layout it must be a QGraphicsWidget.
QApplication::postEvent(static_cast<QGraphicsWidget *>(layoutItem), new QEvent(QEvent::LayoutRequest));
if (layoutItem) {
layoutItem->d_func()->sizeHintCacheDirty = true;
layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
}
bool postIt = layoutItem ? !layoutItem->isLayout() : false;
if (postIt) {
layoutItem = this;
while (layoutItem && layoutItem->isLayout()
&& static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated) {
static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated = false;
layoutItem = layoutItem->parentLayoutItem();
}
if (layoutItem && !layoutItem->isLayout()) {
// If a layout has a parent that is not a layout it must be a QGraphicsWidget.
QApplication::postEvent(static_cast<QGraphicsWidget *>(layoutItem), new QEvent(QEvent::LayoutRequest));
}
}
}
}
@ -335,12 +347,27 @@ void QGraphicsLayout::invalidate()
*/
void QGraphicsLayout::updateGeometry()
{
QGraphicsLayoutItem::updateGeometry();
if (QGraphicsLayoutItem *parentItem = parentLayoutItem()) {
if (parentItem->isLayout()) {
Q_D(QGraphicsLayout);
if (QGraphicsLayout::instantInvalidatePropagation()) {
d->activated = false;
QGraphicsLayoutItem::updateGeometry();
QGraphicsLayoutItem *parentItem = parentLayoutItem();
if (!parentItem)
return;
if (parentItem->isLayout())
static_cast<QGraphicsLayout *>(parentItem)->invalidate();
else
parentItem->updateGeometry();
} else {
invalidate();
} else {
QGraphicsLayoutItem::updateGeometry();
if (QGraphicsLayoutItem *parentItem = parentLayoutItem()) {
if (parentItem->isLayout()) {
parentItem->updateGeometry();
} else {
invalidate();
}
}
}
}
@ -446,6 +473,50 @@ void QGraphicsLayout::addChildLayoutItem(QGraphicsLayoutItem *layoutItem)
d->addChildLayoutItem(layoutItem);
}
static bool g_instantInvalidatePropagation = false;
/*!
\internal
\since 4.8
\see instantInvalidatePropagation
Calling this function with \a enable set to true will enable a feature that
makes propagation of invalidation up to ancestor layout items to be done in
one go. It will propagate up the parentLayoutItem() hierarchy until it has
reached the root. If the root item is a QGraphicsWidget, it will *post* a
layout request to it. When the layout request is consumed it will traverse
down the hierarchy of layouts and widgets and activate all layouts that is
invalid (not activated). This is the recommended behaviour.
If not set it will also propagate up the parentLayoutItem() hierarchy, but
it will stop at the \i first \i widget it encounters, and post a layout
request to the widget. When the layout request is consumed, this might
cause it to continue propagation up to the parentLayoutItem() of the
widget. It will continue in this fashion until it has reached a widget with
no parentLayoutItem(). This strategy might cause drawing artifacts, since
it is not done in one go, and the consumption of layout requests might be
interleaved by consumption of paint events, which might cause significant
flicker.
Note, this is not the recommended behavior, but for compatibility reasons
this is the default behaviour.
*/
void QGraphicsLayout::setInstantInvalidatePropagation(bool enable)
{
g_instantInvalidatePropagation = enable;
}
/*!
\internal
\since 4.8
\see setInstantInvalidatePropagation
returns true if the complete widget/layout hierarchy is rearranged in one go.
*/
bool QGraphicsLayout::instantInvalidatePropagation()
{
return g_instantInvalidatePropagation;
}
QT_END_NAMESPACE
#endif //QT_NO_GRAPHICSVIEW

View File

@ -76,6 +76,8 @@ public:
virtual QGraphicsLayoutItem *itemAt(int i) const = 0;
virtual void removeAt(int index) = 0;
static void setInstantInvalidatePropagation(bool enable);
static bool instantInvalidatePropagation();
protected:
QGraphicsLayout(QGraphicsLayoutPrivate &, QGraphicsLayoutItem *);
void addChildLayoutItem(QGraphicsLayoutItem *layoutItem);

View File

@ -180,9 +180,14 @@ void QGraphicsLayoutPrivate::activateRecursive(QGraphicsLayoutItem *item)
{
if (item->isLayout()) {
QGraphicsLayout *layout = static_cast<QGraphicsLayout *>(item);
if (layout->d_func()->activated)
layout->invalidate();
if (layout->d_func()->activated) {
if (QGraphicsLayout::instantInvalidatePropagation()) {
return;
} else {
layout->invalidate(); // ### LOOKS SUSPICIOUSLY WRONG!!???
}
}
for (int i = layout->count() - 1; i >= 0; --i) {
QGraphicsLayoutItem *childItem = layout->itemAt(i);
if (childItem)

View File

@ -275,17 +275,13 @@ void QGraphicsLinearLayout::insertItem(int index, QGraphicsLayoutItem *item)
qWarning("QGraphicsLinearLayout::insertItem: cannot insert itself");
return;
}
Q_ASSERT(item);
//the order of the following instructions is very important because
//invalidating the layout before adding the child item will make the layout happen
//before we try to paint the item
invalidate();
d->addChildLayoutItem(item);
Q_ASSERT(item);
d->fixIndex(&index);
d->engine.insertRow(index, d->orientation);
new QGridLayoutItem(&d->engine, item, d->gridRow(index), d->gridColumn(index), 1, 1, 0, index);
invalidate();
}
/*!

View File

@ -354,8 +354,10 @@ void QGraphicsWidget::setGeometry(const QRectF &rect)
newGeom = rect;
newGeom.setSize(rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
.boundedTo(effectiveSizeHint(Qt::MaximumSize)));
if (newGeom == d->geom)
return;
if (newGeom == d->geom) {
goto relayoutChildrenAndReturn;
}
// setPos triggers ItemPositionChange, which can adjust position
wd->inSetGeometry = 1;
@ -363,8 +365,9 @@ void QGraphicsWidget::setGeometry(const QRectF &rect)
wd->inSetGeometry = 0;
newGeom.moveTopLeft(pos());
if (newGeom == d->geom)
return;
if (newGeom == d->geom) {
goto relayoutChildrenAndReturn;
}
// Update and prepare to change the geometry (remove from index) if the size has changed.
if (wd->scene) {
@ -375,35 +378,54 @@ void QGraphicsWidget::setGeometry(const QRectF &rect)
}
// Update the layout item geometry
bool moved = oldPos != pos();
if (moved) {
// Send move event.
QGraphicsSceneMoveEvent event;
event.setOldPos(oldPos);
event.setNewPos(pos());
QApplication::sendEvent(this, &event);
if (wd->inSetPos) {
//set the new pos
d->geom.moveTopLeft(pos());
emit geometryChanged();
return;
{
bool moved = oldPos != pos();
if (moved) {
// Send move event.
QGraphicsSceneMoveEvent event;
event.setOldPos(oldPos);
event.setNewPos(pos());
QApplication::sendEvent(this, &event);
if (wd->inSetPos) {
//set the new pos
d->geom.moveTopLeft(pos());
emit geometryChanged();
goto relayoutChildrenAndReturn;
}
}
QSizeF oldSize = size();
QGraphicsLayoutItem::setGeometry(newGeom);
// Send resize event
bool resized = newGeom.size() != oldSize;
if (resized) {
QGraphicsSceneResizeEvent re;
re.setOldSize(oldSize);
re.setNewSize(newGeom.size());
if (oldSize.width() != newGeom.size().width())
emit widthChanged();
if (oldSize.height() != newGeom.size().height())
emit heightChanged();
QGraphicsLayout *lay = wd->layout;
if (QGraphicsLayout::instantInvalidatePropagation()) {
if (!lay || lay->isActivated()) {
QApplication::sendEvent(this, &re);
}
} else {
QApplication::sendEvent(this, &re);
}
}
}
QSizeF oldSize = size();
QGraphicsLayoutItem::setGeometry(newGeom);
// Send resize event
bool resized = newGeom.size() != oldSize;
if (resized) {
QGraphicsSceneResizeEvent re;
re.setOldSize(oldSize);
re.setNewSize(newGeom.size());
if (oldSize.width() != newGeom.size().width())
emit widthChanged();
if (oldSize.height() != newGeom.size().height())
emit heightChanged();
QApplication::sendEvent(this, &re);
}
emit geometryChanged();
relayoutChildrenAndReturn:
if (QGraphicsLayout::instantInvalidatePropagation()) {
if (QGraphicsLayout *lay = wd->layout) {
if (!lay->isActivated()) {
QEvent layoutRequest(QEvent::LayoutRequest);
QApplication::sendEvent(this, &layoutRequest);
}
}
}
}
/*!
@ -1052,16 +1074,31 @@ void QGraphicsWidget::updateGeometry()
QGraphicsLayoutItem *parentItem = parentLayoutItem();
if (parentItem && parentItem->isLayout()) {
parentItem->updateGeometry();
if (QGraphicsLayout::instantInvalidatePropagation()) {
static_cast<QGraphicsLayout *>(parentItem)->invalidate();
} else {
parentItem->updateGeometry();
}
} else {
if (parentItem) {
// This is for custom layouting
QGraphicsWidget *parentWid = parentWidget(); //###
if (parentWid->isVisible())
QApplication::postEvent(parentWid, new QEvent(QEvent::LayoutRequest));
} else {
/**
* If this is the topmost widget, post a LayoutRequest event to the widget.
* When the event is received, it will start flowing all the way down to the leaf
* widgets in one go. This will make a relayout flicker-free.
*/
if (QGraphicsLayout::instantInvalidatePropagation())
QApplication::postEvent(static_cast<QGraphicsWidget *>(this), new QEvent(QEvent::LayoutRequest));
}
if (!QGraphicsLayout::instantInvalidatePropagation()) {
bool wasResized = testAttribute(Qt::WA_Resized);
resize(size()); // this will restrict the size
setAttribute(Qt::WA_Resized, wasResized);
}
bool wasResized = testAttribute(Qt::WA_Resized);
resize(size()); // this will restrict the size
setAttribute(Qt::WA_Resized, wasResized);
}
}

View File

@ -1,13 +1,18 @@
/****************************************************************************
**
** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
** a KDAB Group company, info@kdab.com,
** author Stephen Kelly <stephen.kelly@kdab.com>
** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
** All rights reserved.
** Contact: Nokia Corporation (info@qt.nokia.com)
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
@ -20,17 +25,17 @@
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, 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 have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

View File

@ -1,13 +1,18 @@
/****************************************************************************
**
** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
** a KDAB Group company, info@kdab.com,
** author Stephen Kelly <stephen.kelly@kdab.com>
** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
** All rights reserved.
** Contact: Nokia Corporation (info@qt.nokia.com)
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
@ -20,17 +25,17 @@
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, 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 have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

View File

@ -6,6 +6,7 @@ HEADERS += \
painting/qcolor.h \
painting/qcolor_p.h \
painting/qcolormap.h \
painting/qcosmeticstroker_p.h \
painting/qdrawutil.h \
painting/qemulationpaintengine_p.h \
painting/qgraphicssystem_p.h \
@ -14,7 +15,7 @@ HEADERS += \
painting/qoutlinemapper_p.h \
painting/qpaintdevice.h \
painting/qpaintengine.h \
painting/qpaintengine_p.h \
painting/qpaintengine_p.h \
painting/qpaintengine_alpha_p.h \
painting/qpaintengine_preview_p.h \
painting/qpaintengineex_p.h \
@ -53,6 +54,7 @@ SOURCES += \
painting/qbrush.cpp \
painting/qcolor.cpp \
painting/qcolor_p.cpp \
painting/qcosmeticstroker.cpp \
painting/qcssutil.cpp \
painting/qdrawutil.cpp \
painting/qemulationpaintengine.cpp \

View File

@ -0,0 +1,958 @@
#include "qcosmeticstroker_p.h"
#include "private/qpainterpath_p.h"
#include <qdebug.h>
#include <math.h>
QT_BEGIN_NAMESPACE
#if 0
inline QString capString(int caps)
{
QString str;
if (caps & QCosmeticStroker::CapBegin) {
str += "CapBegin ";
}
if (caps & QCosmeticStroker::CapEnd) {
str += "CapEnd ";
}
return str;
}
#endif
#define toF26Dot6(x) ((int)((x)*64.))
static inline uint sourceOver(uint d, uint color)
{
return color + BYTE_MUL(d, qAlpha(~color));
}
inline static int F16Dot16FixedDiv(int x, int y)
{
if (qAbs(x) > 0x7fff)
return (((qlonglong)x) << 16) / y;
return (x << 16) / y;
}
typedef void (*DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage);
namespace {
struct Dasher {
QCosmeticStroker *stroker;
int *pattern;
int offset;
int dashIndex;
int dashOn;
Dasher(QCosmeticStroker *s, bool reverse, int start, int stop)
: stroker(s)
{
int delta = stop - start;
if (reverse) {
pattern = stroker->reversePattern;
offset = stroker->patternLength - stroker->patternOffset - delta - ((start & 63) - 32);
dashOn = 0;
} else {
pattern = stroker->pattern;
offset = stroker->patternOffset - ((start & 63) - 32);
dashOn = 1;
}
offset %= stroker->patternLength;
if (offset < 0)
offset += stroker->patternLength;
dashIndex = 0;
while (offset>= pattern[dashIndex])
++dashIndex;
// qDebug() << " dasher" << offset/64. << reverse << dashIndex;
stroker->patternOffset += delta;
stroker->patternOffset %= stroker->patternLength;
}
bool on() const {
return (dashIndex + dashOn) & 1;
}
void adjust() {
offset += 64;
if (offset >= pattern[dashIndex]) {
++dashIndex;
dashIndex %= stroker->patternSize;
}
offset %= stroker->patternLength;
// qDebug() << "dasher.adjust" << offset/64. << dashIndex;
}
};
struct NoDasher {
NoDasher(QCosmeticStroker *, bool, int, int) {}
bool on() const { return true; }
void adjust(int = 0) {}
};
};
template<DrawPixel drawPixel, class Dasher>
static void drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
template<DrawPixel drawPixel, class Dasher>
static void drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage)
{
int lastx = stroker->spans[stroker->current_span-1].x + stroker->spans[stroker->current_span-1].len ;
int lasty = stroker->spans[stroker->current_span-1].y;
if (stroker->current_span == QCosmeticStroker::NSPANS || y < lasty || (y == lasty && x < lastx)) {
stroker->blend(stroker->current_span, stroker->spans, &stroker->state->penData);
stroker->current_span = 0;
}
stroker->spans[stroker->current_span].x = ushort(x);
stroker->spans[stroker->current_span].len = 1;
stroker->spans[stroker->current_span].y = y;
stroker->spans[stroker->current_span].coverage = coverage*stroker->opacity >> 8;
++stroker->current_span;
}
inline void drawPixelARGB32(QCosmeticStroker *stroker, int x, int y, int coverage)
{
const QRect &cl = stroker->clip;
if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
return;
int offset = x + stroker->ppl*y;
uint c = BYTE_MUL(stroker->color, coverage);
stroker->pixels[offset] = sourceOver(stroker->pixels[offset], c);
}
inline void drawPixelARGB32Opaque(QCosmeticStroker *stroker, int x, int y, int)
{
const QRect &cl = stroker->clip;
if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
return;
int offset = x + stroker->ppl*y;
stroker->pixels[offset] = sourceOver(stroker->pixels[offset], stroker->color);
}
enum StrokeSelection {
Aliased = 0,
AntiAliased = 1,
Solid = 0,
Dashed = 2,
RegularDraw = 0,
FastDraw = 4
};
static StrokeLine strokeLine(int strokeSelection)
{
StrokeLine stroke;
switch (strokeSelection) {
case Aliased|Solid|RegularDraw:
stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, NoDasher>;
break;
case Aliased|Solid|FastDraw:
stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, NoDasher>;
break;
case Aliased|Dashed|RegularDraw:
stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, Dasher>;
break;
case Aliased|Dashed|FastDraw:
stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, Dasher>;
break;
case AntiAliased|Solid|RegularDraw:
stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, NoDasher>;
break;
case AntiAliased|Solid|FastDraw:
stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, NoDasher>;
break;
case AntiAliased|Dashed|RegularDraw:
stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, Dasher>;
break;
case AntiAliased|Dashed|FastDraw:
stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, Dasher>;
break;
default:
Q_ASSERT(false);
stroke = 0;
}
return stroke;
}
void QCosmeticStroker::setup()
{
blend = state->penData.blend;
if (state->clip && state->clip->enabled && state->clip->hasRectClip && !state->clip->clipRect.isEmpty()) {
clip &= state->clip->clipRect;
blend = state->penData.unclipped_blend;
}
int strokeSelection = 0;
if (blend == state->penData.unclipped_blend
&& state->penData.type == QSpanData::Solid
&& (state->penData.rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
|| state->penData.rasterBuffer->format == QImage::Format_RGB32)
&& state->compositionMode() == QPainter::CompositionMode_SourceOver)
strokeSelection |= FastDraw;
if (state->renderHints & QPainter::Antialiasing)
strokeSelection |= AntiAliased;
const QVector<qreal> &penPattern = state->lastPen.dashPattern();
if (penPattern.isEmpty()) {
Q_ASSERT(!pattern && !reversePattern);
pattern = 0;
reversePattern = 0;
patternLength = 0;
patternSize = 0;
} else {
pattern = (int *)malloc(penPattern.size()*sizeof(int));
reversePattern = (int *)malloc(penPattern.size()*sizeof(int));
patternSize = penPattern.size();
patternLength = 0;
for (int i = 0; i < patternSize; ++i) {
patternLength += (int) qMax(1. , penPattern.at(i)*64.);
pattern[i] = patternLength;
}
patternLength = 0;
for (int i = 0; i < patternSize; ++i) {
patternLength += (int) qMax(1., penPattern.at(patternSize - 1 - i)*64.);
reversePattern[i] = patternLength;
}
strokeSelection |= Dashed;
// qDebug() << "setup: size=" << patternSize << "length=" << patternLength/64.;
}
stroke = strokeLine(strokeSelection);
qreal width = state->lastPen.widthF();
if (width == 0)
opacity = 256;
else if (state->lastPen.isCosmetic())
opacity = (int) 256*width;
else
opacity = (int) 256*width*state->txscale;
opacity = qBound(0, opacity, 256);
drawCaps = state->lastPen.capStyle() != Qt::FlatCap;
if (strokeSelection & FastDraw) {
color = INTERPOLATE_PIXEL_256(state->penData.solid.color, opacity, 0, 0);
QRasterBuffer *buffer = state->penData.rasterBuffer;
pixels = (uint *)buffer->buffer();
ppl = buffer->bytesPerLine()>>2;
}
// setup FP clip bounds
xmin = clip.left() - 1;
xmax = clip.right() + 2;
ymin = clip.top() - 1;
ymax = clip.bottom() + 2;
lastPixel.x = -1;
}
// returns true if the whole line gets clipped away
bool QCosmeticStroker::clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2)
{
// basic/rough clipping is done in floating point coordinates to avoid
// integer overflow problems.
if (x1 < xmin) {
if (x2 <= xmin)
goto clipped;
y1 += (y2 - y1)/(x2 - x1) * (xmin - x1);
x1 = xmin;
} else if (x1 > xmax) {
if (x2 >= xmax)
goto clipped;
y1 += (y2 - y1)/(x2 - x1) * (xmax - x1);
x1 = xmax;
}
if (x2 < xmin) {
lastPixel.x = -1;
y2 += (y2 - y1)/(x2 - x1) * (xmin - x2);
x2 = xmin;
} else if (x2 > xmax) {
lastPixel.x = -1;
y2 += (y2 - y1)/(x2 - x1) * (xmax - x2);
x2 = xmax;
}
if (y1 < ymin) {
if (y2 <= ymin)
goto clipped;
x1 += (x2 - x1)/(y2 - y1) * (ymin - y1);
y1 = ymin;
} else if (y1 > ymax) {
if (y2 >= ymax)
goto clipped;
x1 += (x2 - x1)/(y2 - y1) * (ymax - y1);
y1 = ymax;
}
if (y2 < ymin) {
lastPixel.x = -1;
x2 += (x2 - x1)/(y2 - y1) * (ymin - y2);
y2 = ymin;
} else if (y2 > ymax) {
lastPixel.x = -1;
x2 += (x2 - x1)/(y2 - y1) * (ymax - y2);
y2 = ymax;
}
return false;
clipped:
lastPixel.x = -1;
return true;
}
void QCosmeticStroker::drawLine(const QPointF &p1, const QPointF &p2)
{
QPointF start = p1 * state->matrix;
QPointF end = p2 * state->matrix;
patternOffset = state->lastPen.dashOffset()*64;
lastPixel.x = -1;
stroke(this, start.x(), start.y(), end.x(), end.y(), drawCaps ? CapBegin|CapEnd : 0);
blend(current_span, spans, &state->penData);
current_span = 0;
}
void QCosmeticStroker::drawPoints(const QPoint *points, int num)
{
const QPoint *end = points + num;
while (points < end) {
QPointF p = QPointF(*points) * state->matrix;
drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
++points;
}
blend(current_span, spans, &state->penData);
current_span = 0;
}
void QCosmeticStroker::drawPoints(const QPointF *points, int num)
{
const QPointF *end = points + num;
while (points < end) {
QPointF p = (*points) * state->matrix;
drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
++points;
}
blend(current_span, spans, &state->penData);
current_span = 0;
}
void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2)
{
// this is basically the same code as used in the aliased stroke method,
// but it only determines the direction and last point of a line
//
// This is being used to have proper dropout control for closed contours
// by calculating the direction and last pixel of the last segment in the contour.
// the info is then used to perform dropout control when drawing the first line segment
// of the contour
lastPixel.x = -1;
lastPixel.y = -1;
if (clipLine(rx1, ry1, rx2, ry2))
return;
int x1 = toF26Dot6(rx1);
int y1 = toF26Dot6(ry1);
int x2 = toF26Dot6(rx2);
int y2 = toF26Dot6(ry2);
int dx = qAbs(x2 - x1);
int dy = qAbs(y2 - y1);
if (dx < dy) {
// vertical
bool swapped = false;
if (y1 > y2) {
swapped = true;
qSwap(y1, y2);
qSwap(x1, x2);
}
int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
int x = x1 << 10;
int y = (y1+32) >> 6;
int ys = (y2+32) >> 6;
if (y != ys) {
x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6;
if (swapped) {
lastPixel.x = x >> 16;
lastPixel.y = y;
lastDir = QCosmeticStroker::BottomToTop;
} else {
lastPixel.x = (x + (ys - y - 1)*xinc) >> 16;
lastPixel.y = ys - 1;
lastDir = QCosmeticStroker::TopToBottom;
}
lastAxisAligned = qAbs(xinc) < (1 << 14);
}
} else {
// horizontal
if (!dx)
return;
bool swapped = false;
if (x1 > x2) {
swapped = true;
qSwap(x1, x2);
qSwap(y1, y2);
}
int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
int y = y1 << 10;
int x = (x1+32) >> 6;
int xs = (x2+32) >> 6;
if (x != xs) {
y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6;
if (swapped) {
lastPixel.x = x;
lastPixel.y = y >> 16;
lastDir = QCosmeticStroker::RightToLeft;
} else {
lastPixel.x = xs - 1;
lastPixel.y = (y + (xs - x - 1)*yinc) >> 16;
lastDir = QCosmeticStroker::LeftToRight;
}
lastAxisAligned = qAbs(yinc) < (1 << 14);
}
}
// qDebug() << " moveTo: setting last pixel to x/y dir" << lastPixel.x << lastPixel.y << lastDir;
}
static inline const QPainterPath::ElementType *subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end,
const qreal *points, bool *closed)
{
const QPainterPath::ElementType *start = t;
++t;
// find out if the subpath is closed
while (t < end) {
if (*t == QPainterPath::MoveToElement)
break;
++t;
}
int offset = t - start - 1;
// qDebug() << "subpath" << offset << points[0] << points[1] << points[2*offset] << points[2*offset+1];
*closed = (points[0] == points[2*offset] && points[1] == points[2*offset + 1]);
return t;
}
void QCosmeticStroker::drawPath(const QVectorPath &path)
{
// qDebug() << ">>>> drawpath" << path.convertToPainterPath()
// << "antialiasing:" << (bool)(state->renderHints & QPainter::Antialiasing) << " implicit close:" << path.hasImplicitClose();
if (path.isEmpty())
return;
const qreal *points = path.points();
const QPainterPath::ElementType *type = path.elements();
if (type) {
const QPainterPath::ElementType *end = type + path.elementCount();
while (type < end) {
Q_ASSERT(type == path.elements() || *type == QPainterPath::MoveToElement);
QPointF p = QPointF(points[0], points[1]) * state->matrix;
QPointF movedTo = p;
patternOffset = state->lastPen.dashOffset()*64;
lastPixel.x = -1;
bool closed;
const QPainterPath::ElementType *e = subPath(type, end, points, &closed);
if (closed) {
const qreal *p = points + 2*(e-type);
calculateLastPoint(p[-4], p[-3], p[-2], p[-1]);
}
int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
// qDebug() << "closed =" << closed << capString(caps);
points += 2;
++type;
while (type < e) {
QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
switch (*type) {
case QPainterPath::MoveToElement:
Q_ASSERT(!"Logic error");
break;
case QPainterPath::LineToElement:
if (!closed && drawCaps && type == e - 1)
caps |= CapEnd;
stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
p = p2;
points += 2;
++type;
break;
case QPainterPath::CurveToElement: {
if (!closed && drawCaps && type == e - 3)
caps |= CapEnd;
QPointF p3 = QPointF(points[2], points[3]) * state->matrix;
QPointF p4 = QPointF(points[4], points[5]) * state->matrix;
renderCubic(p, p2, p3, p4, caps);
p = p4;
type += 3;
points += 6;
break;
}
case QPainterPath::CurveToDataElement:
Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
break;
}
caps = NoCaps;
}
}
} else { // !type, simple polygon
QPointF p = QPointF(points[0], points[1]) * state->matrix;
QPointF movedTo = p;
patternOffset = state->lastPen.dashOffset()*64;
lastPixel.x = -1;
const qreal *end = points + 2*path.elementCount();
// handle closed path case
bool closed = path.hasImplicitClose() || (points[0] == end[-2] && points[1] == end[-1]);
int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
if (closed)
calculateLastPoint(end[-2], end[-1], points[0], points[1]);
points += 2;
while (points < end) {
QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
if (!closed && drawCaps && points == end - 2)
caps |= CapEnd;
stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
p = p2;
points += 2;
caps = NoCaps;
}
if (path.hasImplicitClose())
stroke(this, p.x(), p.y(), movedTo.x(), movedTo.y(), NoCaps);
}
blend(current_span, spans, &state->penData);
current_span = 0;
}
void QCosmeticStroker::renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps)
{
// qDebug() << ">>>> renderCubic" << p1 << p2 << p3 << p4 << capString(caps);
const int maxSubDivisions = 6;
PointF points[3*maxSubDivisions + 4];
points[3].x = p1.x();
points[3].y = p1.y();
points[2].x = p2.x();
points[2].y = p2.y();
points[1].x = p3.x();
points[1].y = p3.y();
points[0].x = p4.x();
points[0].y = p4.y();
PointF *p = points;
int level = maxSubDivisions;
renderCubicSubdivision(p, level, caps);
}
static void splitCubic(QCosmeticStroker::PointF *points)
{
const qreal half = .5;
qreal a, b, c, d;
points[6].x = points[3].x;
c = points[1].x;
d = points[2].x;
points[1].x = a = ( points[0].x + c ) * half;
points[5].x = b = ( points[3].x + d ) * half;
c = ( c + d ) * half;
points[2].x = a = ( a + c ) * half;
points[4].x = b = ( b + c ) * half;
points[3].x = ( a + b ) * half;
points[6].y = points[3].y;
c = points[1].y;
d = points[2].y;
points[1].y = a = ( points[0].y + c ) * half;
points[5].y = b = ( points[3].y + d ) * half;
c = ( c + d ) * half;
points[2].y = a = ( a + c ) * half;
points[4].y = b = ( b + c ) * half;
points[3].y = ( a + b ) * half;
}
void QCosmeticStroker::renderCubicSubdivision(QCosmeticStroker::PointF *points, int level, int caps)
{
if (level) {
qreal dx = points[3].x - points[0].x;
qreal dy = points[3].y - points[0].y;
qreal len = ((qreal).25) * (qAbs(dx) + qAbs(dy));
if (qAbs(dx * (points[0].y - points[2].y) - dy * (points[0].x - points[2].x)) > len ||
qAbs(dx * (points[0].y - points[1].y) - dy * (points[0].x - points[1].x)) > len) {
splitCubic(points);
--level;
renderCubicSubdivision(points + 3, level, caps & CapBegin);
renderCubicSubdivision(points, level, caps & CapEnd);
return;
}
}
stroke(this, points[3].x, points[3].y, points[0].x, points[0].y, caps);
}
static inline int swapCaps(int caps)
{
return ((caps & QCosmeticStroker::CapBegin) << 1) |
((caps & QCosmeticStroker::CapEnd) >> 1);
}
// adjust line by half a pixel
static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc)
{
if (caps & QCosmeticStroker::CapBegin) {
x1 -= 32;
y -= yinc >> 1;
}
if (caps & QCosmeticStroker::CapEnd) {
x2 += 32;
}
}
/*
The hard part about this is dropout control and avoiding douple drawing of points when
the drawing shifts from horizontal to vertical or back.
*/
template<DrawPixel drawPixel, class Dasher>
static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
{
if (stroker->clipLine(rx1, ry1, rx2, ry2))
return;
static const int half = 32;
int x1 = toF26Dot6(rx1) + half;
int y1 = toF26Dot6(ry1) + half;
int x2 = toF26Dot6(rx2) + half;
int y2 = toF26Dot6(ry2) + half;
int dx = qAbs(x2 - x1);
int dy = qAbs(y2 - y1);
QCosmeticStroker::Point last = stroker->lastPixel;
// qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64. << capString(caps);
if (dx < dy) {
// vertical
bool swapped = false;
if (y1 > y2) {
swapped = true;
qSwap(y1, y2);
qSwap(x1, x2);
caps = swapCaps(caps);
--x1; --x2; --y1; --y2;
}
int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
int x = x1 << 10;
capAdjust(caps, y1, y2, x, xinc);
int y = (y1+32) >> 6;
int ys = (y2+32) >> 6;
if (y != ys) {
x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6;
// calculate first and last pixel and perform dropout control
QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom;
QCosmeticStroker::Point first;
first.x = x >> 16;
first.y = y;
last.x = (x + (ys - y - 1)*xinc) >> 16;
last.y = ys - 1;
if (swapped) {
qSwap(first, last);
dir = QCosmeticStroker::BottomToTop;
}
bool axisAligned = qAbs(xinc) < (1 << 14);
if (stroker->lastPixel.x >= 0) {
if (first.x == stroker->lastPixel.x &&
first.y == stroker->lastPixel.y) {
// remove duplicated pixel
if (swapped) {
--ys;
} else {
++y;
x += xinc;
}
} else if (stroker->lastDir != dir &&
(((axisAligned && stroker->lastAxisAligned) &&
stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
(qAbs(stroker->lastPixel.x - first.x) > 1 &&
qAbs(stroker->lastPixel.y - first.y) > 1))) {
// have a missing pixel, insert it
if (swapped) {
++ys;
} else {
--y;
x -= xinc;
}
}
}
stroker->lastDir = dir;
stroker->lastAxisAligned = axisAligned;
Dasher dasher(stroker, swapped, y << 6, ys << 6);
do {
if (dasher.on())
drawPixel(stroker, x >> 16, y, 255);
dasher.adjust();
x += xinc;
} while (++y < ys);
}
} else {
// horizontal
if (!dx)
return;
bool swapped = false;
if (x1 > x2) {
swapped = true;
qSwap(x1, x2);
qSwap(y1, y2);
caps = swapCaps(caps);
--x1; --x2; --y1; --y2;
}
int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
int y = y1 << 10;
capAdjust(caps, x1, x2, y, yinc);
int x = (x1+32) >> 6;
int xs = (x2+32) >> 6;
if (x != xs) {
y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6;
// calculate first and last pixel to perform dropout control
QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight;
QCosmeticStroker::Point first;
first.x = x;
first.y = y >> 16;
last.x = xs - 1;
last.y = (y + (xs - x - 1)*yinc) >> 16;
if (swapped) {
qSwap(first, last);
dir = QCosmeticStroker::RightToLeft;
}
bool axisAligned = qAbs(yinc) < (1 << 14);
if (stroker->lastPixel.x >= 0) {
if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) {
// remove duplicated pixel
if (swapped) {
--xs;
} else {
++x;
y += yinc;
}
} else if (stroker->lastDir != dir &&
(((axisAligned && stroker->lastAxisAligned) &&
stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
(qAbs(stroker->lastPixel.x - first.x) > 1 &&
qAbs(stroker->lastPixel.y - first.y) > 1))) {
// have a missing pixel, insert it
if (swapped) {
++xs;
} else {
--x;
y -= yinc;
}
}
}
stroker->lastDir = dir;
stroker->lastAxisAligned = axisAligned;
Dasher dasher(stroker, swapped, x << 6, xs << 6);
do {
if (dasher.on())
drawPixel(stroker, x, y >> 16, 255);
dasher.adjust();
y += yinc;
} while (++x < xs);
}
}
stroker->lastPixel = last;
}
template<DrawPixel drawPixel, class Dasher>
static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
{
if (stroker->clipLine(rx1, ry1, rx2, ry2))
return;
int x1 = toF26Dot6(rx1);
int y1 = toF26Dot6(ry1);
int x2 = toF26Dot6(rx2);
int y2 = toF26Dot6(ry2);
int dx = x2 - x1;
int dy = y2 - y1;
if (qAbs(dx) < qAbs(dy)) {
// vertical
int xinc = F16Dot16FixedDiv(dx, dy);
bool swapped = false;
if (y1 > y2) {
qSwap(y1, y2);
qSwap(x1, x2);
swapped = true;
caps = swapCaps(caps);
}
int x = (x1 - 32) << 10;
x -= ( ((y1 & 63) - 32) * xinc ) >> 6;
capAdjust(caps, y1, y2, x, xinc);
Dasher dasher(stroker, swapped, y1, y2);
int y = y1 >> 6;
int ys = y2 >> 6;
int alphaStart, alphaEnd;
if (y == ys) {
alphaStart = y2 - y1;
Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
alphaEnd = 0;
} else {
alphaStart = 64 - (y1 & 63);
alphaEnd = (y2 & 63);
}
// qDebug() << "vertical" << x1/64. << y1/64. << x2/64. << y2/64.;
// qDebug() << " x=" << x << "dx=" << dx << "xi=" << (x>>16) << "xsi=" << ((x+(ys-y)*dx)>>16) << "y=" << y << "ys=" << ys;
// draw first pixel
if (dasher.on()) {
uint alpha = (quint8)(x >> 8);
drawPixel(stroker, x>>16, y, (255-alpha) * alphaStart >> 6);
drawPixel(stroker, (x>>16) + 1, y, alpha * alphaStart >> 6);
}
dasher.adjust();
x += xinc;
++y;
if (y < ys) {
do {
if (dasher.on()) {
uint alpha = (quint8)(x >> 8);
drawPixel(stroker, x>>16, y, (255-alpha));
drawPixel(stroker, (x>>16) + 1, y, alpha);
}
dasher.adjust();
x += xinc;
} while (++y < ys);
}
// draw last pixel
if (alphaEnd && dasher.on()) {
uint alpha = (quint8)(x >> 8);
drawPixel(stroker, x>>16, y, (255-alpha) * alphaEnd >> 6);
drawPixel(stroker, (x>>16) + 1, y, alpha * alphaEnd >> 6);
}
} else {
// horizontal
if (!dx)
return;
int yinc = F16Dot16FixedDiv(dy, dx);
bool swapped = false;
if (x1 > x2) {
qSwap(x1, x2);
qSwap(y1, y2);
swapped = true;
caps = swapCaps(caps);
}
int y = (y1 - 32) << 10;
y -= ( ((x1 & 63) - 32) * yinc ) >> 6;
capAdjust(caps, x1, x2, y, yinc);
Dasher dasher(stroker, swapped, x1, x2);
int x = x1 >> 6;
int xs = x2 >> 6;
// qDebug() << "horizontal" << x1/64. << y1/64. << x2/64. << y2/64.;
// qDebug() << " y=" << y << "dy=" << dy << "x=" << x << "xs=" << xs << "yi=" << (y>>16) << "ysi=" << ((y+(xs-x)*dy)>>16);
int alphaStart, alphaEnd;
if (x == xs) {
alphaStart = x2 - x1;
Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
alphaEnd = 0;
} else {
alphaStart = 64 - (x1 & 63);
alphaEnd = (x2 & 63);
}
// draw first pixel
if (dasher.on()) {
uint alpha = (quint8)(y >> 8);
drawPixel(stroker, x, y>>16, (255-alpha) * alphaStart >> 6);
drawPixel(stroker, x, (y>>16) + 1, alpha * alphaStart >> 6);
}
dasher.adjust();
y += yinc;
++x;
// draw line
if (x < xs) {
do {
if (dasher.on()) {
uint alpha = (quint8)(y >> 8);
drawPixel(stroker, x, y>>16, (255-alpha));
drawPixel(stroker, x, (y>>16) + 1, alpha);
}
dasher.adjust();
y += yinc;
} while (++x < xs);
}
// draw last pixel
if (alphaEnd && dasher.on()) {
uint alpha = (quint8)(y >> 8);
drawPixel(stroker, x, y>>16, (255-alpha) * alphaEnd >> 6);
drawPixel(stroker, x, (y>>16) + 1, alpha * alphaEnd >> 6);
}
}
}
QT_END_NAMESPACE

View File

@ -0,0 +1,111 @@
#ifndef QCOSMETICSTROKER_P_H
#define QCOSMETICSTROKER_P_H
#include <private/qdrawhelper_p.h>
#include <private/qvectorpath_p.h>
#include <private/qpaintengine_raster_p.h>
#include <qpen.h>
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
QT_MODULE(Gui)
class QCosmeticStroker;
typedef void (*StrokeLine)(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
class QCosmeticStroker
{
public:
struct Point {
int x;
int y;
};
struct PointF {
qreal x;
qreal y;
};
enum Caps {
NoCaps = 0,
CapBegin = 0x1,
CapEnd = 0x2,
};
// used to avoid drop outs or duplicated points
enum Direction {
TopToBottom,
BottomToTop,
LeftToRight,
RightToLeft
};
QCosmeticStroker(QRasterPaintEngineState *s, const QRect &dr)
: state(s),
clip(dr),
pattern(0),
reversePattern(0),
patternSize(0),
patternLength(0),
patternOffset(0),
current_span(0),
lastDir(LeftToRight),
lastAxisAligned(false)
{ setup(); }
~QCosmeticStroker() { free(pattern); free(reversePattern); }
void drawLine(const QPointF &p1, const QPointF &p2);
void drawPath(const QVectorPath &path);
void drawPoints(const QPoint *points, int num);
void drawPoints(const QPointF *points, int num);
QRasterPaintEngineState *state;
QRect clip;
// clip bounds in real
qreal xmin, xmax;
qreal ymin, ymax;
StrokeLine stroke;
bool drawCaps;
int *pattern;
int *reversePattern;
int patternSize;
int patternLength;
int patternOffset;
enum { NSPANS = 255 };
QT_FT_Span spans[NSPANS];
int current_span;
ProcessSpans blend;
int opacity;
uint color;
uint *pixels;
int ppl;
Direction lastDir;
Point lastPixel;
bool lastAxisAligned;
private:
void setup();
void renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps);
void renderCubicSubdivision(PointF *points, int level, int caps);
// used for closed subpaths
void calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2);
public:
bool clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2);
};
QT_END_NAMESPACE
QT_END_HEADER
#endif // QCOSMETICLINE_H

File diff suppressed because it is too large Load Diff

View File

@ -196,9 +196,6 @@ public:
void stroke(const QVectorPath &path, const QPen &pen);
void fill(const QVectorPath &path, const QBrush &brush);
void strokePolygonCosmetic(const QPoint *pts, int pointCount, PolygonDrawMode mode);
void strokePolygonCosmetic(const QPointF *pt, int pointCount, PolygonDrawMode mode);
void clip(const QVectorPath &path, Qt::ClipOperation op);
void clip(const QRect &rect, Qt::ClipOperation op);
void clip(const QRegion &region, Qt::ClipOperation op);
@ -328,8 +325,6 @@ public:
bool isUnclipped_normalized(const QRect &rect) const;
bool isUnclipped(const QRect &rect, int penWidth) const;
bool isUnclipped(const QRectF &rect, int penWidth) const;
ProcessSpans getPenFunc(const QRect &rect, const QSpanData *data) const;
ProcessSpans getPenFunc(const QRectF &rect, const QSpanData *data) const;
ProcessSpans getBrushFunc(const QRect &rect, const QSpanData *data) const;
ProcessSpans getBrushFunc(const QRectF &rect, const QSpanData *data) const;

View File

@ -831,7 +831,7 @@ void QPaintEngineEx::drawEllipse(const QRectF &r)
int point_count = 0;
x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count);
QVectorPath vp((qreal *) pts, point_count, qpaintengineex_ellipse_types, QVectorPath::EllipseHint);
QVectorPath vp((qreal *) pts, point_count + 1, qpaintengineex_ellipse_types, QVectorPath::EllipseHint);
draw(vp);
}

View File

@ -651,7 +651,12 @@ void QLineControl::internalSetText(const QString &txt, int pos, bool edited)
m_modifiedState = m_undoState = 0;
m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
m_textDirty = (oldText != m_text);
finishChange(-1, true, edited);
bool changed = finishChange(-1, true, edited);
#ifndef QT_NO_ACCESSIBILITY
if (changed)
QAccessible::updateAccessibility(parent(), 0, QAccessible::TextUpdated);
#endif
}
@ -1238,6 +1243,9 @@ void QLineControl::emitCursorPositionChanged()
const int oldLast = m_lastCursorPos;
m_lastCursorPos = m_cursor;
cursorPositionChanged(oldLast, m_cursor);
#ifndef QT_NO_ACCESSIBILITY
QAccessible::updateAccessibility(parent(), 0, QAccessible::TextCaretMoved);
#endif
}
}

View File

@ -61,10 +61,10 @@
#include "QtGui/qtextlayout.h"
#include "QtGui/qstyleoption.h"
#include "QtCore/qpointer.h"
#include "QtGui/qlineedit.h"
#include "QtGui/qclipboard.h"
#include "QtCore/qpoint.h"
#include "QtGui/qcompleter.h"
#include "QtGui/qaccessible.h"
QT_BEGIN_HEADER

View File

@ -153,6 +153,7 @@ void QLineEditPrivate::init(const QString& txt)
{
Q_Q(QLineEdit);
control = new QLineControl(txt);
control->setParent(q);
control->setFont(q->font());
QObject::connect(control, SIGNAL(textChanged(QString)),
q, SIGNAL(textChanged(QString)));

View File

@ -84,7 +84,6 @@ public:
~QLineEditPrivate()
{
delete control;
}
QLineControl *control;

View File

@ -103,7 +103,7 @@ SUBDIRS=\
qlistview \
qlistwidget \
qlocale \
#qlocalsocket \ # FIXME: uses qtscript, shouldn't be in qtbase
#qlocalsocket \ # FIXME: uses qtscript (QTBUG-19242)
qmacstyle \
qmainwindow \
qmatrixnxn \

View File

@ -3,7 +3,7 @@ SUBDIRS=\
compiler \
headersclean \
maketestselftest \
#moc \ # FIXME: cannot be built as part of qtbase, since it depends on qtsvg
#moc \ # FIXME: uses qtsvg (QTBUG-19243)
uic \
qmake \
rcc \

View File

@ -10,8 +10,7 @@ SUBDIRS=\
qalgorithms \
qcombobox \
qcssparser \
#qdatastream \ # FIXME: cannot be enabled by default in qtbase,
# since it depends on qtsvg
#qdatastream \ # FIXME: uses qtsvg (QTBUG-19244)
qdir \
qfocusevent \
qimage \

View File

@ -1091,6 +1091,9 @@ void tst_QGraphicsAnchorLayout::setSpacing()
#ifdef Q_WS_MAC
QTest::qWait(200);
#endif
// 21x21
QCOMPARE(p->size(), QSizeF(41, 41));
QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20));
QCOMPARE(b->geometry(), QRectF(21, 0, 20, 20));
QCOMPARE(c->geometry(), QRectF(0, 21, 41, 20));

View File

@ -62,6 +62,7 @@ private slots:
void compressLayoutRequest();
void automaticReparenting();
void verifyActivate();
void invalidate();
void constructors();
void alternativeLayoutItems();
void ownership();
@ -95,6 +96,14 @@ void tst_QGraphicsLayout::sizeHints()
}
enum FunctionType {
SetGeometry = 0,
Invalidate,
NumFunctionTypes
};
class TestGraphicsWidget : public QGraphicsWidget {
public:
TestGraphicsWidget(QGraphicsWidget *parent = 0) : QGraphicsWidget(parent)
@ -108,9 +117,28 @@ public:
int eventCount(QEvent::Type type) {
return m_eventCount.value(int(type));
}
void clearEventCount() {
m_eventCount.clear();
}
void clearCounters() {
m_eventCount.clear();
functionCount.clear();
}
void setGeometry(const QRectF &rect)
{
QGraphicsWidget::setGeometry(rect);
++(functionCount[SetGeometry]);
}
void callUpdateGeometry()
{
// updateGeometry() is protected
QGraphicsWidget::updateGeometry();
}
QMap<FunctionType, int> functionCount;
private:
QMap<int, int> m_eventCount;
};
@ -122,6 +150,8 @@ void tst_QGraphicsLayout::compressLayoutRequest()
TestGraphicsWidget *tw = new TestGraphicsWidget();
scene.addItem(tw);
view.show();
QTest::qWaitForWindowShown(&view);
QGraphicsLinearLayout *lout = new QGraphicsLinearLayout(tw);
for (int i = 0; i < 4; ++i) {
QGraphicsWidget *gw = new QGraphicsWidget(tw);
@ -217,17 +247,27 @@ class TestLayout : public QGraphicsLinearLayout
TestLayout(QGraphicsLayoutItem *parent = 0)
: QGraphicsLinearLayout(parent)
{
m_count = 0;
setContentsMargins(0,0,0,0);
setSpacing(0);
}
void setGeometry(const QRectF &rect) {
++m_count;
void setGeometry(const QRectF &rect)
{
++(functionCount[SetGeometry]);
QGraphicsLinearLayout::setGeometry(rect);
}
void invalidate()
{
++(functionCount[Invalidate]);
QGraphicsLinearLayout::invalidate();
}
int m_count;
void clearCounters() {
functionCount.clear();
}
QMap<FunctionType, int> functionCount;
};
void tst_QGraphicsLayout::verifyActivate()
@ -242,15 +282,280 @@ void tst_QGraphicsLayout::verifyActivate()
lout->addItem(w);
window->setLayout(lout);
QCOMPARE(lout->m_count, 0);
QCOMPARE(lout->functionCount[SetGeometry], 0);
window->setVisible(false);
QCOMPARE(lout->m_count, 0);
QCOMPARE(lout->functionCount[SetGeometry], 0);
window->setVisible(true);
// on polish or the first time a widget is shown, the widget is resized.
QCOMPARE(lout->m_count, 1);
QCOMPARE(lout->functionCount[SetGeometry], 1);
}
static void clearAllCounters(TestGraphicsWidget *widget)
{
if (!widget)
return;
widget->clearCounters();
TestLayout *layout = static_cast<TestLayout *>(widget->layout());
if (layout) {
layout->clearCounters();
for (int i = layout->count() - 1; i >=0; --i) {
QGraphicsLayoutItem *item = layout->itemAt(i);
if (item->isLayout()) {
// ### Not used ATM
//TestLayout *lay = static_cast<TestLayout*>(static_cast<QGraphicsLayout*>(item));
//clearAllCounters(lay);
} else {
TestGraphicsWidget *wid = static_cast<TestGraphicsWidget *>(item);
clearAllCounters(wid);
}
}
}
}
static void activateAndReset(TestGraphicsWidget *widget)
{
QApplication::sendPostedEvents();
QApplication::processEvents();
if (widget->layout())
widget->layout()->activate();
clearAllCounters(widget);
}
void tst_QGraphicsLayout::invalidate()
{
QGraphicsLayout::setInstantInvalidatePropagation(true);
QGraphicsScene scene;
QGraphicsView view(&scene);
TestGraphicsWidget *a = new TestGraphicsWidget;
a->setData(0, QString("a"));
scene.addItem(a);
TestLayout *alay = new TestLayout(a);
TestGraphicsWidget *b = new TestGraphicsWidget;
b->setData(0, QString("b"));
alay->addItem(b);
TestLayout *blay = new TestLayout(b);
TestGraphicsWidget *e = new TestGraphicsWidget;
e->setData(0, QString("e"));
blay->addItem(e);
TestGraphicsWidget *c = new TestGraphicsWidget;
c->setData(0, QString("c"));
alay->addItem(c);
TestLayout *clay = new TestLayout(c);
TestGraphicsWidget *f = new TestGraphicsWidget;
f->setData(0, QString("f"));
clay->addItem(f);
TestGraphicsWidget *d = new TestGraphicsWidget;
d->setData(0, QString("d"));
alay->addItem(d);
TestLayout *dlay = new TestLayout(d);
TestGraphicsWidget *g = new TestGraphicsWidget;
g->setData(0, QString("g"));
dlay->addItem(g);
view.show();
{
clearAllCounters(a);
QCoreApplication::sendPostedEvents();
QCoreApplication::processEvents();
alay->activate();
QCOMPARE(alay->isActivated(), true);
QCOMPARE(blay->isActivated(), true);
QCOMPARE(clay->isActivated(), true);
QCOMPARE(dlay->isActivated(), true);
}
{
clearAllCounters(a);
e->callUpdateGeometry();
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), false);
QCOMPARE(clay->isActivated(), true);
QCOMPARE(dlay->isActivated(), true);
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
// should only invalidate ascendants of e
QCOMPARE(blay->functionCount[Invalidate], 1);
QCOMPARE(alay->functionCount[Invalidate], 1);
// not siblings
QCOMPARE(clay->functionCount[Invalidate], 0);
QCOMPARE(dlay->functionCount[Invalidate], 0);
QApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
}
{
activateAndReset(a);
f->callUpdateGeometry();
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), true);
QCOMPARE(clay->isActivated(), false);
QCOMPARE(dlay->isActivated(), true);
QCoreApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(a->functionCount[SetGeometry], 1);
QCOMPARE(alay->functionCount[SetGeometry], 1);
QCOMPARE(b->functionCount[SetGeometry], 1);
QCOMPARE(c->functionCount[SetGeometry], 1);
QCOMPARE(d->functionCount[SetGeometry], 1);
// Since nothing really changed, blay and dlay don't need
// to be resized.
QCOMPARE(blay->functionCount[SetGeometry], 0);
QCOMPARE(clay->functionCount[SetGeometry], 1);
QCOMPARE(dlay->functionCount[SetGeometry], 0);
QCOMPARE(f->functionCount[SetGeometry], 1);
QCOMPARE(a->size(), QSizeF(150, 50));
}
{
activateAndReset(a);
f->setPreferredSize(QSizeF(60,50));
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), true);
QCOMPARE(clay->isActivated(), false);
QCOMPARE(dlay->isActivated(), true);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCoreApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(a->functionCount[SetGeometry], 1);
QCOMPARE(alay->functionCount[SetGeometry], 1);
QCOMPARE(b->functionCount[SetGeometry], 1);
QCOMPARE(c->functionCount[SetGeometry], 1);
QCOMPARE(d->functionCount[SetGeometry], 1);
// f actually got wider, need to rearrange its siblings
QCOMPARE(blay->functionCount[SetGeometry], 1);
QCOMPARE(clay->functionCount[SetGeometry], 1);
QCOMPARE(dlay->functionCount[SetGeometry], 1);
QCOMPARE(e->functionCount[SetGeometry], 1);
QCOMPARE(f->functionCount[SetGeometry], 1);
QCOMPARE(g->functionCount[SetGeometry], 1);
QVERIFY(e->size().width() < f->size().width());
QVERIFY(g->size().width() < f->size().width());
}
{
// resize f so much that it'll force a resize of the top widget
// this will currently generate two setGeometry() calls on the child layout
// of the top widget.
activateAndReset(a);
f->setPreferredSize(QSizeF());
f->setMinimumSize(QSizeF(200,50));
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), true);
QCOMPARE(clay->isActivated(), false);
QCOMPARE(dlay->isActivated(), true);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCoreApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(a->functionCount[SetGeometry], 1);
/* well, ideally one call to setGeometry(), but it will currently
* get two calls to setGeometry():
* 1. The first LayoutRequest will call activate() - that will call
* setGeometry() on the layout. This geometry will be based on
* the widget geometry which is not correct at this moment.
* (it is still 150 wide)
* 2. Next, we check if the widget is top level, and then we call
* parentWidget->resize(parentWidget->size());
* This will be adjusted to be minimum 200 pixels wide.
* The new size will then be propagated down to the layout
*
*/
QCOMPARE(alay->functionCount[SetGeometry], 2);
QCOMPARE(b->functionCount[SetGeometry], 2);
QCOMPARE(c->functionCount[SetGeometry], 2);
QCOMPARE(d->functionCount[SetGeometry], 2);
// f actually got wider, need to rearrange its siblings
QCOMPARE(blay->functionCount[SetGeometry], 1);
QCOMPARE(clay->functionCount[SetGeometry], 1);
QCOMPARE(dlay->functionCount[SetGeometry], 1);
QCOMPARE(e->functionCount[SetGeometry], 1);
QCOMPARE(f->functionCount[SetGeometry], 1);
QCOMPARE(g->functionCount[SetGeometry], 1);
QVERIFY(e->size().width() < f->size().width());
QVERIFY(g->size().width() < f->size().width());
}
{
f->setPreferredSize(QSizeF());
f->setMinimumSize(QSizeF());
a->adjustSize();
activateAndReset(a);
// update two different leaf widgets,
// eventCount and functionCount should never be >= 2
e->callUpdateGeometry();
g->callUpdateGeometry();
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), false);
QCOMPARE(clay->isActivated(), true);
QCOMPARE(dlay->isActivated(), false);
QCoreApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(a->functionCount[SetGeometry], 1);
QCOMPARE(alay->functionCount[SetGeometry], 1);
QCOMPARE(b->functionCount[SetGeometry], 1);
QCOMPARE(c->functionCount[SetGeometry], 1);
QCOMPARE(d->functionCount[SetGeometry], 1);
// f actually got wider, need to rearrange its siblings
QCOMPARE(blay->functionCount[SetGeometry], 1);
QCOMPARE(clay->functionCount[SetGeometry], 0);
QCOMPARE(dlay->functionCount[SetGeometry], 1);
QCOMPARE(e->functionCount[SetGeometry], 1);
QCOMPARE(f->functionCount[SetGeometry], 0);
QCOMPARE(g->functionCount[SetGeometry], 1);
}
QGraphicsLayout::setInstantInvalidatePropagation(false);
}
class Layout : public QGraphicsLayout
{
public:

View File

@ -94,6 +94,7 @@ private slots:
void itemSpacing();
void setStretchFactor_data();
void setStretchFactor();
void testStretch();
void defaultStretchFactors_data();
void defaultStretchFactors();
void sizeHint_data();
@ -667,6 +668,10 @@ void tst_QGraphicsLinearLayout::invalidate()
layout.setContentsMargins(0, 0, 0, 0);
view.show();
widget->show();
//QTest::qWait(1000);
QTest::qWaitForWindowShown(&view);
qApp->processEvents();
layout.layoutRequest = 0;
layout.setContentsMargins(1, 2, 3, 4);
QApplication::sendPostedEvents(0, 0);
@ -1130,6 +1135,41 @@ void tst_QGraphicsLinearLayout::setStretchFactor()
delete widget;
}
void tst_QGraphicsLinearLayout::testStretch()
{
QGraphicsScene scene;
QGraphicsView *view = new QGraphicsView(&scene);
QGraphicsWidget *form = new QGraphicsWidget(0, Qt::Window);
scene.addItem(form);
form->setMinimumSize(600, 600);
form->setMaximumSize(600, 600);
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Horizontal, form);
QGraphicsWidget *w1 = new RectWidget;
w1->setPreferredSize(100,100);
w1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
QGraphicsWidget *w2 = new RectWidget;
w2->setPreferredSize(200,200);
w2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
layout->addItem(w1);
layout->addStretch(2);
layout->addItem(w2);
QCOMPARE(layout->count(), 2);
QVERIFY(layout->itemAt(0) == w1);
QVERIFY(layout->itemAt(1) == w2);
layout->activate();
//view->setSceneRect(-50, -50, 800, 800);
//view->show();
//QTest::qWaitForWindowShown(view);
//QTest::qWait(5000);
QCOMPARE(form->geometry().size(), QSizeF(600,600));
QCOMPARE(w1->geometry(), QRectF(0, 0, 100, 100));
QCOMPARE(w2->geometry(), QRectF(400, 0, 200, 200));
}
void tst_QGraphicsLinearLayout::defaultStretchFactors_data()
{
QTest::addColumn<Qt::Orientation>("orientation");

View File

@ -186,7 +186,6 @@ private slots:
void task250119_shortcutContext();
void QT_BUG_6544_tabFocusFirstUnsetWhenRemovingItems();
void QT_BUG_12056_tabFocusFirstUnsetWhenRemovingItems();
void QT_BUG_13865_doublePaintWhenAddingASubItem();
};
@ -3367,46 +3366,6 @@ void tst_QGraphicsWidget::QT_BUG_12056_tabFocusFirstUnsetWhenRemovingItems()
//This should not crash
}
struct GreenWidget : public QGraphicsWidget
{
GreenWidget() : count(0)
{
}
void paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * )
{
count++;
painter->setPen(Qt::green);
painter->drawRect(option->rect.adjusted(0,0,-1,-1));
}
int count;
};
void tst_QGraphicsWidget::QT_BUG_13865_doublePaintWhenAddingASubItem()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
QGraphicsWidget *widget = new QGraphicsWidget;
widget->resize(100, 100);
scene.addItem(widget);
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(widget);
view.show();
QTest::qWaitForWindowShown(&view);
QApplication::processEvents();
GreenWidget *sub = new GreenWidget;
layout->addItem(sub);
QTest::qWait(100);
QCOMPARE(sub->count, 1); //it should only be painted once
}
QTEST_MAIN(tst_QGraphicsWidget)
#include "tst_qgraphicswidget.moc"

View File

@ -1,13 +1,18 @@
/****************************************************************************
**
** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
** a KDAB Group company, info@kdab.com,
** author Stephen Kelly <stephen.kelly@kdab.com>
** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
** All rights reserved.
** Contact: Nokia Corporation (info@qt.nokia.com)
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
@ -20,17 +25,17 @@
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, 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 have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

View File

@ -0,0 +1,3 @@
int main(int,char**)
{
}

View File

@ -0,0 +1,5 @@
TEMPLATE = app
SOURCES = main.cpp
extratarget.commands = @echo extra target worked OK
QMAKE_EXTRA_TARGETS += extratarget

View File

@ -0,0 +1,7 @@
TEMPLATE = subdirs
SUBDIRS = simple
extratarget.CONFIG = recursive
extratarget.recurse = $$SUBDIRS
extratarget.recurse_target = extratarget
QMAKE_EXTRA_TARGETS += extratarget

View File

@ -0,0 +1,7 @@
TEMPLATE = subdirs
SUBDIRS = subdir.pro
extratarget.CONFIG = recursive
extratarget.recurse = $$SUBDIRS
extratarget.recurse_target = extratarget
QMAKE_EXTRA_TARGETS += extratarget

View File

@ -69,6 +69,7 @@ private slots:
void simple_lib();
void simple_dll();
void subdirs();
void subdir_via_pro_file_extra_target();
void functions();
void operators();
void variables();
@ -234,6 +235,19 @@ void tst_qmake::subdirs()
QVERIFY( test_compiler.removeMakefile( workDir ) );
}
void tst_qmake::subdir_via_pro_file_extra_target()
{
QString workDir = base_path + "/testdata/subdir_via_pro_file_extra_target";
QDir D;
D.remove( workDir + "/Makefile");
D.remove( workDir + "/Makefile.subdir");
D.remove( workDir + "/simple/Makefile");
D.remove( workDir + "/simple/Makefile.subdir");
QVERIFY( test_compiler.qmake( workDir, "subdir_via_pro_file_extra_target" ));
QVERIFY( test_compiler.make( workDir, "extratarget" ));
}
void tst_qmake::functions()
{
QString workDir = base_path + "/testdata/functions";