diff options
Diffstat (limited to 'src')
546 files changed, 43256 insertions, 17355 deletions
diff --git a/src/3rdparty/easing/easing.cpp b/src/3rdparty/easing/easing.cpp new file mode 100644 index 0000000..81af40f --- /dev/null +++ b/src/3rdparty/easing/easing.cpp @@ -0,0 +1,670 @@ +/* +Disclaimer for Robert Penner's Easing Equations license: + +TERMS OF USE - EASING EQUATIONS + +Open source under the BSD License. + +Copyright © 2001 Robert Penner +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <QtCore/qmath.h> +#include <math.h> +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 (M_PI / 2) +#endif + +QT_USE_NAMESPACE + +/** + * Easing equation function for a simple linear tweening, with no easing. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeNone(qreal progress) +{ + return progress; +} + +/** + * Easing equation function for a quadratic (t^2) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInQuad(qreal t) +{ + return t*t; +} + +/** +* Easing equation function for a quadratic (t^2) easing out: decelerating to zero velocity. +* +* @param t Current time (in frames or seconds). +* @return The correct value. +*/ +static qreal easeOutQuad(qreal t) +{ + return -t*(t-2); +} + +/** + * Easing equation function for a quadratic (t^2) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutQuad(qreal t) +{ + t*=2.0; + if (t < 1) { + return t*t/qreal(2); + } else { + --t; + return -0.5 * (t*(t-2) - 1); + } +} + +/** + * Easing equation function for a quadratic (t^2) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInQuad(qreal t) +{ + if (t < 0.5) return easeOutQuad (t*2)/2; + return easeInQuad((2*t)-1)/2 + 0.5; +} + +/** + * Easing equation function for a cubic (t^3) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInCubic(qreal t) +{ + return t*t*t; +} + +/** + * Easing equation function for a cubic (t^3) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutCubic(qreal t) +{ + t-=1.0; + return t*t*t + 1; +} + +/** + * Easing equation function for a cubic (t^3) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutCubic(qreal t) +{ + t*=2.0; + if(t < 1) { + return 0.5*t*t*t; + } else { + t -= qreal(2.0); + return 0.5*(t*t*t + 2); + } +} + +/** + * Easing equation function for a cubic (t^3) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInCubic(qreal t) +{ + if (t < 0.5) return easeOutCubic (2*t)/2; + return easeInCubic(2*t - 1)/2 + 0.5; +} + +/** + * Easing equation function for a quartic (t^4) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInQuart(qreal t) +{ + return t*t*t*t; +} + +/** + * Easing equation function for a quartic (t^4) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutQuart(qreal t) +{ + t-= qreal(1.0); + return - (t*t*t*t- 1); +} + +/** + * Easing equation function for a quartic (t^4) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutQuart(qreal t) +{ + t*=2; + if (t < 1) return 0.5*t*t*t*t; + else { + t -= 2.0f; + return -0.5 * (t*t*t*t- 2); + } +} + +/** + * Easing equation function for a quartic (t^4) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInQuart(qreal t) +{ + if (t < 0.5) return easeOutQuart (2*t)/2; + return easeInQuart(2*t-1)/2 + 0.5; +} + +/** + * Easing equation function for a quintic (t^5) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInQuint(qreal t) +{ + return t*t*t*t*t; +} + +/** + * Easing equation function for a quintic (t^5) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutQuint(qreal t) +{ + t-=1.0; + return t*t*t*t*t + 1; +} + +/** + * Easing equation function for a quintic (t^5) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutQuint(qreal t) +{ + t*=2.0; + if (t < 1) return 0.5*t*t*t*t*t; + else { + t -= 2.0; + return 0.5*(t*t*t*t*t + 2); + } +} + +/** + * Easing equation function for a quintic (t^5) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInQuint(qreal t) +{ + if (t < 0.5) return easeOutQuint (2*t)/2; + return easeInQuint(2*t - 1)/2 + 0.5; +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInSine(qreal t) +{ + return (t == 1.0) ? 1.0 : -::cos(t * M_PI_2) + 1.0; +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutSine(qreal t) +{ + return ::sin(t* M_PI_2); +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutSine(qreal t) +{ + return -0.5 * (::cos(M_PI*t) - 1); +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInSine(qreal t) +{ + if (t < 0.5) return easeOutSine (2*t)/2; + return easeInSine(2*t - 1)/2 + 0.5; +} + +/** + * Easing equation function for an exponential (2^t) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInExpo(qreal t) +{ + return (t==0 || t == 1.0) ? t : ::qPow(2.0, 10 * (t - 1)) - qreal(0.001); +} + +/** + * Easing equation function for an exponential (2^t) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutExpo(qreal t) +{ + return (t==1.0) ? 1.0 : 1.001 * (-::qPow(2.0f, -10 * t) + 1); +} + +/** + * Easing equation function for an exponential (2^t) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutExpo(qreal t) +{ + if (t==0.0) return qreal(0.0); + if (t==1.0) return qreal(1.0); + t*=2.0; + if (t < 1) return 0.5 * ::qPow(qreal(2.0), 10 * (t - 1)) - 0.0005; + return 0.5 * 1.0005 * (-::qPow(qreal(2.0), -10 * (t - 1)) + 2); +} + +/** + * Easing equation function for an exponential (2^t) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInExpo(qreal t) +{ + if (t < 0.5) return easeOutExpo (2*t)/2; + return easeInExpo(2*t - 1)/2 + 0.5; +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInCirc(qreal t) +{ + return -(::sqrt(1 - t*t) - 1); +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutCirc(qreal t) +{ + t-= qreal(1.0); + return ::sqrt(1 - t* t); +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeInOutCirc(qreal t) +{ + t*=qreal(2.0); + if (t < 1) { + return -0.5 * (::sqrt(1 - t*t) - 1); + } else { + t -= qreal(2.0); + return 0.5 * (::sqrt(1 - t*t) + 1); + } +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @return The correct value. + */ +static qreal easeOutInCirc(qreal t) +{ + if (t < 0.5) return easeOutCirc (2*t)/2; + return easeInCirc(2*t - 1)/2 + 0.5; +} + +static qreal easeInElastic_helper(qreal t, qreal b, qreal c, qreal d, qreal a, qreal p) +{ + if (t==0) return b; + qreal t_adj = (qreal)t / (qreal)d; + if (t_adj==1) return b+c; + + qreal s; + if(a < ::fabs(c)) { + a = c; + s = p / 4.0f; + } else { + s = p / (2 * M_PI) * ::asin(c / a); + } + + t_adj -= 1.0f; + return -(a*::qPow(2.0f,10*t_adj) * ::sin( (t_adj*d-s)*(2*M_PI)/p )) + b; +} + +/** + * Easing equation function for an elastic (exponentially decaying sine wave) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static qreal easeInElastic(qreal t, qreal a, qreal p) +{ + return easeInElastic_helper(t, 0, 1, 1, a, p); +} + +static qreal easeOutElastic_helper(qreal t, qreal /*b*/, qreal c, qreal /*d*/, qreal a, qreal p) +{ + if (t==0) return 0; + if (t==1) return c; + + qreal s; + if(a < c) { + a = c; + s = p / 4.0f; + } else { + s = p / (2 * M_PI) * ::asin(c / a); + } + + return (a*::qPow(2.0f,-10*t) * ::sin( (t-s)*(2*M_PI)/p ) + c); +} + +/** + * Easing equation function for an elastic (exponentially decaying sine wave) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static qreal easeOutElastic(qreal t, qreal a, qreal p) +{ + return easeOutElastic_helper(t, 0, 1, 1, a, p); +} + +/** + * Easing equation function for an elastic (exponentially decaying sine wave) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static qreal easeInOutElastic(qreal t, qreal a, qreal p) +{ + if (t==0) return 0.0; + t*=2.0; + if (t==2) return 1.0; + + qreal s; + if(a < 1.0) { + a = 1.0; + s = p / 4.0f; + } else { + s = p / (2 * M_PI) * ::asin(1.0 / a); + } + + if (t < 1) return -.5*(a*::qPow(2.0f,10*(t-1)) * ::sin( (t-1-s)*(2*M_PI)/p )); + return a*::qPow(2.0f,-10*(t-1)) * ::sin( (t-1-s)*(2*M_PI)/p )*.5 + 1.0; +} + +/** + * Easing equation function for an elastic (exponentially decaying sine wave) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static qreal easeOutInElastic(qreal t, qreal a, qreal p) +{ + if (t < 0.5) return easeOutElastic_helper(t*2, 0, 0.5, 1.0, a, p); + return easeInElastic_helper(2*t - 1.0, 0.5, 0.5, 1.0, a, p); +} + +/** + * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). + * @return The correct value. + */ +static qreal easeInBack(qreal t, qreal s) +{ + return t*t*((s+1)*t - s); +} + +/** + * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). + * @return The correct value. + */ +static qreal easeOutBack(qreal t, qreal s) +{ + t-= qreal(1.0); + return t*t*((s+1)*t+ s) + 1; +} + +/** + * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). + * @return The correct value. + */ +static qreal easeInOutBack(qreal t, qreal s) +{ + t *= 2.0; + if (t < 1) { + s *= 1.525f; + return 0.5*(t*t*((s+1)*t - s)); + } else { + t -= 2; + s *= 1.525f; + return 0.5*(t*t*((s+1)*t+ s) + 2); + } +} + +/** + * Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @param s Overshoot ammount: higher s means greater overshoot (0 produces cubic easing with no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent). + * @return The correct value. + */ +static qreal easeOutInBack(qreal t, qreal s) +{ + if (t < 0.5) return easeOutBack (2*t, s)/2; + return easeInBack(2*t - 1, s)/2 + 0.5; +} + +static qreal easeOutBounce_helper(qreal t, qreal c, qreal a) +{ + if (t == 1.0) return c; + if (t < (4/11.0)) { + return c*(7.5625*t*t); + } else if (t < (8/11.0)) { + t -= (6/11.0); + return -a * (1. - (7.5625*t*t + .75)) + c; + } else if (t < (10/11.0)) { + t -= (9/11.0); + return -a * (1. - (7.5625*t*t + .9375)) + c; + } else { + t -= (21/22.0); + return -a * (1. - (7.5625*t*t + .984375)) + c; + } +} + +/** + * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @return The correct value. + */ +static qreal easeOutBounce(qreal t, qreal a) +{ + return easeOutBounce_helper(t, 1, a); +} + +/** + * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @return The correct value. + */ +static qreal easeInBounce(qreal t, qreal a) +{ + return 1.0 - easeOutBounce_helper(1.0-t, 1.0, a); +} + + +/** + * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @return The correct value. + */ +static qreal easeInOutBounce(qreal t, qreal a) +{ + if (t < 0.5) return easeInBounce (2*t, a)/2; + else return (t == 1.0) ? 1.0 : easeOutBounce (2*t - 1, a)/2 + 0.5; +} + +/** + * Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @param a Amplitude. + * @return The correct value. + */ +static qreal easeOutInBounce(qreal t, qreal a) +{ + if (t < 0.5) return easeOutBounce_helper(t*2, 0.5, a); + return 1.0 - easeOutBounce_helper (2.0-2*t, 0.5, a); +} + +static inline qreal qt_sinProgress(qreal value) +{ + return qSin((value * M_PI) - M_PI_2) / 2 + qreal(0.5); +} + +static inline qreal qt_smoothBeginEndMixFactor(qreal value) +{ + return qMin(qMax(1 - value * 2 + qreal(0.3), qreal(0.0)), qreal(1.0)); +} + +// SmoothBegin blends Smooth and Linear Interpolation. +// Progress 0 - 0.3 : Smooth only +// Progress 0.3 - ~ 0.5 : Mix of Smooth and Linear +// Progress ~ 0.5 - 1 : Linear only + +/** + * Easing function that starts growing slowly, then increases in speed. At the end of the curve the speed will be constant. + */ +static qreal easeInCurve(qreal t) +{ + const qreal sinProgress = qt_sinProgress(t); + const qreal mix = qt_smoothBeginEndMixFactor(t); + return sinProgress * mix + t * (1 - mix); +} + +/** + * Easing function that starts growing steadily, then ends slowly. The speed will be constant at the beginning of the curve. + */ +static qreal easeOutCurve(qreal t) +{ + const qreal sinProgress = qt_sinProgress(t); + const qreal mix = qt_smoothBeginEndMixFactor(1 - t); + return sinProgress * mix + t * (1 - mix); +} + +/** + * Easing function where the value grows sinusoidally. Note that the calculated end value will be 0 rather than 1. + */ +static qreal easeSineCurve(qreal t) +{ + return (qSin(((t * M_PI * 2)) - M_PI_2) + 1) / 2; +} + +/** + * Easing function where the value grows cosinusoidally. Note that the calculated start value will be 0.5 and the end value will be 0.5 + * contrary to the usual 0 to 1 easing curve. + */ +static qreal easeCosineCurve(qreal t) +{ + return (qCos(((t * M_PI * 2)) - M_PI_2) + 1) / 2; +} + diff --git a/src/3rdparty/easing/legal.qdoc b/src/3rdparty/easing/legal.qdoc new file mode 100644 index 0000000..25f67e1 --- /dev/null +++ b/src/3rdparty/easing/legal.qdoc @@ -0,0 +1,35 @@ +/*! +\page legal-easing.html +\title Easing Equations by Robert Penner +\ingroup animation + +\legalese +\code +Copyright (c) 2001 Robert Penner +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the author nor the names of contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endcode +\endlegalese +*/ diff --git a/src/3rdparty/phonon/phonon/phonon_export.h b/src/3rdparty/phonon/phonon/phonon_export.h index e579f67..5f93ea0 100644 --- a/src/3rdparty/phonon/phonon/phonon_export.h +++ b/src/3rdparty/phonon/phonon/phonon_export.h @@ -32,7 +32,11 @@ # define PHONON_EXPORT Q_DECL_IMPORT # endif # else /* UNIX */ -# define PHONON_EXPORT Q_DECL_EXPORT +# ifdef MAKE_PHONON_LIB /* We are building this library */ +# define PHONON_EXPORT Q_DECL_EXPORT +# else /* We are using this library */ +# define PHONON_EXPORT Q_DECL_IMPORT +# endif # endif #endif 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/activeqt/container/qaxwidget.cpp b/src/activeqt/container/qaxwidget.cpp index 4e8473f..7f20a5c 100644 --- a/src/activeqt/container/qaxwidget.cpp +++ b/src/activeqt/container/qaxwidget.cpp @@ -383,7 +383,7 @@ public: bool eventTranslated : 1; private: -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) struct OleMenuItem { OleMenuItem(HMENU hm = 0, int ID = 0, QMenu *menu = 0) : hMenu(hm), id(ID), subMenu(menu) @@ -408,7 +408,7 @@ private: bool canHostDocument : 1; DWORD m_dwOleObject; -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) HWND m_menuOwner; #endif CONTROLINFO control_info; @@ -417,7 +417,7 @@ private: unsigned long ref; QAxWidget *widget; QAxHostWidget *host; -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) QPointer<QMenuBar> menuBar; QMap<QAction*,OleMenuItem> menuItemMap; #endif @@ -467,7 +467,7 @@ static QAbstractEventDispatcher::EventFilter previous_filter = 0; #if QT_VERSION >= 0x050000 #error "Fix QAbstractEventDispatcher::setEventFilter" #endif -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) static int filter_ref = 0; #else static const char *qaxatom = "QAxContainer4_Atom"; @@ -551,7 +551,7 @@ QAxClientSite::QAxClientSite(QAxWidget *c) canHostDocument = false; m_dwOleObject = 0; -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) m_menuOwner = 0; menuBar = 0; #endif @@ -1132,7 +1132,7 @@ HRESULT WINAPI QAxClientSite::OnPosRectChange(LPCRECT /*lprcPosRect*/) } //**** IOleInPlaceFrame -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) HRESULT WINAPI QAxClientSite::InsertMenus(HMENU /*hmenuShared*/, LPOLEMENUGROUPWIDTHS /*lpMenuWidths*/) { return E_NOTIMPL; diff --git a/src/corelib/animation/animation.pri b/src/corelib/animation/animation.pri new file mode 100644 index 0000000..cb7850c --- /dev/null +++ b/src/corelib/animation/animation.pri @@ -0,0 +1,25 @@ +# Qt core animation module + +HEADERS += \ + animation/qabstractanimation.h \ + animation/qabstractanimation_p.h \ + animation/qvariantanimation.h \ + animation/qvariantanimation_p.h \ + animation/qpropertyanimation.h \ + animation/qpropertyanimation_p.h \ + animation/qanimationgroup.h \ + animation/qanimationgroup_p.h \ + animation/qsequentialanimationgroup.h \ + animation/qsequentialanimationgroup_p.h \ + animation/qparallelanimationgroup.h \ + animation/qparallelanimationgroup_p.h \ + animation/qpauseanimation.h + +SOURCES += \ + animation/qabstractanimation.cpp \ + animation/qvariantanimation.cpp \ + animation/qpropertyanimation.cpp \ + animation/qanimationgroup.cpp \ + animation/qsequentialanimationgroup.cpp \ + animation/qparallelanimationgroup.cpp \ + animation/qpauseanimation.cpp diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp new file mode 100644 index 0000000..16307e6 --- /dev/null +++ b/src/corelib/animation/qabstractanimation.cpp @@ -0,0 +1,733 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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$ +** +****************************************************************************/ + +/*! + \class QAbstractAnimation + \ingroup animation + \brief The QAbstractAnimation class is the base of all animations. + \since 4.6 + + The class defines the functions for the functionality shared by + all animations. By inheriting this class, you can create custom + animations that plug into the rest of the animation framework. + + The progress of an animation is given by its current time + (currentTime()), which is measured in milliseconds from the start + of the animation (0) to its end (duration()). The value is updated + automatically while the animation is running. It can also be set + directly with setCurrentTime(). + + At any point an animation is in one of three states: + \l{QAbstractAnimation::}{Running}, + \l{QAbstractAnimation::}{Stopped}, or + \l{QAbstractAnimation::}{Paused}--as defined by the + \l{QAbstractAnimation::}{State} enum. The current state can be + changed by calling start(), stop(), pause(), or resume(). An + animation will always reset its \l{currentTime()}{current time} + when it is started. If paused, it will continue with the same + current time when resumed. When an animation is stopped, it cannot + be resumed, but will keep its current time (until started again). + QAbstractAnimation will emit stateChanged() whenever its state + changes. + + An animation can loop any number of times by setting the loopCount + property. When an animation's current time reaches its duration(), + it will reset the current time and keep running. A loop count of 1 + (the default value) means that the animation will run one time. + Note that a duration of -1 means that the animation will run until + stopped; the current time will increase indefinitely. When the + current time equals duration() and the animation is in its + final loop, the \l{QAbstractAnimation::}{Stopped} state is + entered, and the finished() signal is emitted. + + QAbstractAnimation provides pure virtual functions used by + subclasses to track the progress of the animation: duration() and + updateCurrentTime(). The duration() function lets you report a + duration for the animation (as discussed above). The current time + is delivered by the animation framework through calls to + updateCurrentTime(). By reimplementing this function, you can + track the animation progress. Note that neither the interval + between calls nor the number of calls to this function are + defined; though, it will normally be 60 updates per second. + + By reimplementing updateState(), you can track the animation's + state changes, which is particularly useful for animations that + are not driven by time. + + \sa QVariantAnimation, QPropertyAnimation, QAnimationGroup, {The Animation Framework} +*/ + +/*! + \enum QAbstractAnimation::DeletionPolicy + + \value KeepWhenStopped The animation will not be deleted when stopped. + \value DeleteWhenStopped The animation will be automatically deleted when + stopped. +*/ + +/*! + \fn QAbstractAnimation::finished() + + QAbstractAnimation emits this signal after the animation has stopped and + has reached the end. + + This signal is emitted after stateChanged(). + + \sa stateChanged() +*/ + +/*! + \fn QAbstractAnimation::stateChanged(QAbstractAnimation::State oldState, QAbstractAnimation::State newState) + + QAbstractAnimation emits this signal whenever the state of the animation has + changed from \a oldState to \a newState. This signal is emitted after the virtual + updateState() function is called. + + \sa updateState() +*/ + +/*! + \fn QAbstractAnimation::currentLoopChanged(int currentLoop) + + QAbstractAnimation emits this signal whenever the current loop + changes. \a currentLoop is the current loop. + + \sa currentLoop(), loopCount() +*/ + +/*! + \fn QAbstractAnimation::directionChanged(QAbstractAnimation::Direction newDirection); + + QAbstractAnimation emits this signal whenever the direction has been + changed. \a newDirection is the new direction. + + \sa direction +*/ + +#ifndef QT_NO_ANIMATION + +#include "qabstractanimation.h" +#include "qanimationgroup.h" +#include <QtCore/qdebug.h> + +#include "qabstractanimation_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qthreadstorage.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qpointer.h> + +#define DEFAULT_TIMER_INTERVAL 16 + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer); + +QUnifiedTimer::QUnifiedTimer() : QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), consistentTiming(false) +{ +} + +QUnifiedTimer *QUnifiedTimer::instance() +{ + QUnifiedTimer *inst; + if (!unifiedTimer()->hasLocalData()) { + inst = new QUnifiedTimer; + unifiedTimer()->setLocalData(inst); + } else { + inst = unifiedTimer()->localData(); + } + return inst; +} + +void QUnifiedTimer::updateRecentlyStartedAnimations() +{ + if (animationsToStart.isEmpty()) + return; + + animations += animationsToStart; + updateTimer(); //we make sure we start the timer there + + animationsToStart.clear(); +} + +void QUnifiedTimer::timerEvent(QTimerEvent *event) +{ + //this is simply the time we last received a tick + const int oldLastTick = lastTick; + if (time.isValid()) + lastTick = consistentTiming ? oldLastTick + timingInterval : time.elapsed(); + + //we transfer the waiting animations into the "really running" state + updateRecentlyStartedAnimations(); + + if (event->timerId() == startStopAnimationTimer.timerId()) { + startStopAnimationTimer.stop(); + if (animations.isEmpty()) { + animationTimer.stop(); + time = QTime(); + } else { + animationTimer.start(timingInterval, this); + lastTick = 0; + time.start(); + } + } else if (event->timerId() == animationTimer.timerId()) { + const int delta = lastTick - oldLastTick; + for (int i = 0; i < animations.count(); ++i) { + QAbstractAnimation *animation = animations.at(i); + int elapsed = QAbstractAnimationPrivate::get(animation)->totalCurrentTime + + (animation->direction() == QAbstractAnimation::Forward ? delta : -delta); + animation->setCurrentTime(elapsed); + } + } +} + +void QUnifiedTimer::updateTimer() +{ + //we delay the call to start and stop for the animation timer so that if you + //stop and start animations in batch you don't stop/start the timer too often. + if (!startStopAnimationTimer.isActive()) + startStopAnimationTimer.start(0, this); // we delay the actual start of the animation +} + +void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation) +{ + if (animations.contains(animation) ||animationsToStart.contains(animation)) + return; + animationsToStart << animation; + updateTimer(); +} + +void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) +{ + animations.removeAll(animation); + animationsToStart.removeAll(animation); + updateTimer(); +} + + +void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) +{ + Q_Q(QAbstractAnimation); + if (state == newState) + return; + + QAbstractAnimation::State oldState = state; + int oldCurrentTime = currentTime; + int oldCurrentLoop = currentLoop; + QAbstractAnimation::Direction oldDirection = direction; + + state = newState; + + QPointer<QAbstractAnimation> guard(q); + + guard->updateState(oldState, newState); + + //this is to be safe if updateState changes the state + if (state == oldState) + return; + + // Notify state change + if (guard) + emit guard->stateChanged(oldState, newState); + + // Enter running state. + switch (state) + { + case QAbstractAnimation::Paused: + case QAbstractAnimation::Running: + { + // Rewind + if (oldState == QAbstractAnimation::Stopped) { + if (guard) { + if (direction == QAbstractAnimation::Forward) + q->setCurrentTime(0); + else + q->setCurrentTime(loopCount == -1 ? q->duration() : q->totalDuration()); + } + + // Check if the setCurrentTime() function called stop(). + // This can happen for a 0-duration animation + if (state == QAbstractAnimation::Stopped) + return; + } + + // Register timer if our parent is not running. + if (state == QAbstractAnimation::Running && guard) { + if (!group || group->state() == QAbstractAnimation::Stopped) { + QUnifiedTimer::instance()->registerAnimation(q); + } + } else { + //new state is paused + QUnifiedTimer::instance()->unregisterAnimation(q); + } + } + break; + case QAbstractAnimation::Stopped: + // Leave running state. + int dura = q->duration(); + if (deleteWhenStopped && guard) + q->deleteLater(); + + QUnifiedTimer::instance()->unregisterAnimation(q); + + if (dura == -1 || loopCount < 0 + || (oldDirection == QAbstractAnimation::Forward && (oldCurrentTime * (oldCurrentLoop + 1)) == (dura * loopCount)) + || (oldDirection == QAbstractAnimation::Backward && oldCurrentTime == 0)) { + if (guard) + emit q->finished(); + } + break; + } + +} + +/*! + Constructs the QAbstractAnimation base class, and passes \a parent to + QObject's constructor. + + \sa QVariantAnimation, QAnimationGroup +*/ +QAbstractAnimation::QAbstractAnimation(QObject *parent) + : QObject(*new QAbstractAnimationPrivate, 0) +{ + // Allow auto-add on reparent + setParent(parent); +} + +/*! + \internal +*/ +QAbstractAnimation::QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent) + : QObject(dd, 0) +{ + // Allow auto-add on reparent + setParent(parent); +} + +/*! + Stops the animation if it's running, then destroys the + QAbstractAnimation. If the animation is part of a QAnimationGroup, it is + automatically removed before it's destroyed. +*/ +QAbstractAnimation::~QAbstractAnimation() +{ + Q_D(QAbstractAnimation); + //we can't call stop here. Otherwise we get pure virtual calls + if (d->state != Stopped) { + QAbstractAnimation::State oldState = d->state; + d->state = Stopped; + emit stateChanged(oldState, d->state); + QUnifiedTimer::instance()->unregisterAnimation(this); + } +} + +/*! + \property QAbstractAnimation::state + \brief state of the animation. + + This property describes the current state of the animation. When the + animation state changes, QAbstractAnimation emits the stateChanged() + signal. +*/ +QAbstractAnimation::State QAbstractAnimation::state() const +{ + Q_D(const QAbstractAnimation); + return d->state; +} + +/*! + If this animation is part of a QAnimationGroup, this function returns a + pointer to the group; otherwise, it returns 0. + + \sa QAnimationGroup::addAnimation() +*/ +QAnimationGroup *QAbstractAnimation::group() const +{ + Q_D(const QAbstractAnimation); + return d->group; +} + +/*! + \enum QAbstractAnimation::State + + This enum describes the state of the animation. + + \value Stopped The animation is not running. This is the initial state + of QAbstractAnimation, and the state QAbstractAnimation reenters when finished. The current + time remain unchanged until either setCurrentTime() is + called, or the animation is started by calling start(). + + \value Paused The animation is paused (i.e., temporarily + suspended). Calling resume() will resume animation activity. + + \value Running The animation is running. While control is in the event + loop, QAbstractAnimation will update its current time at regular intervals, + calling updateCurrentTime() when appropriate. + + \sa state(), stateChanged() +*/ + +/*! + \enum QAbstractAnimation::Direction + + This enum describes the direction of the animation when in \l Running state. + + \value Forward The current time of the animation increases with time (i.e., + moves from 0 and towards the end / duration). + + \value Backward The current time of the animation decreases with time (i.e., + moves from the end / duration and towards 0). + + \sa direction +*/ + +/*! + \property QAbstractAnimation::direction + \brief the direction of the animation when it is in \l Running + state. + + This direction indicates whether the time moves from 0 towards the + animation duration, or from the value of the duration and towards 0 after + start() has been called. + + By default, this property is set to \l Forward. +*/ +QAbstractAnimation::Direction QAbstractAnimation::direction() const +{ + Q_D(const QAbstractAnimation); + return d->direction; +} +void QAbstractAnimation::setDirection(Direction direction) +{ + Q_D(QAbstractAnimation); + if (d->direction == direction) + return; + + d->direction = direction; + if (state() == Stopped) { + if (direction == Backward) { + d->currentTime = duration(); + d->currentLoop = d->loopCount - 1; + } else { + d->currentTime = 0; + d->currentLoop = 0; + } + } + updateDirection(direction); + emit directionChanged(direction); +} + +/*! + \property QAbstractAnimation::duration + \brief the duration of the animation. + + If the duration is -1, it means that the duration is undefined. + In this case, loopCount is ignored. +*/ + +/*! + \property QAbstractAnimation::loopCount + \brief the loop count of the animation + + This property describes the loop count of the animation as an integer. + By default this value is 1, indicating that the animation + should run once only, and then stop. By changing it you can let the + animation loop several times. With a value of 0, the animation will not + run at all, and with a value of -1, the animation will loop forever + until stopped. + It is not supported to have loop on an animation that has an undefined + duration. It will only run once. +*/ +int QAbstractAnimation::loopCount() const +{ + Q_D(const QAbstractAnimation); + return d->loopCount; +} +void QAbstractAnimation::setLoopCount(int loopCount) +{ + Q_D(QAbstractAnimation); + d->loopCount = loopCount; +} + +/*! + \property QAbstractAnimation::currentLoop + \brief the current loop of the animation + + This property describes the current loop of the animation. By default, + the animation's loop count is 1, and so the current loop will + always be 0. If the loop count is 2 and the animation runs past its + duration, it will automatically rewind and restart at current time 0, and + current loop 1, and so on. + + When the current loop changes, QAbstractAnimation emits the + currentLoopChanged() signal. +*/ +int QAbstractAnimation::currentLoop() const +{ + Q_D(const QAbstractAnimation); + return d->currentLoop; +} + +/*! + \fn virtual int QAbstractAnimation::duration() const = 0 + + This pure virtual function returns the duration of the animation, and + defines for how long QAbstractAnimation should update the current + time. This duration is local, and does not include the loop count. + + A return value of -1 indicates that the animation has no defined duration; + the animation should run forever until stopped. This is useful for + animations that are not time driven, or where you cannot easily predict + its duration (e.g., event driven audio playback in a game). + + If the animation is a parallel QAnimationGroup, the duration will be the longest + duration of all its animations. If the animation is a sequential QAnimationGroup, + the duration will be the sum of the duration of all its animations. + \sa loopCount +*/ + +/*! + Returns the total and effective duration of the animation, including the + loop count. + + \sa duration(), currentTime +*/ +int QAbstractAnimation::totalDuration() const +{ + Q_D(const QAbstractAnimation); + if (d->loopCount < 0) + return -1; + int dura = duration(); + if (dura == -1) + return -1; + return dura * d->loopCount; +} + +/*! + \property QAbstractAnimation::currentTime + \brief the current time and progress of the animation + + This property describes the animation's current time. You can change the + current time by calling setCurrentTime, or you can call start() and let + the animation run, setting the current time automatically as the animation + progresses. + + The animation's current time starts at 0, and ends at duration(). If the + animation's loopCount is larger than 1, the current time will rewind and + start at 0 again for the consecutive loops. If the animation has a pause. + currentTime will also include the duration of the pause. + + \sa loopCount + */ +int QAbstractAnimation::currentTime() const +{ + Q_D(const QAbstractAnimation); + return d->currentTime; +} +void QAbstractAnimation::setCurrentTime(int msecs) +{ + Q_D(QAbstractAnimation); + msecs = qMax(msecs, 0); + + // Calculate new time and loop. + int dura = duration(); + int totalDura = (d->loopCount < 0 || dura == -1) ? -1 : dura * d->loopCount; + if (totalDura != -1) + msecs = qMin(totalDura, msecs); + d->totalCurrentTime = msecs; + + // Update new values. + int oldLoop = d->currentLoop; + d->currentLoop = ((dura <= 0) ? 0 : (msecs / dura)); + if (d->currentLoop == d->loopCount) { + //we're at the end + d->currentTime = qMax(0, dura); + d->currentLoop = qMax(0, d->loopCount - 1); + } else { + if (d->direction == Forward) { + d->currentTime = (dura <= 0) ? msecs : (msecs % dura); + } else { + d->currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1; + if (d->currentTime == dura) + --d->currentLoop; + } + } + + updateCurrentTime(msecs); + if (d->currentLoop != oldLoop) + emit currentLoopChanged(d->currentLoop); + + // All animations are responsible for stopping the animation when their + // own end state is reached; in this case the animation is time driven, + // and has reached the end. + if ((d->direction == Forward && d->totalCurrentTime == totalDura) + || (d->direction == Backward && d->totalCurrentTime == 0)) { + stop(); + } +} + +/*! + Starts the animation. The \a policy argument says whether or not the + animation should be deleted when it's done. When the animation starts, the + stateChanged() signal is emitted, and state() returns Running. When control + reaches the event loop, the animation will run by itself, periodically + calling updateCurrentTime() as the animation progresses. + + If the animation is currently stopped or has already reached the end, + calling start() will rewind the animation and start again from the beginning. + When the animation reaches the end, the animation will either stop, or + if the loop level is more than 1, it will rewind and continue from the beginning. + + If the animation is already running, this function does nothing. + + \sa stop(), state() +*/ +void QAbstractAnimation::start(DeletionPolicy policy) +{ + Q_D(QAbstractAnimation); + if (d->state == Running) + return; + d->setState(Running); + d->deleteWhenStopped = policy; +} + +/*! + Stops the animation. When the animation is stopped, it emits the stateChanged() + signal, and state() returns Stopped. The current time is not changed. + + If the animation stops by itself after reaching the end (i.e., + currentTime() == duration() and currentLoop() > loopCount() - 1), the + finished() signal is emitted. + + \sa start(), state() + */ +void QAbstractAnimation::stop() +{ + Q_D(QAbstractAnimation); + + d->setState(Stopped); +} + +/*! + Pauses the animation. When the animation is paused, state() returns Paused. + The currenttime will remain unchanged until resume() or start() is called. + If you want to continue from the current time, call resume(). + + + \sa start(), state(), resume() + */ +void QAbstractAnimation::pause() +{ + Q_D(QAbstractAnimation); + if (d->state == Stopped) { + qWarning("QAbstractAnimation::pause: Cannot pause a stopped animation"); + return; + } + + d->setState(Paused); +} + +/*! + Resumes the animation after it was paused. When the animation is resumed, + it emits the resumed() and stateChanged() signals. The currenttime is not + changed. + + \sa start(), pause(), state() + */ +void QAbstractAnimation::resume() +{ + Q_D(QAbstractAnimation); + if (d->state != Paused) { + qWarning("QAbstractAnimation::resume: " + "Cannot resume an animation that is not paused"); + return; + } + + d->setState(Running); +} + +/*! + \reimp +*/ +bool QAbstractAnimation::event(QEvent *event) +{ + return QObject::event(event); +} + +/*! + \fn virtual void QAbstractAnimation::updateCurrentTime(int msecs) = 0; + + This pure virtual function is called every time the animation's current + time changes. The \a msecs argument is the current time. + + \sa updateState() +*/ + +/*! + This virtual function is called by QAbstractAnimation when the state + of the animation is changed from \a oldState to \a newState. + + \sa start(), stop(), pause(), resume() +*/ +void QAbstractAnimation::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_UNUSED(oldState); + Q_UNUSED(newState); +} + +/*! + This virtual function is called by QAbstractAnimation when the direction + of the animation is changed. The \a direction argument is the new direction. + + \sa setDirection(), direction() +*/ +void QAbstractAnimation::updateDirection(QAbstractAnimation::Direction direction) +{ + Q_UNUSED(direction); +} + + +QT_END_NAMESPACE + +#include "moc_qabstractanimation.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qabstractanimation.h b/src/corelib/animation/qabstractanimation.h new file mode 100644 index 0000000..d6d50dc --- /dev/null +++ b/src/corelib/animation/qabstractanimation.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QABSTRACTANIMATION_H +#define QABSTRACTANIMATION_H + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QAnimationGroup; +class QSequentialAnimationGroup; + +class QAbstractAnimationPrivate; +class Q_CORE_EXPORT QAbstractAnimation : public QObject +{ + Q_OBJECT + Q_PROPERTY(State state READ state NOTIFY stateChanged) + Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount) + Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime) + Q_PROPERTY(int currentLoop READ currentLoop NOTIFY currentLoopChanged) + Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged) + Q_PROPERTY(int duration READ duration) + +public: + enum Direction { + Forward, + Backward + }; + + enum State { + Stopped, + Paused, + Running + }; + + enum DeletionPolicy { + KeepWhenStopped = 0, + DeleteWhenStopped + }; + + QAbstractAnimation(QObject *parent = 0); + virtual ~QAbstractAnimation(); + + State state() const; + + QAnimationGroup *group() const; + + Direction direction() const; + void setDirection(Direction direction); + + int loopCount() const; + void setLoopCount(int loopCount); + int currentLoop() const; + + virtual int duration() const = 0; + int totalDuration() const; + + int currentTime() const; + +Q_SIGNALS: + void finished(); + void stateChanged(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + void currentLoopChanged(int currentLoop); + void directionChanged(QAbstractAnimation::Direction); + +public Q_SLOTS: + void start(QAbstractAnimation::DeletionPolicy policy = KeepWhenStopped); + void pause(); + void resume(); + void stop(); + void setCurrentTime(int msecs); + +protected: + QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent = 0); + bool event(QEvent *event); + + virtual void updateCurrentTime(int msecs) = 0; + virtual void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + virtual void updateDirection(QAbstractAnimation::Direction direction); + +private: + Q_DISABLE_COPY(QAbstractAnimation) + Q_DECLARE_PRIVATE(QAbstractAnimation) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QABSTRACTANIMATION_H diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h new file mode 100644 index 0000000..7a4bfcf --- /dev/null +++ b/src/corelib/animation/qabstractanimation_p.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QABSTRACTANIMATION_P_H +#define QABSTRACTANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qbasictimer.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qtimer.h> +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QAnimationGroup; +class QAbstractAnimation; +class QAbstractAnimationPrivate : public QObjectPrivate +{ +public: + QAbstractAnimationPrivate() + : state(QAbstractAnimation::Stopped), + direction(QAbstractAnimation::Forward), + deleteWhenStopped(false), + totalCurrentTime(0), + currentTime(0), + loopCount(1), + currentLoop(0), + group(0) + { + } + + virtual ~QAbstractAnimationPrivate() {} + + static QAbstractAnimationPrivate *get(QAbstractAnimation *q) + { + return q->d_func(); + } + + QAbstractAnimation::State state; + QAbstractAnimation::Direction direction; + bool deleteWhenStopped; + void setState(QAbstractAnimation::State state); + + int totalCurrentTime; + int currentTime; + int loopCount; + int currentLoop; + + QAnimationGroup *group; + +private: + Q_DECLARE_PUBLIC(QAbstractAnimation) +}; + + +class QUnifiedTimer : public QObject +{ +private: + QUnifiedTimer(); + +public: + //XXX this is needed by dui + static Q_CORE_EXPORT QUnifiedTimer *instance(); + + void registerAnimation(QAbstractAnimation *animation); + void unregisterAnimation(QAbstractAnimation *animation); + + //defines the timing interval. Default is DEFAULT_TIMER_INTERVAL + void setTimingInterval(int interval) + { + timingInterval = interval; + if (animationTimer.isActive()) { + //we changed the timing interval + animationTimer.start(timingInterval, this); + } + } + + /* + this allows to have a consistent timer interval at each tick from the timer + not taking the real time that passed into account. + */ + void setConsistentTiming(bool consistent) { consistentTiming = consistent; } + + int elapsedTime() const { return lastTick; } + +protected: + void timerEvent(QTimerEvent *); + void updateTimer(); + +private: + void updateRecentlyStartedAnimations(); + + QBasicTimer animationTimer, startStopAnimationTimer; + QTime time; + int lastTick; + int timingInterval; + bool consistentTiming; + QList<QAbstractAnimation*> animations, animationsToStart; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/corelib/animation/qanimationgroup.cpp b/src/corelib/animation/qanimationgroup.cpp new file mode 100644 index 0000000..839b522 --- /dev/null +++ b/src/corelib/animation/qanimationgroup.cpp @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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$ +** +****************************************************************************/ + +/*! + \class QAnimationGroup + \brief The QAnimationGroup class is an abstract base class for groups of animations. + \since 4.6 + \ingroup animation + + An animation group is a container for animations (subclasses of + QAbstractAnimation). A group is usually responsible for managing + the \l{QAbstractAnimation::State}{state} of its animations, i.e., + it decides when to start, stop, resume, and pause them. Currently, + Qt provides two such groups: QParallelAnimationGroup and + QSequentialAnimationGroup. Look up their class descriptions for + details. + + Since QAnimationGroup inherits from QAbstractAnimation, you can + combine groups, and easily construct complex animation graphs. + You can query QAbstractAnimation for the group it belongs to + (using the \l{QAbstractAnimation::}{group()} function). + + To start a top-level animation group, you simply use the + \l{QAbstractAnimation::}{start()} function from + QAbstractAnimation. By a top-level animation group, we think of a + group that itself is not contained within another group. Starting + sub groups directly is not supported, and may lead to unexpected + behavior. + + \omit OK, we'll put in a snippet on this here \endomit + + QAnimationGroup provides methods for adding and retrieving + animations. Besides that, you can remove animations by calling + remove(), and clear the animation group by calling + clearAnimations(). You may keep track of changes in the group's + animations by listening to QEvent::ChildAdded and + QEvent::ChildRemoved events. + + \omit OK, let's find a snippet here as well. \endomit + + QAnimationGroup takes ownership of the animations it manages, and + ensures that they are deleted when the animation group is deleted. + + You can also use a \l{The State Machine Framework}{state machine} + to create complex animations. The framework provides a special + state, QAnimationState, that plays an animation upon entry and + transitions to a new state when the animation has finished + playing. This technique can also be combined with using animation + groups. + + \sa QAbstractAnimation, QVariantAnimation, {The Animation Framework} +*/ + +#ifndef QT_NO_ANIMATION + +#include "qanimationgroup.h" +#include <QtCore/qdebug.h> +#include <QtCore/qcoreevent.h> +#include "qanimationgroup_p.h" + +QT_BEGIN_NAMESPACE + + +/*! + Constructs a QAnimationGroup. + \a parent is passed to QObject's constructor. +*/ +QAnimationGroup::QAnimationGroup(QObject *parent) + : QAbstractAnimation(*new QAnimationGroupPrivate, parent) +{ +} + +/*! + \internal +*/ +QAnimationGroup::QAnimationGroup(QAnimationGroupPrivate &dd, QObject *parent) + : QAbstractAnimation(dd, parent) +{ +} + +/*! + Destroys the animation group. It will also destroy all its animations. +*/ +QAnimationGroup::~QAnimationGroup() +{ +} + +/*! + Returns a pointer to the animation at \a index in this group. This + function is useful when you need access to a particular animation. \a + index is between 0 and animationCount() - 1. + + \sa animationCount(), indexOfAnimation() +*/ +QAbstractAnimation *QAnimationGroup::animationAt(int index) const +{ + Q_D(const QAnimationGroup); + + if (index < 0 || index >= d->animations.size()) { + qWarning("QAnimationGroup::animationAt: index is out of bounds"); + return 0; + } + + return d->animations.at(index); +} + + +/*! + Returns the number of animations managed by this group. + + \sa indexOfAnimation(), addAnimation(), animationAt() +*/ +int QAnimationGroup::animationCount() const +{ + Q_D(const QAnimationGroup); + return d->animations.size(); +} + +/*! + Returns the index of \a animation. The returned index can be passed + to the other functions that take an index as an argument. + + \sa insertAnimationAt(), animationAt(), takeAnimationAt() +*/ +int QAnimationGroup::indexOfAnimation(QAbstractAnimation *animation) const +{ + Q_D(const QAnimationGroup); + return d->animations.indexOf(animation); +} + +/*! + Adds \a animation to this group. This will call insertAnimationAt with + index equals to animationCount(). + + \note The group takes ownership of the animation. + + \sa removeAnimation() +*/ +void QAnimationGroup::addAnimation(QAbstractAnimation *animation) +{ + Q_D(QAnimationGroup); + insertAnimationAt(d->animations.count(), animation); +} + +/*! + Inserts \a animation into this animation group at \a index. + If \a index is 0 the animation is inserted at the beginning. + If \a index is animationCount(), the animation is inserted at the end. + + \note The group takes ownership of the animation. + + \sa takeAnimationAt(), addAnimation(), indexOfAnimation(), removeAnimation() +*/ +void QAnimationGroup::insertAnimationAt(int index, QAbstractAnimation *animation) +{ + Q_D(QAnimationGroup); + + if (index < 0 || index > d->animations.size()) { + qWarning("QAnimationGroup::insertAnimationAt: index is out of bounds"); + return; + } + + if (QAnimationGroup *oldGroup = animation->group()) + oldGroup->removeAnimation(animation); + + d->animations.insert(index, animation); + QAbstractAnimationPrivate::get(animation)->group = this; + // this will make sure that ChildAdded event is sent to 'this' + animation->setParent(this); + d->animationInsertedAt(index); +} + +/*! + Removes \a animation from this group. The ownership of \a animation is + transferred to the caller. + + \sa takeAnimationAt(), insertAnimationAt(), addAnimation() +*/ +void QAnimationGroup::removeAnimation(QAbstractAnimation *animation) +{ + Q_D(QAnimationGroup); + + if (!animation) { + qWarning("QAnimationGroup::remove: cannot remove null animation"); + return; + } + int index = d->animations.indexOf(animation); + if (index == -1) { + qWarning("QAnimationGroup::remove: animation is not part of this group"); + return; + } + + takeAnimationAt(index); +} + +/*! + Returns the animation at \a index and removes it from the animation group. + + \note The ownership of the animation is transferred to the caller. + + \sa removeAnimation(), addAnimation(), insertAnimationAt(), indexOfAnimation() +*/ +QAbstractAnimation *QAnimationGroup::takeAnimationAt(int index) +{ + Q_D(QAnimationGroup); + if (index < 0 || index >= d->animations.size()) { + qWarning("QAnimationGroup::takeAnimationAt: no animation at index %d", index); + return 0; + } + QAbstractAnimation *animation = d->animations.at(index); + QAbstractAnimationPrivate::get(animation)->group = 0; + // ### removing from list before doing setParent to avoid inifinite recursion + // in ChildRemoved event + d->animations.removeAt(index); + animation->setParent(0); + d->animationRemovedAt(index); + return animation; +} + +/*! + Removes and deletes all animations in this animation group, and resets the current + time to 0. + + \sa addAnimation(), removeAnimation() +*/ +void QAnimationGroup::clearAnimations() +{ + Q_D(QAnimationGroup); + qDeleteAll(d->animations); +} + +/*! + \reimp +*/ +bool QAnimationGroup::event(QEvent *event) +{ + Q_D(QAnimationGroup); + if (event->type() == QEvent::ChildAdded) { + QChildEvent *childEvent = static_cast<QChildEvent *>(event); + if (QAbstractAnimation *a = qobject_cast<QAbstractAnimation *>(childEvent->child())) { + if (a->group() != this) + addAnimation(a); + } + } else if (event->type() == QEvent::ChildRemoved) { + QChildEvent *childEvent = static_cast<QChildEvent *>(event); + QAbstractAnimation *a = static_cast<QAbstractAnimation *>(childEvent->child()); + // You can only rely on the child being a QObject because in the QEvent::ChildRemoved + // case it might be called from the destructor. + int index = d->animations.indexOf(a); + if (index != -1) + takeAnimationAt(index); + } + return QAbstractAnimation::event(event); +} + + +void QAnimationGroupPrivate::animationRemovedAt(int index) +{ + Q_Q(QAnimationGroup); + Q_UNUSED(index); + if (animations.isEmpty()) { + currentTime = 0; + q->stop(); + } +} + +QT_END_NAMESPACE + +#include "moc_qanimationgroup.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qanimationgroup.h b/src/corelib/animation/qanimationgroup.h new file mode 100644 index 0000000..263bc38 --- /dev/null +++ b/src/corelib/animation/qanimationgroup.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QANIMATIONGROUP_H +#define QANIMATIONGROUP_H + +#include <QtCore/qabstractanimation.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QAnimationGroupPrivate; +class Q_CORE_EXPORT QAnimationGroup : public QAbstractAnimation +{ + Q_OBJECT + +public: + QAnimationGroup(QObject *parent = 0); + ~QAnimationGroup(); + + QAbstractAnimation *animationAt(int index) const; + int animationCount() const; + int indexOfAnimation(QAbstractAnimation *animation) const; + void addAnimation(QAbstractAnimation *animation); + void insertAnimationAt(int index, QAbstractAnimation *animation); + void removeAnimation(QAbstractAnimation *animation); + QAbstractAnimation *takeAnimationAt(int index); + void clearAnimations(); + +protected: + QAnimationGroup(QAnimationGroupPrivate &dd, QObject *parent); + bool event(QEvent *event); + +private: + Q_DISABLE_COPY(QAnimationGroup) + Q_DECLARE_PRIVATE(QAnimationGroup) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QANIMATIONGROUP_H diff --git a/src/gui/painting/qwindowsurface_d3d_p.h b/src/corelib/animation/qanimationgroup_p.h index 9cdfe29..a7bd0fa 100644 --- a/src/gui/painting/qwindowsurface_d3d_p.h +++ b/src/corelib/animation/qanimationgroup_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 QtGui module of the Qt Toolkit. +** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,46 +39,41 @@ ** ****************************************************************************/ -#ifndef QWINDOWSURFACE_D3D_P_H -#define QWINDOWSURFACE_D3D_P_H +#ifndef QANIMATIONGROUP_P_H +#define QANIMATIONGROUP_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from -// version to version without notice, or even be removed. +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // -#include <qglobal.h> -#include "private/qwindowsurface_p.h" +#include "qanimationgroup.h" -QT_BEGIN_NAMESPACE +#include <QtCore/qlist.h> + +#include "qabstractanimation_p.h" -class QPaintDevice; -class QPoint; -class QRegion; -class QWidget; -struct QD3DWindowSurfacePrivate; +QT_BEGIN_NAMESPACE -class QD3DWindowSurface : public QWindowSurface +class QAnimationGroupPrivate : public QAbstractAnimationPrivate { + Q_DECLARE_PUBLIC(QAnimationGroup) public: - QD3DWindowSurface(QWidget *widget); - ~QD3DWindowSurface(); + QAnimationGroupPrivate() + { } - QPaintDevice *paintDevice(); - void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); - void setGeometry(const QRect &rect); - bool scroll(const QRegion &area, int dx, int dy); + virtual void animationInsertedAt(int index) { Q_UNUSED(index) }; + virtual void animationRemovedAt(int index); -private: - QD3DWindowSurfacePrivate *d_ptr; + QList<QAbstractAnimation *> animations; }; QT_END_NAMESPACE -#endif // QWINDOWSURFACE_D3D_P_H +#endif //QANIMATIONGROUP_P_H diff --git a/src/corelib/animation/qparallelanimationgroup.cpp b/src/corelib/animation/qparallelanimationgroup.cpp new file mode 100644 index 0000000..13f6073 --- /dev/null +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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$ +** +****************************************************************************/ + +/*! + \class QParallelAnimationGroup + \brief The QParallelAnimationGroup class provides a parallel group of animations. + \since 4.6 + \ingroup animation + + QParallelAnimationGroup--a \l{QAnimationGroup}{container for + animations}--starts all its animations when it is + \l{QAbstractAnimation::start()}{started} itself, i.e., runs all + animations in parallel. The animation group finishes when the + longest lasting animation has finished. + + You can treat QParallelAnimation as any other QAbstractAnimation, + e.g., pause, resume, or add it to other animation groups. + + \code + QParallelAnimationGroup *group = new QParallelAnimationGroup; + group->addAnimation(anim1); + group->addAnimation(anim2); + + group->start(); + \endcode + + In this example, \c anim1 and \c anim2 are two + \l{QPropertyAnimation}s that have already been set up. + + \sa QAnimationGroup, QPropertyAnimation, {The Animation Framework} +*/ + +#ifndef QT_NO_ANIMATION + +#include "qparallelanimationgroup.h" +#include "qparallelanimationgroup_p.h" +//#define QANIMATION_DEBUG +QT_BEGIN_NAMESPACE + +/*! + Constructs a QParallelAnimationGroup. + \a parent is passed to QObject's constructor. +*/ +QParallelAnimationGroup::QParallelAnimationGroup(QObject *parent) + : QAnimationGroup(*new QParallelAnimationGroupPrivate, parent) +{ +} + +/*! + \internal +*/ +QParallelAnimationGroup::QParallelAnimationGroup(QParallelAnimationGroupPrivate &dd, + QObject *parent) + : QAnimationGroup(dd, parent) +{ +} + +/*! + Destroys the animation group. It will also destroy all its animations. +*/ +QParallelAnimationGroup::~QParallelAnimationGroup() +{ +} + +/*! + \reimp +*/ +int QParallelAnimationGroup::duration() const +{ + Q_D(const QParallelAnimationGroup); + int ret = 0; + + for (int i = 0; i < d->animations.size(); ++i) { + QAbstractAnimation *animation = d->animations.at(i); + const int currentDuration = animation->totalDuration(); + if (currentDuration == -1) + return -1; // Undetermined length + + ret = qMax(ret, currentDuration); + } + + return ret; +} + +/*! + \reimp +*/ +void QParallelAnimationGroup::updateCurrentTime(int) +{ + Q_D(QParallelAnimationGroup); + if (d->animations.isEmpty()) + return; + + if (d->currentLoop > d->lastLoop) { + // simulate completion of the loop + int dura = duration(); + if (dura > 0) { + foreach (QAbstractAnimation *animation, d->animations) { + animation->setCurrentTime(dura); // will stop + } + } + } else if (d->currentLoop < d->lastLoop) { + // simulate completion of the loop seeking backwards + foreach (QAbstractAnimation *animation, d->animations) { + animation->setCurrentTime(0); + animation->stop(); + } + } + + bool timeFwd = ((d->currentLoop == d->lastLoop && d->currentTime >= d->lastCurrentTime) + || d->currentLoop > d->lastLoop); +#ifdef QANIMATION_DEBUG + qDebug("QParallellAnimationGroup %5d: setCurrentTime(%d), loop:%d, last:%d, timeFwd:%d, lastcurrent:%d, %d", + __LINE__, d->currentTime, d->currentLoop, d->lastLoop, timeFwd, d->lastCurrentTime, state()); +#endif + // finally move into the actual time of the current loop + foreach (QAbstractAnimation *animation, d->animations) { + const int dura = animation->totalDuration(); + if (dura == -1 && d->isUncontrolledAnimationFinished(animation)) + continue; + if (dura == -1 || (d->currentTime <= dura && dura != 0) + || (dura == 0 && d->currentLoop != d->lastLoop)) { + switch (state()) { + case Running: + animation->start(); + break; + case Paused: + animation->pause(); + break; + case Stopped: + default: + break; + } + } + + if (dura <= 0) { + if (dura == -1) + animation->setCurrentTime(d->currentTime); + continue; + } + + if ((timeFwd && d->lastCurrentTime <= dura) + || (!timeFwd && d->currentTime <= dura)) + animation->setCurrentTime(d->currentTime); + if (d->currentTime > dura) + animation->stop(); + } + d->lastLoop = d->currentLoop; + d->lastCurrentTime = d->currentTime; +} + +/*! + \reimp +*/ +void QParallelAnimationGroup::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_D(QParallelAnimationGroup); + QAnimationGroup::updateState(oldState, newState); + + switch (newState) { + case Stopped: + foreach (QAbstractAnimation *animation, d->animations) + animation->stop(); + d->disconnectUncontrolledAnimations(); + break; + case Paused: + foreach (QAbstractAnimation *animation, d->animations) + animation->pause(); + break; + case Running: + d->connectUncontrolledAnimations(); + foreach (QAbstractAnimation *animation, d->animations) { + animation->stop(); + animation->setDirection(d->direction); + animation->start(); + } + break; + } +} + +void QParallelAnimationGroupPrivate::_q_uncontrolledAnimationFinished() +{ + Q_Q(QParallelAnimationGroup); + + QAbstractAnimation *animation = qobject_cast<QAbstractAnimation *>(q->sender()); + Q_ASSERT(animation); + + int uncontrolledRunningCount = 0; + if (animation->duration() == -1 || animation->loopCount() < 0) { + QHash<QAbstractAnimation *, int>::iterator it = uncontrolledFinishTime.begin(); + while (it != uncontrolledFinishTime.end()) { + if (it.key() == animation) { + *it = animation->currentTime(); + } + if (it.value() == -1) + ++uncontrolledRunningCount; + ++it; + } + } + + if (uncontrolledRunningCount > 0) + return; + + int maxDuration = 0; + foreach (QAbstractAnimation *a, animations) + maxDuration = qMax(maxDuration, a->totalDuration()); + + if (currentTime >= maxDuration) + q->stop(); +} + +void QParallelAnimationGroupPrivate::disconnectUncontrolledAnimations() +{ + Q_Q(QParallelAnimationGroup); + + QHash<QAbstractAnimation *, int>::iterator it = uncontrolledFinishTime.begin(); + while (it != uncontrolledFinishTime.end()) { + QObject::disconnect(it.key(), SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished())); + ++it; + } + + uncontrolledFinishTime.clear(); +} + +void QParallelAnimationGroupPrivate::connectUncontrolledAnimations() +{ + Q_Q(QParallelAnimationGroup); + + foreach (QAbstractAnimation *animation, animations) { + if (animation->duration() == -1 || animation->loopCount() < 0) { + uncontrolledFinishTime[animation] = -1; + QObject::connect(animation, SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished())); + } + } +} + +bool QParallelAnimationGroupPrivate::isUncontrolledAnimationFinished(QAbstractAnimation *anim) const +{ + return uncontrolledFinishTime.value(anim, -1) >= 0; +} + +/*! + \reimp +*/ +void QParallelAnimationGroup::updateDirection(QAbstractAnimation::Direction direction) +{ + Q_D(QParallelAnimationGroup); + //we need to update the direction of the current animation + if (state() != Stopped) { + foreach(QAbstractAnimation *anim, d->animations) { + anim->setDirection(direction); + } + } else { + if (direction == Forward) { + d->lastLoop = 0; + d->lastCurrentTime = 0; + } else { + // Looping backwards with loopCount == -1 does not really work well... + d->lastLoop = (d->loopCount == -1 ? 0 : d->loopCount - 1); + d->lastCurrentTime = duration(); + } + } +} + +/*! + \reimp +*/ +bool QParallelAnimationGroup::event(QEvent *event) +{ + return QAnimationGroup::event(event); +} + +QT_END_NAMESPACE + +#include "moc_qparallelanimationgroup.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qparallelanimationgroup.h b/src/corelib/animation/qparallelanimationgroup.h new file mode 100644 index 0000000..57a8146 --- /dev/null +++ b/src/corelib/animation/qparallelanimationgroup.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QPARALLELANIMATIONGROUP_H +#define QPARALLELANIMATIONGROUP_H + +#include <QtCore/qanimationgroup.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QParallelAnimationGroupPrivate; +class Q_CORE_EXPORT QParallelAnimationGroup : public QAnimationGroup +{ + Q_OBJECT + +public: + QParallelAnimationGroup(QObject *parent = 0); + ~QParallelAnimationGroup(); + + int duration() const; + +protected: + QParallelAnimationGroup(QParallelAnimationGroupPrivate &dd, QObject *parent); + bool event(QEvent *event); + + void updateCurrentTime(int msecs); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + void updateDirection(QAbstractAnimation::Direction direction); + +private: + Q_DISABLE_COPY(QParallelAnimationGroup) + Q_DECLARE_PRIVATE(QParallelAnimationGroup) + Q_PRIVATE_SLOT(d_func(), void _q_uncontrolledAnimationFinished()) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPARALLELANIMATIONGROUP diff --git a/src/corelib/animation/qparallelanimationgroup_p.h b/src/corelib/animation/qparallelanimationgroup_p.h new file mode 100644 index 0000000..f36d972 --- /dev/null +++ b/src/corelib/animation/qparallelanimationgroup_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QPARALLELANIMATIONGROUP_P_H +#define QPARALLELANIMATIONGROUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qparallelanimationgroup.h" +#include "qanimationgroup_p.h" +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +class QParallelAnimationGroupPrivate : public QAnimationGroupPrivate +{ + Q_DECLARE_PUBLIC(QParallelAnimationGroup) +public: + QParallelAnimationGroupPrivate() + : lastLoop(0), lastCurrentTime(0) + { + } + + QHash<QAbstractAnimation*, int> uncontrolledFinishTime; + int lastLoop; + int lastCurrentTime; + + bool isUncontrolledAnimationFinished(QAbstractAnimation *anim) const; + void connectUncontrolledAnimations(); + void disconnectUncontrolledAnimations(); + + // private slot + void _q_uncontrolledAnimationFinished(); +}; + +QT_END_NAMESPACE + +#endif //QPARALLELANIMATIONGROUP_P_H diff --git a/src/corelib/animation/qpauseanimation.cpp b/src/corelib/animation/qpauseanimation.cpp new file mode 100644 index 0000000..b175f0c --- /dev/null +++ b/src/corelib/animation/qpauseanimation.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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$ +** +****************************************************************************/ + +/*! + \class QPauseAnimation + \brief The QPauseAnimation class provides a pause for QSequentialAnimationGroup. + \since 4.6 + \ingroup animation + + If you wish to introduce a delay between animations in a + QSequentialAnimationGroup, you can insert a QPauseAnimation. This + class does not animate anything, but does not + \l{QAbstractAnimation::finished()}{finish} before a specified + number of milliseconds have elapsed from when it was started. You + specify the duration of the pause in the constructor. It can also + be set directly with setDuration(). + + It is not necessary to construct a QPauseAnimation yourself. + QSequentialAnimationGroup provides the convenience functions + \l{QSequentialAnimationGroup::}{addPause()} and + \l{QSequentialAnimationGroup::}{insertPauseAt()}. These functions + simply take the number of milliseconds the pause should last. + + \sa QSequentialAnimationGroup +*/ + +#ifndef QT_NO_ANIMATION + +#include "qpauseanimation.h" +#include "qabstractanimation_p.h" + + +QT_BEGIN_NAMESPACE + +class QPauseAnimationPrivate : public QAbstractAnimationPrivate +{ +public: + QPauseAnimationPrivate() : QAbstractAnimationPrivate(), duration(0) + { + } + + int duration; +}; + +/*! + Constructs a QPauseAnimation. + \a parent is passed to QObject's constructor. + The default duration is 0. +*/ + +QPauseAnimation::QPauseAnimation(QObject *parent) : QAbstractAnimation(*new QPauseAnimationPrivate, parent) +{ +} + +/*! + Constructs a QPauseAnimation. + \a msecs is the duration of the pause. + \a parent is passed to QObject's constructor. +*/ + +QPauseAnimation::QPauseAnimation(int msecs, QObject *parent) : QAbstractAnimation(*new QPauseAnimationPrivate, parent) +{ + setDuration(msecs); +} + +/*! + Destroys the pause animation. +*/ +QPauseAnimation::~QPauseAnimation() +{ +} + +/*! + \property QPauseAnimation::duration + \brief the duration of the pause. + + The duration of the pause. The duration should not be negative. +*/ +int QPauseAnimation::duration() const +{ + Q_D(const QPauseAnimation); + return d->duration; +} + +void QPauseAnimation::setDuration(int msecs) +{ + if (msecs < 0) { + qWarning("QPauseAnimation::setDuration: cannot set a negative duration"); + return; + } + Q_D(QPauseAnimation); + d->duration = msecs; +} + +/*! + \reimp + */ +bool QPauseAnimation::event(QEvent *e) +{ + return QAbstractAnimation::event(e); +} + +/*! + \reimp + */ +void QPauseAnimation::updateCurrentTime(int msecs) +{ + Q_UNUSED(msecs); +} + + +QT_END_NAMESPACE + +#include "moc_qpauseanimation.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qpauseanimation.h b/src/corelib/animation/qpauseanimation.h new file mode 100644 index 0000000..cb6e041 --- /dev/null +++ b/src/corelib/animation/qpauseanimation.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QPAUSEANIMATION_P_H +#define QPAUSEANIMATION_P_H + +#include <QtCore/qanimationgroup.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QPauseAnimationPrivate; + +class Q_CORE_EXPORT QPauseAnimation : public QAbstractAnimation +{ + Q_OBJECT + Q_PROPERTY(int duration READ duration WRITE setDuration) +public: + QPauseAnimation(QObject *parent = 0); + QPauseAnimation(int msecs, QObject *parent = 0); + ~QPauseAnimation(); + + int duration() const; + void setDuration(int msecs); + +protected: + bool event(QEvent *e); + void updateCurrentTime(int msecs); + +private: + Q_DISABLE_COPY(QPauseAnimation) + Q_DECLARE_PRIVATE(QPauseAnimation) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPAUSEANIMATION_P_H diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp new file mode 100644 index 0000000..47361a5 --- /dev/null +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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$ +** +****************************************************************************/ + +/*! + \class QPropertyAnimation + \brief The QPropertyAnimation class animates Qt properties + \since 4.6 + \ingroup animation + + QPropertyAnimation interpolates over \l{Qt's Property System}{Qt + properties}. As property values are stored in \l{QVariant}s, the + class inherits QVariantAnimation, and supports animation of the + same \l{QVariant::Type}{variant types} as its super class. + + A class declaring properties must be a QObject. To make it + possible to animate a property, it must provide a setter (so that + QPropertyAnimation can set the property's value). Note that this + makes it possible to animate many of Qt's widgets. Let's look at + an example: + + \code + QPropertyAnimation animation(myWidget, "geometry"); + animation.setDuration(10000); + animation.setStartValue(QRect(0, 0, 100, 30)); + animation.setEndValue(QRect(250, 250, 100, 30)); + + animation.start(); + \endcode + + The property name and the QObject instance of which property + should be animated are passed to the constructor. You can then + specify the start and end value of the property. The procedure is + equal for properties in classes you have implemented + yourself--just check with QVariantAnimation that your QVariant + type is supported. + + The QVariantAnimation class description explains how to set up the + animation in detail. Note, however, that if a start value is not + set, the property will start at the value it had when the + QPropertyAnimation instance was created. + + QPropertyAnimation works like a charm on its own. For complex + animations that, for instance, contain several objects, + QAnimationGroup is provided. An animation group is an animation + that can contain other animations, and that can manage when its + animations are played. Look at QParallelAnimationGroup for an + example. + + \sa QVariantAnimation, QAnimationGroup, {The Animation Framework} +*/ + +#ifndef QT_NO_ANIMATION + +#include "qpropertyanimation.h" +#include "qanimationgroup.h" +#include <QtCore/qdebug.h> + +#include "qpropertyanimation_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +typedef QPair<QObject *, QByteArray> QPropertyAnimationPair; +typedef QHash<QPropertyAnimationPair, QPropertyAnimation*> QPropertyAnimationHash; +Q_GLOBAL_STATIC(QPropertyAnimationHash, _q_runningAnimations); +Q_GLOBAL_STATIC_WITH_ARGS(QMutex, guardHashLock, (QMutex::Recursive) ) + +void QPropertyAnimationPrivate::updateMetaProperty() +{ + if (!target || propertyName.isEmpty()) + return; + + if (!hasMetaProperty && !property.isValid()) { + const QMetaObject *mo = target->metaObject(); + propertyIndex = mo->indexOfProperty(propertyName); + if (propertyIndex != -1) { + hasMetaProperty = true; + property = mo->property(propertyIndex); + propertyType = property.userType(); + } else { + if (!target->dynamicPropertyNames().contains(propertyName)) + qWarning("QPropertyAnimation: you're trying to animate a non-existing property %s of your QObject", propertyName.constData()); + } + } + + if (property.isValid()) + convertValues(propertyType); +} + +void QPropertyAnimationPrivate::updateProperty(const QVariant &newValue) +{ + if (!target || state == QAbstractAnimation::Stopped) + return; + + if (hasMetaProperty) { + if (newValue.userType() == propertyType) { + //no conversion is needed, we directly call the QObject::qt_metacall + void *data = const_cast<void*>(newValue.constData()); + target->qt_metacall(QMetaObject::WriteProperty, propertyIndex, &data); + } else { + property.write(target, newValue); + } + } else { + target->setProperty(propertyName.constData(), newValue); + } +} + +void QPropertyAnimationPrivate::_q_targetDestroyed() +{ + Q_Q(QPropertyAnimation); + //we stop here so that this animation is removed from the global hash + q->stop(); + target = 0; +} + +/*! + Construct a QPropertyAnimation object. \a parent is passed to QObject's + constructor. +*/ +QPropertyAnimation::QPropertyAnimation(QObject *parent) + : QVariantAnimation(*new QPropertyAnimationPrivate, parent) +{ +} + +/*! + Construct a QPropertyAnimation object. \a parent is passed to QObject's + constructor. The animation changes the property \a propertyName on \a + target. The default duration is 250ms. + + \sa targetObject, propertyName +*/ +QPropertyAnimation::QPropertyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent) + : QVariantAnimation(*new QPropertyAnimationPrivate, parent) +{ + setTargetObject(target); + setPropertyName(propertyName); +} + +/*! + Destroys the QPropertyAnimation instance. + */ +QPropertyAnimation::~QPropertyAnimation() +{ + stop(); +} + +/*! + \property QPropertyAnimation::targetObject + \brief the target QObject for this animation. + + This property defines the target QObject for this animation. + */ +QObject *QPropertyAnimation::targetObject() const +{ + Q_D(const QPropertyAnimation); + return d->target; +} + +void QPropertyAnimation::setTargetObject(QObject *target) +{ + Q_D(QPropertyAnimation); + if (d->target == target) + return; + + if (d->state != QAbstractAnimation::Stopped) { + qWarning("QPropertyAnimation::setTargetObject: you can't change the target of a running animation"); + return; + } + + //we need to get notified when the target is destroyed + if (d->target) + disconnect(d->target, SIGNAL(destroyed()), this, SLOT(_q_targetDestroyed())); + + if (target) + connect(target, SIGNAL(destroyed()), SLOT(_q_targetDestroyed())); + + d->target = target; + d->hasMetaProperty = false; + d->updateMetaProperty(); +} + +/*! + \property QPropertyAnimation::propertyName + \brief the target property name for this animation + + This property defines the target property name for this animation. The + property name is required for the animation to operate. + */ +QByteArray QPropertyAnimation::propertyName() const +{ + Q_D(const QPropertyAnimation); + return d->propertyName; +} + +void QPropertyAnimation::setPropertyName(const QByteArray &propertyName) +{ + Q_D(QPropertyAnimation); + if (d->state != QAbstractAnimation::Stopped) { + qWarning("QPropertyAnimation::setPropertyName: you can't change the property name of a running animation"); + return; + } + + d->propertyName = propertyName; + d->hasMetaProperty = false; + d->updateMetaProperty(); +} + + +/*! + \reimp + */ +bool QPropertyAnimation::event(QEvent *event) +{ + return QVariantAnimation::event(event); +} + +/*! + This virtual function is called by QVariantAnimation whenever the current value + changes. \a value is the new, updated value. It updates the current value + of the property on the target object. + + \sa currentValue, currentTime + */ +void QPropertyAnimation::updateCurrentValue(const QVariant &value) +{ + Q_D(QPropertyAnimation); + d->updateProperty(value); +} + +/*! + \reimp + + If the startValue is not defined when the state of the animation changes from Stopped to Running, + the current property value is used as the initial value for the animation. +*/ +void QPropertyAnimation::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_D(QPropertyAnimation); + + if (!d->target) { + qWarning("QPropertyAnimation::updateState: Changing state of an animation without target"); + return; + } + + QVariantAnimation::updateState(oldState, newState); + QMutexLocker locker(guardHashLock()); + QPropertyAnimationHash * hash = _q_runningAnimations(); + QPropertyAnimationPair key(d->target, d->propertyName); + if (newState == Running) { + d->updateMetaProperty(); + QPropertyAnimation *oldAnim = hash->value(key, 0); + if (oldAnim) { + // try to stop the top level group + QAbstractAnimation *current = oldAnim; + while (current->group() && current->state() != Stopped) + current = current->group(); + current->stop(); + } + hash->insert(key, this); + + // update the default start value + if (oldState == Stopped) { + d->setDefaultStartValue(d->target->property(d->propertyName.constData())); + } + } else if (hash->value(key) == this) { + hash->remove(key); + } +} + +#include "moc_qpropertyanimation.cpp" + +QT_END_NAMESPACE + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qpropertyanimation.h b/src/corelib/animation/qpropertyanimation.h new file mode 100644 index 0000000..39317ba --- /dev/null +++ b/src/corelib/animation/qpropertyanimation.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QPROPERTYANIMATION_H +#define QPROPERTYANIMATION_H + +#include <QtCore/qvariantanimation.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QPropertyAnimationPrivate; +class Q_CORE_EXPORT QPropertyAnimation : public QVariantAnimation +{ + Q_OBJECT + Q_PROPERTY(QByteArray propertyName READ propertyName WRITE setPropertyName) + Q_PROPERTY(QObject* targetObject READ targetObject WRITE setTargetObject) + +public: + QPropertyAnimation(QObject *parent = 0); + QPropertyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = 0); + ~QPropertyAnimation(); + + QObject *targetObject() const; + void setTargetObject(QObject *target); + + QByteArray propertyName() const; + void setPropertyName(const QByteArray &propertyName); + +protected: + bool event(QEvent *event); + void updateCurrentValue(const QVariant &value); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + +private: + Q_PRIVATE_SLOT(d_func(), void _q_targetDestroyed()); + Q_DISABLE_COPY(QPropertyAnimation) + Q_DECLARE_PRIVATE(QPropertyAnimation) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPROPERTYANIMATION_H diff --git a/src/corelib/animation/qpropertyanimation_p.h b/src/corelib/animation/qpropertyanimation_p.h new file mode 100644 index 0000000..a4387dd --- /dev/null +++ b/src/corelib/animation/qpropertyanimation_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QPROPERTYANIMATION_P_H +#define QPROPERTYANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QIODevice. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpropertyanimation.h" +#include <QtCore/qmetaobject.h> + +#include "qvariantanimation_p.h" + +QT_BEGIN_NAMESPACE + +class QPropertyAnimationPrivate : public QVariantAnimationPrivate +{ + Q_DECLARE_PUBLIC(QPropertyAnimation) +public: + QPropertyAnimationPrivate() + : target(0), propertyType(0), propertyIndex(0), hasMetaProperty(false) + { + } + + void _q_targetDestroyed(); + + QObject *target; + + //for the QProperty + QMetaProperty property; + int propertyType; + int propertyIndex; + + bool hasMetaProperty; + QByteArray propertyName; + void updateProperty(const QVariant &); + void updateMetaProperty(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/animation/qsequentialanimationgroup.cpp b/src/corelib/animation/qsequentialanimationgroup.cpp new file mode 100644 index 0000000..14814a7 --- /dev/null +++ b/src/corelib/animation/qsequentialanimationgroup.cpp @@ -0,0 +1,594 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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$ +** +****************************************************************************/ + +/*! + \class QSequentialAnimationGroup + \brief The QSequentialAnimationGroup class provides a sequential group of animations. + \since 4.6 + \ingroup animation + + QSequentialAnimationGroup is a QAnimationGroup that runs its + animations in sequence, i.e., it starts one animation after + another has finished playing. The animations are played in the + order they are added to the group (using + \l{QAnimationGroup::}{addAnimation()} or + \l{QAnimationGroup::}{insertAnimationAt()}). The animation group + finishes when its last animation has finished. + + At each moment there is at most one animation that is active in + the group; it is returned by currentAnimation(). An empty group + has no current animation. + + A sequential animation group can be treated as any other + animation, i.e., it can be started, stopped, and added to other + groups. You can also call addPause() or insertPauseAt() to add a + pause to a sequential animation group. + + \code + QSequentialAnimationGroup group; + + group.addAnimation(anim1); + group.addAnimation(anim2); + + group.start(); + \endcode + + In this example, \c anim1 and \c anim2 are two already set up + \l{QPropertyAnimation}s. + + \sa QAnimationGroup, QAbstractAnimation, {The Animation Framework} +*/ + +#ifndef QT_NO_ANIMATION + +#include "qsequentialanimationgroup.h" +#include "qsequentialanimationgroup_p.h" + +#include "qpauseanimation.h" + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + + + +bool QSequentialAnimationGroupPrivate::atEnd() const +{ + // we try to detect if we're at the end of the group + //this is true if the following conditions are true: + // 1. we're in the last loop + // 2. the direction is forward + // 3. the current animation is the last one + // 4. the current animation has reached its end + const int animTotalCurrentTime = QAbstractAnimationPrivate::get(currentAnimation)->totalCurrentTime; + return (currentLoop == loopCount - 1 + && direction == QAbstractAnimation::Forward + && currentAnimation == animations.last() + && animTotalCurrentTime == animationActualTotalDuration(currentAnimationIndex)); +} + +int QSequentialAnimationGroupPrivate::animationActualTotalDuration(int index) const +{ + QAbstractAnimation *anim = animations.at(index); + int ret = anim->totalDuration(); + if (ret == -1 && actualDuration.size() > index) + ret = actualDuration.at(index); //we can try the actual duration there + return ret; +} + +QSequentialAnimationGroupPrivate::AnimationIndex QSequentialAnimationGroupPrivate::indexForTime(int msecs) const +{ + Q_Q(const QSequentialAnimationGroup); + Q_ASSERT(!animations.isEmpty()); + + AnimationIndex ret; + int duration = 0; + + // in case duration is -1, currentLoop will always be 0 + ret.timeOffset = currentLoop * q->duration(); + + for (int i = 0; i < animations.size(); ++i) { + duration = animationActualTotalDuration(i); + + // 'animation' is the current animation if one of these reasons is true: + // 1. it's duration is undefined + // 2. it ends after msecs + // 3. it is the last animation (this can happen in case there is at least 1 uncontrolled animation) + // 4. it ends exactly in msecs and the direction is backwards + if (duration == -1 || msecs < (ret.timeOffset + duration) + || (msecs == (ret.timeOffset + duration) && direction == QAbstractAnimation::Backward)) { + ret.index = i; + return ret; + } + + // 'animation' has a non-null defined duration and is not the one at time 'msecs'. + ret.timeOffset += duration; + } + + // this can only happen when one of those conditions is true: + // 1. the duration of the group is undefined and we passed its actual duration + // 2. there are only 0-duration animations in the group + ret.timeOffset -= duration; + ret.index = animations.size() - 1; + return ret; +} + +void QSequentialAnimationGroupPrivate::restart() +{ + // restarting the group by making the first/last animation the current one + if (direction == QAbstractAnimation::Forward) { + lastLoop = 0; + if (currentAnimationIndex == 0) + activateCurrentAnimation(); + else + setCurrentAnimation(0); + } else { // direction == QAbstractAnimation::Backward + lastLoop = loopCount - 1; + int index = animations.size() - 1; + if (currentAnimationIndex == index) + activateCurrentAnimation(); + else + setCurrentAnimation(index); + } +} + +/*! + \internal + This manages advancing the execution of a group running forwards (time has gone forward), + which is the same behaviour for rewinding the execution of a group running backwards + (time has gone backward). +*/ +void QSequentialAnimationGroupPrivate::advanceForwards(const AnimationIndex &newAnimationIndex) +{ + if (lastLoop < currentLoop) { + // we need to fast forward to the end + for (int i = currentAnimationIndex; i < animations.size(); ++i) { + QAbstractAnimation *anim = animations.at(i); + setCurrentAnimation(i, true); + anim->setCurrentTime(animationActualTotalDuration(i)); + } + // this will make sure the current animation is reset to the beginning + if (animations.size() == 1) + // we need to force activation because setCurrentAnimation will have no effect + activateCurrentAnimation(); + else + setCurrentAnimation(0, true); + } + + // and now we need to fast forward from the current position to + for (int i = currentAnimationIndex; i < newAnimationIndex.index; ++i) { //### WRONG, + QAbstractAnimation *anim = animations.at(i); + setCurrentAnimation(i, true); + anim->setCurrentTime(animationActualTotalDuration(i)); + } + // setting the new current animation will happen later +} + +/*! + \internal + This manages rewinding the execution of a group running forwards (time has gone forward), + which is the same behaviour for advancing the execution of a group running backwards + (time has gone backward). +*/ +void QSequentialAnimationGroupPrivate::rewindForwards(const AnimationIndex &newAnimationIndex) +{ + if (lastLoop > currentLoop) { + // we need to fast rewind to the beginning + for (int i = currentAnimationIndex; i >= 0 ; --i) { + QAbstractAnimation *anim = animations.at(i); + setCurrentAnimation(i, true); + anim->setCurrentTime(0); + } + // this will make sure the current animation is reset to the end + if (animations.size() == 1) + // we need to force activation because setCurrentAnimation will have no effect + activateCurrentAnimation(); + else + setCurrentAnimation(animations.count() - 1, true); + } + + // and now we need to fast rewind from the current position to + for (int i = currentAnimationIndex; i > newAnimationIndex.index; --i) { + QAbstractAnimation *anim = animations.at(i); + setCurrentAnimation(i, true); + anim->setCurrentTime(0); + } + // setting the new current animation will happen later +} + +/*! + \fn QSequentialAnimationGroup::currentAnimationChanged(QAbstractAnimation *current) + + QSequentialAnimationGroup emits this signal when currentAnimation + has been changed. \a current is the current animation. + + \sa currentAnimation() +*/ + + +/*! + Constructs a QSequentialAnimationGroup. + \a parent is passed to QObject's constructor. +*/ +QSequentialAnimationGroup::QSequentialAnimationGroup(QObject *parent) + : QAnimationGroup(*new QSequentialAnimationGroupPrivate, parent) +{ +} + +/*! + \internal +*/ +QSequentialAnimationGroup::QSequentialAnimationGroup(QSequentialAnimationGroupPrivate &dd, + QObject *parent) + : QAnimationGroup(dd, parent) +{ +} + +/*! + Destroys the animation group. It will also destroy all its animations. +*/ +QSequentialAnimationGroup::~QSequentialAnimationGroup() +{ +} + +/*! + Adds a pause of \a msecs to this animation group. + The pause is considered as a special type of animation, thus count() will be + increased by one. + \sa insertPauseAt(), QAnimationGroup::addAnimation() +*/ +QPauseAnimation *QSequentialAnimationGroup::addPause(int msecs) +{ + QPauseAnimation *pause = new QPauseAnimation(msecs); + addAnimation(pause); + return pause; +} + +/*! + Inserts a pause of \a msecs milliseconds at \a index in this animation + group. + + \sa addPause(), QAnimationGroup::insertAnimationAt() +*/ +QPauseAnimation *QSequentialAnimationGroup::insertPauseAt(int index, int msecs) +{ + Q_D(const QSequentialAnimationGroup); + + if (index < 0 || index > d->animations.size()) { + qWarning("QSequentialAnimationGroup::insertPauseAt: index is out of bounds"); + return 0; + } + + QPauseAnimation *pause = new QPauseAnimation(msecs); + insertAnimationAt(index, pause); + return pause; +} + + +/*! + \property QSequentialAnimationGroup::currentAnimation + Returns the animation in the current time. + + \sa currentAnimationChanged() +*/ +QAbstractAnimation *QSequentialAnimationGroup::currentAnimation() const +{ + Q_D(const QSequentialAnimationGroup); + return d->currentAnimation; +} + +/*! + \reimp +*/ +int QSequentialAnimationGroup::duration() const +{ + Q_D(const QSequentialAnimationGroup); + int ret = 0; + + for (int i = 0; i < d->animations.size(); ++i) { + QAbstractAnimation *animation = d->animations.at(i); + const int currentDuration = animation->totalDuration(); + if (currentDuration == -1) + return -1; // Undetermined length + + ret += currentDuration; + } + + return ret; +} + +/*! + \reimp +*/ +void QSequentialAnimationGroup::updateCurrentTime(int msecs) +{ + Q_D(QSequentialAnimationGroup); + if (!d->currentAnimation) + return; + + const QSequentialAnimationGroupPrivate::AnimationIndex newAnimationIndex = d->indexForTime(msecs); + + // remove unneeded animations from actualDuration list + while (newAnimationIndex.index < d->actualDuration.size()) + d->actualDuration.removeLast(); + + // newAnimationIndex.index is the new current animation + if (d->lastLoop < d->currentLoop + || (d->lastLoop == d->currentLoop && d->currentAnimationIndex < newAnimationIndex.index)) { + // advancing with forward direction is the same as rewinding with backwards direction + d->advanceForwards(newAnimationIndex); + } else if (d->lastLoop > d->currentLoop + || (d->lastLoop == d->currentLoop && d->currentAnimationIndex > newAnimationIndex.index)) { + // rewinding with forward direction is the same as advancing with backwards direction + d->rewindForwards(newAnimationIndex); + } + + d->setCurrentAnimation(newAnimationIndex.index); + + const int newCurrentTime = msecs - newAnimationIndex.timeOffset; + + if (d->currentAnimation) { + d->currentAnimation->setCurrentTime(newCurrentTime); + if (d->atEnd()) { + //we make sure that we don't exceed the duration here + d->currentTime += QAbstractAnimationPrivate::get(d->currentAnimation)->totalCurrentTime - newCurrentTime; + stop(); + } + } else { + //the only case where currentAnimation could be null + //is when all animations have been removed + Q_ASSERT(d->animations.isEmpty()); + d->currentTime = 0; + stop(); + } + + d->lastLoop = d->currentLoop; +} + +/*! + \reimp +*/ +void QSequentialAnimationGroup::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_D(QSequentialAnimationGroup); + QAnimationGroup::updateState(oldState, newState); + + if (!d->currentAnimation) + return; + + switch (newState) { + case Stopped: + d->currentAnimation->stop(); + break; + case Paused: + if (oldState == d->currentAnimation->state() + && oldState == QSequentialAnimationGroup::Running) { + d->currentAnimation->pause(); + } + else + d->restart(); + break; + case Running: + if (oldState == d->currentAnimation->state() + && oldState == QSequentialAnimationGroup::Paused) + d->currentAnimation->start(); + else + d->restart(); + break; + } +} + +/*! + \reimp +*/ +void QSequentialAnimationGroup::updateDirection(QAbstractAnimation::Direction direction) +{ + Q_D(QSequentialAnimationGroup); + // we need to update the direction of the current animation + if (state() != Stopped && d->currentAnimation) + d->currentAnimation->setDirection(direction); +} + +/*! + \reimp +*/ +bool QSequentialAnimationGroup::event(QEvent *event) +{ + return QAnimationGroup::event(event); +} + +void QSequentialAnimationGroupPrivate::setCurrentAnimation(int index, bool intermediate) +{ + Q_Q(QSequentialAnimationGroup); + + index = qMin(index, animations.count() - 1); + + if (index == -1) { + Q_ASSERT(animations.isEmpty()); + currentAnimationIndex = -1; + currentAnimation = 0; + return; + } + + // need these two checks below because this func can be called after the current animation + // has been removed + if (index == currentAnimationIndex && animations.at(index) == currentAnimation) + return; + + // stop the old current animation + if (currentAnimation) + currentAnimation->stop(); + + currentAnimation = animations.at(index); + currentAnimationIndex = index; + + emit q->currentAnimationChanged(currentAnimation); + + activateCurrentAnimation(intermediate); +} + +void QSequentialAnimationGroupPrivate::activateCurrentAnimation(bool intermediate) +{ + Q_Q(QSequentialAnimationGroup); + + if (!currentAnimation) + return; + + if (state == QSequentialAnimationGroup::Stopped) + return; + + currentAnimation->stop(); + + // we ensure the direction is consistent with the group's direction + currentAnimation->setDirection(direction); + + // connects to the finish signal of uncontrolled animations + if (currentAnimation->totalDuration() == -1) + QObject::connect(currentAnimation, SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished())); + + currentAnimation->start(); + if (!intermediate && state == QSequentialAnimationGroup::Paused) + currentAnimation->pause(); +} + +void QSequentialAnimationGroupPrivate::_q_uncontrolledAnimationFinished() +{ + Q_Q(QSequentialAnimationGroup); + Q_ASSERT(qobject_cast<QAbstractAnimation *>(q->sender()) == currentAnimation); + + // we trust the duration returned by the animation + while (actualDuration.size() < (currentAnimationIndex + 1)) + actualDuration.append(-1); + actualDuration[currentAnimationIndex] = currentAnimation->currentTime(); + + QObject::disconnect(currentAnimation, SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished())); + + if ((direction == QAbstractAnimation::Forward && currentAnimation == animations.last()) + || (direction == QAbstractAnimation::Backward && currentAnimationIndex == 0)) { + // we don't handle looping of a group with undefined duration + q->stop(); + } else if (direction == QAbstractAnimation::Forward) { + // set the current animation to be the next one + setCurrentAnimation(currentAnimationIndex + 1); + } else { + // set the current animation to be the previous one + setCurrentAnimation(currentAnimationIndex - 1); + } +} + +/*! + \internal + This method is called whenever an animation is added to + the group at index \a index. + Note: We only support insertion after the current animation +*/ +void QSequentialAnimationGroupPrivate::animationInsertedAt(int index) +{ + if (currentAnimation == 0) + setCurrentAnimation(0); // initialize the current animation + + if (currentAnimationIndex == index + && currentAnimation->currentTime() == 0 && currentAnimation->currentLoop() == 0) { + //in this case we simply insert an animation before the current one has actually started + setCurrentAnimation(index); + } + + //we update currentAnimationIndex in case it has changed (the animation pointer is still valid) + currentAnimationIndex = animations.indexOf(currentAnimation); + + if (index < currentAnimationIndex || currentLoop != 0) { + qWarning("QSequentialGroup::insertAnimationAt only supports to add animations after the current one."); + return; //we're not affected because it is added after the current one + } +} + +/*! + \internal + This method is called whenever an animation is removed from + the group at index \a index. The animation is no more listed when this + method is called. +*/ +void QSequentialAnimationGroupPrivate::animationRemovedAt(int index) +{ + Q_Q(QSequentialAnimationGroup); + QAnimationGroupPrivate::animationRemovedAt(index); + + Q_ASSERT(currentAnimation); // currentAnimation should always be set + + if (actualDuration.size() > index) + actualDuration.removeAt(index); + + const int currentIndex = animations.indexOf(currentAnimation); + if (currentIndex == -1) { + //we're removing the current animation, let's update it to another one + if (index < animations.count()) + setCurrentAnimation(index); //let's try to take the next one + else if (index > 0) + setCurrentAnimation(index - 1); + else// case all animations were removed + setCurrentAnimation(-1); + } else if (currentAnimationIndex > index) { + currentAnimationIndex--; + } + + // duration of the previous animations up to the current animation + currentTime = 0; + for (int i = 0; i < currentAnimationIndex; ++i) { + const int current = animationActualTotalDuration(i); + currentTime += current; + } + + if (currentIndex != -1) { + //the current animation is not the one being removed + //so we add its current time to the current time of this group + currentTime += QAbstractAnimationPrivate::get(currentAnimation)->totalCurrentTime; + } + + //let's also update the total current time + totalCurrentTime = currentTime + loopCount * q->duration(); +} + +QT_END_NAMESPACE + +#include "moc_qsequentialanimationgroup.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qsequentialanimationgroup.h b/src/corelib/animation/qsequentialanimationgroup.h new file mode 100644 index 0000000..4701a76 --- /dev/null +++ b/src/corelib/animation/qsequentialanimationgroup.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QSEQUENTIALANIMATIONGROUP_H +#define QSEQUENTIALANIMATIONGROUP_H + +#include <QtCore/qanimationgroup.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QPauseAnimation; +class QSequentialAnimationGroupPrivate; + +class Q_CORE_EXPORT QSequentialAnimationGroup : public QAnimationGroup +{ + Q_OBJECT + Q_PROPERTY(QAbstractAnimation* currentAnimation READ currentAnimation NOTIFY currentAnimationChanged) + +public: + QSequentialAnimationGroup(QObject *parent = 0); + ~QSequentialAnimationGroup(); + + QPauseAnimation *addPause(int msecs); + QPauseAnimation *insertPauseAt(int index, int msecs); + + QAbstractAnimation *currentAnimation() const; + int duration() const; + +Q_SIGNALS: + void currentAnimationChanged(QAbstractAnimation *current); + +protected: + QSequentialAnimationGroup(QSequentialAnimationGroupPrivate &dd, QObject *parent); + bool event(QEvent *event); + + void updateCurrentTime(int msecs); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + void updateDirection(QAbstractAnimation::Direction direction); + +private: + Q_DISABLE_COPY(QSequentialAnimationGroup) + Q_DECLARE_PRIVATE(QSequentialAnimationGroup) + Q_PRIVATE_SLOT(d_func(), void _q_uncontrolledAnimationFinished()) +}; + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QSEQUENTIALANIMATIONGROUP_H diff --git a/src/gui/painting/qpaintengine_d3d_p.h b/src/corelib/animation/qsequentialanimationgroup_p.h index 8fa5cf6..3ac90f8 100644 --- a/src/gui/painting/qpaintengine_d3d_p.h +++ b/src/corelib/animation/qsequentialanimationgroup_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 QtGui module of the Qt Toolkit. +** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,82 +39,73 @@ ** ****************************************************************************/ -#ifndef QPAINTENGINE_D3D_P_H -#define QPAINTENGINE_D3D_P_H +#ifndef QSEQUENTIALANIMATIONGROUP_P_H +#define QSEQUENTIALANIMATIONGROUP_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to +// of QIODevice. This header file may change from version to // version without notice, or even be removed. // // We mean it. // -#include "QtGui/qpaintengine.h" -#include <d3d9.h> +#include "qsequentialanimationgroup.h" +#include "qanimationgroup_p.h" + QT_BEGIN_NAMESPACE -class QDirect3DPaintEnginePrivate; -class QDirect3DPaintEngine : public QPaintEngine +class QSequentialAnimationGroupPrivate : public QAnimationGroupPrivate { - Q_DECLARE_PRIVATE(QDirect3DPaintEngine) + Q_DECLARE_PUBLIC(QSequentialAnimationGroup) public: - QDirect3DPaintEngine(); - ~QDirect3DPaintEngine(); - bool begin(QPaintDevice *device); - - void drawEllipse(const QRectF &rect); - void drawEllipse(const QRect &rect); - - void drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, - Qt::ImageConversionFlags flags = Qt::AutoColor); + QSequentialAnimationGroupPrivate() + : currentAnimation(0), currentAnimationIndex(-1), lastLoop(0) + { } - void drawLines(const QLineF *lines, int lineCount); - void drawLines(const QLine *lines, int lineCount); - void drawPath(const QPainterPath &path); + struct AnimationIndex + { + AnimationIndex() : index(0), timeOffset(0) {} + // index points to the animation at timeOffset, skipping 0 duration animations. + // Note that the index semantic is slightly different depending on the direction. + int index; // the index of the animation in timeOffset + int timeOffset; // time offset when the animation at index starts. + }; - void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + int animationActualTotalDuration(int index) const; + AnimationIndex indexForTime(int msecs) const; - void drawPoints(const QPointF *points, int pointCount); - void drawPoints(const QPoint *points, int pointCount); + void setCurrentAnimation(int index, bool intermediate = false); + void activateCurrentAnimation(bool intermediate = false); - void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode); - void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode); + void animationInsertedAt(int index); + void animationRemovedAt(int index); - void drawRects(const QRectF *rects, int rectCount); - void drawRects(const QRect * rects, int rectCount); + bool atEnd() const; - void drawTextItem(const QPointF &p, const QTextItem &textItem); + QAbstractAnimation *currentAnimation; + int currentAnimationIndex; - void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr); + // this is the actual duration of uncontrolled animations + // it helps seeking and even going forward + QList<int> actualDuration; - bool end(); + void restart(); + int lastLoop; - Type type() const { return Direct3D; } - void updateState(const QPaintEngineState &state); + // handle time changes + void rewindForwards(const AnimationIndex &newAnimationIndex); + void advanceForwards(const AnimationIndex &newAnimationIndex); - void cleanup(); - - HDC getDC() const; - void setFlushOnEnd(bool flushOnEnd); - bool hasDirect3DSupport(); - -public: - void scroll(QPaintDevice *pd, const RECT &srcrect, const RECT &destrect); - LPDIRECT3DSWAPCHAIN9 swapChain(QPaintDevice *pd); - void releaseSwapChain(QPaintDevice *pd); - -private: - Q_DISABLE_COPY(QDirect3DPaintEngine) - friend class QPixmap; - friend class QD3DGlyphCache; + // private slot + void _q_uncontrolledAnimationFinished(); }; QT_END_NAMESPACE -#endif +#endif //QSEQUENTIALANIMATIONGROUP_P_H diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp new file mode 100644 index 0000000..e973ec5 --- /dev/null +++ b/src/corelib/animation/qvariantanimation.cpp @@ -0,0 +1,672 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QT_NO_ANIMATION + +#include "qvariantanimation.h" +#include "qvariantanimation_p.h" + +#include <QtCore/QRect> +#include <QtCore/QLineF> +#include <QtCore/QLine> +#include <QtCore/QReadWriteLock> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QVariantAnimation + \ingroup animation + \brief The QVariantAnimation class provides an abstract base class for animations. + \since 4.6 + + This class is part of \l{The Animation Framework}. It serves as a + base class for property and item animations, with functions for + shared functionality. + + QVariantAnimation cannot be used directly as it is an abstract + class; it has a pure virtual method called updateCurrentValue(). + The class performs interpolation over + \l{QVariant}s, but leaves using the interpolated values to its + subclasses. Currently, Qt provides QPropertyAnimation, which + animates Qt \l{Qt's Property System}{properties}. See the + QPropertyAnimation class description if you wish to animate such + properties. + + You can then set start and end values for the property by calling + setStartValue() and setEndValue(), and finally call start() to + start the animation. QVariantAnimation will interpolate the + property of the target object and emit valueChanged(). To react to + a change in the current value you have to reimplement the + updateCurrentValue() virtual function. + + It is also possible to set values at specified steps situated + between the start and end value. The interpolation will then + touch these points at the specified steps. Note that the start and + end values are defined as the key values at 0.0 and 1.0. + + There are two ways to affect how QVariantAnimation interpolates + the values. You can set an easing curve by calling + setEasingCurve(), and configure the duration by calling + setDuration(). You can change how the QVariants are interpolated + by creating a subclass of QVariantAnimation, and reimplementing + the virtual interpolated() function. + + Subclassing QVariantAnimation can be an alternative if you have + \l{QVariant}s that you do not wish to declare as Qt properties. + Note, however, that you in most cases will be better off declaring + your QVariant as a property. + + Not all QVariant types are supported. Below is a list of currently + supported QVariant types: + + \list + \o \l{QMetaType::}{Int} + \o \l{QMetaType::}{Double} + \o \l{QMetaType::}{Float} + \o \l{QMetaType::}{QLine} + \o \l{QMetaType::}{QLineF} + \o \l{QMetaType::}{QPoint} + \o \l{QMetaType::}{QSize} + \o \l{QMetaType::}{QSizeF} + \o \l{QMetaType::}{QRect} + \o \l{QMetaType::}{QRectF} + \endlist + + If you need to interpolate other variant types, including custom + types, you have to implement interpolation for these yourself. + You do this by reimplementing interpolated(), which returns + interpolation values for the value being interpolated. + + \omit We need some snippets around here. \endomit + + \sa QPropertyAnimation, QAbstractAnimation, {The Animation Framework} +*/ + +/*! + \fn void QVariantAnimation::valueChanged(const QVariant &value) + + QVariantAnimation emits this signal whenever the current \a value changes. + + \sa currentValue, startValue, endValue +*/ + +/*! + \fn void QVariantAnimation::updateCurrentValue(const QVariant &value) = 0; + + This pure virtual function is called every time the animation's current + value changes. The \a value argument is the new current value. + + \sa currentValue +*/ + + +static bool animationValueLessThan(const QVariantAnimation::KeyValue &p1, const QVariantAnimation::KeyValue &p2) +{ + return p1.first < p2.first; +} + +static QVariant defaultInterpolator(const void *, const void *, qreal) +{ + return QVariant(); +} + +template<> Q_INLINE_TEMPLATE QRect _q_interpolate(const QRect &f, const QRect &t, qreal progress) +{ + QRect ret; + ret.setCoords(_q_interpolate(f.left(), t.left(), progress), + _q_interpolate(f.top(), t.top(), progress), + _q_interpolate(f.right(), t.right(), progress), + _q_interpolate(f.bottom(), t.bottom(), progress)); + return ret; +} + +template<> Q_INLINE_TEMPLATE QRectF _q_interpolate(const QRectF &f, const QRectF &t, qreal progress) +{ + qreal x1, y1, w1, h1; + f.getRect(&x1, &y1, &w1, &h1); + qreal x2, y2, w2, h2; + t.getRect(&x2, &y2, &w2, &h2); + return QRectF(_q_interpolate(x1, x2, progress), _q_interpolate(y1, y2, progress), + _q_interpolate(w1, w2, progress), _q_interpolate(h1, h2, progress)); +} + +template<> Q_INLINE_TEMPLATE QLine _q_interpolate(const QLine &f, const QLine &t, qreal progress) +{ + return QLine( _q_interpolate(f.p1(), t.p1(), progress), _q_interpolate(f.p2(), t.p2(), progress)); +} + +template<> Q_INLINE_TEMPLATE QLineF _q_interpolate(const QLineF &f, const QLineF &t, qreal progress) +{ + return QLineF( _q_interpolate(f.p1(), t.p1(), progress), _q_interpolate(f.p2(), t.p2(), progress)); +} + +QVariantAnimationPrivate::QVariantAnimationPrivate() : duration(250), hasStartValue(false), + interpolator(&defaultInterpolator), + changedSignalMask(1 << QVariantAnimation::staticMetaObject.indexOfSignal("valueChanged(QVariant)")) +{ + //we keep the mask so that we emit valueChanged only when needed (for performance reasons) +} + +void QVariantAnimationPrivate::convertValues(int t) +{ + //this ensures that all the keyValues are of type t + for (int i = 0; i < keyValues.count(); ++i) { + QVariantAnimation::KeyValue &pair = keyValues[i]; + pair.second.convert(static_cast<QVariant::Type>(t)); + } + //we also need update to the current interval if needed + currentInterval.start.second.convert(static_cast<QVariant::Type>(t)); + currentInterval.end.second.convert(static_cast<QVariant::Type>(t)); + + //... and the interpolator + updateInterpolator(); +} + +void QVariantAnimationPrivate::updateInterpolator() +{ + int type = currentInterval.start.second.userType(); + if (type == currentInterval.end.second.userType()) + interpolator = getInterpolator(type); + else + interpolator = 0; + + //we make sure that the interpolator is always set to something + if (!interpolator) + interpolator = &defaultInterpolator; +} + +/*! + \internal + The goal of this function is to update the currentInterval member. As a consequence, we also + need to update the currentValue. + Set \a force to true to always recalculate the interval. +*/ +void QVariantAnimationPrivate::recalculateCurrentInterval(bool force/*=false*/) +{ + // can't interpolate if we have only 1 key value + if (keyValues.count() <= 1) + return; + + const qreal progress = easing.valueForProgress(((duration == 0) ? qreal(1) : qreal(currentTime) / qreal(duration))); + + if (force || progress < currentInterval.start.first || progress > currentInterval.end.first) { + //let's update currentInterval + QVariantAnimation::KeyValues::const_iterator itStart = qLowerBound(keyValues.constBegin(), + keyValues.constEnd(), + qMakePair(progress, QVariant()), + animationValueLessThan); + QVariantAnimation::KeyValues::const_iterator itEnd = itStart; + + // If we are at the end we should continue to use the last keyValues in case of extrapolation (progress > 1.0). + // This is because the easing function can return a value slightly outside the range [0, 1] + if (itStart != keyValues.constEnd()) { + // this can't happen because we always prepend the default start value there + if (itStart == keyValues.constBegin()) { + ++itEnd; + } else { + --itStart; + } + + // update all the values of the currentInterval + currentInterval.start = *itStart; + currentInterval.end = *itEnd; + updateInterpolator(); + } + } + setCurrentValueForProgress(progress); +} + +void QVariantAnimationPrivate::setCurrentValueForProgress(const qreal progress) +{ + Q_Q(QVariantAnimation); + + const qreal startProgress = currentInterval.start.first; + const qreal endProgress = currentInterval.end.first; + const qreal localProgress = (progress - startProgress) / (endProgress - startProgress); + + QVariant ret = q->interpolated(currentInterval.start.second, + currentInterval.end.second, + localProgress); + qSwap(currentValue, ret); + q->updateCurrentValue(currentValue); + if ((connectedSignals & changedSignalMask) && currentValue != ret) { + //the value has changed + emit q->valueChanged(currentValue); + } +} + +QVariant QVariantAnimationPrivate::valueAt(qreal step) const +{ + QVariantAnimation::KeyValues::const_iterator result = + qBinaryFind(keyValues.begin(), keyValues.end(), qMakePair(step, QVariant()), animationValueLessThan); + if (result != keyValues.constEnd()) + return result->second; + + return QVariant(); +} + +void QVariantAnimationPrivate::setValueAt(qreal step, const QVariant &value) +{ + if (step < qreal(0.0) || step > qreal(1.0)) { + qWarning("QVariantAnimation::setValueAt: invalid step = %f", step); + return; + } + + QVariantAnimation::KeyValue pair(step, value); + + QVariantAnimation::KeyValues::iterator result = qLowerBound(keyValues.begin(), keyValues.end(), pair, animationValueLessThan); + if (result == keyValues.end() || result->first != step) { + keyValues.insert(result, pair); + } else { + if (value.isValid()) + result->second = value; // replaces the previous value + else if (step == 0 && !hasStartValue && defaultStartValue.isValid()) + result->second = defaultStartValue; // resets to the default start value + else + keyValues.erase(result); // removes the previous value + } + + recalculateCurrentInterval(/*force=*/true); +} + +void QVariantAnimationPrivate::setDefaultStartValue(const QVariant &value) +{ + defaultStartValue = value; + if (!hasStartValue) + setValueAt(0, value); +} + +/*! + Construct a QVariantAnimation object. \a parent is passed to QAbstractAnimation's + constructor. +*/ +QVariantAnimation::QVariantAnimation(QObject *parent) : QAbstractAnimation(*new QVariantAnimationPrivate, parent) +{ +} + +/*! + \internal +*/ +QVariantAnimation::QVariantAnimation(QVariantAnimationPrivate &dd, QObject *parent) : QAbstractAnimation(dd, parent) +{ +} + +/*! + Destroys the animation. +*/ +QVariantAnimation::~QVariantAnimation() +{ +} + +/*! + \property QVariantAnimation::easingCurve + \brief the easing curve of the animation + + This property defines the easing curve of the animation. By + default, a linear easing curve is used, resulting in linear + interpolation. Other curves are provided, for instance, + QEasingCurve::InCirc, which provides a circular entry curve. + Another example is QEasingCurve::InOutElastic, which provides an + elastic effect on the values of the interpolated variant. + + The easing curve is used with the interpolator, the interpolated() + virtual function, the animation's duration, and iterationCount, to + control how the current value changes as the animation progresses. +*/ +QEasingCurve QVariantAnimation::easingCurve() const +{ + Q_D(const QVariantAnimation); + return d->easing; +} + +void QVariantAnimation::setEasingCurve(const QEasingCurve &easing) +{ + Q_D(QVariantAnimation); + d->easing = easing; + d->recalculateCurrentInterval(); +} + +Q_GLOBAL_STATIC(QVector<QVariantAnimation::Interpolator>, registeredInterpolators) +Q_GLOBAL_STATIC(QReadWriteLock, registeredInterpolatorsLock) + +/*! + \fn void qRegisterAnimationInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress)) + \relates QVariantAnimation + \threadsafe + + Registers a custom interpolator \a func for the template type \c{T}. + The interpolator has to be registered before the animation is constructed. + To unregister (and use the default interpolator) set \a func to 0. + */ + +/*! + \internal + \typedef QVariantAnimation::Interpolator + + This is a typedef for a pointer to a function with the following + signature: + \code + QVariant myInterpolator(const QVariant &from, const QVariant &to, qreal progress); + \endcode + +*/ + +/*! \internal + * Registers a custom interpolator \a func for the specific \a interpolationType. + * The interpolator has to be registered before the animation is constructed. + * To unregister (and use the default interpolator) set \a func to 0. + */ +void QVariantAnimation::registerInterpolator(QVariantAnimation::Interpolator func, int interpolationType) +{ + // will override any existing interpolators + QWriteLocker locker(registeredInterpolatorsLock()); + if (int(interpolationType) >= registeredInterpolators()->count()) + registeredInterpolators()->resize(int(interpolationType) + 1); + registeredInterpolators()->replace(interpolationType, func); +} + + +template<typename T> static inline QVariantAnimation::Interpolator castToInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress)) +{ + return reinterpret_cast<QVariantAnimation::Interpolator>(func); +} + +QVariantAnimation::Interpolator QVariantAnimationPrivate::getInterpolator(int interpolationType) +{ + QReadLocker locker(registeredInterpolatorsLock()); + QVariantAnimation::Interpolator ret = 0; + if (interpolationType < registeredInterpolators()->count()) { + ret = registeredInterpolators()->at(interpolationType); + if (ret) return ret; + } + + switch(interpolationType) + { + case QMetaType::Int: + return castToInterpolator(_q_interpolateVariant<int>); + case QMetaType::Double: + return castToInterpolator(_q_interpolateVariant<double>); + case QMetaType::Float: + return castToInterpolator(_q_interpolateVariant<float>); + case QMetaType::QLine: + return castToInterpolator(_q_interpolateVariant<QLine>); + case QMetaType::QLineF: + return castToInterpolator(_q_interpolateVariant<QLineF>); + case QMetaType::QPoint: + return castToInterpolator(_q_interpolateVariant<QPoint>); + case QMetaType::QPointF: + return castToInterpolator(_q_interpolateVariant<QPointF>); + case QMetaType::QSize: + return castToInterpolator(_q_interpolateVariant<QSize>); + case QMetaType::QSizeF: + return castToInterpolator(_q_interpolateVariant<QSizeF>); + case QMetaType::QRect: + return castToInterpolator(_q_interpolateVariant<QRect>); + case QMetaType::QRectF: + return castToInterpolator(_q_interpolateVariant<QRectF>); + default: + return 0; //this type is not handled + } +} + +/*! + \property QVariantAnimation::duration + \brief the duration of the animation + + This property describes the duration in milliseconds of the + animation. The default duration is 250 milliseconds. + + \sa QAbstractAnimation::duration() + */ +int QVariantAnimation::duration() const +{ + Q_D(const QVariantAnimation); + return d->duration; +} + +void QVariantAnimation::setDuration(int msecs) +{ + Q_D(QVariantAnimation); + if (msecs < 0) { + qWarning("QVariantAnimation::setDuration: cannot set a negative duration"); + return; + } + if (d->duration == msecs) + return; + d->duration = msecs; + d->recalculateCurrentInterval(); +} + +/*! + \property QVariantAnimation::startValue + \brief the optional start value of the animation + + This property describes the optional start value of the animation. If + omitted, or if a null QVariant is assigned as the start value, the + animation will use the current position of the end when the animation + is started. + + \sa endValue +*/ +QVariant QVariantAnimation::startValue() const +{ + return keyValueAt(0); +} + +void QVariantAnimation::setStartValue(const QVariant &value) +{ + setKeyValueAt(0, value); +} + +/*! + \property QVariantAnimation::endValue + \brief the end value of the animation + + This property describes the end value of the animation. + + \sa startValue + */ +QVariant QVariantAnimation::endValue() const +{ + return keyValueAt(1); +} + +void QVariantAnimation::setEndValue(const QVariant &value) +{ + setKeyValueAt(1, value); +} + + +/*! + Returns the key frame value for the given \a step. The given \a step + must be in the range 0 to 1. If there is no KeyValue for \a step, + it returns an invalid QVariant. + + \sa keyValues(), setKeyValueAt() +*/ +QVariant QVariantAnimation::keyValueAt(qreal step) const +{ + Q_D(const QVariantAnimation); + if (step == 0 && !d->hasStartValue) + return QVariant(); //special case where we don't have an explicit startValue + + return d->valueAt(step); +} + +/*! + \typedef QVariantAnimation::KeyValue + + This is a typedef for QPair<qreal, QVariant>. +*/ +/*! + \typedef QVariantAnimation::KeyValues + + This is a typedef for QVector<KeyValue> +*/ + +/*! + Creates a key frame at the given \a step with the given \a value. + The given \a step must be in the range 0 to 1. + + \sa setKeyValues(), keyValueAt() +*/ +void QVariantAnimation::setKeyValueAt(qreal step, const QVariant &value) +{ + Q_D(QVariantAnimation); + if (step == 0) + d->hasStartValue = value.isValid(); + d->setValueAt(step, value); +} + +/*! + Returns the key frames of this animation. + + \sa keyValueAt(), setKeyValues() +*/ +QVariantAnimation::KeyValues QVariantAnimation::keyValues() const +{ + Q_D(const QVariantAnimation); + QVariantAnimation::KeyValues ret = d->keyValues; + //in case we added the default start value, we remove it + if (!d->hasStartValue && !ret.isEmpty() && ret.at(0).first == 0) + ret.remove(0); + return ret; +} + +/*! + Replaces the current set of key frames with the given \a keyValues. + the step of the key frames must be in the range 0 to 1. + + \sa keyValues(), keyValueAt() +*/ +void QVariantAnimation::setKeyValues(const KeyValues &keyValues) +{ + Q_D(QVariantAnimation); + d->keyValues = keyValues; + qSort(d->keyValues.begin(), d->keyValues.end(), animationValueLessThan); + d->hasStartValue = !d->keyValues.isEmpty() && d->keyValues.at(0).first == 0; + d->recalculateCurrentInterval(/*force=*/true); +} + +/*! + \property QVariantAnimation::currentValue + \brief the current value of the animation. + + This property describes the current value; an interpolated value + between the \l{startValue}{start value} and the \l{endValue}{end + value}, using the current time for progress. The value itself is + obtained from interpolated(), which is called repeatedly as the + animation is running. + + QVariantAnimation calls the virtual updateCurrentValue() function + when the current value changes. This is particularly useful for + subclasses that need to track updates. For example, + QPropertyAnimation uses this function to animate Qt \l{Qt's + Property System}{properties}. + + \sa startValue, endValue +*/ +QVariant QVariantAnimation::currentValue() const +{ + Q_D(const QVariantAnimation); + if (!d->currentValue.isValid()) + const_cast<QVariantAnimationPrivate*>(d)->recalculateCurrentInterval(); + return d->currentValue; +} + +/*! + \reimp + */ +bool QVariantAnimation::event(QEvent *event) +{ + return QAbstractAnimation::event(event); +} + +/*! + \reimp +*/ +void QVariantAnimation::updateState(QAbstractAnimation::State oldState, + QAbstractAnimation::State newState) +{ + Q_UNUSED(oldState); + Q_UNUSED(newState); +} + +/*! + + This virtual function returns the linear interpolation between + variants \a from and \a to, at \a progress, usually a value + between 0 and 1. You can reimplement this function in a subclass + of QVariantAnimation to provide your own interpolation algorithm. + + Note that in order for the interpolation to work with a + QEasingCurve that return a value smaller than 0 or larger than 1 + (such as QEasingCurve::InBack) you should make sure that it can + extrapolate. If the semantic of the datatype does not allow + extrapolation this function should handle that gracefully. + + You should call the QVariantAnimation implementation of this + function if you want your class to handle the types already + supported by Qt (see class QVariantAnimation description for a + list of supported types). + + \sa QEasingCurve + */ +QVariant QVariantAnimation::interpolated(const QVariant &from, const QVariant &to, qreal progress) const +{ + return d_func()->interpolator(from.constData(), to.constData(), progress); +} + +/*! + \reimp + */ +void QVariantAnimation::updateCurrentTime(int msecs) +{ + Q_UNUSED(msecs); + d_func()->recalculateCurrentInterval(); +} + +QT_END_NAMESPACE + +#include "moc_qvariantanimation.cpp" + +#endif //QT_NO_ANIMATION diff --git a/src/corelib/animation/qvariantanimation.h b/src/corelib/animation/qvariantanimation.h new file mode 100644 index 0000000..3ae172f --- /dev/null +++ b/src/corelib/animation/qvariantanimation.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QANIMATION_H +#define QANIMATION_H + +#include <QtCore/qeasingcurve.h> +#include <QtCore/qabstractanimation.h> +#include <QtCore/qvector.h> +#include <QtCore/qvariant.h> +#include <QtCore/qpair.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_ANIMATION + +class QVariantAnimationPrivate; +class Q_CORE_EXPORT QVariantAnimation : public QAbstractAnimation +{ + Q_OBJECT + Q_PROPERTY(QVariant startValue READ startValue WRITE setStartValue) + Q_PROPERTY(QVariant endValue READ endValue WRITE setEndValue) + Q_PROPERTY(QVariant currentValue READ currentValue NOTIFY currentValueChanged) + Q_PROPERTY(int duration READ duration WRITE setDuration) + Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve) + +public: + typedef QPair<qreal, QVariant> KeyValue; + typedef QVector<KeyValue> KeyValues; + + QVariantAnimation(QObject *parent = 0); + ~QVariantAnimation(); + + QVariant startValue() const; + void setStartValue(const QVariant &value); + + QVariant endValue() const; + void setEndValue(const QVariant &value); + + QVariant keyValueAt(qreal step) const; + void setKeyValueAt(qreal step, const QVariant &value); + + KeyValues keyValues() const; + void setKeyValues(const KeyValues &values); + + QVariant currentValue() const; + + int duration() const; + void setDuration(int msecs); + + QEasingCurve easingCurve() const; + void setEasingCurve(const QEasingCurve &easing); + + typedef QVariant (*Interpolator)(const void *from, const void *to, qreal progress); + +Q_SIGNALS: + void valueChanged(const QVariant &value); + +protected: + QVariantAnimation(QVariantAnimationPrivate &dd, QObject *parent = 0); + bool event(QEvent *event); + + void updateCurrentTime(int msecs); + void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); + + virtual void updateCurrentValue(const QVariant &value) = 0; + virtual QVariant interpolated(const QVariant &from, const QVariant &to, qreal progress) const; + +private: + template <typename T> friend void qRegisterAnimationInterpolator(QVariant (*func)(const T &, const T &, qreal)); + static void registerInterpolator(Interpolator func, int interpolationType); + + Q_DISABLE_COPY(QVariantAnimation) + Q_DECLARE_PRIVATE(QVariantAnimation) +}; + +template <typename T> +void qRegisterAnimationInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress)) { + QVariantAnimation::registerInterpolator(reinterpret_cast<QVariantAnimation::Interpolator>(func), qMetaTypeId<T>()); +} + +#endif //QT_NO_ANIMATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QANIMATION_H diff --git a/src/corelib/io/qfileinfo_p.h b/src/corelib/animation/qvariantanimation_p.h index 7d66581..2c559cf 100644 --- a/src/corelib/io/qfileinfo_p.h +++ b/src/corelib/animation/qvariantanimation_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QFILEINFO_P_H -#define QFILEINFO_P_H +#ifndef QANIMATION_P_H +#define QANIMATION_P_H // // W A R N I N G @@ -53,93 +53,70 @@ // We mean it. // -#include "qfileinfo.h" +#include "qvariantanimation.h" +#include <QtCore/qeasingcurve.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qvector.h> + +#include "qabstractanimation_p.h" QT_BEGIN_NAMESPACE -class QFileInfoPrivate +class QVariantAnimationPrivate : public QAbstractAnimationPrivate { + Q_DECLARE_PUBLIC(QVariantAnimation) public: - QFileInfoPrivate(const QFileInfo *copy=0); - ~QFileInfoPrivate(); - - void initFileEngine(const QString &); - - enum Access { - ReadAccess, - WriteAccess, - ExecuteAccess - }; - bool hasAccess(Access access) const; - - uint getFileFlags(QAbstractFileEngine::FileFlags) const; - QDateTime &getFileTime(QAbstractFileEngine::FileTime) const; - QString getFileName(QAbstractFileEngine::FileName) const; - - enum { - CachedFileFlags = 0x01, - CachedLinkTypeFlag = 0x02, - CachedBundleTypeFlag= 0x04, - CachedMTime = 0x10, - CachedCTime = 0x20, - CachedATime = 0x40, - CachedSize = 0x08 - }; - - struct Data + + QVariantAnimationPrivate(); + + static QVariantAnimationPrivate *get(QVariantAnimation *q) { - inline Data() - : ref(1), fileEngine(0), cache_enabled(1) - { - clear(); - } - - inline Data(const Data ©) - : ref(1), fileEngine(QAbstractFileEngine::create(copy.fileName)), - fileName(copy.fileName), cache_enabled(copy.cache_enabled) - { - clear(); - } - - inline ~Data() - { - delete fileEngine; - } - - inline void clear() - { - fileNames.clear(); - fileFlags = 0; - cachedFlags = 0; - } - - mutable QAtomicInt ref; - - QAbstractFileEngine *fileEngine; - mutable QString fileName; - mutable QHash<int, QString> fileNames; - mutable uint cachedFlags : 31; - mutable uint cache_enabled : 1; - mutable uint fileFlags; - mutable qint64 fileSize; - mutable QDateTime fileTimes[3]; - - inline bool getCachedFlag(uint c) const - { return cache_enabled ? (cachedFlags & c) : 0; } - - inline void setCachedFlag(uint c) - { if (cache_enabled) cachedFlags |= c; } - } *data; - - inline void reset() { - detach(); - data->clear(); + return q->d_func(); } - void detach(); + void setDefaultStartValue(const QVariant &value); + + int duration; + QEasingCurve easing; + + QVariantAnimation::KeyValues keyValues; + QVariant currentValue; + QVariant defaultStartValue; + bool hasStartValue; + + //this is used to keep track of the KeyValue interval in which we currently are + struct + { + QVariantAnimation::KeyValue start, end; + } currentInterval; + + QVariantAnimation::Interpolator interpolator; + + const quint32 changedSignalMask; + + void setCurrentValueForProgress(const qreal progress); + void recalculateCurrentInterval(bool force=false); + void setValueAt(qreal, const QVariant &); + QVariant valueAt(qreal step) const; + void convertValues(int t); + + void updateInterpolator(); + + static QVariantAnimation::Interpolator getInterpolator(int interpolationType); }; +//this should make the interpolation faster +template<typename T> inline T _q_interpolate(const T &f, const T &t, qreal progress) +{ + return T(f + (t - f) * progress); +} + +template<typename T > inline QVariant _q_interpolateVariant(const T &from, const T &to, qreal progress) +{ + return _q_interpolate(from, to, progress); +} + QT_END_NAMESPACE -#endif +#endif //QANIMATION_P_H diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index 6e8ffa1..51ca43e 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -1508,9 +1508,14 @@ QString QTextDecoder::toUnicode(const QByteArray &ba) /*! \since 4.4 - Tries to detect the encoding of the provided snippet of HTML in the given byte array, \a ba, - and returns a QTextCodec instance that is capable of decoding the html to unicode. - If the codec cannot be detected from the content provided, \a defaultCodec is returned. + Tries to detect the encoding of the provided snippet of HTML in + the given byte array, \a ba, by checking the BOM (Byte Order Mark) + and the content-type meta header and returns a QTextCodec instance + that is capable of decoding the html to unicode. If the codec + cannot be detected from the content provided, \a defaultCodec is + returned. + + \sa codecForUtfText */ QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba, QTextCodec *defaultCodec) { @@ -1518,15 +1523,8 @@ QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba, QTextCodec *defaultCo int pos; QTextCodec *c = 0; - if (ba.size() > 1 && (((uchar)ba[0] == 0xfe && (uchar)ba[1] == 0xff) - || ((uchar)ba[0] == 0xff && (uchar)ba[1] == 0xfe))) { - c = QTextCodec::codecForMib(1015); // utf16 - } else if (ba.size() > 2 - && (uchar)ba[0] == 0xef - && (uchar)ba[1] == 0xbb - && (uchar)ba[2] == 0xbf) { - c = QTextCodec::codecForMib(106); // utf-8 - } else { + c = QTextCodec::codecForUtfText(ba, c); + if (!c) { QByteArray header = ba.left(512).toLower(); if ((pos = header.indexOf("http-equiv=")) != -1) { pos = header.indexOf("charset=", pos) + int(strlen("charset=")); @@ -1554,6 +1552,61 @@ QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba) return codecForHtml(ba, QTextCodec::codecForMib(/*Latin 1*/ 4)); } +/*! + \since 4.6 + + Tries to detect the encoding of the provided snippet \a ba by + using the BOM (Byte Order Mark) and returns a QTextCodec instance + that is capable of decoding the text to unicode. If the codec + cannot be detected from the content provided, \a defaultCodec is + returned. + + \sa codecForHtml +*/ +QTextCodec *QTextCodec::codecForUtfText(const QByteArray &ba, QTextCodec *defaultCodec) +{ + const uint arraySize = ba.size(); + + if (arraySize > 3) { + if ((uchar)ba[0] == 0x00 + && (uchar)ba[1] == 0x00 + && (uchar)ba[2] == 0xFE + && (uchar)ba[3] == 0xFF) + return QTextCodec::codecForMib(1018); // utf-32 be + else if ((uchar)ba[0] == 0xFF + && (uchar)ba[1] == 0xFE + && (uchar)ba[2] == 0x00 + && (uchar)ba[3] == 0x00) + return QTextCodec::codecForMib(1019); // utf-32 le + } + + if (arraySize < 2) + return defaultCodec; + if ((uchar)ba[0] == 0xfe && (uchar)ba[1] == 0xff) + return QTextCodec::codecForMib(1013); // utf16 be + else if ((uchar)ba[0] == 0xff && (uchar)ba[1] == 0xfe) + return QTextCodec::codecForMib(1014); // utf16 le + + if (arraySize < 3) + return defaultCodec; + if ((uchar)ba[0] == 0xef + && (uchar)ba[1] == 0xbb + && (uchar)ba[2] == 0xbf) + return QTextCodec::codecForMib(106); // utf-8 + + return defaultCodec; +} + +/*! + \overload + + If the codec cannot be detected, this overload returns a Latin-1 QTextCodec. +*/ +QTextCodec *QTextCodec::codecForUtfText(const QByteArray &ba) +{ + return codecForUtfText(ba, QTextCodec::codecForMib(/*Latin 1*/ 4)); +} + /*! \internal \since 4.3 diff --git a/src/corelib/codecs/qtextcodec.h b/src/corelib/codecs/qtextcodec.h index e32650f..83097a5 100644 --- a/src/corelib/codecs/qtextcodec.h +++ b/src/corelib/codecs/qtextcodec.h @@ -82,6 +82,9 @@ public: static QTextCodec *codecForHtml(const QByteArray &ba); static QTextCodec *codecForHtml(const QByteArray &ba, QTextCodec *defaultCodec); + static QTextCodec *codecForUtfText(const QByteArray &ba); + static QTextCodec *codecForUtfText(const QByteArray &ba, QTextCodec *defaultCodec); + QTextDecoder* makeDecoder() const; QTextEncoder* makeEncoder() const; diff --git a/src/corelib/codecs/qtsciicodec_p.h b/src/corelib/codecs/qtsciicodec_p.h index 8f11e48..425e7fd 100644 --- a/src/corelib/codecs/qtsciicodec_p.h +++ b/src/corelib/codecs/qtsciicodec_p.h @@ -88,7 +88,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_CODECS -class Q_CORE_EXPORT QTsciiCodec : public QTextCodec { +class QTsciiCodec : public QTextCodec { public: ~QTsciiCodec(); diff --git a/src/corelib/concurrent/qfuturewatcher_p.h b/src/corelib/concurrent/qfuturewatcher_p.h index 324839d..d53a1bd 100644 --- a/src/corelib/concurrent/qfuturewatcher_p.h +++ b/src/corelib/concurrent/qfuturewatcher_p.h @@ -63,8 +63,8 @@ QT_BEGIN_NAMESPACE class QFutureWatcherBase; -class Q_CORE_EXPORT QFutureWatcherBasePrivate : public QObjectPrivate, - public QFutureCallOutInterface +class QFutureWatcherBasePrivate : public QObjectPrivate, + public QFutureCallOutInterface { Q_DECLARE_PUBLIC(QFutureWatcherBase) diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index f99d57f..db51d43 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -5,6 +5,7 @@ DEFINES += QT_BUILD_CORE_LIB QT_NO_USING_NAMESPACE win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x67000000 include(../qbase.pri) +include(animation/animation.pri) include(arch/arch.pri) include(concurrent/concurrent.pri) include(global/global.pri) @@ -14,6 +15,7 @@ include(io/io.pri) include(plugin/plugin.pri) include(kernel/kernel.pri) include(codecs/codecs.pri) +include(statemachine/statemachine.pri) include(xml/xml.pri) mac|darwin:LIBS += -framework ApplicationServices diff --git a/src/corelib/global/qfeatures.h b/src/corelib/global/qfeatures.h index 2189723..f11c9df 100644 --- a/src/corelib/global/qfeatures.h +++ b/src/corelib/global/qfeatures.h @@ -361,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 diff --git a/src/corelib/global/qfeatures.txt b/src/corelib/global/qfeatures.txt index 40ccb15..23ec7b0 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 @@ -1150,6 +1157,14 @@ Requires: PROPERTIES Name: Accessibility SeeAlso: ??? +Feature: ANIMATION +Description: Provides a framework for animations. +Section: Utilities +Requires: PROPERTIES +Name: Animation +SeeAlso: ??? + + # SVG Feature: SVG diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index f767bb9..763935e 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -2010,7 +2010,7 @@ void qt_message_output(QtMsgType msgType, const char *buf) mac_default_handler(buf); #elif defined(Q_OS_WINCE) QString fstr = QString::fromLatin1(buf); - fstr += QLatin1String("\n"); + fstr += QLatin1Char('\n'); OutputDebugString(reinterpret_cast<const wchar_t *> (fstr.utf16())); #else fprintf(stderr, "%s\n", buf); @@ -2268,7 +2268,7 @@ bool qputenv(const char *varName, const QByteArray& value) return _putenv_s(varName, value.constData()) == 0; #else QByteArray buffer(varName); - buffer += "="; + buffer += '='; buffer += value; return putenv(qstrdup(buffer.constData())) == 0; #endif @@ -2984,7 +2984,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 36d87e2..3990c7d 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.2" +#define QT_VERSION_STR "4.6.0" /* QT_VERSION is (major << 16) + (minor << 8) + patch. */ -#define QT_VERSION 0x040502 +#define QT_VERSION 0x040600 /* can be used like #if (QT_VERSION >= QT_VERSION_CHECK(4, 4, 0)) */ @@ -747,6 +747,10 @@ namespace QT_NAMESPACE {} # endif #elif defined(Q_OS_WINCE) # define Q_WS_WIN32 +# define Q_WS_WINCE +# if defined(Q_OS_WINCE_WM) +# define Q_WS_WINCE_WM +# endif #elif defined(Q_OS_OS2) # define Q_WS_PM # error "Qt does not work with OS/2 Presentation Manager or Workplace Shell" @@ -764,10 +768,11 @@ namespace QT_NAMESPACE {} # endif #endif -#if defined(Q_WS_WIN16) || defined(Q_WS_WIN32) +#if defined(Q_WS_WIN16) || defined(Q_WS_WIN32) || defined(Q_WS_WINCE) # define Q_WS_WIN #endif + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -821,17 +826,12 @@ typedef quint64 qulonglong; sizeof(void *) == sizeof(quintptr) && sizeof(void *) == sizeof(qptrdiff) */ -template <int> class QUintForSize { private: typedef void Type; }; -template <> class QUintForSize<4> { public: typedef quint32 Type; }; -template <> class QUintForSize<8> { public: typedef quint64 Type; }; -template <typename T> class QUintForType : public QUintForSize<sizeof(T)> { }; -typedef QUintForType<void *>::Type quintptr; - -template <int> class QIntForSize { private: typedef void Type; }; -template <> class QIntForSize<4> { public: typedef qint32 Type; }; -template <> class QIntForSize<8> { public: typedef qint64 Type; }; -template <typename T> class QIntForType : public QIntForSize<sizeof(T)> { }; -typedef QIntForType<void *>::Type qptrdiff; +template <int> struct QIntegerForSize; +template <> struct QIntegerForSize<4> { typedef quint32 Unsigned; typedef qint32 Signed; }; +template <> struct QIntegerForSize<8> { typedef quint64 Unsigned; typedef qint64 Signed; }; +template <class T> struct QIntegerForSizeof: QIntegerForSize<sizeof(T)> { }; +typedef QIntegerForSizeof<void*>::Unsigned quintptr; +typedef QIntegerForSizeof<void*>::Signed qptrdiff; /* Useful type definitions for Qt @@ -1706,6 +1706,22 @@ static inline bool qFuzzyCompare(float p1, float p2) return (qAbs(p1 - p2) <= 0.00001f * qMin(qAbs(p1), qAbs(p2))); } +/*! + \internal +*/ +static inline bool qFuzzyIsNull(double d) +{ + return qAbs(d) <= 0.000000000001; +} + +/*! + \internal +*/ +static inline bool qFuzzyIsNull(float f) +{ + return qAbs(f) <= 0.00001f; +} + /* This function tests a double for a null value. It doesn't check whether the actual value is 0 or close to 0, but whether @@ -1837,7 +1853,7 @@ enum { /* TYPEINFO flags */ #define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \ template <> \ -class QTypeInfo<TYPE> \ +class QTypeInfo<TYPE > \ { \ public: \ enum { \ diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index 29e356e..7f6ff20 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -434,14 +434,14 @@ QLibraryInfo::location(LibraryLocation loc) #else if (QCoreApplication::instance()) { #ifdef Q_OS_MAC - CFBundleRef bundleRef = CFBundleGetMainBundle(); - if (bundleRef) { - QCFType<CFURLRef> urlRef = CFBundleCopyBundleURL(bundleRef); - if (urlRef) { - QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle); - return QDir::cleanPath(path + QLatin1String("/Contents")); - } - } + CFBundleRef bundleRef = CFBundleGetMainBundle(); + if (bundleRef) { + QCFType<CFURLRef> urlRef = CFBundleCopyBundleURL(bundleRef); + if (urlRef) { + QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle); + return QDir::cleanPath(path + QLatin1String("/Contents/") + ret); + } + } #endif return QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(ret); } else { diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index a519f4a..9e53d89 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -188,6 +188,12 @@ public: #endif }; + enum TileRule { + Stretch, + Repeat, + Round + }; + // Text formatting flags for QPainter::drawText and QLabel. // The following two enums can be combined to one integer which // is passed as 'flags' to drawText and qt_format_text. @@ -497,6 +503,8 @@ public: AA_NativeWindows = 3, AA_DontCreateNativeWidgetSiblings = 4, AA_MacPluginApplication = 5, + AA_DontUseNativeMenuBar = 6, + AA_MacDontSwapCtrlAndMeta = 7, // Add new attributes before this line AA_AttributeCount @@ -1319,7 +1327,8 @@ public: DirectConnection, QueuedConnection, AutoCompatConnection, - BlockingQueuedConnection + BlockingQueuedConnection, + UniqueConnection = 0x80 }; enum ShortcutContext { diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 3690d4b..8f37e25 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -10,9 +10,9 @@ HEADERS += \ io/qdiriterator.h \ io/qfile.h \ io/qfileinfo.h \ - io/qfileinfo_p.h \ io/qiodevice.h \ io/qiodevice_p.h \ + io/qnoncontiguousbytedevice_p.h \ io/qprocess.h \ io/qprocess_p.h \ io/qtextstream.h \ @@ -38,6 +38,7 @@ SOURCES += \ io/qfile.cpp \ io/qfileinfo.cpp \ io/qiodevice.cpp \ + io/qnoncontiguousbytedevice.cpp \ io/qprocess.cpp \ io/qtextstream.cpp \ io/qtemporaryfile.cpp \ 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/qdebug.h b/src/corelib/io/qdebug.h index 8334146..54121f6 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -51,6 +51,7 @@ #include <QtCore/qstring.h> #include <QtCore/qvector.h> #include <QtCore/qset.h> +#include <QtCore/qcontiguouscache.h> QT_BEGIN_HEADER @@ -84,11 +85,11 @@ public: delete stream; } } - inline QDebug &space() { stream->space = true; stream->ts << " "; return *this; } + inline QDebug &space() { stream->space = true; stream->ts << ' '; return *this; } inline QDebug &nospace() { stream->space = false; return *this; } - inline QDebug &maybeSpace() { if (stream->space) stream->ts << " "; return *this; } + inline QDebug &maybeSpace() { if (stream->space) stream->ts << ' '; return *this; } - inline QDebug &operator<<(QChar t) { stream->ts << "\'" << t << "\'"; return maybeSpace(); } + inline QDebug &operator<<(QChar t) { stream->ts << '\'' << t << '\''; return maybeSpace(); } inline QDebug &operator<<(QBool t) { stream->ts << (bool(t) ? "true" : "false"); return maybeSpace(); } inline QDebug &operator<<(bool t) { stream->ts << (t ? "true" : "false"); return maybeSpace(); } inline QDebug &operator<<(char t) { stream->ts << t; return maybeSpace(); } @@ -105,10 +106,10 @@ public: inline QDebug &operator<<(float t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(double t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(const char* t) { stream->ts << QString::fromAscii(t); return maybeSpace(); } - inline QDebug &operator<<(const QString & t) { stream->ts << "\"" << t << "\""; return maybeSpace(); } + inline QDebug &operator<<(const QString & t) { stream->ts << '\"' << t << '\"'; return maybeSpace(); } inline QDebug &operator<<(const QStringRef & t) { return operator<<(t.toString()); } - inline QDebug &operator<<(const QLatin1String &t) { stream->ts << "\"" << t.latin1() << "\""; return maybeSpace(); } - inline QDebug &operator<<(const QByteArray & t) { stream->ts << "\"" << t << "\""; return maybeSpace(); } + inline QDebug &operator<<(const QLatin1String &t) { stream->ts << '\"' << t.latin1() << '\"'; return maybeSpace(); } + inline QDebug &operator<<(const QByteArray & t) { stream->ts << '\"' << t << '\"'; return maybeSpace(); } inline QDebug &operator<<(const void * t) { stream->ts << t; return maybeSpace(); } inline QDebug &operator<<(QTextStreamFunction f) { stream->ts << f; @@ -158,13 +159,13 @@ template <class T> inline QDebug operator<<(QDebug debug, const QList<T> &list) #endif { - debug.nospace() << "("; + debug.nospace() << '('; for (Q_TYPENAME QList<T>::size_type i = 0; i < list.count(); ++i) { if (i) debug << ", "; debug << list.at(i); } - debug << ")"; + debug << ')'; return debug.space(); } @@ -191,9 +192,9 @@ inline QDebug operator<<(QDebug debug, const QMap<aKey, aT> &map) debug.nospace() << "QMap("; for (typename QMap<aKey, aT>::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { - debug << "(" << it.key() << ", " << it.value() << ")"; + debug << '(' << it.key() << ", " << it.value() << ')'; } - debug << ")"; + debug << ')'; return debug.space(); } @@ -208,8 +209,8 @@ inline QDebug operator<<(QDebug debug, const QHash<aKey, aT> &hash) debug.nospace() << "QHash("; for (typename QHash<aKey, aT>::const_iterator it = hash.constBegin(); it != hash.constEnd(); ++it) - debug << "(" << it.key() << ", " << it.value() << ")"; - debug << ")"; + debug << '(' << it.key() << ", " << it.value() << ')'; + debug << ')'; return debug.space(); } @@ -221,7 +222,7 @@ template <class T1, class T2> inline QDebug operator<<(QDebug debug, const QPair<T1, T2> &pair) #endif { - debug.nospace() << "QPair(" << pair.first << "," << pair.second << ")"; + debug.nospace() << "QPair(" << pair.first << ',' << pair.second << ')'; return debug.space(); } @@ -232,6 +233,24 @@ inline QDebug operator<<(QDebug debug, const QSet<T> &set) return operator<<(debug, set.toList()); } +#if defined(FORCE_UREF) +template <class T> +inline QDebug &operator<<(QDebug debug, const QContiguousCache<T> &cache) +#else +template <class T> +inline QDebug operator<<(QDebug debug, const QContiguousCache<T> &cache) +#endif +{ + debug.nospace() << "QContiguousCache("; + for (int i = cache.firstIndex(); i <= cache.lastIndex(); ++i) { + debug << cache[i]; + if (i != cache.lastIndex()) + debug << ", "; + } + debug << ')'; + return debug.space(); +} + #if !defined(QT_NO_DEBUG_STREAM) Q_CORE_EXPORT_INLINE QDebug qDebug() { return QDebug(QtDebugMsg); } diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index b7861ba..7d330e6 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -55,6 +55,8 @@ # include "qresource.h" #endif +#include "qvarlengtharray.h" + #include "../kernel/qcoreglobaldata_p.h" #include <stdlib.h> @@ -2054,11 +2056,13 @@ QString QDir::cleanPath(const QString &path) QString name = path; QChar dir_separator = separator(); if(dir_separator != QLatin1Char('/')) - name.replace(dir_separator, QLatin1Char('/')); + name.replace(dir_separator, QLatin1Char('/')); int used = 0, levels = 0; const int len = name.length(); - QVector<QChar> out(len); + QVarLengthArray<QChar> outVector(len); + QChar *out = outVector.data(); + const QChar *p = name.unicode(); for(int i = 0, last = -1, iwrite = 0; i < len; i++) { if(p[i] == QLatin1Char('/')) { @@ -2158,7 +2162,7 @@ QString QDir::cleanPath(const QString &path) if(used == len) ret = name; else - ret = QString(out.data(), used); + ret = QString(out, used); // Strip away last slash except for root directories if (ret.endsWith(QLatin1Char('/')) @@ -2221,7 +2225,7 @@ QStringList QDir::nameFiltersFromString(const QString &nameFilter) \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 13 - If the file name contains characters that cannot be part of a valid C++ function name + If the file name contains characters that cannot be part of a valid C++ function name (such as '-'), they have to be replaced by the underscore character ('_'). Note: This macro cannot be used in a namespace. It should be called from @@ -2413,7 +2417,7 @@ QDebug operator<<(QDebug debug, QDir::Filters filters) if (filters & QDir::System) flags << QLatin1String("System"); if (filters & QDir::CaseSensitive) flags << QLatin1String("CaseSensitive"); } - debug << "QDir::Filters(" << qPrintable(flags.join(QLatin1String("|"))) << ")"; + debug << "QDir::Filters(" << qPrintable(flags.join(QLatin1String("|"))) << ')'; return debug; } @@ -2435,8 +2439,8 @@ QDebug operator<<(QDebug debug, QDir::SortFlags sorting) if (sorting & QDir::LocaleAware) flags << QLatin1String("LocaleAware"); if (sorting & QDir::Type) flags << QLatin1String("Type"); debug << "QDir::SortFlags(" << qPrintable(type) - << "|" - << qPrintable(flags.join(QLatin1String("|"))) << ")"; + << '|' + << qPrintable(flags.join(QLatin1String("|"))) << ')'; } return debug; } @@ -2448,9 +2452,9 @@ QDebug operator<<(QDebug debug, const QDir &dir) << qPrintable(dir.nameFilters().join(QLatin1String(","))) << "}, " << dir.sorting() - << "," + << ',' << dir.filter() - << ")"; + << ')'; return debug.space(); } diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index 10b812b..4deaddb 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -708,6 +708,7 @@ QFile::rename(const QString &newName) d->setError(QFile::RenameError, tr("Destination file exists")); return false; } + unsetError(); close(); if(error() == QFile::NoError) { if (fileEngine()->rename(newName)) { @@ -858,6 +859,7 @@ QFile::copy(const QString &newName) d->setError(QFile::CopyError, tr("Destination file exists")); return false; } + unsetError(); close(); if(error() == QFile::NoError) { if(fileEngine()->copy(newName)) { diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index 96e0f82..36b1ed8 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -537,8 +537,7 @@ QFileInfo &QFileInfo::operator=(const QFileInfo &fileinfo) void QFileInfo::setFile(const QString &file) { - Q_D(QFileInfo); - d->initFileEngine(file); + *this = QFileInfo(file); } /*! @@ -555,8 +554,7 @@ void QFileInfo::setFile(const QString &file) void QFileInfo::setFile(const QFile &file) { - Q_D(QFileInfo); - d->initFileEngine(file.fileName()); + *this = QFileInfo(file.fileName()); } /*! @@ -573,8 +571,7 @@ void QFileInfo::setFile(const QFile &file) void QFileInfo::setFile(const QDir &dir, const QString &file) { - Q_D(QFileInfo); - d->initFileEngine(dir.filePath(file)); + *this = QFileInfo(dir.filePath(file)); } /*! @@ -921,7 +918,7 @@ QString QFileInfo::suffix() const \bold{Note:} The QDir returned always corresponds to the object's parent directory, even if the QFileInfo represents a directory. - + For each of the follwing, dir() returns a QDir for \c{"~/examples/191697"}. diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index e073a08..65cfb71 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -118,7 +118,7 @@ public: void stop(); -private slots: +private Q_SLOTS: void timeout(); }; diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index 459725c..8ea48d1 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -932,6 +932,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/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp index b22d6c7..0b87e2b 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -433,7 +433,7 @@ static QString nativeAbsoluteFilePathW(const QString &path) if (retLen != 0) ret = QString::fromUtf16((unsigned short *)buf.data(), retLen); #else - if (path.startsWith(QLatin1String("/")) || path.startsWith(QLatin1String("\\"))) + if (path.startsWith(QLatin1Char('/')) || path.startsWith(QLatin1Char('\\'))) ret = QDir::toNativeSeparators(path); else ret = QDir::toNativeSeparators(QDir::cleanPath(qfsPrivateCurrentDir + QLatin1Char('/') + path)); @@ -1240,7 +1240,7 @@ QString QFSFileEngine::rootPath() QString ret = QString::fromLatin1(qgetenv("SystemDrive").constData()); if(ret.isEmpty()) ret = QLatin1String("c:"); - ret += QLatin1String("/"); + ret += QLatin1Char('/'); #elif defined(Q_OS_OS2EMX) char dir[4]; _abspath(dir, QLatin1String("/"), _MAX_PATH); @@ -1288,19 +1288,17 @@ QFileInfoList QFSFileEngine::drives() exit(1); driveBits &= 0x3ffffff; #endif - char driveName[4]; - - qstrcpy(driveName, "A:/"); + char driveName[] = "A:/"; while(driveBits) { if(driveBits & 1) - ret.append(QString::fromLatin1(driveName).toUpper()); + ret.append(QString::fromLatin1(driveName)); driveName[0]++; driveBits = driveBits >> 1; } return ret; #else - ret.append(QString::fromLatin1("/").toUpper()); + ret.append(QString::fromLatin1("/")); return ret; #endif } @@ -1333,8 +1331,6 @@ bool QFSFileEnginePrivate::doStat() const if (tmpAttributes != -1) { fileAttrib = tmpAttributes; could_stat = true; - } else { - return false; } #endif } else { diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp index efa4b25..8924b77 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 } @@ -1741,7 +1745,7 @@ QDebug operator<<(QDebug debug, QIODevice::OpenMode modes) } qSort(modeList); debug << modeList.join(QLatin1String("|")); - debug << ")"; + debug << ')'; return debug; } #endif diff --git a/src/corelib/io/qnoncontiguousbytedevice.cpp b/src/corelib/io/qnoncontiguousbytedevice.cpp new file mode 100644 index 0000000..f50acdb --- /dev/null +++ b/src/corelib/io/qnoncontiguousbytedevice.cpp @@ -0,0 +1,544 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qnoncontiguousbytedevice_p.h" +#include <QObject> +#include <QBuffer> +#include <QDebug> +#include <QFile> + +QT_BEGIN_NAMESPACE + +/*! + \class QNonContiguousByteDevice + \brief A QNonContiguousByteDevice is a representation of a + file, array or buffer that allows access with a read pointer. + \since 4.6 + + \inmodule QtCore + + The goal of this class is to have a data representation that + allows us to avoid doing a memcpy as we have to do with QIODevice. + + \sa QNonContiguousByteDeviceFactory + + \internal +*/ +/*! + \fn virtual const char* QNonContiguousByteDevice::readPointer(qint64 maximumLength, qint64 &len) + + Return a byte pointer for at most \a maximumLength bytes of that device. + if \a maximumLength is -1, the caller does not care about the length and + the device may return what it desires to. + The actual number of bytes the pointer is valid for is returned in + the \a len variable. + \a len will be -1 if EOF or an error occurs. + If it was really EOF can then afterwards be checked with atEnd() + Returns 0 if it is not possible to read at that position. + + \sa atEnd() + + \internal +*/ +/*! + \fn virtual bool QNonContiguousByteDevice::advanceReadPointer(qint64 amount) + + will advance the internal read pointer by \a amount bytes. + The old readPointer is invalid after this call. + + \sa readPointer() + + \internal +*/ +/*! + \fn virtual bool QNonContiguousByteDevice::atEnd() + + Returns true if everything has been read and the read + pointer cannot be advanced anymore. + + \sa readPointer(), advanceReadPointer(), reset() + + \internal +*/ +/*! + \fn virtual bool QNonContiguousByteDevice::reset() + + Moves the internal read pointer back to the beginning. + Returns false if this was not possible. + + \sa atEnd(), disableReset() + + \internal +*/ +/*! + \fn void QNonContiguousByteDevice::disableReset() + + Disable the reset() call, e.g. it will always + do nothing and return false. + + \sa reset() + + \internal +*/ +/*! + \fn virtual qint64 QNonContiguousByteDevice::size() + + Returns the size of the complete device or -1 if unknown. + May also return less/more than what can be actually read with readPointer() + + \internal +*/ +/*! + \fn void QNonContiguousByteDevice::readyRead() + + Emitted when there is data available + + \internal +*/ +/*! + \fn void QNonContiguousByteDevice::readProgress(qint64 current, qint64 total) + + Emitted when data has been "read" by advancing the read pointer + + \internal +*/ + +QNonContiguousByteDevice::QNonContiguousByteDevice() : QObject((QObject*)0), resetDisabled(false) +{ +}; + +QNonContiguousByteDevice::~QNonContiguousByteDevice() +{ +}; + +void QNonContiguousByteDevice::disableReset() +{ + resetDisabled = true; +} + +QNonContiguousByteDeviceBufferImpl::QNonContiguousByteDeviceBufferImpl(QBuffer *b) : QNonContiguousByteDevice() +{ + buffer = b; + byteArray = QByteArray::fromRawData(buffer->buffer().constData() + buffer->pos(), buffer->size() - buffer->pos()); + arrayImpl = new QNonContiguousByteDeviceByteArrayImpl(&byteArray); + arrayImpl->setParent(this); + connect(arrayImpl, SIGNAL(readyRead()), SIGNAL(readyRead())); + connect(arrayImpl, SIGNAL(readProgress(qint64,qint64)), SIGNAL(readProgress(qint64,qint64))); +} + +QNonContiguousByteDeviceBufferImpl::~QNonContiguousByteDeviceBufferImpl() +{ +} + +const char* QNonContiguousByteDeviceBufferImpl::readPointer(qint64 maximumLength, qint64 &len) +{ + return arrayImpl->readPointer(maximumLength, len); +} + +bool QNonContiguousByteDeviceBufferImpl::advanceReadPointer(qint64 amount) +{ + return arrayImpl->advanceReadPointer(amount); +} + +bool QNonContiguousByteDeviceBufferImpl::atEnd() +{ + return arrayImpl->atEnd(); +} + +bool QNonContiguousByteDeviceBufferImpl::reset() +{ + if (resetDisabled) + return false; + return arrayImpl->reset(); +} + +qint64 QNonContiguousByteDeviceBufferImpl::size() +{ + return arrayImpl->size(); +} + +QNonContiguousByteDeviceByteArrayImpl::QNonContiguousByteDeviceByteArrayImpl(QByteArray *ba) : QNonContiguousByteDevice(), currentPosition(0) +{ + byteArray = ba; +} + +QNonContiguousByteDeviceByteArrayImpl::~QNonContiguousByteDeviceByteArrayImpl() +{ +} + +const char* QNonContiguousByteDeviceByteArrayImpl::readPointer(qint64 maximumLength, qint64 &len) +{ + if (atEnd()) { + len = -1; + return 0; + } + + if (maximumLength != -1) + len = qMin(maximumLength, size() - currentPosition); + else + len = size() - currentPosition; + + return byteArray->constData() + currentPosition; +} + +bool QNonContiguousByteDeviceByteArrayImpl::advanceReadPointer(qint64 amount) +{ + currentPosition += amount; + emit readProgress(currentPosition, size()); + return true; +} + +bool QNonContiguousByteDeviceByteArrayImpl::atEnd() +{ + return currentPosition >= size(); +} + +bool QNonContiguousByteDeviceByteArrayImpl::reset() +{ + if (resetDisabled) + return false; + + currentPosition = 0; + return true; +} + +qint64 QNonContiguousByteDeviceByteArrayImpl::size() +{ + return byteArray->size(); +} + +QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QRingBuffer *rb) + : QNonContiguousByteDevice(), currentPosition(0) +{ + ringBuffer = rb; +} + +QNonContiguousByteDeviceRingBufferImpl::~QNonContiguousByteDeviceRingBufferImpl() +{ +}; + +const char* QNonContiguousByteDeviceRingBufferImpl::readPointer(qint64 maximumLength, qint64 &len) +{ + if (atEnd()) { + len = -1; + return 0; + } + + const char *returnValue = ringBuffer->readPointerAtPosition(currentPosition, len); + + if (maximumLength != -1) + len = qMin(len, maximumLength); + + return returnValue; +}; + +bool QNonContiguousByteDeviceRingBufferImpl::advanceReadPointer(qint64 amount) +{ + currentPosition += amount; + emit readProgress(currentPosition, size()); + return true; +}; + +bool QNonContiguousByteDeviceRingBufferImpl::atEnd() +{ + return currentPosition >= size(); +}; + +bool QNonContiguousByteDeviceRingBufferImpl::reset() +{ + if (resetDisabled) + return false; + + currentPosition = 0; + return true; +}; + +qint64 QNonContiguousByteDeviceRingBufferImpl::size() +{ + return ringBuffer->size(); +}; + +QNonContiguousByteDeviceIoDeviceImpl::QNonContiguousByteDeviceIoDeviceImpl(QIODevice *d) + : QNonContiguousByteDevice(), + currentReadBuffer(0), currentReadBufferSize(16*1024), + currentReadBufferAmount(0), currentReadBufferPosition(0), totalAdvancements(0), + eof(false) +{ + device = d; + initialPosition = d->pos(); + connect(device, SIGNAL(readyRead()), this, SIGNAL(readyRead()), Qt::QueuedConnection); + connect(device, SIGNAL(readChannelFinished()), this, SIGNAL(readyRead()), Qt::QueuedConnection); +}; + +QNonContiguousByteDeviceIoDeviceImpl::~QNonContiguousByteDeviceIoDeviceImpl() +{ + delete currentReadBuffer; +}; + +const char* QNonContiguousByteDeviceIoDeviceImpl::readPointer(qint64 maximumLength, qint64 &len) +{ + if (eof == true) { + len = -1; + return 0; + } + + if (currentReadBuffer == 0) + currentReadBuffer = new QByteArray(currentReadBufferSize, '\0'); // lazy alloc + + if (maximumLength == -1) + maximumLength = currentReadBufferSize; + + if (currentReadBufferAmount - currentReadBufferPosition > 0) { + len = currentReadBufferAmount - currentReadBufferPosition; + return currentReadBuffer->data() + currentReadBufferPosition; + } + + qint64 haveRead = device->read(currentReadBuffer->data(), qMin(maximumLength, currentReadBufferSize)); + + if ((haveRead == -1) || (haveRead == 0 && device->atEnd() && !device->isSequential())) { + eof = true; + len = -1; + // size was unknown before, emit a readProgress with the final size + if (size() == -1) + emit readProgress(totalAdvancements, totalAdvancements); + return 0; + } + + currentReadBufferAmount = haveRead; + currentReadBufferPosition = 0; + + len = haveRead; + return currentReadBuffer->data(); +}; + +bool QNonContiguousByteDeviceIoDeviceImpl::advanceReadPointer(qint64 amount) +{ + totalAdvancements += amount; + + // normal advancement + currentReadBufferPosition += amount; + + // advancing over that what has actually been read before + if (currentReadBufferPosition > currentReadBufferAmount) { + qint64 i = currentReadBufferPosition - currentReadBufferAmount; + while (i > 0) { + if (device->getChar(0) == false) { + emit readProgress(totalAdvancements - i, size()); + return false; // ### FIXME handle eof + } + i--; + } + + currentReadBufferPosition = 0; + currentReadBufferAmount = 0; + } + + if (size() == -1) + emit readProgress(totalAdvancements, totalAdvancements); + else + emit readProgress(totalAdvancements, size()); + + return true; +}; + +bool QNonContiguousByteDeviceIoDeviceImpl::atEnd() +{ + return eof == true; +}; + +bool QNonContiguousByteDeviceIoDeviceImpl::reset() +{ + if (resetDisabled) + return false; + + if (device->seek(initialPosition)) { + eof = false; // assume eof is false, it will be true after a read has been attempted + return true; + } + + return false; +}; + +qint64 QNonContiguousByteDeviceIoDeviceImpl::size() +{ + // note that this is different from the size() implementation of QIODevice! + + if (device->isSequential()) + return -1; + + return device->size() - initialPosition; +}; + +QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)0) +{ + byteDevice = bd; + connect(bd, SIGNAL(readyRead()), SIGNAL(readyRead())); + + open(ReadOnly); +} + +QByteDeviceWrappingIoDevice::~QByteDeviceWrappingIoDevice() +{ + +} + +bool QByteDeviceWrappingIoDevice::isSequential() const +{ + return (byteDevice->size() == -1); +} + +bool QByteDeviceWrappingIoDevice::atEnd() const +{ + return byteDevice->atEnd(); +} + +bool QByteDeviceWrappingIoDevice::reset() +{ + return byteDevice->reset(); +} + +qint64 QByteDeviceWrappingIoDevice::size() const +{ + if (isSequential()) + return 0; + + return byteDevice->size(); +} + + +qint64 QByteDeviceWrappingIoDevice::readData( char * data, qint64 maxSize) +{ + qint64 len; + const char *readPointer = byteDevice->readPointer(maxSize, len); + if (len == -1) + return -1; + + memcpy(data, readPointer, len); + byteDevice->advanceReadPointer(len); + return len; +} + +qint64 QByteDeviceWrappingIoDevice::writeData( const char* data, qint64 maxSize) +{ + Q_UNUSED(data); + Q_UNUSED(maxSize); + return -1; +} + +/*! + \class QNonContiguousByteDeviceFactory + \since 4.6 + + \inmodule QtCore + + Creates a QNonContiguousByteDevice out of a QIODevice, + QByteArray etc. + + \sa QNonContiguousByteDevice + + \internal +*/ + +/*! + \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *device); + + Create a QNonContiguousByteDevice out of a QIODevice. + For QFile, QBuffer and all other QIoDevice, sequential or not. + + \internal +*/ +QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QIODevice *device) +{ + // shortcut if it is a QBuffer + if (QBuffer* buffer = qobject_cast<QBuffer*>(device)) { + return new QNonContiguousByteDeviceBufferImpl(buffer); + } + + // ### FIXME special case if device is a QFile that supports map() + // then we can actually deal with the file without using read/peek + + // generic QIODevice + return new QNonContiguousByteDeviceIoDeviceImpl(device); // FIXME +}; + +/*! + \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QRingBuffer *ringBuffer); + + Create a QNonContiguousByteDevice out of a QRingBuffer. + + \internal +*/ +QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QRingBuffer *ringBuffer) +{ + return new QNonContiguousByteDeviceRingBufferImpl(ringBuffer); +}; + +/*! + \fn static QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray); + + Create a QNonContiguousByteDevice out of a QByteArray. + + \internal +*/ +QNonContiguousByteDevice* QNonContiguousByteDeviceFactory::create(QByteArray *byteArray) +{ + return new QNonContiguousByteDeviceByteArrayImpl(byteArray); +}; + +/*! + \fn static QIODevice* QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice* byteDevice); + + Wrap the \a byteDevice (possibly again) into a QIODevice. + + \internal +*/ +QIODevice* QNonContiguousByteDeviceFactory::wrap(QNonContiguousByteDevice* byteDevice) +{ + // ### FIXME if it already has been based on QIoDevice, we could that one out again + // and save some calling + + // needed for FTP backend + + return new QByteDeviceWrappingIoDevice(byteDevice); +} + +QT_END_NAMESPACE + diff --git a/src/corelib/io/qnoncontiguousbytedevice_p.h b/src/corelib/io/qnoncontiguousbytedevice_p.h new file mode 100644 index 0000000..acfc6eb --- /dev/null +++ b/src/corelib/io/qnoncontiguousbytedevice_p.h @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QNONCONTIGUOUSBYTEDEVICE_H +#define QNONCONTIGUOUSBYTEDEVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QObject> +#include <QtCore/qbytearray.h> +#include <QtCore/qbuffer.h> +#include <QtCore/qiodevice.h> +#include "private/qringbuffer_p.h" + +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QNonContiguousByteDevice : public QObject +{ + Q_OBJECT +public: + virtual const char* readPointer(qint64 maximumLength, qint64 &len) = 0; + virtual bool advanceReadPointer(qint64 amount) = 0; + virtual bool atEnd() = 0; + virtual bool reset() = 0; + void disableReset(); + virtual qint64 size() = 0; + +protected: + QNonContiguousByteDevice(); + virtual ~QNonContiguousByteDevice(); + + bool resetDisabled; +Q_SIGNALS: + void readyRead(); + void readProgress(qint64 current, qint64 total); +}; + +class Q_CORE_EXPORT QNonContiguousByteDeviceFactory +{ +public: + static QNonContiguousByteDevice* create(QIODevice *device); + static QNonContiguousByteDevice* create(QByteArray *byteArray); + static QNonContiguousByteDevice* create(QRingBuffer *ringBuffer); + static QIODevice* wrap(QNonContiguousByteDevice* byteDevice); +}; + +// the actual implementations +// + +class QNonContiguousByteDeviceByteArrayImpl : public QNonContiguousByteDevice +{ + Q_OBJECT +public: + QNonContiguousByteDeviceByteArrayImpl(QByteArray *ba); + ~QNonContiguousByteDeviceByteArrayImpl(); + const char* readPointer(qint64 maximumLength, qint64 &len); + bool advanceReadPointer(qint64 amount); + bool atEnd(); + bool reset(); + qint64 size(); +protected: + QByteArray* byteArray; + qint64 currentPosition; +}; + +class QNonContiguousByteDeviceRingBufferImpl : public QNonContiguousByteDevice +{ + Q_OBJECT +public: + QNonContiguousByteDeviceRingBufferImpl(QRingBuffer *rb); + ~QNonContiguousByteDeviceRingBufferImpl(); + const char* readPointer(qint64 maximumLength, qint64 &len); + bool advanceReadPointer(qint64 amount); + bool atEnd(); + bool reset(); + qint64 size(); +protected: + QRingBuffer* ringBuffer; + qint64 currentPosition; +}; + + +class QNonContiguousByteDeviceIoDeviceImpl : public QNonContiguousByteDevice +{ + Q_OBJECT +public: + QNonContiguousByteDeviceIoDeviceImpl(QIODevice *d); + ~QNonContiguousByteDeviceIoDeviceImpl(); + const char* readPointer(qint64 maximumLength, qint64 &len); + bool advanceReadPointer(qint64 amount); + bool atEnd(); + bool reset(); + qint64 size(); +protected: + QIODevice* device; + QByteArray* currentReadBuffer; + qint64 currentReadBufferSize; + qint64 currentReadBufferAmount; + qint64 currentReadBufferPosition; + qint64 totalAdvancements; + bool eof; + qint64 initialPosition; +}; + +class QNonContiguousByteDeviceBufferImpl : public QNonContiguousByteDevice +{ + Q_OBJECT +public: + QNonContiguousByteDeviceBufferImpl(QBuffer *b); + ~QNonContiguousByteDeviceBufferImpl(); + const char* readPointer(qint64 maximumLength, qint64 &len); + bool advanceReadPointer(qint64 amount); + bool atEnd(); + bool reset(); + qint64 size(); +protected: + QBuffer* buffer; + QByteArray byteArray; + QNonContiguousByteDeviceByteArrayImpl* arrayImpl; +}; + +// ... and the reverse thing +class QByteDeviceWrappingIoDevice : public QIODevice +{ + Q_OBJECT +public: + QByteDeviceWrappingIoDevice (QNonContiguousByteDevice *bd); + ~QByteDeviceWrappingIoDevice (); + virtual bool isSequential () const; + virtual bool atEnd () const; + virtual bool reset (); + virtual qint64 size () const; +protected: + virtual qint64 readData ( char * data, qint64 maxSize ); + virtual qint64 writeData ( const char * data, qint64 maxSize ); + + QNonContiguousByteDevice *byteDevice; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index a128482..afeaad6 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>(); } /*! @@ -1507,7 +1586,7 @@ void QProcess::start(const QString &program, const QStringList &arguments, OpenM } #if defined QPROCESS_DEBUG - qDebug() << "QProcess::start(" << program << "," << arguments << "," << mode << ")"; + qDebug() << "QProcess::start(" << program << ',' << arguments << ',' << mode << ')'; #endif d->outputReadBuffer.clear(); @@ -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 1fedd55..e87c314 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -385,17 +385,10 @@ static void qt_create_pipe(int *pipe) qt_native_close(pipe[0]); if (pipe[1] != -1) qt_native_close(pipe[1]); -#ifdef Q_OS_IRIX - if (::socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) == -1) { - qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s", - pipe, qPrintable(qt_error_string(errno))); - } -#else if (::pipe(pipe) != 0) { qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s", pipe, qPrintable(qt_error_string(errno))); } -#endif ::fcntl(pipe[0], F_SETFD, FD_CLOEXEC); ::fcntl(pipe[1], F_SETFD, FD_CLOEXEC); } @@ -522,8 +515,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. @@ -532,27 +529,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; } @@ -581,29 +580,27 @@ void QProcessPrivate::startProcess() processManager()->start(); // Initialize pipes + if (!createChannel(stdinChannel) || + !createChannel(stdoutChannel) || + !createChannel(stderrChannel)) + return; qt_create_pipe(childStartedPipe); + qt_create_pipe(deathPipe); + ::fcntl(deathPipe[0], F_SETFD, FD_CLOEXEC); + ::fcntl(deathPipe[1], F_SETFD, FD_CLOEXEC); + if (threadData->eventDispatcher) { startupSocketNotifier = new QSocketNotifier(childStartedPipe[0], QSocketNotifier::Read, q); QObject::connect(startupSocketNotifier, SIGNAL(activated(int)), q, SLOT(_q_startupNotification())); - } - qt_create_pipe(deathPipe); - ::fcntl(deathPipe[0], F_SETFD, FD_CLOEXEC); - ::fcntl(deathPipe[1], F_SETFD, FD_CLOEXEC); - if (threadData->eventDispatcher) { deathNotifier = new QSocketNotifier(deathPipe[0], QSocketNotifier::Read, q); QObject::connect(deathNotifier, SIGNAL(activated(int)), q, SLOT(_q_processDied())); } - if (!createChannel(stdinChannel) || - !createChannel(stdoutChannel) || - !createChannel(stderrChannel)) - return; - // Start the process (platform dependent) q->setProcessState(QProcess::Starting); @@ -791,7 +788,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..179c3d0 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -272,12 +272,12 @@ static QString qt_create_commandline(const QString &program, const QStringList & QString args; if (!program.isEmpty()) { QString programName = program; - if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1String(" "))) - programName = QLatin1String("\"") + programName + QLatin1String("\""); - programName.replace(QLatin1String("/"), QLatin1String("\\")); + if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' '))) + programName = QLatin1Char('\"') + programName + QLatin1Char('\"'); + programName.replace(QLatin1Char('/'), QLatin1Char('\\')); // add the prgram as the first arg ... it works better - args = programName + QLatin1String(" "); + args = programName + QLatin1Char(' '); } for (int i=0; i<arguments.size(); ++i) { @@ -285,16 +285,16 @@ static QString qt_create_commandline(const QString &program, const QStringList & // in the case of \" already being in the string the \ must also be escaped tmp.replace( QLatin1String("\\\""), QLatin1String("\\\\\"") ); // escape a single " because the arguments will be parsed - tmp.replace( QLatin1String("\""), QLatin1String("\\\"") ); + tmp.replace( QLatin1Char('\"'), QLatin1String("\\\"") ); if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) { // The argument must not end with a \ since this would be interpreted // as escaping the quote -- rather put the \ behind the quote: e.g. // rather use "foo"\ than "foo\" - QString endQuote(QLatin1String("\"")); + QString endQuote(QLatin1Char('\"')); int i = tmp.length(); while (i>0 && tmp.at(i-1) == QLatin1Char('\\')) { --i; - endQuote += QLatin1String("\\"); + endQuote += QLatin1Char('\\'); } args += QLatin1String(" \"") + tmp.left(i) + endQuote; } else { @@ -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); @@ -402,7 +427,7 @@ void QProcessPrivate::startProcess() QString fullPathProgram = program; if (!QDir::isAbsolutePath(fullPathProgram)) fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath(); - fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\")); + fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\')); success = CreateProcessW((WCHAR*)fullPathProgram.utf16(), (WCHAR*)args.utf16(), 0, 0, false, 0, 0, 0, 0, pid); @@ -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); @@ -862,8 +887,8 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a #if defined(Q_OS_WINCE) QString fullPathProgram = program; if (!QDir::isAbsolutePath(fullPathProgram)) - fullPathProgram.prepend(QDir::currentPath().append(QLatin1String("/"))); - fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\")); + fullPathProgram.prepend(QDir::currentPath().append(QLatin1Char('/'))); + fullPathProgram.replace(QLatin1Char('/'), QLatin1Char('\\')); success = CreateProcessW((WCHAR*)fullPathProgram.utf16(), (WCHAR*)args.utf16(), 0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo); diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index 1f77caa..94dfd4d 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -60,6 +60,37 @@ QT_BEGIN_NAMESPACE + +class QStringSplitter +{ +public: + QStringSplitter(const QString &s) + : m_string(s), m_data(m_string.constData()), m_len(s.length()), m_pos(0) + { + m_splitChar = QLatin1Char('/'); + } + + inline bool hasNext() { + while (m_pos < m_len && m_data[m_pos] == m_splitChar) + ++m_pos; + return m_pos < m_len; + } + + inline QStringRef next() { + int start = m_pos; + while (m_pos < m_len && m_data[m_pos] != m_splitChar) + ++m_pos; + return QStringRef(&m_string, start, m_pos - start); + } + + QString m_string; + const QChar *m_data; + QChar m_splitChar; + int m_len; + int m_pos; +}; + + //resource glue class QResourceRoot { @@ -101,6 +132,16 @@ protected: } }; +static QString cleanPath(const QString &_path) +{ + QString path = QDir::cleanPath(_path); + // QDir::cleanPath does not remove two trailing slashes under _Windows_ + // due to support for UNC paths. Remove those manually. + if (path.startsWith(QLatin1String("//"))) + path.remove(0, 1); + return path; +} + Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE); Q_GLOBAL_STATIC_WITH_ARGS(QMutex, resourceMutex, (QMutex::Recursive)) @@ -216,9 +257,10 @@ QResourcePrivate::load(const QString &file) related.clear(); QMutexLocker lock(resourceMutex()); const ResourceList *list = resourceList(); + QString cleaned = cleanPath(file); for(int i = 0; i < list->size(); ++i) { QResourceRoot *res = list->at(i); - const int node = res->findNode(file); + const int node = res->findNode(cleaned); if(node != -1) { if(related.isEmpty()) { container = res->isContainer(node); @@ -292,6 +334,7 @@ QResourcePrivate::ensureChildren() const if(path.startsWith(QLatin1Char(':'))) path = path.mid(1); QSet<QString> kids; + QString cleaned = cleanPath(path); for(int i = 0; i < related.size(); ++i) { QResourceRoot *res = related.at(i); if(res->mappingRootSubdir(path, &k) && !k.isEmpty()) { @@ -300,7 +343,7 @@ QResourcePrivate::ensureChildren() const kids.insert(k); } } else { - const int node = res->findNode(path); + const int node = res->findNode(cleaned); if(node != -1) { QStringList related_children = res->children(node); for(int kid = 0; kid < related_children.size(); ++kid) { @@ -562,30 +605,32 @@ inline QString QResourceRoot::name(int node) const (names[name_offset+1] << 0); name_offset += 2; name_offset += 4; //jump past hash - for(int i = 0; i < name_length*2; i+=2) - ret += QChar(names[name_offset+i+1], names[name_offset+i]); + + ret.resize(name_length); + QChar *strData = ret.data(); + for(int i = 0; i < name_length*2; i+=2) { + QChar c(names[name_offset+i+1], names[name_offset+i]); + *strData = c; + ++strData; + } return ret; } + int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const { - QString path = QDir::cleanPath(_path); - // QDir::cleanPath does not remove two trailing slashes under _Windows_ - // due to support for UNC paths. Remove those manually. - if (path.startsWith(QLatin1String("//"))) - path.remove(0, 1); - + QString path = _path; { QString root = mappingRoot(); if(!root.isEmpty()) { if(root == path) { - path = QLatin1String("/"); + path = QLatin1Char('/'); } else { - if(!root.endsWith(QLatin1String("/"))) - root += QLatin1String("/"); + if(!root.endsWith(QLatin1Char('/'))) + root += QLatin1Char('/'); if(path.size() >= root.size() && path.startsWith(root)) path = path.mid(root.length()-1); if(path.isEmpty()) - path = QLatin1String("/"); + path = QLatin1Char('/'); } } } @@ -604,12 +649,11 @@ int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const //now iterate up the tree int node = -1; - QStringList segments = path.split(QLatin1Char('/'), QString::SkipEmptyParts); -#ifdef DEBUG_RESOURCE_MATCH - qDebug() << "****" << segments; -#endif - for(int i = 0; child_count && i < segments.size(); ++i) { - const QString &segment = segments[i]; + + QStringSplitter splitter(path); + while (child_count && splitter.hasNext()) { + QStringRef segment = splitter.next(); + #ifdef DEBUG_RESOURCE_MATCH qDebug() << " CHILDREN" << segment; for(int j = 0; j < child_count; ++j) { @@ -651,7 +695,7 @@ int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const (tree[offset+1] << 0); offset += 2; - if(i == segments.size()-1) { + if(!splitter.hasNext()) { if(!(flags & Directory)) { const short country = (tree[offset+0] << 8) + (tree[offset+1] << 0); diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h index dd72fd9..93d07e0 100644 --- a/src/corelib/io/qsettings_p.h +++ b/src/corelib/io/qsettings_p.h @@ -146,7 +146,7 @@ inline QString QSettingsGroup::toString() const return result; } -class Q_CORE_EXPORT QConfFile +class QConfFile { public: ParsedSettingsMap mergedKeyMap() const; diff --git a/src/corelib/io/qsettings_win.cpp b/src/corelib/io/qsettings_win.cpp index a08c969..57f65d6 100644 --- a/src/corelib/io/qsettings_win.cpp +++ b/src/corelib/io/qsettings_win.cpp @@ -148,7 +148,7 @@ static QString errorCodeToString(DWORD errorCode) if (data != 0) LocalFree(data); }) - if (result.endsWith(QLatin1String("\n"))) + if (result.endsWith(QLatin1Char('\n'))) result.truncate(result.length() - 1); return result; diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index b4d8a48..728bf4f 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -203,7 +203,7 @@ static int _gettemp(char *path, int *doopen, int domkdir, int slen) if (QDir::isAbsolutePath(QString::fromLatin1(path))) targetPath = QLatin1String(path); else - targetPath = QDir::currentPath().append(QLatin1String("/")) + QLatin1String(path); + targetPath = QDir::currentPath().append(QLatin1Char('/')) + QLatin1String(path); if ((*doopen = QT_OPEN(targetPath.toLocal8Bit(), O_CREAT|O_EXCL|O_RDWR diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp index 1167671..7c925f1 100644 --- a/src/corelib/io/qtextstream.cpp +++ b/src/corelib/io/qtextstream.cpp @@ -333,7 +333,7 @@ public: this->stream = stream; } -public slots: +public Q_SLOTS: inline void flushStream() { stream->flush(); } private: @@ -411,6 +411,7 @@ public: QString writeBuffer; QString readBuffer; int readBufferOffset; + int readConverterSavedStateOffset; //the offset between readBufferStartDevicePos and that start of the buffer qint64 readBufferStartDevicePos; // streaming parameters @@ -437,6 +438,7 @@ QTextStreamPrivate::QTextStreamPrivate(QTextStream *q_ptr) #ifndef QT_NO_TEXTCODEC readConverterSavedState(0), #endif + readConverterSavedStateOffset(0), locale(QLocale::C) { this->q_ptr = q_ptr; @@ -559,13 +561,8 @@ bool QTextStreamPrivate::fillReadBuffer(qint64 maxBytes) if (!codec || autoDetectUnicode) { autoDetectUnicode = false; - if (bytesRead >= 4 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe && uchar(buf[2]) == 0 && uchar(buf[3]) == 0) - || (uchar(buf[0]) == 0 && uchar(buf[1]) == 0 && uchar(buf[2]) == 0xfe && uchar(buf[3]) == 0xff))) { - codec = QTextCodec::codecForName("UTF-32"); - } else if (bytesRead >= 2 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe) - || (uchar(buf[0]) == 0xfe && uchar(buf[1]) == 0xff))) { - codec = QTextCodec::codecForName("UTF-16"); - } else if (!codec) { + codec = QTextCodec::codecForUtfText(QByteArray::fromRawData(buf, bytesRead), 0); + if (!codec) { codec = QTextCodec::codecForLocale(); writeConverterState.flags |= QTextCodec::IgnoreHeader; } @@ -835,6 +832,10 @@ inline void QTextStreamPrivate::consume(int size) readBufferOffset = 0; readBuffer.clear(); saveConverterState(device->pos()); + } else if (readBufferOffset > QTEXTSTREAM_BUFFERSIZE) { + readBuffer = readBuffer.remove(0,readBufferOffset); + readConverterSavedStateOffset += readBufferOffset; + readBufferOffset = 0; } } } @@ -856,6 +857,7 @@ inline void QTextStreamPrivate::saveConverterState(qint64 newPos) #endif readBufferStartDevicePos = newPos; + readConverterSavedStateOffset = 0; } /*! \internal @@ -1220,7 +1222,7 @@ qint64 QTextStream::pos() const // Rewind the device to get to the current position Ensure that // readBufferOffset is unaffected by fillReadBuffer() - int oldReadBufferOffset = d->readBufferOffset; + int oldReadBufferOffset = d->readBufferOffset + d->readConverterSavedStateOffset; while (d->readBuffer.size() < oldReadBufferOffset) { if (!thatd->fillReadBuffer(1)) return qint64(-1); @@ -2290,7 +2292,7 @@ bool QTextStreamPrivate::putNumber(qulonglong number, bool negative) // ShowBase flag set zero should be written as '00' if (number == 0 && base == 8 && numberFlags & QTextStream::ShowBase && result == QLatin1String("0")) { - result.prepend(QLatin1String("0")); + result.prepend(QLatin1Char('0')); } } return putString(result, true); diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index d1a5cdd..94a53f1 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -5698,7 +5698,7 @@ QUrl QUrl::fromLocalFile(const QString &localFile) // magic for drives on windows if (deslashified.length() > 1 && deslashified.at(1) == QLatin1Char(':') && deslashified.at(0) != QLatin1Char('/')) { - url.setPath(QLatin1String("/") + deslashified); + url.setPath(QLatin1Char('/') + deslashified); // magic for shared drive on windows } else if (deslashified.startsWith(QLatin1String("//"))) { int indexOfPath = deslashified.indexOf(QLatin1Char('/'), 2); @@ -5728,7 +5728,7 @@ QString QUrl::toLocalFile() const // magic for shared drive on windows if (!d->host.isEmpty()) { tmp = QLatin1String("//") + d->host + (ourPath.length() > 0 && ourPath.at(0) != QLatin1Char('/') - ? QLatin1String("/") + ourPath : ourPath); + ? QLatin1Char('/') + ourPath : ourPath); } else { tmp = ourPath; // magic for drives on windows @@ -5976,7 +5976,7 @@ QDataStream &operator>>(QDataStream &in, QUrl &url) #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QUrl &url) { - d.maybeSpace() << "QUrl(" << url.toString() << ")"; + d.maybeSpace() << "QUrl(" << url.toString() << ')'; return d.space(); } #endif diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h index 9242092..e9c4a8d 100644 --- a/src/corelib/io/qurl.h +++ b/src/corelib/io/qurl.h @@ -223,7 +223,7 @@ public: inline QT3_SUPPORT QString ref() const { return fragment(); } inline QT3_SUPPORT void setRef(const QString &txt) { setFragment(txt); } inline QT3_SUPPORT bool hasRef() const { return !fragment().isEmpty(); } - inline QT3_SUPPORT void addPath(const QString &p) { setPath(path() + QLatin1String("/") + p); } + inline QT3_SUPPORT void addPath(const QString &p) { setPath(path() + QLatin1Char('/') + p); } QT3_SUPPORT void setFileName(const QString &txt); QT3_SUPPORT QString fileName() const; QT3_SUPPORT QString dirPath() const; diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index d90ecae..ecef555 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -12,7 +12,7 @@ HEADERS += \ kernel/qcoreevent.h \ kernel/qmetaobject.h \ kernel/qmetatype.h \ - kernel/qmimedata.h \ + kernel/qmimedata.h \ kernel/qobject.h \ kernel/qobjectdefs.h \ kernel/qsignalmapper.h \ @@ -27,8 +27,8 @@ HEADERS += \ kernel/qvariant_p.h \ kernel/qmetaobject_p.h \ kernel/qobject_p.h \ - kernel/qcoreglobaldata_p.h \ - kernel/qsharedmemory.h \ + kernel/qcoreglobaldata_p.h \ + kernel/qsharedmemory.h \ kernel/qsharedmemory_p.h \ kernel/qsystemsemaphore.h \ kernel/qsystemsemaphore_p.h \ @@ -43,7 +43,7 @@ SOURCES += \ kernel/qcoreevent.cpp \ kernel/qmetaobject.cpp \ kernel/qmetatype.cpp \ - kernel/qmimedata.cpp \ + kernel/qmimedata.cpp \ kernel/qobject.cpp \ kernel/qobjectcleanuphandler.cpp \ kernel/qsignalmapper.cpp \ @@ -53,7 +53,8 @@ SOURCES += \ kernel/qvariant.cpp \ kernel/qcoreglobaldata.cpp \ kernel/qsharedmemory.cpp \ - kernel/qsystemsemaphore.cpp + kernel/qsystemsemaphore.cpp \ + kernel/qpointer.cpp win32 { SOURCES += \ diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp index fd0e105..935c0aa 100644 --- a/src/corelib/kernel/qabstractitemmodel.cpp +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -428,8 +428,8 @@ bool QPersistentModelIndex::isValid() const QDebug operator<<(QDebug dbg, const QModelIndex &idx) { #ifndef Q_BROKEN_DEBUG_STREAM - dbg.nospace() << "QModelIndex(" << idx.row() << "," << idx.column() - << "," << idx.internalPointer() << "," << idx.model() << ")"; + dbg.nospace() << "QModelIndex(" << idx.row() << ',' << idx.column() + << ',' << idx.internalPointer() << ',' << idx.model() << ')'; return dbg.space(); #else qWarning("This compiler doesn't support streaming QModelIndex to QDebug"); @@ -525,7 +525,7 @@ void QAbstractItemModelPrivate::rowsInserted(const QModelIndex &parent, if (data->index.isValid()) { persistent.insertMultiAtEnd(data->index, data); } else { - qWarning() << "QAbstractItemModel::endInsertRows: Invalid index (" << old.row() + count << "," << old.column() << ") in model" << q_func(); + qWarning() << "QAbstractItemModel::endInsertRows: Invalid index (" << old.row() + count << ',' << old.column() << ") in model" << q_func(); } } } @@ -574,7 +574,7 @@ void QAbstractItemModelPrivate::rowsRemoved(const QModelIndex &parent, if (data->index.isValid()) { persistent.insertMultiAtEnd(data->index, data); } else { - qWarning() << "QAbstractItemModel::endRemoveRows: Invalid index (" << old.row() - count << "," << old.column() << ") in model" << q_func(); + qWarning() << "QAbstractItemModel::endRemoveRows: Invalid index (" << old.row() - count << ',' << old.column() << ") in model" << q_func(); } } QVector<QPersistentModelIndexData *> persistent_invalidated = persistent.invalidated.pop(); @@ -619,7 +619,7 @@ void QAbstractItemModelPrivate::columnsInserted(const QModelIndex &parent, if (data->index.isValid()) { persistent.insertMultiAtEnd(data->index, data); } else { - qWarning() << "QAbstractItemModel::endInsertColumns: Invalid index (" << old.row() << "," << old.column() + count << ") in model" << q_func(); + qWarning() << "QAbstractItemModel::endInsertColumns: Invalid index (" << old.row() << ',' << old.column() + count << ") in model" << q_func(); } } } @@ -669,7 +669,7 @@ void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent, if (data->index.isValid()) { persistent.insertMultiAtEnd(data->index, data); } else { - qWarning() << "QAbstractItemModel::endRemoveColumns: Invalid index (" << old.row() << "," << old.column() - count << ") in model" << q_func(); + qWarning() << "QAbstractItemModel::endRemoveColumns: Invalid index (" << old.row() << ',' << old.column() - count << ") in model" << q_func(); } } QVector<QPersistentModelIndexData *> persistent_invalidated = persistent.invalidated.pop(); diff --git a/src/corelib/kernel/qabstractitemmodel_p.h b/src/corelib/kernel/qabstractitemmodel_p.h index df1a6ce..27f1b28 100644 --- a/src/corelib/kernel/qabstractitemmodel_p.h +++ b/src/corelib/kernel/qabstractitemmodel_p.h @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE -class Q_CORE_EXPORT QPersistentModelIndexData +class QPersistentModelIndexData { public: QPersistentModelIndexData() : model(0) {} diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index f6ce4b3..6847173 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -377,12 +377,12 @@ QString qAppName() QLibrary) can be retrieved with libraryPaths() and manipulated by setLibraryPaths(), addLibraryPath(), and removeLibraryPath(). - On Unix/Linux Qt is configured to use the system local settings by - default. This can cause a conflict when using POSIX functions, for - instance, when converting between data types such as floats and - strings, since the notation may differ between locales. To get - around this problem call the POSIX function setlocale(LC_NUMERIC,"C") - right after initializing QApplication or QCoreApplication to reset + On Unix/Linux Qt is configured to use the system local settings by + default. This can cause a conflict when using POSIX functions, for + instance, when converting between data types such as floats and + strings, since the notation may differ between locales. To get + around this problem call the POSIX function setlocale(LC_NUMERIC,"C") + right after initializing QApplication or QCoreApplication to reset the locale that is used for number formatting to "C"-locale. \sa QApplication, QAbstractEventDispatcher, QEventLoop, @@ -556,6 +556,20 @@ void QCoreApplication::setAttribute(Qt::ApplicationAttribute attribute, bool on) QCoreApplicationPrivate::attribs |= 1 << attribute; else QCoreApplicationPrivate::attribs &= ~(1 << attribute); +#ifdef Q_OS_MAC + // Turn on the no native menubar here, since we used to + // do this implicitly. We DO NOT flip it off if someone sets + // it to false. + // Ideally, we'd have magic that would be something along the lines of + // "follow MacPluginApplication" unless explicitly set. + // Considering this attribute isn't only at the beginning + // it's unlikely it will ever be a problem, but I want + // to have the behavior documented here. + if (attribute == Qt::AA_MacPluginApplication && on + && !testAttribute(Qt::AA_DontUseNativeMenuBar)) { + setAttribute(Qt::AA_DontUseNativeMenuBar, true); + } +#endif } /*! @@ -1784,7 +1798,7 @@ QString QCoreApplication::applicationFilePath() */ QByteArray pEnv = qgetenv("PATH"); QDir currentDir = QDir::current(); - QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1String(":")); + QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1Char(':')); for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) { if ((*p).isEmpty()) continue; @@ -1894,7 +1908,7 @@ QStringList QCoreApplication::arguments() wchar_t tempFilename[MAX_PATH+1]; if (GetModuleFileNameW(0, tempFilename, MAX_PATH)) { tempFilename[MAX_PATH] = 0; - cmdline.prepend(QString(QLatin1String("\"")) + QString::fromUtf16((unsigned short *)tempFilename) + QString(QLatin1String("\" "))); + cmdline.prepend(QLatin1Char('\"') + QString::fromUtf16((unsigned short *)tempFilename) + QString(QLatin1String("\" "))); } #endif // Q_OS_WINCE @@ -1908,8 +1922,7 @@ QStringList QCoreApplication::arguments() l1arg == "-qdebug" || l1arg == "-reverse" || l1arg == "-stylesheet" || - l1arg == "-widgetcount" || - l1arg == "-direct3d") + l1arg == "-widgetcount") ; else if (l1arg.startsWith("-style=")) ; diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp index 225821f..9868c23 100644 --- a/src/corelib/kernel/qcoreapplication_win.cpp +++ b/src/corelib/kernel/qcoreapplication_win.cpp @@ -51,12 +51,11 @@ QT_BEGIN_NAMESPACE -// ############### DON'T EXPORT HERE!!! -Q_CORE_EXPORT char appFileName[MAX_PATH+1]; // application file name -Q_CORE_EXPORT char theAppName[MAX_PATH+1]; // application name -Q_CORE_EXPORT HINSTANCE appInst = 0; // handle to app instance -Q_CORE_EXPORT HINSTANCE appPrevInst = 0; // handle to prev app instance -Q_CORE_EXPORT int appCmdShow = 0; +char appFileName[MAX_PATH+1]; // application file name +char theAppName[MAX_PATH+1]; // application name +HINSTANCE appInst = 0; // handle to app instance +HINSTANCE appPrevInst = 0; // handle to prev app instance +int appCmdShow = 0; bool usingWinMain = false; // whether the qWinMain() is used or not Q_CORE_EXPORT HINSTANCE qWinAppInst() // get Windows app handle @@ -69,6 +68,12 @@ Q_CORE_EXPORT HINSTANCE qWinAppPrevInst() // get Windows prev app return appPrevInst; } +Q_CORE_EXPORT int qWinAppCmdShow() // get main window show command +{ + return appCmdShow; +} + + void set_winapp_name() { static bool already_set = false; @@ -89,6 +94,14 @@ void set_winapp_name() int l = qstrlen(theAppName); if ((l > 4) && !qstricmp(theAppName + l - 4, ".exe")) theAppName[l-4] = '\0'; // drop .exe extension + + if (appInst == 0) { + QT_WA({ + appInst = GetModuleHandle(0); + }, { + appInst = GetModuleHandleA(0); + }); + } } } @@ -134,11 +147,11 @@ Q_CORE_EXPORT void qWinMsgHandler(QtMsgType t, const char* str) staticCriticalSection.lock(); QT_WA({ QString s(QString::fromLocal8Bit(str)); - s += QLatin1String("\n"); + s += QLatin1Char('\n'); OutputDebugStringW((TCHAR*)s.utf16()); }, { QByteArray s(str); - s += "\n"; + s += '\n'; OutputDebugStringA(s.data()); }) staticCriticalSection.unlock(); @@ -173,14 +186,14 @@ void qWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParam, // Create command line - set_winapp_name(); - argv = qWinCmdLine<char>(cmdParam, int(strlen(cmdParam)), argc); // Get Windows parameters appInst = instance; appPrevInst = prevInstance; appCmdShow = cmdShow; + + set_winapp_name(); } /*! @@ -618,7 +631,7 @@ QString valueCheck(uint actual, ...) #ifdef Q_CC_BOR -Q_CORE_EXPORT QString decodeMSG(const MSG& msg) +QString decodeMSG(const MSG& msg) { return QString::fromLatin1("THis is not supported on Borland"); } diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index 11a2d3c..d6b0174 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -264,6 +264,8 @@ QT_BEGIN_NAMESPACE \omitvalue NetworkReplyUpdated \omitvalue FutureCallOut \omitvalue CocoaRequestModal + \omitvalue Wrapped + \omitvalue Signal */ /*! diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index fa472e6..18188a8 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -44,6 +44,7 @@ #include <QtCore/qnamespace.h> #include <QtCore/qbytearray.h> +#include <QtCore/qobjectdefs.h> QT_BEGIN_HEADER @@ -54,7 +55,9 @@ QT_MODULE(Core) class QEventPrivate; class Q_CORE_EXPORT QEvent // event base class { + Q_GADGET QDOC_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + Q_ENUMS(Type) public: enum Type { /* @@ -266,7 +269,10 @@ public: CocoaRequestModal = 190, // Internal for requesting an application modal Cocoa Window MacGLClearDrawable = 191, // Internal Cocoa, the window has changed, so we must clear - // 512 reserved for Qt Jambi's MetaCall event + Signal = 192, + Wrapped = 193, + + // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event User = 1000, // first user event id diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h index 41329cf..8c08bd2 100644 --- a/src/corelib/kernel/qeventdispatcher_unix_p.h +++ b/src/corelib/kernel/qeventdispatcher_unix_p.h @@ -153,14 +153,14 @@ public: int activateTimers(); }; -struct Q_CORE_EXPORT QSockNot +struct QSockNot { QSocketNotifier *obj; int fd; fd_set *queue; }; -class Q_CORE_EXPORT QSockNotType +class QSockNotType { public: QSockNotType(); diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index c4061f4..2dd5534 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -396,13 +396,6 @@ Q_CORE_EXPORT bool winPostMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa { return PostMessageA(hWnd, msg, wParam, lParam); }); } -Q_CORE_EXPORT bool winGetMessage(MSG* msg, HWND hWnd, UINT wMsgFilterMin, - UINT wMsgFilterMax) -{ - QT_WA({ return GetMessage(msg, hWnd, wMsgFilterMin, wMsgFilterMax); } , - { return GetMessageA(msg, hWnd, wMsgFilterMin, wMsgFilterMax); }); -} - // This function is called by a workerthread void WINAPI CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/, DWORD_PTR user, DWORD_PTR /*reserved*/, DWORD_PTR /*reserved*/) { diff --git a/src/corelib/kernel/qfunctions_wince.cpp b/src/corelib/kernel/qfunctions_wince.cpp index 1c929c7..e0f7687 100644 --- a/src/corelib/kernel/qfunctions_wince.cpp +++ b/src/corelib/kernel/qfunctions_wince.cpp @@ -285,11 +285,6 @@ int qt_wince_SetErrorMode(int newValue) return result; } -HRESULT qt_wince_CoInitialize(void* reserved) -{ - return CoInitializeEx(reserved, 0); -} - bool qt_wince__chmod(const char *file, int mode) { return _wchmod( reinterpret_cast<const wchar_t *> (QString::fromLatin1(file).utf16()), mode); diff --git a/src/corelib/kernel/qfunctions_wince.h b/src/corelib/kernel/qfunctions_wince.h index 123bd23..5f08bb3 100644 --- a/src/corelib/kernel/qfunctions_wince.h +++ b/src/corelib/kernel/qfunctions_wince.h @@ -199,7 +199,9 @@ int qt_wince__fstat( int handle, struct stat *buffer); #define SEM_FAILCRITICALERRORS 0x0001 #define SEM_NOOPENFILEERRORBOX 0x0002 int qt_wince_SetErrorMode(int); -HRESULT qt_wince_CoInitialize(void* reserved); +#ifndef CoInitialize +#define CoInitialize(x) CoInitializeEx(x, COINIT_MULTITHREADED) +#endif bool qt_wince__chmod(const char *file, int mode); bool qt_wince__wchmod(const WCHAR *file, int mode); @@ -376,7 +378,6 @@ typedef DWORD OLE_COLOR; #define _rename(a,b) qt_wince__rename(a,b) #define _remove(a) qt_wince__remove(a) #define SetErrorMode(a) qt_wince_SetErrorMode(a) -#define CoInitialize(a) qt_wince_CoInitialize(a) #define _chmod(a,b) qt_wince__chmod(a,b) #define _wchmod(a,b) qt_wince__wchmod(a,b) #define CreateFileA(a,b,c,d,e,f,g) qt_wince_CreateFileA(a,b,c,d,e,f,g) diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index f1a1eb5..be622d9 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -58,6 +58,7 @@ #include <qsemaphore.h> #include <private/qorderedmutexlocker_p.h> +#include <private/qmutexpool_p.h> #include <new> @@ -70,7 +71,7 @@ static int DIRECT_CONNECTION_ONLY = 0; static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) { - int *types = static_cast<int *>(qMalloc((typeNames.count() + 1) * sizeof(int))); + int *types = new int [typeNames.count() + 1]; for (int i = 0; i < typeNames.count(); ++i) { const QByteArray typeName = typeNames.at(i); if (typeName.endsWith('*')) @@ -82,7 +83,7 @@ static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" "(Make sure '%s' is registered using qRegisterMetaType().)", typeName.constData(), typeName.constData()); - qFree(types); + delete [] types; return 0; } } @@ -91,12 +92,34 @@ static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) return types; } +static QBasicAtomicPointer<QMutexPool> signalSlotMutexes = Q_BASIC_ATOMIC_INITIALIZER(0); +static QBasicAtomicInt objectCount = Q_BASIC_ATOMIC_INITIALIZER(0); + +/** \internal + * mutex to be locked when accessing the connectionlists or the senders list + */ +static QMutex *signalSlotLock(const QObject *o) +{ + if (!signalSlotMutexes) { + QMutexPool *mp = new QMutexPool; + if (!signalSlotMutexes.testAndSetOrdered(0, mp)) { + delete mp; + } + } + return signalSlotMutexes->get(o); +} + extern "C" Q_CORE_EXPORT void qt_addObject(QObject *) { + objectCount.ref(); } extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *) { + if(!objectCount.deref()) { + QMutexPool *old = signalSlotMutexes.fetchAndStoreAcquire(0); + delete old; + } } QObjectPrivate::QObjectPrivate(int version) @@ -216,19 +239,20 @@ public: } }; +// Used by QAccessibleWidget bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const { Q_Q(const QObject); int signal_index = q->metaObject()->indexOfSignal(signal); if (signal_index < 0) return false; - QMutexLocker locker(&threadData->mutex); + QMutexLocker locker(signalSlotLock(q)); if (connectionLists) { if (signal_index < connectionLists->count()) { const ConnectionList &connectionList = connectionLists->at(signal_index); for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - if (c.receiver && c.receiver == receiver) + const QObjectPrivate::Connection *c = connectionList.at(i); + if (c->receiver == receiver) return true; } } @@ -236,6 +260,7 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const return false; } +// Used by QAccessibleWidget QObjectList QObjectPrivate::receiverList(const char *signal) const { Q_Q(const QObject); @@ -243,26 +268,27 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const int signal_index = q->metaObject()->indexOfSignal(signal); if (signal_index < 0) return returnValue; - QMutexLocker locker(&threadData->mutex); + QMutexLocker locker(signalSlotLock(q)); if (connectionLists) { if (signal_index < connectionLists->count()) { const ConnectionList &connectionList = connectionLists->at(signal_index); for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - if (c.receiver) - returnValue << c.receiver; + const QObjectPrivate::Connection *c = connectionList.at(i); + if (c->receiver) + returnValue << c->receiver; } } } return returnValue; } +// Used by QAccessibleWidget QObjectList QObjectPrivate::senderList() const { QObjectList returnValue; - QMutexLocker locker(&threadData->mutex); + QMutexLocker locker(signalSlotLock(q_func())); for (int i = 0; i < senders.count(); ++i) - returnValue << senders.at(i).sender; + returnValue << senders.at(i)->sender; return returnValue; } @@ -274,33 +300,10 @@ void QObjectPrivate::addConnection(int signal, Connection *c) connectionLists->resize(signal + 1); ConnectionList &connectionList = (*connectionLists)[signal]; - connectionList.append(*c); - + connectionList.append(c); cleanConnectionLists(); } -void QObjectPrivate::removeReceiver(int signal, QObject *receiver) -{ - if (!connectionLists) - return; - - if (signal >= connectionLists->count()) - return; - - ConnectionList &connectionList = (*connectionLists)[signal]; - for (int i = 0; i < connectionList.count(); ++i) { - Connection &c = connectionList[i]; - if (c.receiver == receiver) { - c.receiver = 0; - if (c.argumentTypes && c.argumentTypes != &DIRECT_CONNECTION_ONLY) { - qFree(c.argumentTypes); - c.argumentTypes = 0; - } - connectionLists->dirty = true; - } - } -} - void QObjectPrivate::cleanConnectionLists() { if (connectionLists->dirty && !connectionLists->inUse) { @@ -308,43 +311,17 @@ void QObjectPrivate::cleanConnectionLists() for (int signal = -1; signal < connectionLists->count(); ++signal) { QObjectPrivate::ConnectionList &connectionList = (*connectionLists)[signal]; for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - if (!c.receiver) + QObjectPrivate::Connection *c = connectionList.at(i); + if (!c->receiver) { + delete c; connectionList.removeAt(i--); + } } } connectionLists->dirty = false; } } -void QObjectPrivate::refSender(QObject *sender, int signal) -{ - for (int i = 0; i < senders.count(); ++i) { - Sender &s = senders[i]; - if (s.sender == sender && s.signal == signal) { - ++s.ref; - return; - } - } - - Sender s = { sender, signal, 1 }; - senders.append(s); -} - -void QObjectPrivate::derefSender(QObject *sender, int signal) -{ - for (int i = 0; i < senders.count(); ++i) { - Sender &s = senders[i]; - if (s.sender == sender && s.signal == signal) { - if (--s.ref == 0) { - senders.removeAt(i); - break; - } - } - } - // Q_ASSERT_X(false, "QObjectPrivate::derefSender", "sender not found"); -} - QObjectPrivate::Sender *QObjectPrivate::setCurrentSender(QObject *receiver, Sender *sender) { @@ -757,7 +734,7 @@ QObject::~QObject() emit destroyed(this); { - QMutexLocker locker(&d->threadData->mutex); + QMutexLocker locker(signalSlotLock(this)); // set ref to zero to indicate that this object has been deleted if (d->currentSender != 0) @@ -770,23 +747,21 @@ QObject::~QObject() for (int signal = -1; signal < d->connectionLists->count(); ++signal) { QObjectPrivate::ConnectionList &connectionList = (*d->connectionLists)[signal]; for (int i = 0; i < connectionList.count(); ++i) { - QObjectPrivate::Connection *c = &connectionList[i]; - if (!c->receiver) + QObjectPrivate::Connection *c = connectionList[i]; + if (!c->receiver) { + delete c; continue; + } - QMutex *m = &c->receiver->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(c->receiver); bool needToUnlock = QOrderedMutexLocker::relock(locker.mutex(), m); - c = &connectionList[i]; + c = connectionList[i]; if (c->receiver) - c->receiver->d_func()->derefSender(this, signal); + c->receiver->d_func()->senders.removeOne(c); if (needToUnlock) m->unlock(); - if (c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { - qFree(c->argumentTypes); - c->argumentTypes = 0; - } - c->receiver = 0; + delete c; } } @@ -800,18 +775,22 @@ QObject::~QObject() // disconnect all senders for (int i = 0; i < d->senders.count(); ) { - QObjectPrivate::Sender *s = &d->senders[i]; + QObjectPrivate::Connection *s = d->senders[i]; - QMutex *m = &s->sender->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(s->sender); bool needToUnlock = QOrderedMutexLocker::relock(locker.mutex(), m); if (m < locker.mutex()) { - if (i >= d->senders.count() || s != &d->senders[i]) { + if (i >= d->senders.count() || s != d->senders[i]) { if (needToUnlock) m->unlock(); continue; } } - s->sender->d_func()->removeReceiver(s->signal, this); + s->receiver = 0; + QObjectConnectionListVector *senderLists = s->sender->d_func()->connectionLists; + if (senderLists) + senderLists->dirty = true; + if (needToUnlock) m->unlock(); ++i; @@ -858,6 +837,12 @@ QObject::~QObject() d_ptr = 0; } +QObjectPrivate::Connection::~Connection() +{ + if (argumentTypes != &DIRECT_CONNECTION_ONLY) + delete [] argumentTypes; +} + /*! \fn QMetaObject *QObject::metaObject() const @@ -2289,14 +2274,14 @@ QObject *QObject::sender() const { Q_D(const QObject); - QMutexLocker(&d->threadData->mutex); + QMutexLocker(signalSlotLock(this)); if (!d->currentSender) return 0; // Return 0 if d->currentSender isn't in d->senders bool found = false; for (int i = 0; !found && i < d->senders.count(); ++i) - found = (d->senders.at(i).sender == d->currentSender->sender); + found = (d->senders.at(i)->sender == d->currentSender->sender); if (!found) return 0; return d->currentSender->sender; @@ -2345,14 +2330,14 @@ int QObject::receivers(const char *signal) const } Q_D(const QObject); - QMutexLocker locker(&d->threadData->mutex); + QMutexLocker locker(signalSlotLock(this)); if (d->connectionLists) { if (signal_index < d->connectionLists->count()) { const QObjectPrivate::ConnectionList &connectionList = d->connectionLists->at(signal_index); for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - receivers += c.receiver ? 1 : 0; + const QObjectPrivate::Connection *c = connectionList.at(i); + receivers += c->receiver ? 1 : 0; } } } @@ -2391,7 +2376,8 @@ int QObject::receivers(const char *signal) const can be connected to one slot. If a signal is connected to several slots, the slots are activated - in an arbitrary order when the signal is emitted. + in the same order as the order the connection was made, when the + signal is emitted. The function returns true if it successfully connects the signal to the slot. It will return false if it cannot create the @@ -2399,9 +2385,13 @@ int QObject::receivers(const char *signal) const existence of either \a signal or \a method, or if their signatures aren't compatible. - For every connection you make, a signal is emitted; two signals are emitted - for duplicate connections. You can break all of these connections with a - single disconnect() call. + By default, a signal is emitted for every connection you make; + two signals are emitted for duplicate connections. You can break + all of these connections with a single disconnect() call. + If you pass the Qt::UniqueConnection \a type, the connection will only + be made if it is not a duplicate. If there is already a duplicate + (exact same signal to the exact same slot on the same objects), + the connection will fail and connect will return false The optional \a type parameter describes the type of connection to establish. In particular, it determines whether a particular @@ -2534,7 +2524,8 @@ bool QObject::connect(const QObject *sender, const char *signal, } } #endif - QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); + if (!QMetaObject::connect(sender, signal_index, receiver, method_index, type, types)) + return false; const_cast<QObject*>(sender)->connectNotify(signal - 1); return true; } @@ -2785,20 +2776,31 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, QObject *s = const_cast<QObject *>(sender); QObject *r = const_cast<QObject *>(receiver); - QOrderedMutexLocker locker(&s->d_func()->threadData->mutex, - &r->d_func()->threadData->mutex); + QOrderedMutexLocker locker(signalSlotLock(sender), + signalSlotLock(receiver)); -#if defined(Q_CC_HPACC) && defined(QT_ARCH_PARISC) - QObjectPrivate::Connection c; - c.receiver = r; - c.method = method_index; - c.connectionType = type; - c.argumentTypes = types; -#else - QObjectPrivate::Connection c = { r, method_index, type, Q_BASIC_ATOMIC_INITIALIZER(types) }; -#endif - s->d_func()->addConnection(signal_index, &c); - r->d_func()->refSender(s, signal_index); + if (type & Qt::UniqueConnection) { + QObjectConnectionListVector *connectionLists = s->d_func()->connectionLists; + if (connectionLists && connectionLists->count() > signal_index) { + QObjectPrivate::ConnectionList &connectionList = (*connectionLists)[signal_index]; + for (int i = 0; i < connectionList.count(); ++i) { + QObjectPrivate::Connection *c2 = connectionList.at(i); + if (c2->receiver == receiver && c2->method == method_index) + return false; + } + } + type &= Qt::UniqueConnection - 1; + } + + QObjectPrivate::Connection *c = new QObjectPrivate::Connection; + c->sender = s; + c->receiver = r; + c->method = method_index; + c->connectionType = type; + c->argumentTypes = types; + + s->d_func()->addConnection(signal_index, c); + r->d_func()->senders.append(c); if (signal_index < 0) sender->d_func()->connectedSignals = ~0u; @@ -2820,8 +2822,8 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, QObject *s = const_cast<QObject *>(sender); QObject *r = const_cast<QObject *>(receiver); - QMutex *senderMutex = &s->d_func()->threadData->mutex; - QMutex *receiverMutex = r ? &r->d_func()->threadData->mutex : 0; + QMutex *senderMutex = signalSlotLock(sender); + QMutex *receiverMutex = receiver ? signalSlotLock(receiver) : 0; QOrderedMutexLocker locker(senderMutex, receiverMutex); QObjectConnectionListVector *connectionLists = s->d_func()->connectionLists; @@ -2837,27 +2839,23 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, for (signal_index = -1; signal_index < connectionLists->count(); ++signal_index) { QObjectPrivate::ConnectionList &connectionList = (*connectionLists)[signal_index]; for (int i = 0; i < connectionList.count(); ++i) { - QObjectPrivate::Connection *c = &connectionList[i]; + QObjectPrivate::Connection *c = connectionList[i]; if (c->receiver && (r == 0 || (c->receiver == r && (method_index < 0 || c->method == method_index)))) { - QMutex *m = &c->receiver->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(c->receiver); + bool needToUnlock = false; if (!receiverMutex && senderMutex != m) { // need to relock this receiver and sender in the correct order - bool needToUnlock = QOrderedMutexLocker::relock(senderMutex, m); - c = &connectionList[i]; - if (c->receiver) - c->receiver->d_func()->derefSender(s, signal_index); - if (needToUnlock) - m->unlock(); - } else { - // no need to unlock - c->receiver->d_func()->derefSender(s, signal_index); - } - if (c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { - qFree(c->argumentTypes); - c->argumentTypes = 0; + needToUnlock = QOrderedMutexLocker::relock(senderMutex, m); + c = connectionList[i]; } + if (c->receiver) + c->receiver->d_func()->senders.removeOne(c); + + if (needToUnlock) + m->unlock(); + c->receiver = 0; success = true; @@ -2868,27 +2866,22 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, } else if (signal_index < connectionLists->count()) { QObjectPrivate::ConnectionList &connectionList = (*connectionLists)[signal_index]; for (int i = 0; i < connectionList.count(); ++i) { - QObjectPrivate::Connection *c = &connectionList[i]; + QObjectPrivate::Connection *c = connectionList[i]; if (c->receiver && (r == 0 || (c->receiver == r && (method_index < 0 || c->method == method_index)))) { - QMutex *m = &c->receiver->d_func()->threadData->mutex; + QMutex *m = signalSlotLock(c->receiver); + bool needToUnlock = false; if (!receiverMutex && senderMutex != m) { // need to relock this receiver and sender in the correct order - bool needToUnlock = QOrderedMutexLocker::relock(senderMutex, m); - c = &connectionList[i]; - if (c->receiver) - c->receiver->d_func()->derefSender(s, signal_index); - if (needToUnlock) - m->unlock(); - } else { - // no need to unlock - c->receiver->d_func()->derefSender(s, signal_index); - } - if (c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { - qFree(c->argumentTypes); - c->argumentTypes = 0; + needToUnlock = QOrderedMutexLocker::relock(senderMutex, m); + c = connectionList[i]; } + if (c->receiver) + c->receiver->d_func()->senders.removeOne(c); + + if (needToUnlock) + m->unlock(); c->receiver = 0; success = true; @@ -2971,32 +2964,31 @@ void QMetaObject::connectSlotsByName(QObject *o) } } -static void queued_activate(QObject *sender, int signal, const QObjectPrivate::Connection &c, +static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv, QSemaphore *semaphore = 0) { - if (!c.argumentTypes || c.argumentTypes != &DIRECT_CONNECTION_ONLY) { + if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { QMetaMethod m = sender->metaObject()->method(signal); - QObjectPrivate::Connection &x = const_cast<QObjectPrivate::Connection &>(c); int *tmp = queuedConnectionTypes(m.parameterTypes()); if (!tmp) // cannot queue arguments tmp = &DIRECT_CONNECTION_ONLY; - if (!x.argumentTypes.testAndSetOrdered(0, tmp)) { + if (!c->argumentTypes.testAndSetOrdered(0, tmp)) { if (tmp != &DIRECT_CONNECTION_ONLY) - qFree(tmp); + delete [] tmp; } } - if (c.argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate + if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate return; int nargs = 1; // include return type - while (c.argumentTypes[nargs-1]) + while (c->argumentTypes[nargs-1]) ++nargs; int *types = (int *) qMalloc(nargs*sizeof(int)); void **args = (void **) qMalloc(nargs*sizeof(void *)); types[0] = 0; // return type args[0] = 0; // return value for (int n = 1; n < nargs; ++n) - args[n] = QMetaType::construct((types[n] = c.argumentTypes[n-1]), argv[n]); - QCoreApplication::postEvent(c.receiver, new QMetaCallEvent(c.method, + args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]); + QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method, sender, signal, nargs, @@ -3005,13 +2997,13 @@ static void queued_activate(QObject *sender, int signal, const QObjectPrivate::C semaphore)); } -static void blocking_activate(QObject *sender, int signal, const QObjectPrivate::Connection &c, void **argv) +static void blocking_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv) { - if (QThread::currentThread() == c.receiver->thread()) { + if (QThread::currentThread() == c->receiver->thread()) { qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: " "Sender is %s(%p), receiver is %s(%p)", sender->metaObject()->className(), sender, - c.receiver->metaObject()->className(), c.receiver); + c->receiver->metaObject()->className(), c->receiver); } #ifdef QT_NO_THREAD @@ -3019,7 +3011,7 @@ static void blocking_activate(QObject *sender, int signal, const QObjectPrivate: #else QSemaphore semaphore; queued_activate(sender, signal, c, argv, &semaphore); - QMutex *mutex = &QThreadData::get2(sender->thread())->mutex; + QMutex *mutex = signalSlotLock(sender); mutex->unlock(); semaphore.acquire(); mutex->lock(); @@ -3039,7 +3031,7 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal argv ? argv : empty_argv); } - QMutexLocker locker(&sender->d_func()->threadData->mutex); + QMutexLocker locker(signalSlotLock(sender)); QThreadData *currentThreadData = QThreadData::current(); QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists; @@ -3061,7 +3053,7 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal } int count = connectionLists->at(signal).count(); for (int i = 0; i < count; ++i) { - const QObjectPrivate::Connection *c = &connectionLists->at(signal)[i]; + QObjectPrivate::Connection *c = connectionLists->at(signal)[i]; if (!c->receiver) continue; @@ -3073,10 +3065,10 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal && (currentThreadData != sender->d_func()->threadData || receiver->d_func()->threadData != sender->d_func()->threadData)) || (c->connectionType == Qt::QueuedConnection)) { - queued_activate(sender, signal, *c, argv); + queued_activate(sender, signal, c, argv); continue; } else if (c->connectionType == Qt::BlockingQueuedConnection) { - blocking_activate(sender, signal, *c, argv); + blocking_activate(sender, signal, c, argv); continue; } @@ -3391,7 +3383,7 @@ void QObject::dumpObjectInfo() objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data()); Q_D(QObject); - QMutexLocker locker(&d->threadData->mutex); + QMutexLocker locker(signalSlotLock(this)); // first, look for connections where this object is the sender qDebug(" SIGNALS OUT"); @@ -3404,16 +3396,16 @@ void QObject::dumpObjectInfo() // receivers const QObjectPrivate::ConnectionList &connectionList = d->connectionLists->at(signal_index); for (int i = 0; i < connectionList.count(); ++i) { - const QObjectPrivate::Connection &c = connectionList.at(i); - if (!c.receiver) { + const QObjectPrivate::Connection *c = connectionList.at(i); + if (!c->receiver) { qDebug(" <Disconnected receiver>"); continue; } - const QMetaObject *receiverMetaObject = c.receiver->metaObject(); - const QMetaMethod method = receiverMetaObject->method(c.method); + const QMetaObject *receiverMetaObject = c->receiver->metaObject(); + const QMetaMethod method = receiverMetaObject->method(c->method); qDebug(" --> %s::%s %s", receiverMetaObject->className(), - c.receiver->objectName().isEmpty() ? "unnamed" : qPrintable(c.receiver->objectName()), + c->receiver->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver->objectName()), method.signature()); } } @@ -3426,13 +3418,12 @@ void QObject::dumpObjectInfo() if (!d->senders.isEmpty()) { for (int i = 0; i < d->senders.count(); ++i) { - const QObjectPrivate::Sender &s = d->senders.at(i); - const QMetaObject *senderMetaObject = s.sender->metaObject(); - const QMetaMethod signal = senderMetaObject->method(s.signal); - qDebug(" <-- %s::%s %s", - senderMetaObject->className(), - s.sender->objectName().isEmpty() ? "unnamed" : qPrintable(s.sender->objectName()), - signal.signature()); + const QObjectPrivate::Connection *s = d->senders.at(i); + const QMetaMethod slot = metaObject()->method(s->method); + qDebug(" <-- %s::%s %s", + s->sender->metaObject()->className(), + s->sender->objectName().isEmpty() ? "unnamed" : qPrintable(s->sender->objectName()), + slot.signature()); } } else { qDebug(" <None>"); @@ -3488,7 +3479,7 @@ QDebug operator<<(QDebug dbg, const QObject *o) { #ifndef Q_BROKEN_DEBUG_STREAM if (!o) return dbg << "QObject(0x0) "; - dbg.nospace() << o->metaObject()->className() << "(" << (void *)o; + dbg.nospace() << o->metaObject()->className() << '(' << (void *)o; if (!o->objectName().isEmpty()) dbg << ", name = " << o->objectName(); dbg << ')'; diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 0eed938..96d79af 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -114,7 +114,6 @@ public: int signal; int ref; }; - // object currently activating the object Sender *currentSender; @@ -148,21 +147,20 @@ public: // Note: you must hold the signalSlotLock() before accessing the lists below or calling the functions struct Connection { + QObject *sender; QObject *receiver; int method; uint connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking QBasicAtomicPointer<int> argumentTypes; + ~Connection(); }; - typedef QList<Connection> ConnectionList; + typedef QList<Connection *> ConnectionList; QObjectConnectionListVector *connectionLists; void addConnection(int signal, Connection *c); - void removeReceiver(int signal, QObject *receiver); void cleanConnectionLists(); - QList<Sender> senders; - void refSender(QObject *sender, int signal); - void derefSender(QObject *sender, int signal); + ConnectionList senders; static Sender *setCurrentSender(QObject *receiver, Sender *sender); @@ -207,7 +205,7 @@ private: QSemaphore *semaphore_; }; -class Q_CORE_EXPORT QBoolBlocker +class QBoolBlocker { public: inline QBoolBlocker(bool &b, bool value=true):block(b), reset(b){block = value;} diff --git a/src/corelib/kernel/qsharedmemory_unix.cpp b/src/corelib/kernel/qsharedmemory_unix.cpp index 61d56f4..12f21b7 100644 --- a/src/corelib/kernel/qsharedmemory_unix.cpp +++ b/src/corelib/kernel/qsharedmemory_unix.cpp @@ -61,6 +61,8 @@ QT_BEGIN_NAMESPACE #include <fcntl.h> #include <unistd.h> +QT_BEGIN_NAMESPACE + QSharedMemoryPrivate::QSharedMemoryPrivate() : QObjectPrivate(), memory(0), size(0), error(QSharedMemory::NoError), #ifndef QT_NO_SYSTEMSEMAPHORE diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp index 4b3feb0..08821d4 100644 --- a/src/corelib/kernel/qtimer.cpp +++ b/src/corelib/kernel/qtimer.cpp @@ -269,7 +269,7 @@ class QSingleShotTimer : public QObject public: ~QSingleShotTimer(); QSingleShotTimer(int msec, QObject *r, const char * m); -signals: +Q_SIGNALS: void timeout(); protected: void timerEvent(QTimerEvent *); diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 0a0500d..2ff9818 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -71,27 +71,6 @@ QT_BEGIN_NAMESPACE # define FLT_DIG 6 #endif - -static const void *constDataHelper(const QVariant::Private &d) -{ - switch (d.type) { - case QVariant::Int: - return &d.data.i; - case QVariant::UInt: - return &d.data.u; - case QVariant::Bool: - return &d.data.b; - case QVariant::LongLong: - return &d.data.ll; - case QVariant::ULongLong: - return &d.data.ull; - case QVariant::Double: - return &d.data.d; - default: - return d.is_shared ? d.data.shared->ptr : reinterpret_cast<const void *>(&d.data.ptr); - } -} - static void construct(QVariant::Private *x, const void *copy) { x->is_shared = false; @@ -179,6 +158,9 @@ static void construct(QVariant::Private *x, const void *copy) case QVariant::Double: x->data.d = copy ? *static_cast<const double*>(copy) : 0.0; break; + case QMetaType::Float: + x->data.f = copy ? *static_cast<const float*>(copy) : 0.0f; + break; case QVariant::LongLong: x->data.ll = copy ? *static_cast<const qlonglong *>(copy) : Q_INT64_C(0); break; @@ -274,6 +256,7 @@ static void clear(QVariant::Private *d) case QVariant::LongLong: case QVariant::ULongLong: case QVariant::Double: + case QMetaType::Float: break; case QVariant::Invalid: case QVariant::UserType: @@ -342,6 +325,7 @@ static bool isNull(const QVariant::Private *d) case QVariant::ULongLong: case QVariant::Bool: case QVariant::Double: + case QMetaType::Float: break; } return d->is_null; @@ -433,6 +417,8 @@ static bool compare(const QVariant::Private *a, const QVariant::Private *b) return a->data.b == b->data.b; case QVariant::Double: return a->data.d == b->data.d; + case QMetaType::Float: + return a->data.f == b->data.f; case QVariant::Date: return *v_cast<QDate>(a) == *v_cast<QDate>(b); case QVariant::Time: @@ -491,7 +477,7 @@ static qlonglong qMetaTypeNumber(const QVariant::Private *d) case QMetaType::Long: return qlonglong(*static_cast<long *>(d->data.shared->ptr)); case QMetaType::Float: - return qRound64(*static_cast<float *>(d->data.shared->ptr)); + return qRound64(d->data.f); case QVariant::Double: return qRound64(d->data.d); } @@ -628,7 +614,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, *str = QString::number(qMetaTypeUNumber(d)); break; case QMetaType::Float: - *str = QString::number(*static_cast<float *>(d->data.shared->ptr), 'g', FLT_DIG); + *str = QString::number(d->data.f, 'g', FLT_DIG); break; case QVariant::Double: *str = QString::number(d->data.d, 'g', DBL_DIG); @@ -799,7 +785,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, *ba = QByteArray::number(d->data.d, 'g', DBL_DIG); break; case QMetaType::Float: - *ba = QByteArray::number(*static_cast<float *>(d->data.shared->ptr), 'g', FLT_DIG); + *ba = QByteArray::number(d->data.f, 'g', FLT_DIG); break; case QMetaType::Char: case QMetaType::UChar: @@ -901,7 +887,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, *f = double(d->data.b); break; case QMetaType::Float: - *f = *static_cast<float *>(d->data.shared->ptr); + *f = double(d->data.f); break; case QVariant::LongLong: case QVariant::Int: @@ -1035,7 +1021,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, #if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM) static void streamDebug(QDebug dbg, const QVariant &v) { - switch (v.type()) { + switch (v.userType()) { case QVariant::Int: dbg.nospace() << v.toInt(); break; @@ -1048,6 +1034,9 @@ static void streamDebug(QDebug dbg, const QVariant &v) case QVariant::ULongLong: dbg.nospace() << v.toULongLong(); break; + case QMetaType::Float: + dbg.nospace() << qVariantValue<float>(v); + break; case QVariant::Double: dbg.nospace() << v.toDouble(); break; @@ -1356,7 +1345,7 @@ void QVariant::create(int type, const void *copy) QVariant::~QVariant() { - if (d.type > Char && (!d.is_shared || !d.data.shared->ref.deref())) + if (d.type > Char && d.type != QMetaType::Float && (!d.is_shared || !d.data.shared->ref.deref())) handler->clear(&d); } @@ -1372,7 +1361,7 @@ QVariant::QVariant(const QVariant &p) { if (d.is_shared) { d.data.shared->ref.ref(); - } else if (p.d.type > Char) { + } else if (p.d.type > Char && p.d.type != QMetaType::Float) { handler->construct(&d, p.constData()); d.is_null = p.d.is_null; } @@ -1566,6 +1555,12 @@ QVariant::QVariant(const char *val) */ /*! + \fn QVariant::QVariant(float val) + + Constructs a new variant with a floating point value, \a val. +*/ + +/*! \fn QVariant::QVariant(const QList<QVariant> &val) Constructs a new variant with a list value, \a val. @@ -1620,44 +1615,44 @@ QVariant::QVariant(double val) { d.is_null = false; d.type = Double; d.data.d = val; } QVariant::QVariant(const QByteArray &val) -{ create(ByteArray, &val); } +{ d.is_null = false; d.type = ByteArray; v_construct<QByteArray>(&d, val); } QVariant::QVariant(const QBitArray &val) -{ create(BitArray, &val); } +{ d.is_null = false; d.type = BitArray; v_construct<QBitArray>(&d, val); } QVariant::QVariant(const QString &val) -{ create(String, &val); } +{ d.is_null = false; d.type = String; v_construct<QString>(&d, val); } QVariant::QVariant(const QChar &val) -{ create (Char, &val); } +{ d.is_null = false; d.type = Char; v_construct<QChar>(&d, val); } QVariant::QVariant(const QLatin1String &val) -{ QString str(val); create(String, &str); } +{ QString str(val); d.is_null = false; d.type = String; v_construct<QString>(&d, str); } QVariant::QVariant(const QStringList &val) -{ create(StringList, &val); } +{ d.is_null = false; d.type = StringList; v_construct<QStringList>(&d, val); } QVariant::QVariant(const QDate &val) -{ create(Date, &val); } +{ d.is_null = false; d.type = Date; v_construct<QDate>(&d, val); } QVariant::QVariant(const QTime &val) -{ create(Time, &val); } +{ d.is_null = false; d.type = Time; v_construct<QTime>(&d, val); } QVariant::QVariant(const QDateTime &val) -{ create(DateTime, &val); } +{ d.is_null = false; d.type = DateTime; v_construct<QDateTime>(&d, val); } QVariant::QVariant(const QList<QVariant> &list) -{ create(List, &list); } +{ d.is_null = false; d.type = List; v_construct<QVariantList>(&d, list); } QVariant::QVariant(const QMap<QString, QVariant> &map) -{ create(Map, &map); } +{ d.is_null = false; d.type = Map; v_construct<QVariantMap>(&d, map); } QVariant::QVariant(const QHash<QString, QVariant> &hash) -{ create(Hash, &hash); } +{ d.is_null = false; d.type = Hash; v_construct<QVariantHash>(&d, hash); } #ifndef QT_NO_GEOM_VARIANT -QVariant::QVariant(const QPoint &pt) { create(Point, &pt); } -QVariant::QVariant(const QPointF &pt) { create (PointF, &pt); } -QVariant::QVariant(const QRectF &r) { create (RectF, &r); } -QVariant::QVariant(const QLineF &l) { create (LineF, &l); } -QVariant::QVariant(const QLine &l) { create (Line, &l); } -QVariant::QVariant(const QRect &r) { create(Rect, &r); } -QVariant::QVariant(const QSize &s) { create(Size, &s); } -QVariant::QVariant(const QSizeF &s) { create(SizeF, &s); } +QVariant::QVariant(const QPoint &pt) { d.is_null = false; d.type = Point; v_construct<QPoint>(&d, pt); } +QVariant::QVariant(const QPointF &pt) { d.is_null = false; d.type = PointF; v_construct<QPointF>(&d, pt); } +QVariant::QVariant(const QRectF &r) { d.is_null = false; d.type = RectF; v_construct<QRectF>(&d, r); } +QVariant::QVariant(const QLineF &l) { d.is_null = false; d.type = LineF; v_construct<QLineF>(&d, l); } +QVariant::QVariant(const QLine &l) { d.is_null = false; d.type = Line; v_construct<QLine>(&d, l); } +QVariant::QVariant(const QRect &r) { d.is_null = false; d.type = Rect; v_construct<QRect>(&d, r); } +QVariant::QVariant(const QSize &s) { d.is_null = false; d.type = Size; v_construct<QSize>(&d, s); } +QVariant::QVariant(const QSizeF &s) { d.is_null = false; d.type = SizeF; v_construct<QSizeF>(&d, s); } #endif -QVariant::QVariant(const QUrl &u) { create(Url, &u); } -QVariant::QVariant(const QLocale &l) { create(Locale, &l); } +QVariant::QVariant(const QUrl &u) { d.is_null = false; d.type = Url; v_construct<QUrl>(&d, u); } +QVariant::QVariant(const QLocale &l) { d.is_null = false; d.type = Locale; v_construct<QLocale>(&d, l); } #ifndef QT_NO_REGEXP -QVariant::QVariant(const QRegExp ®Exp) { create(RegExp, ®Exp); } +QVariant::QVariant(const QRegExp ®Exp) { d.is_null = false; d.type = RegExp; v_construct<QRegExp>(&d, regExp); } #endif QVariant::QVariant(Qt::GlobalColor color) { create(62, &color); } @@ -1722,7 +1717,7 @@ QVariant& QVariant::operator=(const QVariant &variant) if (variant.d.is_shared) { variant.d.data.shared->ref.ref(); d = variant.d; - } else if (variant.d.type > Char) { + } else if (variant.d.type > Char && variant.d.type != QMetaType::Float) { d.type = variant.d.type; handler->construct(&d, variant.constData()); d.is_null = variant.d.is_null; @@ -1908,7 +1903,7 @@ void QVariant::load(QDataStream &s) } // const cast is safe since we operate on a newly constructed variant - if (!QMetaType::load(s, d.type, const_cast<void *>(constDataHelper(d)))) { + if (!QMetaType::load(s, d.type, const_cast<void *>(constData()))) { s.setStatus(QDataStream::ReadCorruptData); qWarning("QVariant::load: unable to load type %d.", d.type); } @@ -1948,7 +1943,7 @@ void QVariant::save(QDataStream &s) const return; } - if (!QMetaType::save(s, d.type, constDataHelper(d))) { + if (!QMetaType::save(s, d.type, constData())) { Q_ASSERT_X(false, "QVariant::save", "Invalid type to save"); qWarning("QVariant::save: unable to save type %d.", d.type); } @@ -2566,57 +2561,63 @@ static const quint32 qCanConvertMatrix[QVariant::LastCoreType + 1] = */ bool QVariant::canConvert(Type t) const { - if (d.type == uint(t)) + //we can treat floats as double + //the reason for not doing it the "proper" way is that QMetaType::Float's value is 135, + //which can't be handled by qCanConvertMatrix + //In addition QVariant::Type doesn't have a Float value, so we're using QMetaType::Float + const uint currentType = ((d.type == QMetaType::Float) ? QVariant::Double : d.type); + if (uint(t) == uint(QMetaType::Float)) t = QVariant::Double; + + if (currentType == uint(t)) return true; - if (d.type > QVariant::LastCoreType || t > QVariant::LastCoreType) { + if (currentType > QVariant::LastCoreType || t > QVariant::LastCoreType) { switch (uint(t)) { case QVariant::Int: - return d.type == QVariant::KeySequence - || d.type == QMetaType::ULong - || d.type == QMetaType::Long - || d.type == QMetaType::UShort - || d.type == QMetaType::UChar - || d.type == QMetaType::Char - || d.type == QMetaType::Short; + return currentType == QVariant::KeySequence + || currentType == QMetaType::ULong + || currentType == QMetaType::Long + || currentType == QMetaType::UShort + || currentType == QMetaType::UChar + || currentType == QMetaType::Char + || currentType == QMetaType::Short; case QVariant::Image: - return d.type == QVariant::Pixmap || d.type == QVariant::Bitmap; + return currentType == QVariant::Pixmap || currentType == QVariant::Bitmap; case QVariant::Pixmap: - return d.type == QVariant::Image || d.type == QVariant::Bitmap - || d.type == QVariant::Brush; + return currentType == QVariant::Image || currentType == QVariant::Bitmap + || currentType == QVariant::Brush; case QVariant::Bitmap: - return d.type == QVariant::Pixmap || d.type == QVariant::Image; + return currentType == QVariant::Pixmap || currentType == QVariant::Image; case QVariant::ByteArray: - return d.type == QVariant::Color; + return currentType == QVariant::Color; case QVariant::String: - return d.type == QVariant::KeySequence || d.type == QVariant::Font - || d.type == QVariant::Color; + return currentType == QVariant::KeySequence || currentType == QVariant::Font + || currentType == QVariant::Color; case QVariant::KeySequence: - return d.type == QVariant::String || d.type == QVariant::Int; + return currentType == QVariant::String || currentType == QVariant::Int; case QVariant::Font: - return d.type == QVariant::String; + return currentType == QVariant::String; case QVariant::Color: - return d.type == QVariant::String || d.type == QVariant::ByteArray - || d.type == QVariant::Brush; + return currentType == QVariant::String || currentType == QVariant::ByteArray + || currentType == QVariant::Brush; case QVariant::Brush: - return d.type == QVariant::Color || d.type == QVariant::Pixmap; + return currentType == QVariant::Color || currentType == QVariant::Pixmap; case QMetaType::Long: case QMetaType::Char: case QMetaType::UChar: case QMetaType::ULong: case QMetaType::Short: case QMetaType::UShort: - case QMetaType::Float: - return qCanConvertMatrix[QVariant::Int] & (1 << d.type) || d.type == QVariant::Int; + return qCanConvertMatrix[QVariant::Int] & (1 << currentType) || currentType == QVariant::Int; default: return false; } } - if(t == String && d.type == StringList) + if(t == String && currentType == StringList) return v_cast<QStringList>(&d)->count() == 1; else - return qCanConvertMatrix[t] & (1 << d.type); + return qCanConvertMatrix[t] & (1 << currentType); } /*! @@ -2727,7 +2728,7 @@ bool QVariant::cmp(const QVariant &v) const const void *QVariant::constData() const { - return constDataHelper(d); + return d.is_shared ? d.data.shared->ptr : reinterpret_cast<const void *>(&d.data.ptr); } /*! @@ -2740,7 +2741,7 @@ const void *QVariant::constData() const void* QVariant::data() { detach(); - return const_cast<void *>(constDataHelper(d)); + return const_cast<void *>(constData()); } diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index d7b7e3c..d73fcbc 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -181,6 +181,7 @@ class Q_CORE_EXPORT QVariant QVariant(qulonglong ull); QVariant(bool b); QVariant(double d); + QVariant(float f) { d.is_null = false; d.type = QMetaType::Float; d.data.f = f; } #ifndef QT_NO_CAST_FROM_ASCII QT_ASCII_CAST_WARN_CONSTRUCTOR QVariant(const char *str); #endif @@ -349,6 +350,7 @@ class Q_CORE_EXPORT QVariant uint u; bool b; double d; + float f; qlonglong ll; qulonglong ull; void *ptr; @@ -443,7 +445,18 @@ inline QVariant qVariantFromValue(const QVariant &t) { return t; } template <typename T> inline void qVariantSetValue(QVariant &v, const T &t) { - v = QVariant(qMetaTypeId<T>(reinterpret_cast<T *>(0)), &t); + //if possible we reuse the current QVariant private + const int type = qMetaTypeId<T>(reinterpret_cast<T *>(0)); + QVariant::Private &d = v.data_ptr(); + if (type <= int(QVariant::Char) || (type == d.type && v.isDetached())) { + d.type = type; + T *old = reinterpret_cast<T*>(d.is_shared ? d.data.shared->ptr : &d.data.ptr); + if (QTypeInfo<T>::isComplex) + old->~T(); + new (old) T(t); //call the copy constructor + } else { + v = QVariant(type, &t); + } } inline QVariant::QVariant() {} diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index 727a390..033b760 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -91,13 +91,36 @@ inline T *v_cast(QVariant::Private *d, T * = 0) #endif + +//a simple template that avoids to allocate 2 memory chunks when creating a QVariant +template <class T> class QVariantPrivateSharedEx : public QVariant::PrivateShared +{ +public: + QVariantPrivateSharedEx() : QVariant::PrivateShared(&m_t) { } + QVariantPrivateSharedEx(const T&t) : QVariant::PrivateShared(&m_t), m_t(t) { } + +private: + T m_t; +}; + // constructs a new variant if copy is 0, otherwise copy-constructs template <class T> +inline void v_construct(QVariant::Private *x, const T &t) +{ + if (sizeof(T) > sizeof(QVariant::Private::Data)) { + x->data.shared = new QVariantPrivateSharedEx<T>(t); + x->is_shared = true; + } else { + new (&x->data.ptr) T(t); + } +} + +template <class T> inline void v_construct(QVariant::Private *x, const void *copy, T * = 0) { if (sizeof(T) > sizeof(QVariant::Private::Data)) { - x->data.shared = copy ? new QVariant::PrivateShared(new T(*static_cast<const T *>(copy))) - : new QVariant::PrivateShared(new T); + x->data.shared = copy ? new QVariantPrivateSharedEx<T>(*static_cast<const T *>(copy)) + : new QVariantPrivateSharedEx<T>; x->is_shared = true; } else { if (copy) @@ -111,12 +134,15 @@ inline void v_construct(QVariant::Private *x, const void *copy, T * = 0) template <class T> inline void v_clear(QVariant::Private *d, T* = 0) { + if (sizeof(T) > sizeof(QVariant::Private::Data)) { - delete v_cast<T>(d); - delete d->data.shared; + //now we need to cast + //because QVariant::PrivateShared doesn't have a virtual destructor + delete static_cast<QVariantPrivateSharedEx<T>*>(d->data.shared); } else { v_cast<T>(d)->~T(); } + } QT_END_NAMESPACE diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h index 4d0e53c..121a875 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -63,6 +63,21 @@ typedef QObject *(*QtPluginInstanceFunction)(); void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunction function); +struct qt_plugin_instance_deleter +{ + qt_plugin_instance_deleter(QPointer<QObject> &instance) + : instance_(instance) + { + } + + ~qt_plugin_instance_deleter() + { + delete instance_; + } + + QPointer<QObject> &instance_; +}; + #define Q_IMPORT_PLUGIN(PLUGIN) \ extern QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##PLUGIN(); \ class Static##PLUGIN##PluginInstance{ \ @@ -76,8 +91,10 @@ void Q_CORE_EXPORT qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunctio #define Q_PLUGIN_INSTANCE(IMPLEMENTATION) \ { \ static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \ - if (!_instance) \ + if (!_instance) { \ + static QT_PREPEND_NAMESPACE(qt_plugin_instance_deleter) deleter(_instance); \ _instance = new IMPLEMENTATION; \ + } \ return _instance; \ } diff --git a/src/corelib/statemachine/qabstractstate.cpp b/src/corelib/statemachine/qabstractstate.cpp new file mode 100644 index 0000000..942722f --- /dev/null +++ b/src/corelib/statemachine/qabstractstate.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qabstractstate.h" +#include "qabstractstate_p.h" +#include "qstate.h" +#include "qstate_p.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractState + + \brief The QAbstractState class is the base class of states of a QStateMachine. + + \since 4.6 + \ingroup statemachine + + The QAbstractState class is the abstract base class of states that are part + of a QStateMachine. It defines the interface that all state objects have in + common. QAbstractState is part of \l{The State Machine Framework}. + + The entered() signal is emitted when the state has been entered. The + exited() signal is emitted when the state has been exited. + + The parentState() function returns the state's parent state. The machine() + function returns the state machine that the state is part of. + + \section1 Subclassing + + The onEntry() function is called when the state is entered; reimplement this + function to perform custom processing when the state is entered. + + The onExit() function is called when the state is exited; reimplement this + function to perform custom processing when the state is exited. +*/ + +QAbstractStatePrivate::QAbstractStatePrivate() +{ +} + +QAbstractStatePrivate *QAbstractStatePrivate::get(QAbstractState *q) +{ + return q->d_func(); +} + +QStateMachine *QAbstractStatePrivate::machine() const +{ + Q_Q(const QAbstractState); + QObject *par = q->parent(); + while (par != 0) { + if (QStateMachine *mach = qobject_cast<QStateMachine*>(par)) + return mach; + par = par->parent(); + } + return 0; +} + +void QAbstractStatePrivate::callOnEntry(QEvent *e) +{ + Q_Q(QAbstractState); + q->onEntry(e); +} + +void QAbstractStatePrivate::callOnExit(QEvent *e) +{ + Q_Q(QAbstractState); + q->onExit(e); +} + +void QAbstractStatePrivate::emitEntered() +{ + Q_Q(QAbstractState); + emit q->entered(); +} + +void QAbstractStatePrivate::emitExited() +{ + Q_Q(QAbstractState); + emit q->exited(); +} + +/*! + Constructs a new state with the given \a parent state. +*/ +QAbstractState::QAbstractState(QState *parent) + : QObject(*new QAbstractStatePrivate, parent) +{ +} + +/*! + \internal +*/ +QAbstractState::QAbstractState(QAbstractStatePrivate &dd, QState *parent) + : QObject(dd, parent) +{ +} + +/*! + Destroys this state. +*/ +QAbstractState::~QAbstractState() +{ +} + +/*! + Returns this state's parent state, or 0 if the state has no parent state. +*/ +QState *QAbstractState::parentState() const +{ + return qobject_cast<QState*>(parent()); +} + +/*! + Returns the state machine that this state is part of, or 0 if the state is + not part of a state machine. +*/ +QStateMachine *QAbstractState::machine() const +{ + Q_D(const QAbstractState); + return d->machine(); +} + +/*! + \fn QAbstractState::onExit(QEvent *event) + + This function is called when the state is exited. The given \a event is what + caused the state to be exited. Reimplement this function to perform custom + processing when the state is exited. +*/ + +/*! + \fn QAbstractState::onEntry(QEvent *event) + + This function is called when the state is entered. The given \a event is + what caused the state to be entered. Reimplement this function to perform + custom processing when the state is entered. +*/ + +/*! + \fn QAbstractState::entered() + + This signal is emitted when the state has been entered (after onEntry() has + been called). +*/ + +/*! + \fn QAbstractState::exited() + + This signal is emitted when the state has been exited (after onExit() has + been called). +*/ + +/*! + \reimp +*/ +bool QAbstractState::event(QEvent *e) +{ + return QObject::event(e); +} + +QT_END_NAMESPACE diff --git a/src/gui/embedded/qkbdpc101_qws.h b/src/corelib/statemachine/qabstractstate.h index f9f0104..d0ebb52 100644 --- a/src/gui/embedded/qkbdpc101_qws.h +++ b/src/corelib/statemachine/qabstractstate.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 QtGui module of the Qt Toolkit. +** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,57 +39,52 @@ ** ****************************************************************************/ -#ifndef QKBDPC101_QWS_H -#define QKBDPC101_QWS_H +#ifndef QABSTRACTSTATE_H +#define QABSTRACTSTATE_H -#include <QtGui/qkbd_qws.h> +#include <QtCore/qobject.h> QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -QT_MODULE(Gui) +QT_MODULE(Core) -#ifndef QT_NO_QWS_KEYBOARD +class QState; +class QStateMachine; -#ifndef QT_NO_QWS_KBD_PC101 - -struct QWSKeyMap { - uint key_code; - ushort unicode; - ushort shift_unicode; - ushort ctrl_unicode; -}; - -class QWSPC101KeyboardHandler : public QWSKeyboardHandler +class QAbstractStatePrivate; +class Q_CORE_EXPORT QAbstractState : public QObject { + Q_OBJECT public: - explicit QWSPC101KeyboardHandler(const QString&); - virtual ~QWSPC101KeyboardHandler(); + ~QAbstractState(); - virtual void doKey(uchar scancode); - virtual const QWSKeyMap *keyMap() const; + QState *parentState() const; + QStateMachine *machine() const; + +Q_SIGNALS: + void entered(); + void exited(); protected: - bool shift; - bool alt; - bool ctrl; - bool caps; -#if defined(QT_QWS_IPAQ) - uint ipaq_return_pressed:1; -#endif - uint extended:2; - Qt::KeyboardModifiers modifiers; - int prevuni; - int prevkey; -}; + QAbstractState(QState *parent = 0); -#endif // QT_NO_QWS_KBD_PC101 + virtual void onEntry(QEvent *event) = 0; + virtual void onExit(QEvent *event) = 0; -#endif // QT_NO_QWS_KEYBOARD + bool event(QEvent *e); + +protected: + QAbstractState(QAbstractStatePrivate &dd, QState *parent); + +private: + Q_DISABLE_COPY(QAbstractState) + Q_DECLARE_PRIVATE(QAbstractState) +}; QT_END_NAMESPACE QT_END_HEADER -#endif // QKBDPC101_QWS_H +#endif diff --git a/src/corelib/statemachine/qabstractstate_p.h b/src/corelib/statemachine/qabstractstate_p.h new file mode 100644 index 0000000..b4f3108 --- /dev/null +++ b/src/corelib/statemachine/qabstractstate_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QABSTRACTSTATE_P_H +#define QABSTRACTSTATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QStateMachine; + +class QAbstractState; +class QAbstractStatePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QAbstractState) + +public: + QAbstractStatePrivate(); + + static QAbstractStatePrivate *get(QAbstractState *q); + + QStateMachine *machine() const; + + void callOnEntry(QEvent *e); + void callOnExit(QEvent *e); + + void emitEntered(); + void emitExited(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp new file mode 100644 index 0000000..dfcafeb --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qabstracttransition.h" +#include "qabstracttransition_p.h" +#include "qabstractstate.h" +#include "qstate.h" +#include "qstatemachine.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractTransition + + \brief The QAbstractTransition class is the base class of transitions between QAbstractState objects. + + \since 4.6 + \ingroup statemachine + + The QAbstractTransition class is the abstract base class of transitions + between states (QAbstractState objects) of a + QStateMachine. QAbstractTransition is part of \l{The State Machine + Framework}. + + The sourceState() function returns the source of the transition. The + targetStates() function returns the targets of the transition. The machine() + function returns the state machine that the transition is part of. + + Transitions can cause animations to be played. Use the addAnimation() + function to add an animation to the transition. + + \section1 Subclassing + + The eventTest() function is called by the state machine to determine whether + an event should trigger the transition. In your reimplementation you + typically check the event type and cast the event object to the proper type, + and check that one or more properties of the event meet your criteria. + + The onTransition() function is called when the transition is triggered; + reimplement this function to perform custom processing for the transition. +*/ + +/*! + \property QAbstractTransition::sourceState + + \brief the source state (parent) of this transition +*/ + +/*! + \property QAbstractTransition::targetState + + \brief the target state of this transition +*/ + +/*! + \property QAbstractTransition::targetStates + + \brief the target states of this transition + + If multiple states are specified, all must be descendants of the same + parallel group state. +*/ + +QAbstractTransitionPrivate::QAbstractTransitionPrivate() +{ +} + +QAbstractTransitionPrivate *QAbstractTransitionPrivate::get(QAbstractTransition *q) +{ + return q->d_func(); +} + +QStateMachine *QAbstractTransitionPrivate::machine() const +{ + Q_Q(const QAbstractTransition); + QObject *par = q->parent(); + while (par != 0) { + if (QStateMachine *mach = qobject_cast<QStateMachine*>(par)) + return mach; + par = par->parent(); + } + return 0; +} + +bool QAbstractTransitionPrivate::callEventTest(QEvent *e) +{ + Q_Q(QAbstractTransition); + return q->eventTest(e); +} + +void QAbstractTransitionPrivate::callOnTransition(QEvent *e) +{ + Q_Q(QAbstractTransition); + q->onTransition(e); +} + +QState *QAbstractTransitionPrivate::sourceState() const +{ + Q_Q(const QAbstractTransition); + return qobject_cast<QState*>(q->parent()); +} + +/*! + Constructs a new QAbstractTransition object with the given \a sourceState. +*/ +QAbstractTransition::QAbstractTransition(QState *sourceState) + : QObject(*new QAbstractTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new QAbstractTransition object with the given \a targets and \a + sourceState. +*/ +QAbstractTransition::QAbstractTransition(const QList<QAbstractState*> &targets, + QState *sourceState) + : QObject(*new QAbstractTransitionPrivate, sourceState) +{ + setTargetStates(targets); +} + +/*! + \internal +*/ +QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, + QState *parent) + : QObject(dd, parent) +{ +} + +/*! + \internal +*/ +QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, + const QList<QAbstractState*> &targets, + QState *parent) + : QObject(dd, parent) +{ + setTargetStates(targets); +} + +/*! + Destroys this transition. +*/ +QAbstractTransition::~QAbstractTransition() +{ +} + +/*! + Returns the source state of this transition, or 0 if this transition has no + source state. +*/ +QState *QAbstractTransition::sourceState() const +{ + Q_D(const QAbstractTransition); + return d->sourceState(); +} + +/*! + Returns the target state of this transition, or 0 if the transition has no + target. +*/ +QAbstractState *QAbstractTransition::targetState() const +{ + Q_D(const QAbstractTransition); + if (d->targetStates.isEmpty()) + return 0; + return d->targetStates.first(); +} + +/*! + Sets the \a target state of this transition. +*/ +void QAbstractTransition::setTargetState(QAbstractState* target) +{ + Q_D(QAbstractTransition); + if (!target) + d->targetStates.clear(); + else + setTargetStates(QList<QAbstractState*>() << target); +} + +/*! + Returns the target states of this transition, or an empty list if this + transition has no target states. +*/ +QList<QAbstractState*> QAbstractTransition::targetStates() const +{ + Q_D(const QAbstractTransition); + QList<QAbstractState*> result; + for (int i = 0; i < d->targetStates.size(); ++i) { + QAbstractState *target = d->targetStates.at(i); + if (target) + result.append(target); + } + return result; +} + +/*! + Sets the target states of this transition to be the given \a targets. +*/ +void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets) +{ + Q_D(QAbstractTransition); + + for (int i=0; i<targets.size(); ++i) { + QAbstractState *target = targets.at(i); + if (!target) { + qWarning("QAbstractTransition::setTargetStates: target state(s) cannot be null"); + return; + } + if (target->machine() != 0 && target->machine()->rootState() == target) { + qWarning("QAbstractTransition::setTargetStates: root state cannot be target of transition"); + return; + } + } + + d->targetStates.clear(); + for (int i = 0; i < targets.size(); ++i) + d->targetStates.append(targets.at(i)); +} + +/*! + Returns the state machine that this transition is part of, or 0 if the + transition is not part of a state machine. +*/ +QStateMachine *QAbstractTransition::machine() const +{ + Q_D(const QAbstractTransition); + return d->machine(); +} + +#ifndef QT_NO_ANIMATION + +/*! + Adds the given \a animation to this transition. + The transition does not take ownership of the animation. + + \sa removeAnimation(), animations() +*/ +void QAbstractTransition::addAnimation(QAbstractAnimation *animation) +{ + Q_D(QAbstractTransition); + if (!animation) { + qWarning("QAbstractTransition::addAnimation: cannot add null animation"); + return; + } + d->animations.append(animation); +} + +/*! + Removes the given \a animation from this transition. + + \sa addAnimation() +*/ +void QAbstractTransition::removeAnimation(QAbstractAnimation *animation) +{ + Q_D(QAbstractTransition); + if (!animation) { + qWarning("QAbstractTransition::removeAnimation: cannot remove null animation"); + return; + } + d->animations.removeOne(animation); +} + +/*! + Returns the list of animations associated with this transition, or an empty + list if it has no animations. + + \sa addAnimation() +*/ +QList<QAbstractAnimation*> QAbstractTransition::animations() const +{ + Q_D(const QAbstractTransition); + return d->animations; +} + +#endif + +/*! + \fn QAbstractTransition::eventTest(QEvent *event) + + This function is called to determine whether the given \a event should cause + this transition to trigger. Reimplement this function and return true if the + event should trigger the transition, otherwise return false. +*/ + +/*! + \fn QAbstractTransition::onTransition(QEvent *event) + + This function is called when the transition is triggered. The given \a event + is what caused the transition to trigger. Reimplement this function to + perform custom processing when the transition is triggered. +*/ + +/*! + \reimp +*/ +bool QAbstractTransition::event(QEvent *e) +{ + return QObject::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qabstracttransition.h b/src/corelib/statemachine/qabstracttransition.h new file mode 100644 index 0000000..c63d55a --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QABSTRACTTRANSITION_H +#define QABSTRACTTRANSITION_H + +#include <QtCore/qobject.h> + +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEvent; +class QAbstractState; +class QState; +class QStateMachine; + +#ifndef QT_NO_ANIMATION +class QAbstractAnimation; +#endif + +class QAbstractTransitionPrivate; +class Q_CORE_EXPORT QAbstractTransition : public QObject +{ + Q_OBJECT + Q_PROPERTY(QState* sourceState READ sourceState) + Q_PROPERTY(QAbstractState* targetState READ targetState WRITE setTargetState) + Q_PROPERTY(QList<QAbstractState*> targetStates READ targetStates WRITE setTargetStates) +public: + QAbstractTransition(QState *sourceState = 0); + QAbstractTransition(const QList<QAbstractState*> &targets, QState *sourceState = 0); + virtual ~QAbstractTransition(); + + QState *sourceState() const; + QAbstractState *targetState() const; + void setTargetState(QAbstractState* target); + QList<QAbstractState*> targetStates() const; + void setTargetStates(const QList<QAbstractState*> &targets); + + QStateMachine *machine() const; + +#ifndef QT_NO_ANIMATION + void addAnimation(QAbstractAnimation *animation); + void removeAnimation(QAbstractAnimation *animation); + QList<QAbstractAnimation*> animations() const; +#endif + +protected: + virtual bool eventTest(QEvent *event) = 0; + + virtual void onTransition(QEvent *event) = 0; + + bool event(QEvent *e); + +protected: + QAbstractTransition(QAbstractTransitionPrivate &dd, QState *parent); + QAbstractTransition(QAbstractTransitionPrivate &dd, + const QList<QAbstractState*> &targets, QState *parent); + +private: + Q_DISABLE_COPY(QAbstractTransition) + Q_DECLARE_PRIVATE(QAbstractTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qabstracttransition_p.h b/src/corelib/statemachine/qabstracttransition_p.h new file mode 100644 index 0000000..f067984 --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QABSTRACTTRANSITION_P_H +#define QABSTRACTTRANSITION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qobject_p.h> + +#include <QtCore/qlist.h> +#include <QtCore/qpointer.h> + +QT_BEGIN_NAMESPACE + +class QAbstractState; +class QState; +class QStateMachine; + +class QAbstractTransition; +class Q_CORE_EXPORT QAbstractTransitionPrivate + : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QAbstractTransition) +public: + QAbstractTransitionPrivate(); + + static QAbstractTransitionPrivate *get(QAbstractTransition *q); + + bool callEventTest(QEvent *e); + void callOnTransition(QEvent *e); + QState *sourceState() const; + QStateMachine *machine() const; + + QList<QPointer<QAbstractState> > targetStates; + +#ifndef QT_NO_ANIMATION + QList<QAbstractAnimation*> animations; +#endif +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qeventtransition.cpp b/src/corelib/statemachine/qeventtransition.cpp new file mode 100644 index 0000000..f25d821 --- /dev/null +++ b/src/corelib/statemachine/qeventtransition.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qeventtransition.h" +#include "qeventtransition_p.h" +#include "qwrappedevent.h" +#include "qstate.h" +#include "qstate_p.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QEventTransition + + \brief The QEventTransition class provides a QObject-specific transition for Qt events. + + \since 4.6 + \ingroup statemachine + + A QEventTransition object binds an event to a particular QObject. + QEventTransition is part of \l{The State Machine Framework}. + + Example: + + \code + QPushButton *button = ...; + QState *s1 = ...; + QState *s2 = ...; + // If in s1 and the button receives an Enter event, transition to s2 + QEventTransition *enterTransition = new QEventTransition(button, QEvent::Enter); + enterTransition->setTargetState(s2); + s1->addTransition(enterTransition); + // If in s2 and the button receives an Exit event, transition back to s1 + QEventTransition *leaveTransition = new QEventTransition(button, QEvent::Leave); + leaveTransition->setTargetState(s1); + s2->addTransition(leaveTransition); + \endcode + + \section1 Subclassing + + When reimplementing the eventTest() function, you should first call the base + implementation to verify that the event is a QWrappedEvent for the proper + object and event type. You may then cast the event to a QWrappedEvent and + get the original event by calling QWrappedEvent::event(), and perform + additional checks on that object. + + \sa QState::addTransition() +*/ + +/*! + \property QEventTransition::eventObject + + \brief the event source that this event transition is associated with +*/ + +/*! + \property QEventTransition::eventType + + \brief the type of event that this event transition is associated with +*/ +QEventTransitionPrivate::QEventTransitionPrivate() +{ + object = 0; + eventType = QEvent::None; + registered = false; +} + +QEventTransitionPrivate *QEventTransitionPrivate::get(QEventTransition *q) +{ + return q->d_func(); +} + +void QEventTransitionPrivate::unregister() +{ + Q_Q(QEventTransition); + if (!registered || !machine()) + return; + QStateMachinePrivate::get(machine())->unregisterEventTransition(q); +} + +void QEventTransitionPrivate::maybeRegister() +{ + Q_Q(QEventTransition); + if (!machine() || !machine()->configuration().contains(sourceState())) + return; + QStateMachinePrivate::get(machine())->registerEventTransition(q); +} + +/*! + Constructs a new QEventTransition object with the given \a sourceState. +*/ +QEventTransition::QEventTransition(QState *sourceState) + : QAbstractTransition(*new QEventTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new QEventTransition object associated with events of the given + \a type for the given \a object, and with the given \a sourceState. +*/ +QEventTransition::QEventTransition(QObject *object, QEvent::Type type, + QState *sourceState) + : QAbstractTransition(*new QEventTransitionPrivate, sourceState) +{ + Q_D(QEventTransition); + d->registered = false; + d->object = object; + d->eventType = type; +} + +/*! + Constructs a new QEventTransition object associated with events of the given + \a type for the given \a object. The transition has the given \a targets and + \a sourceState. +*/ +QEventTransition::QEventTransition(QObject *object, QEvent::Type type, + const QList<QAbstractState*> &targets, + QState *sourceState) + : QAbstractTransition(*new QEventTransitionPrivate, targets, sourceState) +{ + Q_D(QEventTransition); + d->registered = false; + d->object = object; + d->eventType = type; +} + +/*! + \internal +*/ +QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QState *parent) + : QAbstractTransition(dd, parent) +{ +} + +/*! + \internal +*/ +QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, QState *parent) + : QAbstractTransition(dd, parent) +{ + Q_D(QEventTransition); + d->registered = false; + d->object = object; + d->eventType = type; +} + +/*! + \internal +*/ +QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, const QList<QAbstractState*> &targets, + QState *parent) + : QAbstractTransition(dd, targets, parent) +{ + Q_D(QEventTransition); + d->registered = false; + d->object = object; + d->eventType = type; +} + +/*! + Destroys this QObject event transition. +*/ +QEventTransition::~QEventTransition() +{ +} + +/*! + Returns the event type that this event transition is associated with. +*/ +QEvent::Type QEventTransition::eventType() const +{ + Q_D(const QEventTransition); + return d->eventType; +} + +/*! + Sets the event \a type that this event transition is associated with. +*/ +void QEventTransition::setEventType(QEvent::Type type) +{ + Q_D(QEventTransition); + if (d->eventType == type) + return; + d->unregister(); + d->eventType = type; + d->maybeRegister(); +} + +/*! + Returns the event source associated with this event transition. +*/ +QObject *QEventTransition::eventObject() const +{ + Q_D(const QEventTransition); + return d->object; +} + +/*! + Sets the event source associated with this event transition to be the given + \a object. +*/ +void QEventTransition::setEventObject(QObject *object) +{ + Q_D(QEventTransition); + if (d->object == object) + return; + d->unregister(); + d->object = object; + d->maybeRegister(); +} + +/*! + \reimp +*/ +bool QEventTransition::eventTest(QEvent *event) +{ + Q_D(const QEventTransition); + if (event->type() == QEvent::Wrapped) { + QWrappedEvent *we = static_cast<QWrappedEvent*>(event); + return (we->object() == d->object) + && (we->event()->type() == d->eventType); + } + return false; +} + +/*! + \reimp +*/ +void QEventTransition::onTransition(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +bool QEventTransition::event(QEvent *e) +{ + return QAbstractTransition::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qeventtransition.h b/src/corelib/statemachine/qeventtransition.h new file mode 100644 index 0000000..3530bdd --- /dev/null +++ b/src/corelib/statemachine/qeventtransition.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QEVENTTRANSITION_H +#define QEVENTTRANSITION_H + +#include <QtCore/qabstracttransition.h> +#include <QtCore/qcoreevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEventTransitionPrivate; +class Q_CORE_EXPORT QEventTransition : public QAbstractTransition +{ + Q_OBJECT + Q_PROPERTY(QObject* eventObject READ eventObject WRITE setEventObject) + Q_PROPERTY(QEvent::Type eventType READ eventType WRITE setEventType) +public: + QEventTransition(QState *sourceState = 0); + QEventTransition(QObject *object, QEvent::Type type, QState *sourceState = 0); + QEventTransition(QObject *object, QEvent::Type type, + const QList<QAbstractState*> &targets, QState *sourceState = 0); + ~QEventTransition(); + + QObject *eventObject() const; + void setEventObject(QObject *object); + + QEvent::Type eventType() const; + void setEventType(QEvent::Type type); + +protected: + bool eventTest(QEvent *event); + void onTransition(QEvent *event); + + bool event(QEvent *e); + +protected: + QEventTransition(QEventTransitionPrivate &dd, QState *parent); + QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, QState *parent); + QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, const QList<QAbstractState*> &targets, + QState *parent); + +private: + Q_DISABLE_COPY(QEventTransition) + Q_DECLARE_PRIVATE(QEventTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qeventtransition_p.h b/src/corelib/statemachine/qeventtransition_p.h new file mode 100644 index 0000000..600cec0 --- /dev/null +++ b/src/corelib/statemachine/qeventtransition_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QEVENTTRANSITION_P_H +#define QEVENTTRANSITION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qabstracttransition_p.h" + +QT_BEGIN_NAMESPACE + +class QEventTransition; +class Q_CORE_EXPORT QEventTransitionPrivate : public QAbstractTransitionPrivate +{ + Q_DECLARE_PUBLIC(QEventTransition) +public: + QEventTransitionPrivate(); + + static QEventTransitionPrivate *get(QEventTransition *q); + + void unregister(); + void maybeRegister(); + + bool registered; + QObject *object; + QEvent::Type eventType; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qfinalstate.cpp b/src/corelib/statemachine/qfinalstate.cpp new file mode 100644 index 0000000..0980336 --- /dev/null +++ b/src/corelib/statemachine/qfinalstate.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qfinalstate.h" +#include "qabstractstate_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QFinalState + + \brief The QFinalState class provides a final state. + + \since 4.6 + \ingroup statemachine + + A final state is used to communicate that (part of) a QStateMachine has + finished its work. When a final top-level state is entered, the state + machine's \l{QStateMachine::finished()}{finished}() signal is emitted. In + general, when a final substate (a child of a QState) is entered, the parent + state's \l{QState::finished()}{finished}() signal is emitted. QFinalState + is part of \l{The State Machine Framework}. + + To use a final state, you create a QFinalState object and add a transition + to it from another state. Example: + + \code + QPushButton button; + + QStateMachine machine; + QState *s1 = new QState(); + QFinalState *s2 = new QFinalState(); + s1->addTransition(&button, SIGNAL(clicked()), s2); + machine.addState(s1); + machine.addState(s2); + + QObject::connect(&machine, SIGNAL(finished()), QApplication::instance(), SLOT(quit())); + machine.setInitialState(s1); + machine.start(); + \endcode + + \sa QStateMachine::finished(), QState::finished() +*/ + +class QFinalStatePrivate : public QAbstractStatePrivate +{ + Q_DECLARE_PUBLIC(QFinalState) + +public: + QFinalStatePrivate(); +}; + +QFinalStatePrivate::QFinalStatePrivate() +{ +} + +/*! + Constructs a new QFinalState object with the given \a parent state. +*/ +QFinalState::QFinalState(QState *parent) + : QAbstractState(*new QFinalStatePrivate, parent) +{ +} + +/*! + Destroys this final state. +*/ +QFinalState::~QFinalState() +{ +} + +/*! + \reimp +*/ +void QFinalState::onEntry(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QFinalState::onExit(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +bool QFinalState::event(QEvent *e) +{ + return QAbstractState::event(e); +} + +QT_END_NAMESPACE diff --git a/src/plugins/kbddrivers/linuxis/linuxiskbddriverplugin.h b/src/corelib/statemachine/qfinalstate.h index b5f599f..fa68394 100644 --- a/src/plugins/kbddrivers/linuxis/linuxiskbddriverplugin.h +++ b/src/corelib/statemachine/qfinalstate.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 QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,20 +39,38 @@ ** ****************************************************************************/ -#ifndef LINUXISKBDDRIVERPLUGIN_H -#define LINUXISKBDDRIVERPLUGIN_H +#ifndef QFINALSTATE_H +#define QFINALSTATE_H -#include <QtGui/QWSKeyboardHandlerFactoryInterface> +#include <QtCore/qabstractstate.h> -class LinuxInputSubsystemKbdDriverPlugin : public QKbdDriverPlugin { +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QFinalStatePrivate; +class Q_CORE_EXPORT QFinalState : public QAbstractState +{ Q_OBJECT public: - LinuxInputSubsystemKbdDriverPlugin( QObject *parent = 0 ); - ~LinuxInputSubsystemKbdDriverPlugin(); + QFinalState(QState *parent = 0); + ~QFinalState(); + +protected: + void onEntry(QEvent *event); + void onExit(QEvent *event); - QWSKeyboardHandler* create(const QString& driver, const QString& device); - QWSKeyboardHandler* create(const QString& driver); - QStringList keys()const; + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QFinalState) + Q_DECLARE_PRIVATE(QFinalState) }; -#endif // LINUXISKBDDRIVERPLUGIN_H +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qhistorystate.cpp b/src/corelib/statemachine/qhistorystate.cpp new file mode 100644 index 0000000..517faa8 --- /dev/null +++ b/src/corelib/statemachine/qhistorystate.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qhistorystate.h" +#include "qhistorystate_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QHistoryState + + \brief The QHistoryState class provides a means of returning to a previously active substate. + + \since 4.6 + \ingroup statemachine + + A history state is a pseudo-state that represents the child state that the + parent state was in the last time the parent state was exited. A transition + with a history state as its target is in fact a transition to one of the + other child states of the parent state. QHistoryState is part of \l{The + State Machine Framework}. + + Use the setDefaultState() function to set the state that should be entered + if the parent state has never been entered. Example: + + \code + QStateMachine machine; + + QState *s1 = new QState(); + QState *s11 = new QState(s1); + QState *s12 = new QState(s1); + + QHistoryState *s1h = new QHistoryState(s1); + s1h->setDefaultState(s11); + + machine.addState(s1); + + QState *s2 = new QState(); + machine.addState(s2); + + QPushButton *button = new QPushButton(); + // Clicking the button will cause the state machine to enter the child state + // that s1 was in the last time s1 was exited, or the history state's default + // state if s1 has never been entered. + s1->addTransition(button, SIGNAL(clicked()), s1h); + \endcode + + By default a history state is shallow, meaning that it won't remember nested + states. This can be configured through the historyType property. +*/ + +/*! + \property QHistoryState::defaultState + + \brief the default state of this history state +*/ + +/*! + \property QHistoryState::historyType + + \brief the type of history that this history state records + + The default value of this property is QHistoryState::ShallowHistory. +*/ + +/*! + \enum QHistoryState::HistoryType + + This enum specifies the type of history that a QHistoryState records. + + \value ShallowHistory Only the immediate child states of the parent state + are recorded. In this case a transition with the history state as its + target will end up in the immediate child state that the parent was in the + last time it was exited. This is the default. + + \value DeepHistory Nested states are recorded. In this case a transition + with the history state as its target will end up in the most deeply nested + descendant state the parent was in the last time it was exited. +*/ + +QHistoryStatePrivate::QHistoryStatePrivate() + : defaultState(0) +{ +} + +QHistoryStatePrivate *QHistoryStatePrivate::get(QHistoryState *q) +{ + return q->d_func(); +} + +/*! + Constructs a new shallow history state with the given \a parent state. +*/ +QHistoryState::QHistoryState(QState *parent) + : QAbstractState(*new QHistoryStatePrivate, parent) +{ + Q_D(QHistoryState); + d->historyType = ShallowHistory; +} +/*! + Constructs a new history state of the given \a type, with the given \a + parent state. +*/ +QHistoryState::QHistoryState(HistoryType type, QState *parent) + : QAbstractState(*new QHistoryStatePrivate, parent) +{ + Q_D(QHistoryState); + d->historyType = type; +} + +/*! + Destroys this history state. +*/ +QHistoryState::~QHistoryState() +{ +} + +/*! + Returns this history state's default state. The default state indicates the + state to transition to if the parent state has never been entered before. +*/ +QAbstractState *QHistoryState::defaultState() const +{ + Q_D(const QHistoryState); + return d->defaultState; +} + +/*! + Sets this history state's default state to be the given \a state. + \a state must be a sibling of this history state. +*/ +void QHistoryState::setDefaultState(QAbstractState *state) +{ + Q_D(QHistoryState); + if (state && state->parentState() != parentState()) { + qWarning("QHistoryState::setDefaultState: state %p does not belong " + "to this history state's group (%p)", state, parentState()); + return; + } + d->defaultState = state; +} + +/*! + Returns the type of history that this history state records. +*/ +QHistoryState::HistoryType QHistoryState::historyType() const +{ + Q_D(const QHistoryState); + return d->historyType; +} + +/*! + Sets the \a type of history that this history state records. +*/ +void QHistoryState::setHistoryType(HistoryType type) +{ + Q_D(QHistoryState); + d->historyType = type; +} + +/*! + \reimp +*/ +void QHistoryState::onEntry(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QHistoryState::onExit(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +bool QHistoryState::event(QEvent *e) +{ + return QAbstractState::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qhistorystate.h b/src/corelib/statemachine/qhistorystate.h new file mode 100644 index 0000000..a0682bd --- /dev/null +++ b/src/corelib/statemachine/qhistorystate.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QHISTORYSTATE_H +#define QHISTORYSTATE_H + +#include <QtCore/qabstractstate.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QHistoryStatePrivate; +class Q_CORE_EXPORT QHistoryState : public QAbstractState +{ + Q_OBJECT + Q_PROPERTY(QAbstractState* defaultState READ defaultState WRITE setDefaultState) + Q_PROPERTY(HistoryType historyType READ historyType WRITE setHistoryType) + Q_ENUMS(HistoryType) +public: + enum HistoryType { + ShallowHistory, + DeepHistory + }; + + QHistoryState(QState *parent = 0); + QHistoryState(HistoryType type, QState *parent = 0); + ~QHistoryState(); + + QAbstractState *defaultState() const; + void setDefaultState(QAbstractState *state); + + HistoryType historyType() const; + void setHistoryType(HistoryType type); + +protected: + void onEntry(QEvent *event); + void onExit(QEvent *event); + + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QHistoryState) + Q_DECLARE_PRIVATE(QHistoryState) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qhistorystate_p.h b/src/corelib/statemachine/qhistorystate_p.h new file mode 100644 index 0000000..5aaa64c --- /dev/null +++ b/src/corelib/statemachine/qhistorystate_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QHISTORYSTATE_P_H +#define QHISTORYSTATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qabstractstate_p.h" + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QHistoryState; +class QHistoryStatePrivate : public QAbstractStatePrivate +{ + Q_DECLARE_PUBLIC(QHistoryState) + +public: + QHistoryStatePrivate(); + + static QHistoryStatePrivate *get(QHistoryState *q); + + QAbstractState *defaultState; + QHistoryState::HistoryType historyType; + QList<QAbstractState*> configuration; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/kbddrivers/linuxis/linuxiskbdhandler.h b/src/corelib/statemachine/qsignalevent.h index 3211309..8221f68 100644 --- a/src/plugins/kbddrivers/linuxis/linuxiskbdhandler.h +++ b/src/corelib/statemachine/qsignalevent.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 QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,42 +39,39 @@ ** ****************************************************************************/ -#ifndef LINUXISKBDHANDLER_H -#define LINUXISKBDHANDLER_H +#ifndef QSIGNALEVENT_H +#define QSIGNALEVENT_H -#include <QObject> -#include <QWSKeyboardHandler> +#include <QtCore/qcoreevent.h> -class QSocketNotifier; -class LinuxInputSubsystemKbdHandler : public QObject, public QWSKeyboardHandler { - Q_OBJECT -public: - LinuxInputSubsystemKbdHandler(const QString &device = QString("/dev/input/event0")); - ~LinuxInputSubsystemKbdHandler(); +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> - struct keytable_s { - int code; - int unicode; - int keycode; - }; +QT_BEGIN_HEADER - struct keymap_s { - int unicode; - int keycode; - }; +QT_BEGIN_NAMESPACE -private: - void initmap(); +QT_MODULE(Core) - QSocketNotifier *m_notify; - int kbdFD; - bool shift; +class Q_CORE_EXPORT QSignalEvent : public QEvent +{ +public: + QSignalEvent(const QObject *sender, int signalIndex, + const QList<QVariant> &arguments); + ~QSignalEvent(); - static struct keytable_s keytable[]; - static struct keymap_s keymap[]; + inline const QObject *sender() const { return m_sender; } + inline int signalIndex() const { return m_signalIndex; } + inline QList<QVariant> arguments() const { return m_arguments; } -private Q_SLOTS: - void readKbdData(); +private: + const QObject *m_sender; + int m_signalIndex; + QList<QVariant> m_arguments; }; -#endif // LINUXISKBDHANDLER_H +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qsignaleventgenerator_p.h b/src/corelib/statemachine/qsignaleventgenerator_p.h new file mode 100644 index 0000000..cf0ea1e --- /dev/null +++ b/src/corelib/statemachine/qsignaleventgenerator_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QSIGNALEVENTGENERATOR_P_H +#define QSIGNALEVENTGENERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> + +QT_BEGIN_NAMESPACE + +class QStateMachine; + +class QSignalEventGenerator : public QObject +{ +public: + QSignalEventGenerator(QStateMachine *parent); + + static const QMetaObject staticMetaObject; + virtual const QMetaObject *metaObject() const; + virtual void *qt_metacast(const char *); + virtual int qt_metacall(QMetaObject::Call, int, void **argv); + +private: + Q_DISABLE_COPY(QSignalEventGenerator) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qsignaltransition.cpp b/src/corelib/statemachine/qsignaltransition.cpp new file mode 100644 index 0000000..9ffcb9c --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qsignaltransition.h" +#include "qsignaltransition_p.h" +#include "qsignalevent.h" +#include "qstate.h" +#include "qstate_p.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QSignalTransition + + \brief The QSignalTransition class provides a transition based on a Qt signal. + + \since 4.6 + \ingroup statemachine + + Typically you would use the overload of QState::addTransition() that takes a + sender and signal as arguments, rather than creating QSignalTransition + objects directly. QSignalTransition is part of \l{The State Machine + Framework}. + + You can subclass QSignalTransition and reimplement eventTest() to make a + signal transition conditional; the event object passed to eventTest() will + be a QSignalEvent object. Example: + + \code + class CheckedTransition : public QSignalTransition + { + public: + CheckedTransition(QCheckBox *check) + : QSignalTransition(check, SIGNAL(stateChanged(int))) {} + protected: + bool eventTest(QEvent *e) const { + if (!QSignalTransition::eventTest(e)) + return false; + QSignalEvent *se = static_cast<QSignalEvent*>(e); + return (se->arguments().at(0).toInt() == Qt::Checked); + } + }; + + ... + + QCheckBox *check = new QCheckBox(); + check->setTristate(true); + + QState *s1 = new QState(); + QState *s2 = new QState(); + CheckedTransition *t1 = new CheckedTransition(check); + t1->setTargetState(s2); + s1->addTransition(t1); + \endcode +*/ + +/*! + \property QSignalTransition::senderObject + + \brief the sender object that this signal transition is associated with +*/ + +/*! + \property QSignalTransition::signal + + \brief the signal that this signal transition is associated with +*/ + +QSignalTransitionPrivate::QSignalTransitionPrivate() +{ + sender = 0; + signalIndex = -1; +} + +QSignalTransitionPrivate *QSignalTransitionPrivate::get(QSignalTransition *q) +{ + return q->d_func(); +} + +void QSignalTransitionPrivate::unregister() +{ + Q_Q(QSignalTransition); + if ((signalIndex == -1) || !machine()) + return; + QStateMachinePrivate::get(machine())->unregisterSignalTransition(q); +} + +void QSignalTransitionPrivate::maybeRegister() +{ + Q_Q(QSignalTransition); + if (!machine() || !machine()->configuration().contains(sourceState())) + return; + QStateMachinePrivate::get(machine())->registerSignalTransition(q); +} + +/*! + Constructs a new signal transition with the given \a sourceState. +*/ +QSignalTransition::QSignalTransition(QState *sourceState) + : QAbstractTransition(*new QSignalTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new signal transition associated with the given \a signal of + the given \a sender, and with the given \a sourceState. +*/ +QSignalTransition::QSignalTransition(QObject *sender, const char *signal, + QState *sourceState) + : QAbstractTransition(*new QSignalTransitionPrivate, sourceState) +{ + Q_D(QSignalTransition); + d->sender = sender; + d->signal = signal; +} + +/*! + Constructs a new signal transition associated with the given \a signal of + the given \a sender. The transition has the given \a targets and \a + sourceState. +*/ +QSignalTransition::QSignalTransition(QObject *sender, const char *signal, + const QList<QAbstractState*> &targets, + QState *sourceState) + : QAbstractTransition(*new QSignalTransitionPrivate, targets, sourceState) +{ + Q_D(QSignalTransition); + d->sender = sender; + d->signal = signal; +} + +/*! + Destroys this signal transition. +*/ +QSignalTransition::~QSignalTransition() +{ +} + +/*! + Returns the sender object associated with this signal transition. +*/ +QObject *QSignalTransition::senderObject() const +{ + Q_D(const QSignalTransition); + return d->sender; +} + +/*! + Sets the \a sender object associated with this signal transition. +*/ +void QSignalTransition::setSenderObject(QObject *sender) +{ + Q_D(QSignalTransition); + if (sender == d->sender) + return; + d->unregister(); + d->sender = sender; + d->maybeRegister(); +} + +/*! + Returns the signal associated with this signal transition. +*/ +QByteArray QSignalTransition::signal() const +{ + Q_D(const QSignalTransition); + return d->signal; +} + +/*! + Sets the \a signal associated with this signal transition. +*/ +void QSignalTransition::setSignal(const QByteArray &signal) +{ + Q_D(QSignalTransition); + if (signal == d->signal) + return; + d->unregister(); + d->signal = signal; + d->maybeRegister(); +} + +/*! + \reimp + + The \a event is a QSignalEvent object. The default implementation returns + true if the event's sender and signal index match this transition, and + returns false otherwise. +*/ +bool QSignalTransition::eventTest(QEvent *event) +{ + Q_D(const QSignalTransition); + if (event->type() == QEvent::Signal) { + if (d->signalIndex == -1) + return false; + QSignalEvent *se = static_cast<QSignalEvent*>(event); + return (se->sender() == d->sender) + && (se->signalIndex() == d->signalIndex); + } + return false; +} + +/*! + \reimp +*/ +void QSignalTransition::onTransition(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +bool QSignalTransition::event(QEvent *e) +{ + return QAbstractTransition::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qsignaltransition.h b/src/corelib/statemachine/qsignaltransition.h new file mode 100644 index 0000000..b485785 --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QSIGNALTRANSITION_H +#define QSIGNALTRANSITION_H + +#include <QtCore/qabstracttransition.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QSignalTransitionPrivate; +class Q_CORE_EXPORT QSignalTransition : public QAbstractTransition +{ + Q_OBJECT + Q_PROPERTY(QObject* senderObject READ senderObject WRITE setSenderObject) + Q_PROPERTY(QByteArray signal READ signal WRITE setSignal) +public: + QSignalTransition(QState *sourceState = 0); + QSignalTransition(QObject *sender, const char *signal, + QState *sourceState = 0); + QSignalTransition(QObject *sender, const char *signal, + const QList<QAbstractState*> &targets, + QState *sourceState = 0); + ~QSignalTransition(); + + QObject *senderObject() const; + void setSenderObject(QObject *sender); + + QByteArray signal() const; + void setSignal(const QByteArray &signal); + +protected: + bool eventTest(QEvent *event); + void onTransition(QEvent *event); + + bool event(QEvent *e); + +private: + Q_DISABLE_COPY(QSignalTransition) + Q_DECLARE_PRIVATE(QSignalTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qsignaltransition_p.h b/src/corelib/statemachine/qsignaltransition_p.h new file mode 100644 index 0000000..339de63 --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QSIGNALTRANSITION_P_H +#define QSIGNALTRANSITION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qabstracttransition_p.h" + +QT_BEGIN_NAMESPACE + +class QSignalTransition; +class QSignalTransitionPrivate : public QAbstractTransitionPrivate +{ + Q_DECLARE_PUBLIC(QSignalTransition) +public: + QSignalTransitionPrivate(); + + static QSignalTransitionPrivate *get(QSignalTransition *q); + + void unregister(); + void maybeRegister(); + + QObject *sender; + QByteArray signal; + int signalIndex; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp new file mode 100644 index 0000000..5463059 --- /dev/null +++ b/src/corelib/statemachine/qstate.cpp @@ -0,0 +1,483 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qstate.h" +#include "qstate_p.h" +#include "qhistorystate.h" +#include "qhistorystate_p.h" +#include "qabstracttransition.h" +#include "qabstracttransition_p.h" +#include "qsignaltransition.h" +#include "qstatemachine.h" +#include "qstatemachine_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QState + + \brief The QState class provides a general-purpose state for QStateMachine. + + \since 4.6 + \ingroup statemachine + + QState objects can have child states, and can have transitions to other + states. QState is part of \l{The State Machine Framework}. + + The addTransition() function adds a transition. The removeTransition() + function removes a transition. + + The assignProperty() function is used for defining property assignments that + should be performed when a state is entered. + + Top-level states must be passed QStateMachine::rootState() as their parent + state, or added to a state machine using QStateMachine::addState(). + + \section1 States with Child States + + The childMode property determines how child states are treated. For + non-parallel state groups, the setInitialState() function must be called to + set the initial state. The child states are mutually exclusive states, and + the state machine needs to know which child state to enter when the parent + state is the target of a transition. + + The state emits the QState::finished() signal when a final child state + (QFinalState) is entered. + + The setErrorState() sets the state's error state. The error state is the + state that the state machine will transition to if an error is detected when + attempting to enter the state (e.g. because no initial state has been set). + +*/ + +/*! + \property QState::initialState + + \brief the initial state of this state (one of its child states) +*/ + +/*! + \property QState::errorState + + \brief the error state of this state +*/ + +/*! + \property QState::childMode + + \brief the child mode of this state + + The default value of this property is QState::ExclusiveStates. +*/ + +/*! + \enum QState::ChildMode + + This enum specifies how a state's child states are treated. + + \value ExclusiveStates The child states are mutually exclusive and an + initial state must be set by calling QState::setInitialState(). + + \value ParallelStates The child states are parallel. When the parent state + is entered, all its child states are entered in parallel. +*/ + +QStatePrivate::QStatePrivate() + : errorState(0), initialState(0), childMode(QState::ExclusiveStates) +{ +} + +QStatePrivate::~QStatePrivate() +{ +} + +void QStatePrivate::emitFinished() +{ + Q_Q(QState); + emit q->finished(); +} + +void QStatePrivate::emitPolished() +{ + Q_Q(QState); + emit q->polished(); +} + +/*! + Constructs a new state with the given \a parent state. +*/ +QState::QState(QState *parent) + : QAbstractState(*new QStatePrivate, parent) +{ +} + +/*! + Constructs a new state with the given \a childMode and the given \a parent + state. +*/ +QState::QState(ChildMode childMode, QState *parent) + : QAbstractState(*new QStatePrivate, parent) +{ + Q_D(QState); + d->childMode = childMode; +} + +/*! + \internal +*/ +QState::QState(QStatePrivate &dd, QState *parent) + : QAbstractState(dd, parent) +{ +} + +/*! + Destroys this state. +*/ +QState::~QState() +{ +} + +QList<QAbstractState*> QStatePrivate::childStates() const +{ + QList<QAbstractState*> result; + QList<QObject*>::const_iterator it; + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QAbstractState *s = qobject_cast<QAbstractState*>(*it); + if (!s || qobject_cast<QHistoryState*>(s)) + continue; + result.append(s); + } + return result; +} + +QList<QHistoryState*> QStatePrivate::historyStates() const +{ + QList<QHistoryState*> result; + QList<QObject*>::const_iterator it; + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QHistoryState *h = qobject_cast<QHistoryState*>(*it); + if (h) + result.append(h); + } + return result; +} + +QList<QAbstractTransition*> QStatePrivate::transitions() const +{ + QList<QAbstractTransition*> result; + QList<QObject*>::const_iterator it; + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QAbstractTransition *t = qobject_cast<QAbstractTransition*>(*it); + if (t) + result.append(t); + } + return result; +} + +/*! + Instructs this state to set the property with the given \a name of the given + \a object to the given \a value when the state is entered. + + \sa polished() +*/ +void QState::assignProperty(QObject *object, const char *name, + const QVariant &value) +{ + Q_D(QState); + if (!object) { + qWarning("QState::assignProperty: cannot assign property '%s' of null object", name); + return; + } + for (int i = 0; i < d->propertyAssignments.size(); ++i) { + QPropertyAssignment &assn = d->propertyAssignments[i]; + if ((assn.object == object) && (assn.propertyName == name)) { + assn.value = value; + return; + } + } + d->propertyAssignments.append(QPropertyAssignment(object, name, value)); +} + +/*! + Returns this state group's error state. + + \sa QStateMachine::errorState(), QStateMachine::setErrorState() +*/ +QAbstractState *QState::errorState() const +{ + Q_D(const QState); + return d->errorState; +} + +/*! + Sets this state's error state to be the given \a state. If the error state + is not set, or if it is set to 0, the state will inherit its parent's error + state recursively. + + \sa QStateMachine::setErrorState(), QStateMachine::errorState() +*/ +void QState::setErrorState(QAbstractState *state) +{ + Q_D(QState); + if (state != 0 && state->machine() != machine()) { + qWarning("QState::setErrorState: error state cannot belong " + "to a different state machine"); + return; + } + if (state != 0 && state->machine() != 0 && state->machine()->rootState() == state) { + qWarning("QStateMachine::setErrorState: root state cannot be error state"); + return; + } + + d->errorState = state; +} + +/*! + Adds the given \a transition. The transition has this state as the source. + This state takes ownership of the transition. If the transition is successfully + added, the function will return the \a transition pointer. Otherwise it will return null. +*/ +QAbstractTransition *QState::addTransition(QAbstractTransition *transition) +{ + Q_D(QState); + if (!transition) { + qWarning("QState::addTransition: cannot add null transition"); + return 0; + } + + // machine() will always be non-null for root state + if (machine() != 0 && machine()->rootState() == this) { + qWarning("QState::addTransition: cannot add transition from root state"); + return 0; + } + + const QList<QPointer<QAbstractState> > &targets = QAbstractTransitionPrivate::get(transition)->targetStates; + for (int i = 0; i < targets.size(); ++i) { + QAbstractState *t = targets.at(i); + if (!t) { + qWarning("QState::addTransition: cannot add transition to null state"); + return 0; + } + if ((QAbstractStatePrivate::get(t)->machine() != d->machine()) + && QAbstractStatePrivate::get(t)->machine() && d->machine()) { + qWarning("QState::addTransition: cannot add transition " + "to a state in a different state machine"); + return 0; + } + } + transition->setParent(this); + if (machine() != 0 && machine()->configuration().contains(this)) + QStateMachinePrivate::get(machine())->registerTransitions(this); + return transition; +} + +/*! + Adds a transition associated with the given \a signal of the given \a sender + object, and returns the new QSignalTransition object. The transition has + this state as the source, and the given \a target as the target state. +*/ +QSignalTransition *QState::addTransition(QObject *sender, const char *signal, + QAbstractState *target) +{ + if (!sender) { + qWarning("QState::addTransition: sender cannot be null"); + return 0; + } + if (!signal) { + qWarning("QState::addTransition: signal cannot be null"); + return 0; + } + if (!target) { + qWarning("QState::addTransition: cannot add transition to null state"); + return 0; + } + if (*signal && sender->metaObject()->indexOfSignal(signal+1) == -1) { + qWarning("QState::addTransition: no such signal %s::%s", + sender->metaObject()->className(), signal+1); + return 0; + } + QSignalTransition *trans = new QSignalTransition(sender, signal, QList<QAbstractState*>() << target); + addTransition(trans); + return trans; +} + +namespace { + +// ### Make public? +class UnconditionalTransition : public QAbstractTransition +{ +public: + UnconditionalTransition(QAbstractState *target) + : QAbstractTransition(QList<QAbstractState*>() << target) {} +protected: + void onTransition(QEvent *) {} + bool eventTest(QEvent *) { return true; } +}; + +} // namespace + +/*! + Adds an unconditional transition from this state to the given \a target + state, and returns then new transition object. +*/ +QAbstractTransition *QState::addTransition(QAbstractState *target) +{ + if (!target) { + qWarning("QState::addTransition: cannot add transition to null state"); + return 0; + } + UnconditionalTransition *trans = new UnconditionalTransition(target); + return addTransition(trans); +} + +/*! + Removes the given \a transition from this state. The state releases + ownership of the transition. + + \sa addTransition() +*/ +void QState::removeTransition(QAbstractTransition *transition) +{ + Q_D(QState); + if (!transition) { + qWarning("QState::removeTransition: cannot remove null transition"); + return; + } + if (transition->sourceState() != this) { + qWarning("QState::removeTransition: transition %p's source state (%p)" + " is different from this state (%p)", + transition, transition->sourceState(), this); + return; + } + QStateMachinePrivate *mach = QStateMachinePrivate::get(d->machine()); + if (mach) + mach->unregisterTransition(transition); + transition->setParent(0); +} + +/*! + \reimp +*/ +void QState::onEntry(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QState::onExit(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + Returns this state's initial state, or 0 if the state has no initial state. +*/ +QAbstractState *QState::initialState() const +{ + Q_D(const QState); + return d->initialState; +} + +/*! + Sets this state's initial state to be the given \a state. + \a state has to be a child of this state. +*/ +void QState::setInitialState(QAbstractState *state) +{ + Q_D(QState); + if (d->childMode == QState::ParallelStates) { + qWarning("QState::setInitialState: ignoring attempt to set initial state " + "of parallel state group %p", this); + return; + } + if (state && (state->parentState() != this)) { + qWarning("QState::setInitialState: state %p is not a child of this state (%p)", + state, this); + return; + } + d->initialState = state; +} + +/*! + Returns the child mode of this state. +*/ +QState::ChildMode QState::childMode() const +{ + Q_D(const QState); + return d->childMode; +} + +/*! + Sets the child \a mode of this state. +*/ +void QState::setChildMode(ChildMode mode) +{ + Q_D(QState); + d->childMode = mode; +} + +/*! + \reimp +*/ +bool QState::event(QEvent *e) +{ + return QAbstractState::event(e); +} + +/*! + \fn QState::finished() + + This signal is emitted when a final child state of this state is entered. + + \sa QFinalState +*/ + +/*! + \fn QState::polished() + + This signal is emitted when all properties have been assigned their final value. + + \sa QState::assignProperty(), QAbstractTransition::addAnimation() +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qstate.h b/src/corelib/statemachine/qstate.h new file mode 100644 index 0000000..6729c69 --- /dev/null +++ b/src/corelib/statemachine/qstate.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QSTATE_H +#define QSTATE_H + +#include <QtCore/qabstractstate.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QAbstractTransition; +class QSignalTransition; + +class QStatePrivate; +class Q_CORE_EXPORT QState : public QAbstractState +{ + Q_OBJECT + Q_PROPERTY(QAbstractState* initialState READ initialState WRITE setInitialState) + Q_PROPERTY(QAbstractState* errorState READ errorState WRITE setErrorState) + Q_PROPERTY(ChildMode childMode READ childMode WRITE setChildMode) + Q_ENUMS(ChildMode) +public: + enum ChildMode { + ExclusiveStates, + ParallelStates + }; + + QState(QState *parent = 0); + QState(ChildMode childMode, QState *parent = 0); + ~QState(); + + QAbstractState *errorState() const; + void setErrorState(QAbstractState *state); + + QAbstractTransition *addTransition(QAbstractTransition *transition); + QSignalTransition *addTransition(QObject *sender, const char *signal, QAbstractState *target); + QAbstractTransition *addTransition(QAbstractState *target); + void removeTransition(QAbstractTransition *transition); + + QAbstractState *initialState() const; + void setInitialState(QAbstractState *state); + + ChildMode childMode() const; + void setChildMode(ChildMode mode); + + void assignProperty(QObject *object, const char *name, + const QVariant &value); + +Q_SIGNALS: + void finished(); + void polished(); + +protected: + void onEntry(QEvent *event); + void onExit(QEvent *event); + + bool event(QEvent *e); + +protected: + QState(QStatePrivate &dd, QState *parent); + +private: + Q_DISABLE_COPY(QState) + Q_DECLARE_PRIVATE(QState) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qstate_p.h b/src/corelib/statemachine/qstate_p.h new file mode 100644 index 0000000..491cb87 --- /dev/null +++ b/src/corelib/statemachine/qstate_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QSTATE_P_H +#define QSTATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qabstractstate_p.h" + +#include <QtCore/qlist.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_NAMESPACE + +struct QPropertyAssignment +{ + QPropertyAssignment() + : object(0) {} + QPropertyAssignment(QObject *o, const QByteArray &n, + const QVariant &v, bool es = true) + : object(o), propertyName(n), value(v), explicitlySet(es) + {} + QObject *object; + QByteArray propertyName; + QVariant value; + bool explicitlySet; +}; + +class QAbstractTransition; +class QHistoryState; + +class QState; +class QStatePrivate : public QAbstractStatePrivate +{ + Q_DECLARE_PUBLIC(QState) +public: + QStatePrivate(); + ~QStatePrivate(); + + static QStatePrivate *get(QState *q) { return q ? q->d_func() : 0; } + static const QStatePrivate *get(const QState *q) { return q? q->d_func() : 0; } + + QList<QAbstractState*> childStates() const; + QList<QHistoryState*> historyStates() const; + QList<QAbstractTransition*> transitions() const; + + void emitFinished(); + void emitPolished(); + + QAbstractState *errorState; + QAbstractState *initialState; + QState::ChildMode childMode; + + QList<QPropertyAssignment> propertyAssignments; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp new file mode 100644 index 0000000..757584a --- /dev/null +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -0,0 +1,2221 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qstatemachine.h" +#include "qstate.h" +#include "qstate_p.h" +#include "qstatemachine_p.h" +#include "qabstracttransition.h" +#include "qabstracttransition_p.h" +#include "qsignaltransition.h" +#include "qsignaltransition_p.h" +#include "qsignalevent.h" +#include "qsignaleventgenerator_p.h" +#include "qabstractstate.h" +#include "qabstractstate_p.h" +#include "qfinalstate.h" +#include "qhistorystate.h" +#include "qhistorystate_p.h" +#include "private/qobject_p.h" +#include "private/qthread_p.h" + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +#include "qeventtransition.h" +#include "qeventtransition_p.h" +#include "qwrappedevent.h" +#endif + +#ifndef QT_NO_ANIMATION +#include "qpropertyanimation.h" +#include "qanimationgroup.h" +#include <private/qvariantanimation_p.h> +#endif + +#include <QtCore/qmetaobject.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QStateMachine + \reentrant + + \brief The QStateMachine class provides a hierarchical finite state machine. + + \since 4.6 + \ingroup statemachine + + QStateMachine is based on the concepts and notation of + \l{Statecharts: A visual formalism for complex + systems}{Statecharts}. QStateMachine is part of \l{The State + Machine Framework}. + + A state machine manages a set of states (classes that inherit from + QAbstractState) and transitions (descendants of + QAbstractTransition) between those states; these states and + transitions define a state graph. Once a state graph has been + built, the state machine can execute it. \l{QStateMachine}'s + execution algorithm is based on the \l{State Chart XML: State + Machine Notation for Control Abstraction}{State Chart XML (SCXML)} + algorithm. The framework's \l{The State Machine + Framework}{overview} gives several state graphs and the code to + build them. + + The rootState() is the parent of all top-level states in the + machine; it is used, for instance, when the state graph is + deleted. It is created by the machine. + + Use the addState() function to add a state to the state machine. + All top-level states are added to the root state. States are + removed with the removeState() function. Removing states while the + machine is running is discouraged. + + Before the machine can be started, the \l{initialState}{initial + state} must be set. The initial state is the state that the + machine enters when started. You can then start() the state + machine. The started() signal is emitted when the initial state is + entered. + + The machine is event driven and keeps its own event loop. Events + are posted to the machine through postEvent(). Note that this + means that it executes asynchronously, and that it will not + progress without a running event loop. You will normally not have + to post events to the machine directly as Qt's transitions, e.g., + QEventTransition and its subclasses, handle this. But for custom + transitions triggered by events, postEvent() is useful. + + The state machine processes events and takes transitions until a + top-level final state is entered; the state machine then emits the + finished() signal. You can also stop() the state machine + explicitly. The stopped() signal is emitted in this case. + + The following snippet shows a state machine that will finish when a button + is clicked: + + \code + QPushButton button; + + QStateMachine machine; + QState *s1 = new QState(); + s1->assignProperty(&button, "text", "Click me"); + + QFinalState *s2 = new QFinalState(); + s1->addTransition(&button, SIGNAL(clicked()), s2); + + machine.addState(s1); + machine.addState(s2); + machine.setInitialState(s1); + machine.start(); + \endcode + + This code example uses QState, which inherits QAbstractState. The + QState class provides a state that you can use to set properties + and invoke methods on \l{QObject}s when the state is entered or + exited. It also contains convenience functions for adding + transitions, e.g., \l{QSignalTransition}s as in this example. See + the QState class description for further details. + + If an error is encountered, the machine will enter the + \l{errorState}{error state}, which is a special state created by + the machine. The types of errors possible are described by the + \l{QStateMachine::}{Error} enum. After the error state is entered, + the type of the error can be retrieved with error(). The execution + of the state graph will not stop when the error state is entered. + So it is possible to handle the error, for instance, by connecting + to the \l{QAbstractState::}{entered()} signal of the error state. + It is also possible to set a custom error state with + setErrorState(). + + \omit This stuff will be moved elsewhere +This is + typically used in conjunction with \l{Signals and Slots}{signals}; the + signals determine the flow of the state graph, whereas the states' property + assignments and method invocations are the actions. + + The postEvent() function posts an event to the state machine. This is useful + when you are using custom events to trigger transitions. + \endomit + + \sa QAbstractState, QAbstractTransition, QState, {The State Machine Framework} +*/ + +/*! + \property QStateMachine::rootState + + \brief the root state of this state machine +*/ + +/*! + \property QStateMachine::initialState + + \brief the initial state of this state machine + + The initial state must be one of the rootState()'s child states. +*/ + +/*! + \property QStateMachine::errorState + + \brief the error state of this state machine +*/ + +/*! + \property QStateMachine::errorString + + \brief the error string of this state machine +*/ + +/*! + \property QStateMachine::globalRestorePolicy + + \brief the restore policy for states of this state machine. + + The default value of this property is + QStateMachine::DoNotRestoreProperties. +*/ + +#ifndef QT_NO_ANIMATION +/*! + \property QStateMachine::animationsEnabled + + \brief whether animations are enabled + + The default value of this property is true. + + \sa QAbstractTransition::addAnimation() +*/ +#endif + +// #define QSTATEMACHINE_DEBUG + +QStateMachinePrivate::QStateMachinePrivate() +{ + state = NotRunning; + processing = false; + processingScheduled = false; + stop = false; + error = QStateMachine::NoError; + globalRestorePolicy = QStateMachine::DoNotRestoreProperties; + rootState = 0; + initialErrorStateForRoot = 0; + signalEventGenerator = 0; +#ifndef QT_NO_ANIMATION + animationsEnabled = true; +#endif +} + +QStateMachinePrivate::~QStateMachinePrivate() +{ + qDeleteAll(internalEventQueue); + qDeleteAll(externalEventQueue); +} + +QStateMachinePrivate *QStateMachinePrivate::get(QStateMachine *q) +{ + if (q) + return q->d_func(); + return 0; +} + +static QEvent *cloneEvent(QEvent *e) +{ + switch (e->type()) { + case QEvent::None: + return new QEvent(*e); + case QEvent::Timer: + return new QTimerEvent(*static_cast<QTimerEvent*>(e)); + default: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + } + return 0; +} + +const QStateMachinePrivate::Handler qt_kernel_statemachine_handler = { + cloneEvent +}; + +const QStateMachinePrivate::Handler *QStateMachinePrivate::handler = &qt_kernel_statemachine_handler; + +Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler() +{ + return &qt_kernel_statemachine_handler; +} + +static int indexOfDescendant(QState *s, QAbstractState *desc) +{ + QList<QAbstractState*> childStates = QStatePrivate::get(s)->childStates(); + for (int i = 0; i < childStates.size(); ++i) { + QAbstractState *c = childStates.at(i); + if ((c == desc) || QStateMachinePrivate::isDescendantOf(desc, c)) { + return i; + } + } + return -1; +} + +bool QStateMachinePrivate::stateEntryLessThan(QAbstractState *s1, QAbstractState *s2) +{ + if (s1->parent() == s2->parent()) { + return s1->children().indexOf(s1) + < s2->children().indexOf(s2); + } else if (isDescendantOf(s1, s2)) { + return false; + } else if (isDescendantOf(s2, s1)) { + return true; + } else { + QState *lca = findLCA(QList<QAbstractState*>() << s1 << s2); + Q_ASSERT(lca != 0); + return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2)); + } +} + +bool QStateMachinePrivate::stateExitLessThan(QAbstractState *s1, QAbstractState *s2) +{ + if (s1->parent() == s2->parent()) { + return s1->children().indexOf(s1) + < s2->children().indexOf(s2); + } else if (isDescendantOf(s1, s2)) { + return true; + } else if (isDescendantOf(s2, s1)) { + return false; + } else { + QState *lca = findLCA(QList<QAbstractState*>() << s1 << s2); + Q_ASSERT(lca != 0); + return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2)); + } +} + +QState *QStateMachinePrivate::findLCA(const QList<QAbstractState*> &states) +{ + if (states.isEmpty()) + return 0; + QList<QState*> ancestors = properAncestors(states.at(0), 0); + for (int i = 0; i < ancestors.size(); ++i) { + QState *anc = ancestors.at(i); + bool ok = true; + for (int j = states.size() - 1; (j > 0) && ok; --j) { + const QAbstractState *s = states.at(j); + if (!isDescendantOf(s, anc)) + ok = false; + } + if (ok) + return anc; + } + return 0; +} + +bool QStateMachinePrivate::isPreempted(const QAbstractState *s, const QSet<QAbstractTransition*> &transitions) const +{ + QSet<QAbstractTransition*>::const_iterator it; + for (it = transitions.constBegin(); it != transitions.constEnd(); ++it) { + QAbstractTransition *t = *it; + QList<QAbstractState*> lst = t->targetStates(); + if (!lst.isEmpty()) { + lst.prepend(t->sourceState()); + QAbstractState *lca = findLCA(lst); + if (isDescendantOf(s, lca)) { +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ':' << transitions << "preempts selection of a transition from" + << s << "because" << s << "is a descendant of" << lca; +#endif + return true; + } + } + } + return false; +} + +QSet<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *event) const +{ + Q_Q(const QStateMachine); + QSet<QAbstractTransition*> enabledTransitions; + QSet<QAbstractState*>::const_iterator it; + const_cast<QStateMachine*>(q)->beginSelectTransitions(event); + for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) { + QAbstractState *state = *it; + if (!isAtomic(state)) + continue; + if (isPreempted(state, enabledTransitions)) + continue; + QList<QState*> lst = properAncestors(state, 0); + if (QState *grp = qobject_cast<QState*>(state)) + lst.prepend(grp); + bool found = false; + for (int j = 0; (j < lst.size()) && !found; ++j) { + QState *s = lst.at(j); + QList<QAbstractTransition*> transitions = QStatePrivate::get(s)->transitions(); + for (int k = 0; k < transitions.size(); ++k) { + QAbstractTransition *t = transitions.at(k); + if (QAbstractTransitionPrivate::get(t)->callEventTest(event)) { +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": selecting transition" << t; +#endif + enabledTransitions.insert(t); + found = true; + break; + } + } + } + } + const_cast<QStateMachine*>(q)->endSelectTransitions(event); + return enabledTransitions; +} + +void QStateMachinePrivate::microstep(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions) +{ +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": begin microstep( enabledTransitions:" << enabledTransitions << ')'; + qDebug() << q_func() << ": configuration before exiting states:" << configuration; +#endif + QList<QAbstractState*> exitedStates = exitStates(event, enabledTransitions); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": configuration after exiting states:" << configuration; +#endif + executeTransitionContent(event, enabledTransitions); + QList<QAbstractState*> enteredStates = enterStates(event, enabledTransitions); + applyProperties(enabledTransitions, exitedStates, enteredStates); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": configuration after entering states:" << configuration; + qDebug() << q_func() << ": end microstep"; +#endif +} + +QList<QAbstractState*> QStateMachinePrivate::exitStates(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions) +{ +// qDebug() << "exitStates(" << enabledTransitions << ')'; + QSet<QAbstractState*> statesToExit; +// QSet<QAbstractState*> statesToSnapshot; + for (int i = 0; i < enabledTransitions.size(); ++i) { + QAbstractTransition *t = enabledTransitions.at(i); + QList<QAbstractState*> lst = t->targetStates(); + if (lst.isEmpty()) + continue; + lst.prepend(t->sourceState()); + QAbstractState *lca = findLCA(lst); + if (lca == 0) { + setError(QStateMachine::NoCommonAncestorForTransitionError, t->sourceState()); + lst = pendingErrorStates.toList(); + lst.prepend(t->sourceState()); + + lca = findLCA(lst); + Q_ASSERT(lca != 0); + } + + { + QSet<QAbstractState*>::const_iterator it; + for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) { + QAbstractState *s = *it; + if (isDescendantOf(s, lca)) + statesToExit.insert(s); + } + } + } + QList<QAbstractState*> statesToExit_sorted = statesToExit.toList(); + qSort(statesToExit_sorted.begin(), statesToExit_sorted.end(), stateExitLessThan); + for (int i = 0; i < statesToExit_sorted.size(); ++i) { + QAbstractState *s = statesToExit_sorted.at(i); + if (QState *grp = qobject_cast<QState*>(s)) { + QList<QHistoryState*> hlst = QStatePrivate::get(grp)->historyStates(); + for (int j = 0; j < hlst.size(); ++j) { + QHistoryState *h = hlst.at(j); + QHistoryStatePrivate::get(h)->configuration.clear(); + QSet<QAbstractState*>::const_iterator it; + for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) { + QAbstractState *s0 = *it; + if (QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) { + if (isAtomic(s0) && isDescendantOf(s0, s)) + QHistoryStatePrivate::get(h)->configuration.append(s0); + } else if (s0->parentState() == s) { + QHistoryStatePrivate::get(h)->configuration.append(s0); + } + } +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": recorded" << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) ? "deep" : "shallow") + << "history for" << s << "in" << h << ':' << QHistoryStatePrivate::get(h)->configuration; +#endif + } + } + } + for (int i = 0; i < statesToExit_sorted.size(); ++i) { + QAbstractState *s = statesToExit_sorted.at(i); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": exiting" << s; +#endif + QAbstractStatePrivate::get(s)->callOnExit(event); + configuration.remove(s); + QAbstractStatePrivate::get(s)->emitExited(); + } + return statesToExit_sorted; +} + +void QStateMachinePrivate::executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions) +{ + for (int i = 0; i < enabledTransitions.size(); ++i) { + QAbstractTransition *t = enabledTransitions.at(i); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": triggering" << t; +#endif + QAbstractTransitionPrivate::get(t)->callOnTransition(event); + } +} + +QList<QAbstractState*> QStateMachinePrivate::enterStates(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions) +{ +#ifdef QSTATEMACHINE_DEBUG + Q_Q(QStateMachine); +#endif +// qDebug() << "enterStates(" << enabledTransitions << ')'; + QSet<QAbstractState*> statesToEnter; + QSet<QAbstractState*> statesForDefaultEntry; + + if (pendingErrorStates.isEmpty()) { + for (int i = 0; i < enabledTransitions.size(); ++i) { + QAbstractTransition *t = enabledTransitions.at(i); + QList<QAbstractState*> lst = t->targetStates(); + if (lst.isEmpty()) + continue; + lst.prepend(t->sourceState()); + QState *lca = findLCA(lst); + for (int j = 1; j < lst.size(); ++j) { + QAbstractState *s = lst.at(j); + addStatesToEnter(s, lca, statesToEnter, statesForDefaultEntry); + if (isParallel(lca)) { + QList<QAbstractState*> lcac = QStatePrivate::get(lca)->childStates(); + foreach (QAbstractState* child,lcac) { + if (!statesToEnter.contains(child)) + addStatesToEnter(child,lca,statesToEnter,statesForDefaultEntry); + } + } + } + } + } + + // Did an error occur while selecting transitions? Then we enter the error state. + if (!pendingErrorStates.isEmpty()) { + statesToEnter.clear(); + statesToEnter = pendingErrorStates; + statesForDefaultEntry = pendingErrorStatesForDefaultEntry; + pendingErrorStates.clear(); + pendingErrorStatesForDefaultEntry.clear(); + } + + QList<QAbstractState*> statesToEnter_sorted = statesToEnter.toList(); + qSort(statesToEnter_sorted.begin(), statesToEnter_sorted.end(), stateEntryLessThan); + + for (int i = 0; i < statesToEnter_sorted.size(); ++i) { + QAbstractState *s = statesToEnter_sorted.at(i); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": entering" << s; +#endif + configuration.insert(s); + registerTransitions(s); + QAbstractStatePrivate::get(s)->callOnEntry(event); + QAbstractStatePrivate::get(s)->emitEntered(); + if (statesForDefaultEntry.contains(s)) { + // ### executeContent(s.initial.transition.children()) + } + if (isFinal(s)) { + QState *parent = s->parentState(); + if (parent) { + QState *grandparent = parent->parentState(); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": emitting finished signal for" << parent; +#endif + QStatePrivate::get(parent)->emitFinished(); + if (grandparent && isParallel(grandparent)) { + bool allChildStatesFinal = true; + QList<QAbstractState*> childStates = QStatePrivate::get(grandparent)->childStates(); + for (int j = 0; j < childStates.size(); ++j) { + QAbstractState *cs = childStates.at(j); + if (!isInFinalState(cs)) { + allChildStatesFinal = false; + break; + } + } + if (allChildStatesFinal) { +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": emitting finished signal for" << grandparent; +#endif + QStatePrivate::get(grandparent)->emitFinished(); + } + } + } + } + } + { + QSet<QAbstractState*>::const_iterator it; + for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) { + if (isFinal(*it) && (*it)->parentState() == rootState) { + processing = false; + stopProcessingReason = Finished; + break; + } + } + } +// qDebug() << "configuration:" << configuration.toList(); + return statesToEnter_sorted; +} + +void QStateMachinePrivate::addStatesToEnter(QAbstractState *s, QState *root, + QSet<QAbstractState*> &statesToEnter, + QSet<QAbstractState*> &statesForDefaultEntry) +{ + if (QHistoryState *h = qobject_cast<QHistoryState*>(s)) { + QList<QAbstractState*> hconf = QHistoryStatePrivate::get(h)->configuration; + if (!hconf.isEmpty()) { + for (int k = 0; k < hconf.size(); ++k) { + QAbstractState *s0 = hconf.at(k); + addStatesToEnter(s0, root, statesToEnter, statesForDefaultEntry); + } + #ifdef QSTATEMACHINE_DEBUG + qDebug() <<q_func() << ": restoring" + << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) ? "deep" : "shallow") + << "history from" << s << ':' << hconf; + #endif + } else { + QList<QAbstractState*> hlst; + if (QHistoryStatePrivate::get(h)->defaultState) + hlst.append(QHistoryStatePrivate::get(h)->defaultState); + + if (hlst.isEmpty()) { + setError(QStateMachine::NoDefaultStateInHistoryStateError, h); + } else { + for (int k = 0; k < hlst.size(); ++k) { + QAbstractState *s0 = hlst.at(k); + addStatesToEnter(s0, root, statesToEnter, statesForDefaultEntry); + } + #ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": initial history targets for" << s << ':' << hlst; + #endif + } + } + } else { + statesToEnter.insert(s); + if (isParallel(s)) { + QState *grp = qobject_cast<QState*>(s); + QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates(); + for (int i = 0; i < lst.size(); ++i) { + QAbstractState *child = lst.at(i); + addStatesToEnter(child, grp, statesToEnter, statesForDefaultEntry); + } + } else if (isCompound(s)) { + statesForDefaultEntry.insert(s); + QState *grp = qobject_cast<QState*>(s); + QAbstractState *initial = grp->initialState(); + if (initial != 0) { + addStatesToEnter(initial, grp, statesToEnter, statesForDefaultEntry); + } else { + setError(QStateMachine::NoInitialStateError, grp); + return; + } + } + QList<QState*> ancs = properAncestors(s, root); + for (int i = 0; i < ancs.size(); ++i) { + QState *anc = ancs.at(i); + if (!anc->parentState()) + continue; + statesToEnter.insert(anc); + if (isParallel(anc)) { + QList<QAbstractState*> lst = QStatePrivate::get(anc)->childStates(); + for (int j = 0; j < lst.size(); ++j) { + QAbstractState *child = lst.at(j); + bool hasDescendantInList = false; + QSet<QAbstractState*>::const_iterator it; + for (it = statesToEnter.constBegin(); it != statesToEnter.constEnd(); ++it) { + if (isDescendantOf(*it, child)) { + hasDescendantInList = true; + break; + } + } + if (!hasDescendantInList) + addStatesToEnter(child, anc, statesToEnter, statesForDefaultEntry); + } + } + } + } +} + +void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &transitionList, + const QList<QAbstractState*> &exitedStates, + const QList<QAbstractState*> &enteredStates) +{ +#ifdef QT_NO_ANIMATION + Q_UNUSED(transitionList); + Q_UNUSED(exitedStates); +#else + Q_Q(QStateMachine); +#endif + // Process the property assignments of the entered states. + QHash<QAbstractState*, QList<QPropertyAssignment> > propertyAssignmentsForState; + QHash<RestorableId, QVariant> pendingRestorables = registeredRestorables; + for (int i = 0; i < enteredStates.size(); ++i) { + QState *s = qobject_cast<QState*>(enteredStates.at(i)); + if (!s) + continue; + + QList<QPropertyAssignment> assignments = QStatePrivate::get(s)->propertyAssignments; + for (int j = 0; j < assignments.size(); ++j) { + const QPropertyAssignment &assn = assignments.at(j); + if (globalRestorePolicy == QStateMachine::RestoreProperties) { + registerRestorable(assn.object, assn.propertyName); + } + pendingRestorables.remove(RestorableId(assn.object, assn.propertyName)); + propertyAssignmentsForState[s].append(assn); + } + + // Remove pending restorables for all parent states to avoid restoring properties + // before the state that assigned them is exited. If state does not explicitly + // assign a property which is assigned by the parent, it inherits the parent's assignment. + QState *parentState = s; + while ((parentState = parentState->parentState()) != 0) { + assignments = QStatePrivate::get(parentState)->propertyAssignments; + for (int j=0; j<assignments.size(); ++j) { + const QPropertyAssignment &assn = assignments.at(j); + int c = pendingRestorables.remove(RestorableId(assn.object, assn.propertyName)); + if (c > 0) + propertyAssignmentsForState[s].append(assn); + } + } + } + if (!pendingRestorables.isEmpty()) { + QAbstractState *s; + if (!enteredStates.isEmpty()) + s = enteredStates.last(); // ### handle if parallel + else + s = 0; + propertyAssignmentsForState[s] << restorablesToPropertyList(pendingRestorables); + } + +#ifndef QT_NO_ANIMATION + // Gracefully terminate playing animations for states that are exited. + for (int i = 0; i < exitedStates.size(); ++i) { + QAbstractState *s = exitedStates.at(i); + QList<QAbstractAnimation*> animations = animationsForState.take(s); + for (int j = 0; j < animations.size(); ++j) { + QAbstractAnimation *anim = animations.at(j); + QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished())); + stateForAnimation.remove(anim); + + // Stop the (top-level) animation. + // ### Stopping nested animation has weird behavior. + QAbstractAnimation *topLevelAnim = anim; + while (QAnimationGroup *group = topLevelAnim->group()) + topLevelAnim = group; + topLevelAnim->stop(); + + if (resetAnimationEndValues.contains(anim)) { + qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize + resetAnimationEndValues.remove(anim); + } + QPropertyAssignment assn = propertyForAnimation.take(anim); + Q_ASSERT(assn.object != 0); + // If there is no property assignment that sets this property, + // set the property to its target value. + bool found = false; + QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it; + for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) { + const QList<QPropertyAssignment> &assignments = it.value(); + for (int k = 0; k < assignments.size(); ++k) { + if ((assignments.at(k).object == assn.object) + && (assignments.at(k).propertyName == assn.propertyName)) { + found = true; + break; + } + } + } + if (!found) { + assn.object->setProperty(assn.propertyName, assn.value); + } + } + } + + // Find the animations to use for the state change. + QList<QAbstractAnimation*> selectedAnimations; + if (animationsEnabled) { + for (int i = 0; i < transitionList.size(); ++i) { + QAbstractTransition *transition = transitionList.at(i); + + selectedAnimations << transition->animations(); + selectedAnimations << defaultAnimationsForSource.values(transition->sourceState()); + + QList<QAbstractState *> targetStates = transition->targetStates(); + for (int j=0; j<targetStates.size(); ++j) + selectedAnimations << defaultAnimationsForTarget.values(targetStates.at(j)); + } + selectedAnimations << defaultAnimations; + } + + // Initialize animations from property assignments. + for (int i = 0; i < selectedAnimations.size(); ++i) { + QAbstractAnimation *anim = selectedAnimations.at(i); + QHash<QAbstractState*, QList<QPropertyAssignment> >::iterator it; + for (it = propertyAssignmentsForState.begin(); it != propertyAssignmentsForState.end(); ) { + QList<QPropertyAssignment>::iterator it2; + QAbstractState *s = it.key(); + QList<QPropertyAssignment> &assignments = it.value(); + for (it2 = assignments.begin(); it2 != assignments.end(); ) { + QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret; + ret = initializeAnimation(anim, *it2); + QList<QAbstractAnimation*> handlers = ret.first; + if (!handlers.isEmpty()) { + for (int j = 0; j < handlers.size(); ++j) { + QAbstractAnimation *a = handlers.at(j); + propertyForAnimation.insert(a, *it2); + stateForAnimation.insert(a, s); + animationsForState[s].append(a); + // ### connect to just the top-level animation? + QObject::disconnect(a, SIGNAL(finished()), q, SLOT(_q_animationFinished())); + QObject::connect(a, SIGNAL(finished()), q, SLOT(_q_animationFinished())); + } + it2 = assignments.erase(it2); + } else { + ++it2; + } + for (int j = 0; j < ret.second.size(); ++j) + resetAnimationEndValues.insert(ret.second.at(j)); + } + if (assignments.isEmpty()) + it = propertyAssignmentsForState.erase(it); + else + ++it; + } + // We require that at least one animation is valid. + // ### generalize + QList<QVariantAnimation*> variantAnims = qFindChildren<QVariantAnimation*>(anim); + if (QVariantAnimation *va = qobject_cast<QVariantAnimation*>(anim)) + variantAnims.append(va); + + bool hasValidEndValue = false; + for (int j = 0; j < variantAnims.size(); ++j) { + if (variantAnims.at(j)->endValue().isValid()) { + hasValidEndValue = true; + break; + } + } + + if (hasValidEndValue) { + anim->start(); + } + } +#endif // !QT_NO_ANIMATION + + // Immediately set the properties that are not animated. + { + QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it; + for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) { + const QList<QPropertyAssignment> &assignments = it.value(); + for (int i = 0; i < assignments.size(); ++i) { + const QPropertyAssignment &assn = assignments.at(i); + assn.object->setProperty(assn.propertyName, assn.value); + } + } + } + + // Emit polished signal for entered states that have no animated properties. + for (int i = 0; i < enteredStates.size(); ++i) { + QState *s = qobject_cast<QState*>(enteredStates.at(i)); + if (s +#ifndef QT_NO_ANIMATION + && !animationsForState.contains(s) +#endif + ) + QStatePrivate::get(s)->emitPolished(); + } +} + +bool QStateMachinePrivate::isFinal(const QAbstractState *s) +{ + return qobject_cast<const QFinalState*>(s) != 0; +} + +bool QStateMachinePrivate::isParallel(const QAbstractState *s) +{ + const QState *ss = qobject_cast<const QState*>(s); + return ss && (QStatePrivate::get(ss)->childMode == QState::ParallelStates); +} + +bool QStateMachinePrivate::isCompound(const QAbstractState *s) +{ + const QState *group = qobject_cast<const QState*>(s); + if (!group) + return false; + return (!isParallel(group) && !QStatePrivate::get(group)->childStates().isEmpty()) + || (qobject_cast<QStateMachine*>(group->parent()) != 0); +} + +bool QStateMachinePrivate::isAtomic(const QAbstractState *s) +{ + const QState *ss = qobject_cast<const QState*>(s); + return (ss && QStatePrivate::get(ss)->childStates().isEmpty()) + || isFinal(s); +} + + +bool QStateMachinePrivate::isDescendantOf(const QAbstractState *state, const QAbstractState *other) +{ + Q_ASSERT(state != 0); + for (QAbstractState *s = state->parentState(); s != 0; s = s->parentState()) { + if (s == other) + return true; + } + return false; +} + +QList<QState*> QStateMachinePrivate::properAncestors(const QAbstractState *state, const QState *upperBound) +{ + Q_ASSERT(state != 0); + QList<QState*> result; + for (QState *s = state->parentState(); s && s != upperBound; s = s->parentState()) { + result.append(s); + } + return result; +} + +bool QStateMachinePrivate::isInFinalState(QAbstractState* s) const +{ + if (isCompound(s)) { + QState *grp = qobject_cast<QState*>(s); + QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates(); + for (int i = 0; i < lst.size(); ++i) { + QAbstractState *cs = lst.at(i); + if (isFinal(cs) && configuration.contains(cs)) + return true; + } + return false; + } else if (isParallel(s)) { + QState *grp = qobject_cast<QState*>(s); + QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates(); + for (int i = 0; i < lst.size(); ++i) { + QAbstractState *cs = lst.at(i); + if (!isInFinalState(cs)) + return false; + } + return true; + } + else + return false; +} + +void QStateMachinePrivate::registerRestorable(QObject *object, const QByteArray &propertyName) +{ + RestorableId id(object, propertyName); + if (!registeredRestorables.contains(id)) + registeredRestorables.insert(id, object->property(propertyName)); +} + +QList<QPropertyAssignment> QStateMachinePrivate::restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const +{ + QList<QPropertyAssignment> result; + QHash<RestorableId, QVariant>::const_iterator it; + for (it = restorables.constBegin(); it != restorables.constEnd(); ++it) { +// qDebug() << "restorable:" << it.key().first << it.key().second << it.value(); + result.append(QPropertyAssignment(it.key().first, it.key().second, it.value(), /*explicitlySet=*/false)); + } + return result; +} + +/*! + \internal + Returns true if the variable with the given \a id has been registered for restoration. +*/ +bool QStateMachinePrivate::hasRestorable(QObject *object, const QByteArray &propertyName) const +{ + return registeredRestorables.contains(RestorableId(object, propertyName)); +} + +QVariant QStateMachinePrivate::restorableValue(QObject *object, const QByteArray &propertyName) const +{ + return registeredRestorables.value(RestorableId(object, propertyName), QVariant()); +} + + +/*! + \internal + Unregisters the variable identified by \a id +*/ +void QStateMachinePrivate::unregisterRestorable(QObject *object, const QByteArray &propertyName) +{ +// qDebug() << "unregisterRestorable(" << object << propertyName << ')'; + RestorableId id(object, propertyName); + registeredRestorables.remove(id); +} + +QAbstractState *QStateMachinePrivate::findErrorState(QAbstractState *context) +{ + // If the user sets the root state's error state to 0, we return the initial error state + if (context == 0) + return initialErrorStateForRoot; + + // Find error state recursively in parent hierarchy if not set explicitly for context state + QAbstractState *errorState = 0; + + QState *s = qobject_cast<QState*>(context); + if (s) + errorState = s->errorState(); + + if (!errorState) + errorState = findErrorState(context->parentState()); + + return errorState; +} + +void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractState *currentContext) +{ + error = errorCode; + + switch (errorCode) { + case QStateMachine::NoInitialStateError: + Q_ASSERT(currentContext != 0); + + errorString = QStateMachine::tr("Missing initial state in compound state '%1'") + .arg(currentContext->objectName()); + + break; + case QStateMachine::NoDefaultStateInHistoryStateError: + Q_ASSERT(currentContext != 0); + + errorString = QStateMachine::tr("Missing default state in history state '%1'") + .arg(currentContext->objectName()); + break; + + case QStateMachine::NoCommonAncestorForTransitionError: + Q_ASSERT(currentContext != 0); + + errorString = QStateMachine::tr("No common ancestor for targets and source of transition from state '%1'") + .arg(currentContext->objectName()); + break; + default: + errorString = QStateMachine::tr("Unknown error"); + }; + + pendingErrorStates.clear(); + pendingErrorStatesForDefaultEntry.clear(); + + QAbstractState *currentErrorState = findErrorState(currentContext); + + // Avoid infinite loop if the error state itself has an error + if (currentContext == currentErrorState) { + Q_ASSERT(currentContext != initialErrorStateForRoot); // RootErrorState is broken + currentErrorState = initialErrorStateForRoot; + } + + Q_ASSERT(currentErrorState != 0); + Q_ASSERT(currentErrorState != rootState); + + QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext); + addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry); +} + +#ifndef QT_NO_ANIMATION + +QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > +QStateMachinePrivate::initializeAnimation(QAbstractAnimation *abstractAnimation, + const QPropertyAssignment &prop) +{ + QList<QAbstractAnimation*> handledAnimations; + QList<QAbstractAnimation*> localResetEndValues; + QAnimationGroup *group = qobject_cast<QAnimationGroup*>(abstractAnimation); + if (group) { + for (int i = 0; i < group->animationCount(); ++i) { + QAbstractAnimation *animationChild = group->animationAt(i); + QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret; + ret = initializeAnimation(animationChild, prop); + handledAnimations << ret.first; + localResetEndValues << ret.second; + } + } else { + QPropertyAnimation *animation = qobject_cast<QPropertyAnimation *>(abstractAnimation); + if (animation != 0 + && prop.object == animation->targetObject() + && prop.propertyName == animation->propertyName()) { + + // Only change end value if it is undefined + if (!animation->endValue().isValid()) { + animation->setEndValue(prop.value); + localResetEndValues.append(animation); + } + handledAnimations.append(animation); + } + } + return qMakePair(handledAnimations, localResetEndValues); +} + +void QStateMachinePrivate::_q_animationFinished() +{ + Q_Q(QStateMachine); + QAbstractAnimation *anim = qobject_cast<QAbstractAnimation*>(q->sender()); + Q_ASSERT(anim != 0); + QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished())); + if (resetAnimationEndValues.contains(anim)) { + qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize + resetAnimationEndValues.remove(anim); + } + + // Set the final property value. + QPropertyAssignment assn = propertyForAnimation.take(anim); + Q_ASSERT(assn.object != 0); + assn.object->setProperty(assn.propertyName, assn.value); + if (!assn.explicitlySet) + unregisterRestorable(assn.object, assn.propertyName); + + QAbstractState *state = stateForAnimation.take(anim); + Q_ASSERT(state != 0); + QHash<QAbstractState*, QList<QAbstractAnimation*> >::iterator it; + it = animationsForState.find(state); + Q_ASSERT(it != animationsForState.end()); + QList<QAbstractAnimation*> &animations = it.value(); + animations.removeOne(anim); + if (animations.isEmpty()) { + animationsForState.erase(it); + QStatePrivate::get(qobject_cast<QState*>(state))->emitPolished(); + } +} + +#endif // !QT_NO_ANIMATION + +namespace { + +class StartState : public QState +{ +public: + StartState(QState *parent) + : QState(parent) {} +protected: + void onEntry(QEvent *) {} + void onExit(QEvent *) {} +}; + +class InitialTransition : public QAbstractTransition +{ +public: + InitialTransition(QAbstractState *target) + : QAbstractTransition(QList<QAbstractState*>() << target) {} +protected: + virtual bool eventTest(QEvent *) { return true; } + virtual void onTransition(QEvent *) {} +}; + +} // namespace + +void QStateMachinePrivate::_q_start() +{ + Q_Q(QStateMachine); + Q_ASSERT(state == Starting); + if (!rootState) { + state = NotRunning; + return; + } + QAbstractState *initial = rootState->initialState(); + if (initial == 0) + setError(QStateMachine::NoInitialStateError, rootState); + + configuration.clear(); + qDeleteAll(internalEventQueue); + internalEventQueue.clear(); + qDeleteAll(externalEventQueue); + externalEventQueue.clear(); + +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": starting"; +#endif + state = Running; + processingScheduled = true; // we call _q_process() below + emit q->started(); + + StartState *start = new StartState(rootState); + QAbstractTransition *initialTransition = new InitialTransition(initial); + start->addTransition(initialTransition); + QList<QAbstractTransition*> transitions; + transitions.append(initialTransition); + QEvent nullEvent(QEvent::None); + executeTransitionContent(&nullEvent, transitions); + enterStates(&nullEvent, transitions); + applyProperties(transitions, QList<QAbstractState*>() << start, + QList<QAbstractState*>() << initial); + delete start; + +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": initial configuration:" << configuration; +#endif + _q_process(); +} + +void QStateMachinePrivate::_q_process() +{ + Q_Q(QStateMachine); + Q_ASSERT(state == Running); + Q_ASSERT(!processing); + processing = true; + processingScheduled = false; +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": starting the event processing loop"; +#endif + while (processing) { + if (stop) { + stop = false; + processing = false; + stopProcessingReason = Stopped; + break; + } + QSet<QAbstractTransition*> enabledTransitions; + QEvent *e = new QEvent(QEvent::None); + enabledTransitions = selectTransitions(e); + if (enabledTransitions.isEmpty()) { + delete e; + e = 0; + } + if (enabledTransitions.isEmpty() && !internalEventQueue.isEmpty()) { + e = internalEventQueue.takeFirst(); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": dequeued internal event" << e << "of type" << e->type(); +#endif + enabledTransitions = selectTransitions(e); + if (enabledTransitions.isEmpty()) { + delete e; + e = 0; + } + } + if (enabledTransitions.isEmpty()) { + if (externalEventQueue.isEmpty()) { + if (internalEventQueue.isEmpty()) { + processing = false; + stopProcessingReason = EventQueueEmpty; + } + } else { + e = externalEventQueue.takeFirst(); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": dequeued external event" << e << "of type" << e->type(); +#endif + enabledTransitions = selectTransitions(e); + if (enabledTransitions.isEmpty()) { + delete e; + e = 0; + } + } + } + if (!enabledTransitions.isEmpty()) { + q->beginMicrostep(e); + microstep(e, enabledTransitions.toList()); + q->endMicrostep(e); + } +#ifdef QSTATEMACHINE_DEBUG + else { + qDebug() << q << ": no transitions enabled"; + } +#endif + delete e; + } +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": finished the event processing loop"; +#endif + switch (stopProcessingReason) { + case EventQueueEmpty: + break; + case Finished: + state = NotRunning; + unregisterAllTransitions(); + emit q->finished(); + break; + case Stopped: + state = NotRunning; + unregisterAllTransitions(); + emit q->stopped(); + break; + } +} + +void QStateMachinePrivate::scheduleProcess() +{ + if ((state != Running) || processing || processingScheduled) + return; + processingScheduled = true; + QMetaObject::invokeMethod(q_func(), "_q_process", Qt::QueuedConnection); +} + +void QStateMachinePrivate::registerTransitions(QAbstractState *state) +{ + QState *group = qobject_cast<QState*>(state); + if (!group) + return; + QList<QAbstractTransition*> transitions = QStatePrivate::get(group)->transitions(); + for (int i = 0; i < transitions.size(); ++i) { + QAbstractTransition *t = transitions.at(i); + if (QSignalTransition *st = qobject_cast<QSignalTransition*>(t)) { + registerSignalTransition(st); + } +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + else if (QEventTransition *oet = qobject_cast<QEventTransition*>(t)) { + registerEventTransition(oet); + } +#endif + } +} + +void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition) +{ + if (QSignalTransition *st = qobject_cast<QSignalTransition*>(transition)) { + unregisterSignalTransition(st); + } +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + else if (QEventTransition *oet = qobject_cast<QEventTransition*>(transition)) { + unregisterEventTransition(oet); + } +#endif +} + +static int senderSignalIndex(const QObject *sender) +{ + QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject*>(sender)); + QMutexLocker(&d->threadData->mutex); + if (!d->currentSender) + return -1; + + // Return -1 if d->currentSender isn't in d->senders + bool found = false; + for (int i = 0; !found && i < d->senders.count(); ++i) + found = (d->senders.at(i)->sender == d->currentSender->sender); + if (!found) + return -1; + return d->currentSender->signal; +} + +void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transition) +{ + Q_Q(QStateMachine); + if (QSignalTransitionPrivate::get(transition)->signalIndex != -1) + return; // already registered + QObject *sender = QSignalTransitionPrivate::get(transition)->sender; + if (!sender) + return; + QByteArray signal = QSignalTransitionPrivate::get(transition)->signal; + if (signal.startsWith('0'+QSIGNAL_CODE)) + signal.remove(0, 1); + int signalIndex = sender->metaObject()->indexOfSignal(signal); + if (signalIndex == -1) { + qWarning("QSignalTransition: no such signal: %s::%s", + sender->metaObject()->className(), signal.constData()); + return; + } + QVector<int> &connectedSignalIndexes = connections[sender]; + if (connectedSignalIndexes.size() <= signalIndex) + connectedSignalIndexes.resize(signalIndex+1); + if (connectedSignalIndexes.at(signalIndex) == 0) { + if (!signalEventGenerator) + signalEventGenerator = new QSignalEventGenerator(q); + bool ok = QMetaObject::connect(sender, signalIndex, signalEventGenerator, + signalEventGenerator->metaObject()->methodOffset()); + if (!ok) { +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": FAILED to add signal transition from" << transition->sourceState() + << ": ( sender =" << sender << ", signal =" << (signal.mid(1)) + << ", targets =" << transition->targetStates() << ')'; +#endif + return; + } + } + ++connectedSignalIndexes[signalIndex]; + QSignalTransitionPrivate::get(transition)->signalIndex = signalIndex; +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": added signal transition from" << transition->sourceState() + << ": ( sender =" << sender << ", signal =" << (signal.mid(1)) + << ", targets =" << transition->targetStates() << ')'; +#endif +} + +void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transition) +{ + int signalIndex = QSignalTransitionPrivate::get(transition)->signalIndex; + if (signalIndex == -1) + return; // not registered + QSignalTransitionPrivate::get(transition)->signalIndex = -1; + const QObject *sender = QSignalTransitionPrivate::get(transition)->sender; + QVector<int> &connectedSignalIndexes = connections[sender]; + Q_ASSERT(connectedSignalIndexes.size() > signalIndex); + Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0); + if (--connectedSignalIndexes[signalIndex] == 0) { + Q_ASSERT(signalEventGenerator != 0); + QMetaObject::disconnect(sender, signalIndex, signalEventGenerator, + signalEventGenerator->metaObject()->methodOffset()); + int sum = 0; + for (int i = 0; i < connectedSignalIndexes.size(); ++i) + sum += connectedSignalIndexes.at(i); + if (sum == 0) + connections.remove(sender); + } +} + +void QStateMachinePrivate::unregisterAllTransitions() +{ + { + QList<QSignalTransition*> transitions = qFindChildren<QSignalTransition*>(rootState); + for (int i = 0; i < transitions.size(); ++i) + unregisterSignalTransition(transitions.at(i)); + } + { + QList<QEventTransition*> transitions = qFindChildren<QEventTransition*>(rootState); + for (int i = 0; i < transitions.size(); ++i) + unregisterEventTransition(transitions.at(i)); + } +} + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +void QStateMachinePrivate::registerEventTransition(QEventTransition *transition) +{ + Q_Q(QStateMachine); + if (QEventTransitionPrivate::get(transition)->registered) + return; + if (transition->eventType() >= QEvent::User) { + qWarning("QObject event transitions are not supported for custom types"); + return; + } + QObject *object = QEventTransitionPrivate::get(transition)->object; + if (!object) + return; + QObjectPrivate *od = QObjectPrivate::get(object); + if (!od->eventFilters.contains(q)) + object->installEventFilter(q); + ++qobjectEvents[object][transition->eventType()]; + QEventTransitionPrivate::get(transition)->registered = true; +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": added event transition from" << transition->sourceState() + << ": ( object =" << object << ", event =" << transition->eventType() + << ", targets =" << transition->targetStates() << ')'; +#endif +} + +void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transition) +{ + Q_Q(QStateMachine); + if (!QEventTransitionPrivate::get(transition)->registered) + return; + QObject *object = QEventTransitionPrivate::get(transition)->object; + QHash<QEvent::Type, int> &events = qobjectEvents[object]; + Q_ASSERT(events.value(transition->eventType()) > 0); + if (--events[transition->eventType()] == 0) { + events.remove(transition->eventType()); + int sum = 0; + QHash<QEvent::Type, int>::const_iterator it; + for (it = events.constBegin(); it != events.constEnd(); ++it) + sum += it.value(); + if (sum == 0) { + qobjectEvents.remove(object); + object->removeEventFilter(q); + } + } + QEventTransitionPrivate::get(transition)->registered = false; +} +#endif + +void QStateMachinePrivate::handleTransitionSignal(const QObject *sender, int signalIndex, + void **argv) +{ + Q_ASSERT(connections[sender].at(signalIndex) != 0); + const QMetaObject *meta = sender->metaObject(); + QMetaMethod method = meta->method(signalIndex); + QList<QByteArray> parameterTypes = method.parameterTypes(); + int argc = parameterTypes.count(); + QList<QVariant> vargs; + for (int i = 0; i < argc; ++i) { + int type = QMetaType::type(parameterTypes.at(i)); + vargs.append(QVariant(type, argv[i+1])); + } + +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q_func() << ": sending signal event ( sender =" << sender + << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ')'; +#endif + internalEventQueue.append(new QSignalEvent(sender, signalIndex, vargs)); + scheduleProcess(); +} + +/*! + Constructs a new state machine with the given \a parent. +*/ +QStateMachine::QStateMachine(QObject *parent) + : QObject(*new QStateMachinePrivate, parent) +{ +} + +/*! + \internal +*/ +QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Destroys this state machine. +*/ +QStateMachine::~QStateMachine() +{ +} + +namespace { + +class RootErrorState : public QAbstractState +{ +public: + RootErrorState(QState *parent) + : QAbstractState(parent) + { + setObjectName(QString::fromLatin1("DefaultErrorState")); + } + + void onEntry(QEvent *) + { + QAbstractStatePrivate *d = QAbstractStatePrivate::get(this); + QStateMachine *machine = d->machine(); + + qWarning("Unrecoverable error detected in running state machine: %s", + qPrintable(machine->errorString())); + } + + void onExit(QEvent *) {} +}; + +class RootState : public QState +{ +public: + RootState(QState *parent) + : QState(parent) + { + } + + void onEntry(QEvent *) {} + void onExit(QEvent *) {} +}; + +} // namespace + +/*! + Returns this state machine's root state. +*/ +QState *QStateMachine::rootState() const +{ + Q_D(const QStateMachine); + if (!d->rootState) { + const_cast<QStateMachinePrivate*>(d)->rootState = new RootState(0); + const_cast<QStateMachinePrivate*>(d)->initialErrorStateForRoot = new RootErrorState(d->rootState); + d->rootState->setParent(const_cast<QStateMachine*>(this)); + d->rootState->setErrorState(d->initialErrorStateForRoot); + } + return d->rootState; +} + +/*! + Returns the error state of the state machine's root state. + + \sa QState::errorState() +*/ +QAbstractState *QStateMachine::errorState() const +{ + return rootState()->errorState(); +} + +/*! + Sets the error state of this state machine's root state to be \a state. When a running state + machine encounters an error which puts it in an undefined state, it will enter an error state + based on the context of the error that occurred. It will enter this state regardless of what + is currently in the event queue. + + If the erroneous state has an error state set, this will be entered by the machine. If no error + state has been set, the state machine will search the parent hierarchy recursively for an + error state. The error state of the root state can thus be seen as a global error state that + applies for the states for which a more specific error state has not been set. + + Before entering the error state, the state machine will set the error code returned by error() and + error message returned by errorString(). + + The default error state will print a warning to the console containing the information returned by + errorString(). By setting a new error state on either the state machine itself, or on specific + states, you can fine tune error handling in the state machine. + + If the root state's error state is set to 0, or if the error state selected by the machine itself + contains an error, the default error state will be used. + + \sa QState::setErrorState(), rootState() +*/ +void QStateMachine::setErrorState(QAbstractState *state) +{ + rootState()->setErrorState(state); +} + +/*! \enum QStateMachine::Error + + This enum type defines errors that can occur in the state machine at run time. When the state + machine encounters an unrecoverable error at run time, it will set the error code returned + by error(), the error message returned by errorString(), and enter an error state based on + the context of the error. + + \value NoError No error has occurred. + \value NoInitialStateError The machine has entered a QState with children which does not have an + initial state set. The context of this error is the state which is missing an initial + state. + \value NoDefaultStateInHistoryStateError The machine has entered a QHistoryState which does not have + a default state set. The context of this error is the QHistoryState which is missing a + default state. + \value NoCommonAncestorForTransitionError The machine has selected a transition whose source + and targets are not part of the same tree of states, and thus are not part of the same + state machine. Commonly, this could mean that one of the states has not been given + any parent or added to any machine. The context of this error is the source state of + the transition. + + \sa setErrorState() +*/ + +/*! + \enum QStateMachine::RestorePolicy + + This enum specifies the restore policy type. The restore policy + takes effect when the machine enters a state which sets one or more + properties. If the restore policy is set to RestoreProperties, + the state machine will save the original value of the property before the + new value is set. + + Later, when the machine either enters a state which does not set + a value for the given property, the property will automatically be restored + to its initial value. + + Only one initial value will be saved for any given property. If a value for a property has + already been saved by the state machine, it will not be overwritten until the property has been + successfully restored. + + \value DoNotRestoreProperties The state machine should not save the initial values of properties + and restore them later. + \value RestoreProperties The state machine should save the initial values of properties + and restore them later. + + \sa QStateMachine::globalRestorePolicy QState::assignProperty() +*/ + + +/*! + Returns the error code of the last error that occurred in the state machine. +*/ +QStateMachine::Error QStateMachine::error() const +{ + Q_D(const QStateMachine); + return d->error; +} + +/*! + Returns the error string of the last error that occurred in the state machine. +*/ +QString QStateMachine::errorString() const +{ + Q_D(const QStateMachine); + return d->errorString; +} + +/*! + Clears the error string and error code of the state machine. +*/ +void QStateMachine::clearError() +{ + Q_D(QStateMachine); + d->errorString.clear(); + d->error = NoError; +} + +/*! + Returns the restore policy of the state machine. + + \sa setGlobalRestorePolicy() +*/ +QStateMachine::RestorePolicy QStateMachine::globalRestorePolicy() const +{ + Q_D(const QStateMachine); + return d->globalRestorePolicy; +} + +/*! + Sets the restore policy of the state machine to \a restorePolicy. The default + restore policy is QAbstractState::DoNotRestoreProperties. + + \sa globalRestorePolicy() +*/ +void QStateMachine::setGlobalRestorePolicy(QStateMachine::RestorePolicy restorePolicy) +{ + Q_D(QStateMachine); + d->globalRestorePolicy = restorePolicy; +} + +/*! + Returns this state machine's initial state, or 0 if no initial state has + been set. +*/ +QAbstractState *QStateMachine::initialState() const +{ + Q_D(const QStateMachine); + if (!d->rootState) + return 0; + return d->rootState->initialState(); +} + +/*! + Sets this state machine's initial \a state. +*/ +void QStateMachine::setInitialState(QAbstractState *state) +{ + Q_D(QStateMachine); + if (!d->rootState) { + if (!state) + return; + rootState()->setInitialState(state); + } + d->rootState->setInitialState(state); +} + +/*! + Adds the given \a state to this state machine. The state becomes a top-level + state (i.e. a child of the rootState()). + + If the state is already in a different machine, it will first be removed + from its old machine, and then added to this machine. + + \sa removeState(), rootState(), setInitialState() +*/ +void QStateMachine::addState(QAbstractState *state) +{ + if (!state) { + qWarning("QStateMachine::addState: cannot add null state"); + return; + } + if (QAbstractStatePrivate::get(state)->machine() == this) { + qWarning("QStateMachine::addState: state has already been added to this machine"); + return; + } + state->setParent(rootState()); +} + +/*! + Removes the given \a state from this state machine. The state machine + releases ownership of the state. + + \sa addState() +*/ +void QStateMachine::removeState(QAbstractState *state) +{ + if (!state) { + qWarning("QStateMachine::removeState: cannot remove null state"); + return; + } + if (QAbstractStatePrivate::get(state)->machine() != this) { + qWarning("QStateMachine::removeState: state %p's machine (%p)" + " is different from this machine (%p)", + state, QAbstractStatePrivate::get(state)->machine(), this); + return; + } + state->setParent(0); +} + +/*! + Returns whether this state machine is running. + + start(), stop() +*/ +bool QStateMachine::isRunning() const +{ + Q_D(const QStateMachine); + return (d->state == QStateMachinePrivate::Running); +} + +/*! + Starts this state machine. The machine will reset its configuration and + transition to the initial state. When a final top-level state (QFinalState) + is entered, the machine will emit the finished() signal. + + \sa started(), finished(), stop(), initialState() +*/ +void QStateMachine::start() +{ + Q_D(QStateMachine); + + if (rootState()->initialState() == 0) { + qWarning("QStateMachine::start: No initial state set for machine. Refusing to start."); + return; + } + + switch (d->state) { + case QStateMachinePrivate::NotRunning: + d->state = QStateMachinePrivate::Starting; + QMetaObject::invokeMethod(this, "_q_start", Qt::QueuedConnection); + break; + case QStateMachinePrivate::Starting: + break; + case QStateMachinePrivate::Running: + qWarning("QStateMachine::start(): already running"); + break; + } +} + +/*! + Stops this state machine. The state machine will stop processing events and + then emit the stopped() signal. + + \sa stopped(), start() +*/ +void QStateMachine::stop() +{ + Q_D(QStateMachine); + switch (d->state) { + case QStateMachinePrivate::NotRunning: + break; + case QStateMachinePrivate::Starting: + // the machine will exit as soon as it enters the event processing loop + d->stop = true; + break; + case QStateMachinePrivate::Running: + d->stop = true; + d->scheduleProcess(); + break; + } +} + +/*! + Posts the given \a event for processing by this state machine, with a delay + of \a delay milliseconds. + + This function returns immediately. The event is added to the state machine's + event queue. Events are processed in the order posted. The state machine + takes ownership of the event and deletes it once it has been processed. + + You can only post events when the state machine is running. +*/ +void QStateMachine::postEvent(QEvent *event, int delay) +{ + Q_D(QStateMachine); + if (d->state != QStateMachinePrivate::Running) { + qWarning("QStateMachine::postEvent: cannot post event when the state machine is not running"); + return; + } +#ifdef QSTATEMACHINE_DEBUG + qDebug() << this << ": posting external event" << event << "with delay" << delay; +#endif + if (delay) { + int tid = startTimer(delay); + d->delayedEvents[tid] = event; + } else { + d->externalEventQueue.append(event); + d->scheduleProcess(); + } +} + +/*! + \internal + + Posts the given internal \a event for processing by this state machine. +*/ +void QStateMachine::postInternalEvent(QEvent *event) +{ + Q_D(QStateMachine); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << this << ": posting internal event" << event; +#endif + d->internalEventQueue.append(event); + d->scheduleProcess(); +} + +/*! + \internal + + Returns the maximal consistent set of states (including parallel and final + states) that this state machine is currently in. If a state \c s is in the + configuration, it is always the case that the parent of \c s is also in + c. Note, however, that the rootState() is not an explicit member of the + configuration. +*/ +QSet<QAbstractState*> QStateMachine::configuration() const +{ + Q_D(const QStateMachine); + return d->configuration; +} + +/*! + \fn QStateMachine::started() + + This signal is emitted when the state machine has entered its initial state + (QStateMachine::initialState). + + \sa QStateMachine::finished(), QStateMachine::start() +*/ + +/*! + \fn QStateMachine::finished() + + This signal is emitted when the state machine has reached a top-level final + state (QFinalState). + + \sa QStateMachine::started() +*/ + +/*! + \fn QStateMachine::stopped() + + This signal is emitted when the state machine has stopped. + + \sa QStateMachine::stop(), QStateMachine::finished() +*/ + +/*! + \reimp +*/ +bool QStateMachine::event(QEvent *e) +{ + Q_D(QStateMachine); + if (e->type() == QEvent::Timer) { + QTimerEvent *te = static_cast<QTimerEvent*>(e); + int tid = te->timerId(); + if (d->delayedEvents.contains(tid)) { + killTimer(tid); + QEvent *ee = d->delayedEvents.take(tid); + d->externalEventQueue.append(ee); + d->scheduleProcess(); + return true; + } + } else if (e->type() == QEvent::ChildAdded) { + QChildEvent *ce = static_cast<QChildEvent*>(e); + if (QAbstractState *state = qobject_cast<QAbstractState*>(ce->child())) { + if (state != rootState()) { + state->setParent(rootState()); + return true; + } + } + } + return QObject::event(e); +} + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +/*! + \reimp +*/ +bool QStateMachine::eventFilter(QObject *watched, QEvent *event) +{ + Q_D(QStateMachine); + Q_ASSERT(d->qobjectEvents.contains(watched)); + if (d->qobjectEvents[watched].contains(event->type())) + postEvent(new QWrappedEvent(watched, d->handler->cloneEvent(event))); + return false; +} +#endif + +/*! + \internal + + This function is called when the state machine is about to select + transitions based on the given \a event. + + The default implementation does nothing. +*/ +void QStateMachine::beginSelectTransitions(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \internal + + This function is called when the state machine has finished selecting + transitions based on the given \a event. + + The default implementation does nothing. +*/ +void QStateMachine::endSelectTransitions(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \internal + + This function is called when the state machine is about to do a microstep. + + The default implementation does nothing. +*/ +void QStateMachine::beginMicrostep(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \internal + + This function is called when the state machine has finished doing a + microstep. + + The default implementation does nothing. +*/ +void QStateMachine::endMicrostep(QEvent *event) +{ + Q_UNUSED(event); +} + +#ifndef QT_NO_ANIMATION + +/*! + Returns whether animations are enabled for this state machine. +*/ +bool QStateMachine::animationsEnabled() const +{ + Q_D(const QStateMachine); + return d->animationsEnabled; +} + +/*! + Sets whether animations are \a enabled for this state machine. +*/ +void QStateMachine::setAnimationsEnabled(bool enabled) +{ + Q_D(QStateMachine); + d->animationsEnabled = enabled; +} + +/*! + Adds a default \a animation to be considered for any transition. +*/ +void QStateMachine::addDefaultAnimation(QAbstractAnimation *animation) +{ + Q_D(QStateMachine); + d->defaultAnimations.append(animation); +} + +/*! + Returns the list of default animations that will be considered for any transition. +*/ +QList<QAbstractAnimation*> QStateMachine::defaultAnimations() const +{ + Q_D(const QStateMachine); + return d->defaultAnimations; +} + +/*! + Removes \a animation from the list of default animations. +*/ +void QStateMachine::removeDefaultAnimation(QAbstractAnimation *animation) +{ + Q_D(QStateMachine); + d->defaultAnimations.removeAll(animation); +} + +#endif // QT_NO_ANIMATION + + +static const uint qt_meta_data_QSignalEventGenerator[] = { + + // content: + 2, // revision + 0, // classname + 0, 0, // classinfo + 1, 12, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + + // slots: signature, parameters, type, tag, flags + 23, 22, 22, 22, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_QSignalEventGenerator[] = { + "QSignalEventGenerator\0\0execute()\0" +}; + +const QMetaObject QSignalEventGenerator::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator, + qt_meta_data_QSignalEventGenerator, 0 } +}; + +const QMetaObject *QSignalEventGenerator::metaObject() const +{ + return &staticMetaObject; +} + +void *QSignalEventGenerator::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator)) + return static_cast<void*>(const_cast< QSignalEventGenerator*>(this)); + return QObject::qt_metacast(_clname); +} + +int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: { +// ### in Qt 4.6 we can use QObject::senderSignalIndex() + int signalIndex = senderSignalIndex(this); + Q_ASSERT(signalIndex != -1); + QStateMachine *machine = qobject_cast<QStateMachine*>(parent()); + QStateMachinePrivate::get(machine)->handleTransitionSignal(sender(), signalIndex, _a); + break; + } + default: ; + } + _id -= 1; + } + return _id; +} + +QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent) + : QObject(parent) +{ +} + +/*! + \class QSignalEvent + + \brief The QSignalEvent class represents a Qt signal event. + + \since 4.6 + \ingroup statemachine + + A signal event is generated by a QStateMachine in response to a Qt + signal. The QSignalTransition class provides a transition associated with a + signal event. QSignalEvent is part of \l{The State Machine Framework}. + + The sender() function returns the object that generated the signal. The + signalIndex() function returns the index of the signal. The arguments() + function returns the arguments of the signal. + + \sa QSignalTransition +*/ + +/*! + \internal + + Constructs a new QSignalEvent object with the given \a sender, \a + signalIndex and \a arguments. +*/ +QSignalEvent::QSignalEvent(const QObject *sender, int signalIndex, + const QList<QVariant> &arguments) + : QEvent(QEvent::Signal), m_sender(sender), + m_signalIndex(signalIndex), m_arguments(arguments) +{ +} + +/*! + Destroys this QSignalEvent. +*/ +QSignalEvent::~QSignalEvent() +{ +} + +/*! + \fn QSignalEvent::sender() const + + Returns the object that emitted the signal. + + \sa QObject::sender() +*/ + +/*! + \fn QSignalEvent::signalIndex() const + + Returns the index of the signal. + + \sa QMetaObject::indexOfSignal(), QMetaObject::method() +*/ + +/*! + \fn QSignalEvent::arguments() const + + Returns the arguments of the signal. +*/ + + +/*! + \class QWrappedEvent + + \brief The QWrappedEvent class holds a clone of an event associated with a QObject. + + \since 4.6 + \ingroup statemachine + + A wrapped event is generated by a QStateMachine in response to a Qt + event. The QEventTransition class provides a transition associated with a + such an event. QWrappedEvent is part of \l{The State Machine Framework}. + + The object() function returns the object that generated the event. The + event() function returns a clone of the original event. + + \sa QEventTransition +*/ + +/*! + \internal + + Constructs a new QWrappedEvent object with the given \a object + and \a event. +*/ +QWrappedEvent::QWrappedEvent(QObject *object, QEvent *event) + : QEvent(QEvent::Wrapped), m_object(object), m_event(event) +{ +} + +/*! + Destroys this QWrappedEvent. +*/ +QWrappedEvent::~QWrappedEvent() +{ +} + +/*! + \fn QWrappedEvent::object() const + + Returns the object that the event is associated with. +*/ + +/*! + \fn QWrappedEvent::event() const + + Returns a clone of the original event. +*/ + +QT_END_NAMESPACE + +#include "moc_qstatemachine.cpp" diff --git a/src/corelib/statemachine/qstatemachine.h b/src/corelib/statemachine/qstatemachine.h new file mode 100644 index 0000000..2a98a9a --- /dev/null +++ b/src/corelib/statemachine/qstatemachine.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QSTATEMACHINE_H +#define QSTATEMACHINE_H + +#include <QtCore/qabstractstate.h> + +#include <QtCore/qlist.h> +#include <QtCore/qobject.h> +#include <QtCore/qset.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEvent; +class QAbstractState; +class QState; + +class QStateMachinePrivate; +class QAbstractAnimation; +class QAbstractState; +class Q_CORE_EXPORT QStateMachine : public QObject +{ + Q_OBJECT + Q_PROPERTY(QState* rootState READ rootState) + Q_PROPERTY(QAbstractState* initialState READ initialState WRITE setInitialState) + Q_PROPERTY(QAbstractState* errorState READ errorState WRITE setErrorState) + Q_PROPERTY(QString errorString READ errorString) + Q_PROPERTY(RestorePolicy globalRestorePolicy READ globalRestorePolicy WRITE setGlobalRestorePolicy) + Q_ENUMS(RestorePolicy) +#ifndef QT_NO_ANIMATION + Q_PROPERTY(bool animationsEnabled READ animationsEnabled WRITE setAnimationsEnabled) +#endif +public: + enum RestorePolicy { + DoNotRestoreProperties, + RestoreProperties + }; + + enum Error { + NoError, + NoInitialStateError, + NoDefaultStateInHistoryStateError, + NoCommonAncestorForTransitionError + }; + + QStateMachine(QObject *parent = 0); + ~QStateMachine(); + + void addState(QAbstractState *state); + void removeState(QAbstractState *state); + + QState *rootState() const; + + QAbstractState *initialState() const; + void setInitialState(QAbstractState *state); + + QAbstractState *errorState() const; + void setErrorState(QAbstractState *state); + + Error error() const; + QString errorString() const; + void clearError(); + + bool isRunning() const; + +#ifndef QT_NO_ANIMATION + bool animationsEnabled() const; + void setAnimationsEnabled(bool enabled); + + void addDefaultAnimation(QAbstractAnimation *animation); + QList<QAbstractAnimation *> defaultAnimations() const; + void removeDefaultAnimation(QAbstractAnimation *animation); +#endif // QT_NO_ANIMATION + + QStateMachine::RestorePolicy globalRestorePolicy() const; + void setGlobalRestorePolicy(QStateMachine::RestorePolicy restorePolicy); + + void postEvent(QEvent *event, int delay = 0); + + QSet<QAbstractState*> configuration() const; + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + bool eventFilter(QObject *watched, QEvent *event); +#endif + +public Q_SLOTS: + void start(); + void stop(); + +Q_SIGNALS: + void started(); + void stopped(); + void finished(); + +protected: + void postInternalEvent(QEvent *event); + + virtual void beginSelectTransitions(QEvent *event); + virtual void endSelectTransitions(QEvent *event); + + virtual void beginMicrostep(QEvent *event); + virtual void endMicrostep(QEvent *event); + + bool event(QEvent *e); + +protected: + QStateMachine(QStateMachinePrivate &dd, QObject *parent); + +private: + Q_DISABLE_COPY(QStateMachine) + Q_DECLARE_PRIVATE(QStateMachine) + Q_PRIVATE_SLOT(d_func(), void _q_start()) + Q_PRIVATE_SLOT(d_func(), void _q_process()) +#ifndef QT_NO_ANIMATION + Q_PRIVATE_SLOT(d_func(), void _q_animationFinished()) +#endif +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h new file mode 100644 index 0000000..54953b4 --- /dev/null +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QSTATEMACHINE_P_H +#define QSTATEMACHINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qobject_p.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qhash.h> +#include <QtCore/qlist.h> +#include <QtCore/qpair.h> +#include <QtCore/qset.h> +#include <QtCore/qvector.h> + +#include "qstate.h" +#include "qstate_p.h" + +QT_BEGIN_NAMESPACE + +class QEvent; +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +class QEventTransition; +#endif +class QSignalEventGenerator; +class QSignalTransition; +class QAbstractState; +class QAbstractTransition; +class QState; + +#ifndef QT_NO_ANIMATION +class QAbstractAnimation; +#endif + +class QStateMachine; +class QStateMachinePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QStateMachine) +public: + enum State { + NotRunning, + Starting, + Running + }; + enum StopProcessingReason { + EventQueueEmpty, + Finished, + Stopped + }; + + QStateMachinePrivate(); + ~QStateMachinePrivate(); + + static QStateMachinePrivate *get(QStateMachine *q); + + static QState *findLCA(const QList<QAbstractState*> &states); + + static bool stateEntryLessThan(QAbstractState *s1, QAbstractState *s2); + static bool stateExitLessThan(QAbstractState *s1, QAbstractState *s2); + + QAbstractState *findErrorState(QAbstractState *context); + void setError(QStateMachine::Error error, QAbstractState *currentContext); + + // private slots + void _q_start(); + void _q_process(); +#ifndef QT_NO_ANIMATION + void _q_animationFinished(); +#endif + + void microstep(QEvent *event, const QList<QAbstractTransition*> &transitionList); + bool isPreempted(const QAbstractState *s, const QSet<QAbstractTransition*> &transitions) const; + QSet<QAbstractTransition*> selectTransitions(QEvent *event) const; + QList<QAbstractState*> exitStates(QEvent *event, const QList<QAbstractTransition*> &transitionList); + void executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &transitionList); + QList<QAbstractState*> enterStates(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions); + void addStatesToEnter(QAbstractState *s, QState *root, + QSet<QAbstractState*> &statesToEnter, + QSet<QAbstractState*> &statesForDefaultEntry); + + void applyProperties(const QList<QAbstractTransition*> &transitionList, + const QList<QAbstractState*> &exitedStates, + const QList<QAbstractState*> &enteredStates); + + bool isInFinalState(QAbstractState *s) const; + static bool isFinal(const QAbstractState *s); + static bool isParallel(const QAbstractState *s); + static bool isCompound(const QAbstractState *s); + static bool isAtomic(const QAbstractState *s); + static bool isDescendantOf(const QAbstractState *s, const QAbstractState *other); + static QList<QState*> properAncestors(const QAbstractState *s, const QState *upperBound); + + void registerTransitions(QAbstractState *state); + void registerSignalTransition(QSignalTransition *transition); + void unregisterSignalTransition(QSignalTransition *transition); +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + void registerEventTransition(QEventTransition *transition); + void unregisterEventTransition(QEventTransition *transition); +#endif + void unregisterTransition(QAbstractTransition *transition); + void unregisterAllTransitions(); + void handleTransitionSignal(const QObject *sender, int signalIndex, + void **args); + void scheduleProcess(); + + typedef QPair<QObject *, QByteArray> RestorableId; + QHash<RestorableId, QVariant> registeredRestorables; + void registerRestorable(QObject *object, const QByteArray &propertyName); + void unregisterRestorable(QObject *object, const QByteArray &propertyName); + bool hasRestorable(QObject *object, const QByteArray &propertyName) const; + QVariant restorableValue(QObject *object, const QByteArray &propertyName) const; + QList<QPropertyAssignment> restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const; + + State state; + bool processing; + bool processingScheduled; + bool stop; + StopProcessingReason stopProcessingReason; + QState *rootState; + QSet<QAbstractState*> configuration; + QList<QEvent*> internalEventQueue; + QList<QEvent*> externalEventQueue; + + QStateMachine::Error error; + QStateMachine::RestorePolicy globalRestorePolicy; + + QString errorString; + QSet<QAbstractState *> pendingErrorStates; + QSet<QAbstractState *> pendingErrorStatesForDefaultEntry; + QAbstractState *initialErrorStateForRoot; + +#ifndef QT_NO_ANIMATION + bool animationsEnabled; + + QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > + initializeAnimation(QAbstractAnimation *abstractAnimation, + const QPropertyAssignment &prop); + + QHash<QAbstractState*, QList<QAbstractAnimation*> > animationsForState; + QHash<QAbstractAnimation*, QPropertyAssignment> propertyForAnimation; + QHash<QAbstractAnimation*, QAbstractState*> stateForAnimation; + QSet<QAbstractAnimation*> resetAnimationEndValues; + + QList<QAbstractAnimation *> defaultAnimations; + QMultiHash<QAbstractState *, QAbstractAnimation *> defaultAnimationsForSource; + QMultiHash<QAbstractState *, QAbstractAnimation *> defaultAnimationsForTarget; + +#endif // QT_NO_ANIMATION + + QSignalEventGenerator *signalEventGenerator; + + QHash<const QObject*, QVector<int> > connections; +#ifndef QT_NO_STATEMACHINE_EVENTFILTER + QHash<QObject*, QHash<QEvent::Type, int> > qobjectEvents; +#endif + QHash<int, QEvent*> delayedEvents; + + typedef QEvent* (*f_cloneEvent)(QEvent*); + struct Handler { + f_cloneEvent cloneEvent; + }; + + static Q_CORE_EXPORT const Handler *handler; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qwrappedevent.h b/src/corelib/statemachine/qwrappedevent.h new file mode 100644 index 0000000..b01c608 --- /dev/null +++ b/src/corelib/statemachine/qwrappedevent.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QWRAPPEDEVENT_H +#define QWRAPPEDEVENT_H + +#include <QtCore/qcoreevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QObject; + +class Q_CORE_EXPORT QWrappedEvent : public QEvent +{ +public: + QWrappedEvent(QObject *object, QEvent *event); + ~QWrappedEvent(); + + inline QObject *object() const { return m_object; } + inline QEvent *event() const { return m_event; } + +private: + QObject *m_object; + QEvent *m_event; + +private: + Q_DISABLE_COPY(QWrappedEvent) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/statemachine.pri b/src/corelib/statemachine/statemachine.pri new file mode 100644 index 0000000..5b19bc1 --- /dev/null +++ b/src/corelib/statemachine/statemachine.pri @@ -0,0 +1,30 @@ +HEADERS += $$PWD/qstatemachine.h \ + $$PWD/qstatemachine_p.h \ + $$PWD/qsignaleventgenerator_p.h \ + $$PWD/qabstractstate.h \ + $$PWD/qabstractstate_p.h \ + $$PWD/qstate.h \ + $$PWD/qstate_p.h \ + $$PWD/qfinalstate.h \ + $$PWD/qhistorystate.h \ + $$PWD/qhistorystate_p.h \ + $$PWD/qabstracttransition.h \ + $$PWD/qabstracttransition_p.h \ + $$PWD/qsignalevent.h \ + $$PWD/qsignaltransition.h \ + $$PWD/qsignaltransition_p.h + +SOURCES += $$PWD/qstatemachine.cpp \ + $$PWD/qabstractstate.cpp \ + $$PWD/qstate.cpp \ + $$PWD/qfinalstate.cpp \ + $$PWD/qhistorystate.cpp \ + $$PWD/qabstracttransition.cpp \ + $$PWD/qsignaltransition.cpp + +!contains(DEFINES, QT_NO_STATEMACHINE_EVENTFILTER) { +HEADERS += $$PWD/qwrappedevent.h \ + $$PWD/qeventtransition.h \ + $$PWD/qeventtransition_p.h +SOURCES += $$PWD/qeventtransition.cpp +} diff --git a/src/corelib/thread/qmutexpool.cpp b/src/corelib/thread/qmutexpool.cpp index 96a9940..0d7c890 100644 --- a/src/corelib/thread/qmutexpool.cpp +++ b/src/corelib/thread/qmutexpool.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE // qt_global_mutexpool is here for backwards compatability only, // use QMutexpool::instance() in new clode. Q_CORE_EXPORT QMutexPool *qt_global_mutexpool = 0; -Q_GLOBAL_STATIC_WITH_ARGS(QMutexPool, globalMutexPool, (true)) +Q_GLOBAL_STATIC_WITH_ARGS(QMutexPool, globalMutexPool, (QMutex::Recursive)) /*! \class QMutexPool @@ -88,17 +88,17 @@ Q_GLOBAL_STATIC_WITH_ARGS(QMutexPool, globalMutexPool, (true)) */ /*! - Constructs a QMutexPool, reserving space for \a size QMutexes. If - \a recursive is true, all QMutexes in the pool will be recursive - mutexes; otherwise they will all be non-recursive (the default). + Constructs a QMutexPool, reserving space for \a size QMutexes. All + mutexes in the pool are created with \a recursionMode. By default, + all mutexes are non-recursive. The QMutexes are created when needed, and deleted when the QMutexPool is destructed. */ -QMutexPool::QMutexPool(bool recursive, int size) - : mutexes(size), count(size), recurs(recursive) +QMutexPool::QMutexPool(QMutex::RecursionMode recursionMode, int size) + : mutexes(size), recursionMode(recursionMode) { - for (int index = 0; index < count; ++index) { + for (int index = 0; index < mutexes.count(); ++index) { mutexes[index] = 0; } } @@ -109,7 +109,7 @@ QMutexPool::QMutexPool(bool recursive, int size) */ QMutexPool::~QMutexPool() { - for (int index = 0; index < count; ++index) { + for (int index = 0; index < mutexes.count(); ++index) { delete mutexes[index]; mutexes[index] = 0; } @@ -130,11 +130,11 @@ QMutexPool *QMutexPool::instance() QMutex *QMutexPool::get(const void *address) { Q_ASSERT_X(address != 0, "QMutexPool::get()", "'address' argument cannot be zero"); - int index = int((quintptr(address) >> (sizeof(address) >> 1)) % count); + int index = int((quintptr(address) >> (sizeof(address) >> 1)) % mutexes.count()); if (!mutexes[index]) { // mutex not created, create one - QMutex *newMutex = new QMutex(recurs ? QMutex::Recursive : QMutex::NonRecursive); + QMutex *newMutex = new QMutex(recursionMode); if (!mutexes[index].testAndSetOrdered(0, newMutex)) delete newMutex; } diff --git a/src/corelib/thread/qmutexpool_p.h b/src/corelib/thread/qmutexpool_p.h index 1009ebb..30a16d4 100644 --- a/src/corelib/thread/qmutexpool_p.h +++ b/src/corelib/thread/qmutexpool_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE class Q_CORE_EXPORT QMutexPool { public: - explicit QMutexPool(bool recursive = false, int size = 128); + explicit QMutexPool(QMutex::RecursionMode recursionMode = QMutex::NonRecursive, int size = 131); ~QMutexPool(); QMutex *get(const void *address); @@ -72,9 +72,8 @@ public: static QMutex *globalInstanceGet(const void *address); private: - QVarLengthArray<QAtomicPointer<QMutex>, 128> mutexes; - int count; - bool recurs; + QVarLengthArray<QAtomicPointer<QMutex>, 131> mutexes; + QMutex::RecursionMode recursionMode; }; extern Q_CORE_EXPORT QMutexPool *qt_global_mutexpool; diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 6aa35f3..49dd52d 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -579,7 +579,7 @@ static inline char qToLower(char c) return c; } -Q_CORE_EXPORT QByteArray::Data QByteArray::shared_null = {Q_BASIC_ATOMIC_INITIALIZER(1), +QByteArray::Data QByteArray::shared_null = {Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, shared_null.array, {0} }; QByteArray::Data QByteArray::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, shared_empty.array, {0} }; diff --git a/src/corelib/tools/qcontiguouscache.cpp b/src/corelib/tools/qcontiguouscache.cpp new file mode 100644 index 0000000..996ac18 --- /dev/null +++ b/src/corelib/tools/qcontiguouscache.cpp @@ -0,0 +1,439 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qcontiguouscache.h" +#ifdef QT_QCONTIGUOUSCACHE_DEBUG +#include <QDebug> +#endif + +QT_BEGIN_NAMESPACE + +#ifdef QT_QCONTIGUOUSCACHE_DEBUG +void QContiguousCacheData::dump() const +{ + qDebug() << "capacity:" << alloc; + qDebug() << "count:" << count; + qDebug() << "start:" << start; + qDebug() << "offset:" << offset; +} +#endif + +/*! \class QContiguousCache + \brief The QContiguousCache class is a template class that provides a contiguous cache. + \ingroup tools + \ingroup shared + \reentrant + + The QContiguousCache class provides an efficient way of caching items for + display in a user interface view. Unlike QCache, it adds a restriction + that elements within the cache are contiguous. This has the advantage + of matching how user interface views most commonly request data, as + a set of rows localized around the current scrolled position. This + restriction allows the cache to consume less memory and processor + cycles than QCache. The QContiguousCache class also can provide + an upper bound on memory usage via setCapacity(). + + The simplest way of using a contiguous cache is to use the append() + and prepend(). + +\code +MyRecord record(int row) const +{ + Q_ASSERT(row >= 0 && row < count()); + + while(row > cache.lastIndex()) + cache.append(slowFetchRecord(cache.lastIndex()+1)); + while(row < cache.firstIndex()) + cache.prepend(slowFetchRecord(cache.firstIndex()-1)); + + return cache.at(row); +} +\endcode + + If the cache is full then the item at the opposite end of the cache from + where the new item is appended or prepended will be removed. + + This usage can be further optimized by using the insert() function + in the case where the requested row is a long way from the currently cached + items. If there is a gap between where the new item is inserted and the currently + cached items then the existing cached items are first removed to retain + the contiguous nature of the cache. Hence it is important to take some care then + when using insert() in order to avoid unwanted clearing of the cache. + + The range of valid indexes for the QContiguousCache class are from + 0 to INT_MAX. Calling prepend() such that the first index would become less + than 0 or append() such that the last index would become greater + than INT_MAX can result in the indexes of the cache being invalid. + When the cache indexes are invalid it is important to call + normalizeIndexes() before calling any of containsIndex(), firstIndex(), + lastIndex(), at() or the [] operator. Calling these + functions when the cache has invalid indexes will result in undefined + behavior. The indexes can be checked by using areIndexesValid() + + In most cases the indexes will not exceed 0 to INT_MAX, and + normalizeIndexes() will not need to be used. + + See the \l{Contiguous Cache Example}{Contiguous Cache} example. +*/ + +/*! \fn QContiguousCache::QContiguousCache(int capacity) + + Constructs a cache with the given \a capacity. + + \sa setCapacity() +*/ + +/*! \fn QContiguousCache::QContiguousCache(const QContiguousCache<T> &other) + + Constructs a copy of \a other. + + This operation takes \l{constant time}, because QContiguousCache is + \l{implicitly shared}. This makes returning a QContiguousCache from a + function very fast. If a shared instance is modified, it will be + copied (copy-on-write), and that takes \l{linear time}. + + \sa operator=() +*/ + +/*! \fn QContiguousCache::~QContiguousCache() + + Destroys the cache. +*/ + +/*! \fn void QContiguousCache::detach() + + \internal +*/ + +/*! \fn bool QContiguousCache::isDetached() const + + \internal +*/ + +/*! \fn void QContiguousCache::setSharable(bool sharable) + + \internal +*/ + +/*! \fn QContiguousCache<T> &QContiguousCache::operator=(const QContiguousCache<T> &other) + + Assigns \a other to this cache and returns a reference to this cache. +*/ + +/*! \fn bool QContiguousCache::operator==(const QContiguousCache<T> &other) const + + Returns true if \a other is equal to this cache; otherwise returns false. + + Two caches are considered equal if they contain the same values at the same + indexes. This function requires the value type to implement the \c operator==(). + + \sa operator!=() +*/ + +/*! \fn bool QContiguousCache::operator!=(const QContiguousCache<T> &other) const + + Returns true if \a other is not equal to this cache; otherwise + returns false. + + Two caches are considered equal if they contain the same values at the same + indexes. This function requires the value type to implement the \c operator==(). + + \sa operator==() +*/ + +/*! \fn int QContiguousCache::capacity() const + + Returns the number of items the cache can store before it is full. + When a cache contains a number of items equal to its capacity, adding new + items will cause items farthest from the added item to be removed. + + \sa setCapacity(), size() +*/ + +/*! \fn int QContiguousCache::count() const + + \overload + + Same as size(). +*/ + +/*! \fn int QContiguousCache::size() const + + Returns the number of items contained within the cache. + + \sa capacity() +*/ + +/*! \fn bool QContiguousCache::isEmpty() const + + Returns true if no items are stored within the cache. + + \sa size(), capacity() +*/ + +/*! \fn bool QContiguousCache::isFull() const + + Returns true if the number of items stored within the cache is equal + to the capacity of the cache. + + \sa size(), capacity() +*/ + +/*! \fn int QContiguousCache::available() const + + Returns the number of items that can be added to the cache before it becomes full. + + \sa size(), capacity(), isFull() +*/ + +/*! \fn void QContiguousCache::clear() + + Removes all items from the cache. The capacity is unchanged. +*/ + +/*! \fn void QContiguousCache::setCapacity(int size) + + Sets the capacity of the cache to the given \a size. A cache can hold a + number of items equal to its capacity. When inserting, appending or prepending + items to the cache, if the cache is already full then the item farthest from + the added item will be removed. + + If the given \a size is smaller than the current count of items in the cache + then only the last \a size items from the cache will remain. + + \sa capacity(), isFull() +*/ + +/*! \fn const T &QContiguousCache::at(int i) const + + Returns the item at index position \a i in the cache. \a i must + be a valid index position in the cache (i.e, firstIndex() <= \a i <= lastIndex()). + + The indexes in the cache refer to the number of positions the item is from the + first item appended into the cache. That is to say a cache with a capacity of + 100, that has had 150 items appended will have a valid index range of + 50 to 149. This allows inserting and retrieving items into the cache based + on a theoretical infinite list + + \sa firstIndex(), lastIndex(), insert(), operator[]() +*/ + +/*! \fn T &QContiguousCache::operator[](int i) + + Returns the item at index position \a i as a modifiable reference. If + the cache does not contain an item at the given index position \a i + then it will first insert an empty item at that position. + + In most cases it is better to use either at() or insert(). + + Note that using non-const operators can cause QContiguousCache to do a deep + copy. + + \sa insert(), at() +*/ + +/*! \fn const T &QContiguousCache::operator[](int i) const + + \overload + + Same as at(\a i). +*/ + +/*! \fn void QContiguousCache::append(const T &value) + + Inserts \a value at the end of the cache. If the cache is already full + the item at the start of the cache will be removed. + + \sa prepend(), insert(), isFull() +*/ + +/*! \fn void QContiguousCache::prepend(const T &value) + + Inserts \a value at the start of the cache. If the cache is already full + the item at the end of the cache will be removed. + + \sa append(), insert(), isFull() +*/ + +/*! \fn void QContiguousCache::insert(int i, const T &value) + + Inserts the \a value at the index position \a i. If the cache already contains + an item at \a i then that value is replaced. If \a i is either one more than + lastIndex() or one less than firstIndex() it is the equivalent to an append() + or a prepend(). + + If the given index \a i is not within the current range of the cache nor adjacent + to the bounds of the cache's index range, the cache is first cleared before + inserting the item. At this point the cache will have a size of 1. It is + worthwhile taking effort to insert items in an order that starts adjacent + to the current index range for the cache. + + The range of valid indexes for the QContiguousCache class are from + 0 to INT_MAX. Inserting outside of this range has undefined behavior. + + + \sa prepend(), append(), isFull(), firstIndex(), lastIndex() +*/ + +/*! \fn bool QContiguousCache::containsIndex(int i) const + + Returns true if the cache's index range includes the given index \a i. + + \sa firstIndex(), lastIndex() +*/ + +/*! \fn int QContiguousCache::firstIndex() const + + Returns the first valid index in the cache. The index will be invalid if the + cache is empty. + + \sa capacity(), size(), lastIndex() +*/ + +/*! \fn int QContiguousCache::lastIndex() const + + Returns the last valid index in the cache. The index will be invalid if the cache is empty. + + \sa capacity(), size(), firstIndex() +*/ + + +/*! \fn T &QContiguousCache::first() + + Returns a reference to the first item in the cache. This function + assumes that the cache isn't empty. + + \sa last(), isEmpty() +*/ + +/*! \fn T &QContiguousCache::last() + + Returns a reference to the last item in the cache. This function + assumes that the cache isn't empty. + + \sa first(), isEmpty() +*/ + +/*! \fn const T& QContiguousCache::first() const + + \overload +*/ + +/*! \fn const T& QContiguousCache::last() const + + \overload +*/ + +/*! \fn void QContiguousCache::removeFirst() + + Removes the first item from the cache. This function assumes that + the cache isn't empty. + + \sa removeLast() +*/ + +/*! \fn void QContiguousCache::removeLast() + + Removes the last item from the cache. This function assumes that + the cache isn't empty. + + \sa removeFirst() +*/ + +/*! \fn T QContiguousCache::takeFirst() + + Removes the first item in the cache and returns it. This function + assumes that the cache isn't empty. + + If you don't use the return value, removeFirst() is more efficient. + + \sa takeLast(), removeFirst() +*/ + +/*! \fn T QContiguousCache::takeLast() + + Removes the last item in the cache and returns it. This function + assumes that the cache isn't empty. + + If you don't use the return value, removeLast() is more efficient. + + \sa takeFirst(), removeLast() +*/ + +/*! \fn void QContiguousCache::normalizeIndexes() + + Moves the first index and last index of the cache + such that they point to valid indexes. The function does not modify + the contents of the cache or the ordering of elements within the cache. + + It is provided so that index overflows can be corrected when using the + cache as a circular buffer. + + \code + QContiguousCache<int> cache(10); + cache.insert(INT_MAX, 1); // cache contains one value and has valid indexes, INT_MAX to INT_MAX + cache.append(2); // cache contains two values but does not have valid indexes. + cache.normalizeIndexes(); // cache has two values, 1 and 2. New first index will be in the range of 0 to capacity(). + \endcode + + \sa areIndexesValid(), append(), prepend() +*/ + +/*! \fn bool QContiguousCache::areIndexesValid() const + + Returns whether the indexes for items stored in the cache are valid. + Indexes can become invalid if items are appended after the index position + INT_MAX or prepended before the index position 0. This is only expected + to occur in very long lived circular buffer style usage of the + contiguous cache. Indexes can be made valid again by calling + normalizeIndexs(). + + \sa normalizeIndexes(), append(), prepend() +*/ + +/*! \fn void QContiguousCache::dump() const + + \internal + + Sends information about the cache's internal structure to qDebug() +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h new file mode 100644 index 0000000..2437f63 --- /dev/null +++ b/src/corelib/tools/qcontiguouscache.h @@ -0,0 +1,430 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QCONTIGUOUSCACHE_H +#define QCONTIGUOUSCACHE_H + +#include <QtCore/qatomic.h> +#include <limits.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#undef QT_QCONTIGUOUSCACHE_DEBUG +QT_MODULE(Core) + + +struct Q_CORE_EXPORT QContiguousCacheData +{ + QBasicAtomicInt ref; + int alloc; + int count; + int start; + int offset; + uint sharable : 1; + +#ifdef QT_QCONTIGUOUSCACHE_DEBUG + void dump() const; +#endif +}; + +template <typename T> +struct QContiguousCacheTypedData +{ + QBasicAtomicInt ref; + int alloc; + int count; + int start; + int offset; + uint sharable : 1; + + T array[1]; +}; + +template<typename T> +class QContiguousCache { + typedef QContiguousCacheTypedData<T> Data; + union { QContiguousCacheData *p; QContiguousCacheTypedData<T> *d; }; +public: + explicit QContiguousCache(int capacity = 0); + QContiguousCache(const QContiguousCache<T> &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } + + inline ~QContiguousCache() { if (!d) return; if (!d->ref.deref()) free(d); } + + inline void detach() { if (d->ref != 1) detach_helper(); } + inline bool isDetached() const { return d->ref == 1; } + inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } + + QContiguousCache<T> &operator=(const QContiguousCache<T> &other); + bool operator==(const QContiguousCache<T> &other) const; + inline bool operator!=(const QContiguousCache<T> &other) const { return !(*this == other); } + + inline int capacity() const {return d->alloc; } + inline int count() const { return d->count; } + inline int size() const { return d->count; } + + inline bool isEmpty() const { return d->count == 0; } + inline bool isFull() const { return d->count == d->alloc; } + inline int available() const { return d->alloc - d->count; } + + void clear(); + void setCapacity(int size); + + const T &at(int pos) const; + T &operator[](int i); + const T &operator[](int i) const; + + void append(const T &value); + void prepend(const T &value); + void insert(int pos, const T &value); + + inline bool containsIndex(int pos) const { return pos >= d->offset && pos - d->offset < d->count; } + inline int firstIndex() const { return d->offset; } + inline int lastIndex() const { return d->offset + d->count - 1; } + + inline const T &first() const { Q_ASSERT(!isEmpty()); return d->array[d->start]; } + inline const T &last() const { Q_ASSERT(!isEmpty()); return d->array[(d->start + d->count -1) % d->alloc]; } + inline T &first() { Q_ASSERT(!isEmpty()); detach(); return d->array[d->start]; } + inline T &last() { Q_ASSERT(!isEmpty()); detach(); return d->array[(d->start + d->count -1) % d->alloc]; } + + void removeFirst(); + T takeFirst(); + void removeLast(); + T takeLast(); + + inline bool areIndexesValid() const + { return d->offset >= 0 && d->offset < INT_MAX - d->count && (d->offset % d->alloc) == d->start; } + + inline void normalizeIndexes() { d->offset = d->start; } + +#ifdef QT_QCONTIGUOUSCACHE_DEBUG + void dump() const { p->dump(); } +#endif +private: + void detach_helper(); + + QContiguousCacheData *malloc(int aalloc); + void free(Data *x); + int sizeOfTypedData() { + // this is more or less the same as sizeof(Data), except that it doesn't + // count the padding at the end + return reinterpret_cast<const char *>(&(reinterpret_cast<const Data *>(this))->array[1]) - reinterpret_cast<const char *>(this); + } +}; + +template <typename T> +void QContiguousCache<T>::detach_helper() +{ + union { QContiguousCacheData *p; QContiguousCacheTypedData<T> *d; } x; + + x.p = malloc(d->alloc); + x.d->ref = 1; + x.d->count = d->count; + x.d->start = d->start; + x.d->offset = d->offset; + x.d->alloc = d->alloc; + x.d->sharable = true; + + T *dest = x.d->array + x.d->start; + T *src = d->array + d->start; + int count = x.d->count; + while (count--) { + if (QTypeInfo<T>::isComplex) { + new (dest) T(*src); + } else { + *dest = *src; + } + dest++; + if (dest == x.d->array + x.d->alloc) + dest = x.d->array; + src++; + if (src == d->array + d->alloc) + src = d->array; + } + + if (!d->ref.deref()) + free(d); + d = x.d; +} + +template <typename T> +void QContiguousCache<T>::setCapacity(int asize) +{ + if (asize == d->alloc) + return; + detach(); + union { QContiguousCacheData *p; QContiguousCacheTypedData<T> *d; } x; + x.p = malloc(asize); + x.d->alloc = asize; + x.d->count = qMin(d->count, asize); + x.d->offset = d->offset + d->count - x.d->count; + x.d->start = x.d->offset % x.d->alloc; + T *dest = x.d->array + (x.d->start + x.d->count-1) % x.d->alloc; + T *src = d->array + (d->start + d->count-1) % d->alloc; + int count = x.d->count; + while (count--) { + if (QTypeInfo<T>::isComplex) { + new (dest) T(*src); + } else { + *dest = *src; + } + if (dest == x.d->array) + dest = x.d->array + x.d->alloc; + dest--; + if (src == d->array) + src = d->array + d->alloc; + src--; + } + /* free old */ + free(d); + d = x.d; +} + +template <typename T> +void QContiguousCache<T>::clear() +{ + if (d->ref == 1) { + if (QTypeInfo<T>::isComplex) { + int count = d->count; + T * i = d->array + d->start; + T * e = d->array + d->alloc; + while (count--) { + i->~T(); + i++; + if (i == e) + i = d->array; + } + } + d->count = d->start = d->offset = 0; + } else { + union { QContiguousCacheData *p; QContiguousCacheTypedData<T> *d; } x; + x.p = malloc(d->alloc); + x.d->ref = 1; + x.d->alloc = d->alloc; + x.d->count = x.d->start = x.d->offset = 0; + x.d->sharable = true; + if (!d->ref.deref()) free(d); + d = x.d; + } +} + +template <typename T> +inline QContiguousCacheData *QContiguousCache<T>::malloc(int aalloc) +{ + return static_cast<QContiguousCacheData *>(qMalloc(sizeOfTypedData() + (aalloc - 1) * sizeof(T))); +} + +template <typename T> +QContiguousCache<T>::QContiguousCache(int capacity) +{ + p = malloc(capacity); + d->ref = 1; + d->alloc = capacity; + d->count = d->start = d->offset = 0; + d->sharable = true; +} + +template <typename T> +QContiguousCache<T> &QContiguousCache<T>::operator=(const QContiguousCache<T> &other) +{ + other.d->ref.ref(); + if (!d->ref.deref()) + free(d); + d = other.d; + if (!d->sharable) + detach_helper(); + return *this; +} + +template <typename T> +bool QContiguousCache<T>::operator==(const QContiguousCache<T> &other) const +{ + if (other.d == d) + return true; + if (other.d->start != d->start + || other.d->count != d->count + || other.d->offset != d->offset + || other.d->alloc != d->alloc) + return false; + for (int i = firstIndex(); i <= lastIndex(); ++i) + if (!(at(i) == other.at(i))) + return false; + return true; +} + +template <typename T> +void QContiguousCache<T>::free(Data *x) +{ + if (QTypeInfo<T>::isComplex) { + int count = d->count; + T * i = d->array + d->start; + T * e = d->array + d->alloc; + while (count--) { + i->~T(); + i++; + if (i == e) + i = d->array; + } + } + qFree(x); +} +template <typename T> +void QContiguousCache<T>::append(const T &value) +{ + detach(); + if (QTypeInfo<T>::isComplex) { + if (d->count == d->alloc) + (d->array + (d->start+d->count) % d->alloc)->~T(); + new (d->array + (d->start+d->count) % d->alloc) T(value); + } else { + d->array[(d->start+d->count) % d->alloc] = value; + } + + if (d->count == d->alloc) { + d->start++; + d->start %= d->alloc; + d->offset++; + } else { + d->count++; + } +} + +template<typename T> +void QContiguousCache<T>::prepend(const T &value) +{ + detach(); + if (d->start) + d->start--; + else + d->start = d->alloc-1; + d->offset--; + + if (d->count != d->alloc) + d->count++; + else + if (d->count == d->alloc) + (d->array + d->start)->~T(); + + if (QTypeInfo<T>::isComplex) + new (d->array + d->start) T(value); + else + d->array[d->start] = value; +} + +template<typename T> +void QContiguousCache<T>::insert(int pos, const T &value) +{ + Q_ASSERT_X(pos >= 0 && pos < INT_MAX, "QContiguousCache<T>::insert", "index out of range"); + detach(); + if (containsIndex(pos)) { + if(QTypeInfo<T>::isComplex) + new (d->array + pos % d->alloc) T(value); + else + d->array[pos % d->alloc] = value; + } else if (pos == d->offset-1) + prepend(value); + else if (pos == d->offset+d->count) + append(value); + else { + // we don't leave gaps. + clear(); + d->offset = pos; + d->start = pos % d->alloc; + d->count = 1; + if (QTypeInfo<T>::isComplex) + new (d->array + d->start) T(value); + else + d->array[d->start] = value; + } +} + +template <typename T> +inline const T &QContiguousCache<T>::at(int pos) const +{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache<T>::at", "index out of range"); return d->array[pos % d->alloc]; } +template <typename T> +inline const T &QContiguousCache<T>::operator[](int pos) const +{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache<T>::at", "index out of range"); return d->array[pos % d->alloc]; } + +template <typename T> +inline T &QContiguousCache<T>::operator[](int pos) +{ + detach(); + if (!containsIndex(pos)) + insert(pos, T()); + return d->array[pos % d->alloc]; +} + +template <typename T> +inline void QContiguousCache<T>::removeFirst() +{ + Q_ASSERT(d->count > 0); + detach(); + d->count--; + if (QTypeInfo<T>::isComplex) + (d->array + d->start)->~T(); + d->start = (d->start + 1) % d->alloc; + d->offset++; +} + +template <typename T> +inline void QContiguousCache<T>::removeLast() +{ + Q_ASSERT(d->count > 0); + detach(); + d->count--; + if (QTypeInfo<T>::isComplex) + (d->array + (d->start + d->count) % d->alloc)->~T(); +} + +template <typename T> +inline T QContiguousCache<T>::takeFirst() +{ T t = first(); removeFirst(); return t; } + +template <typename T> +inline T QContiguousCache<T>::takeLast() +{ T t = last(); removeLast(); return t; } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 781514c..090ca61 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -3833,19 +3833,19 @@ void QDateTimePrivate::getUTC(QDate &outDate, QTime &outTime) const #if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_NO_DATESTRING) QDebug operator<<(QDebug dbg, const QDate &date) { - dbg.nospace() << "QDate(" << date.toString() << ")"; + dbg.nospace() << "QDate(" << date.toString() << ')'; return dbg.space(); } QDebug operator<<(QDebug dbg, const QTime &time) { - dbg.nospace() << "QTime(" << time.toString() << ")"; + dbg.nospace() << "QTime(" << time.toString() << ')'; return dbg.space(); } QDebug operator<<(QDebug dbg, const QDateTime &date) { - dbg.nospace() << "QDateTime(" << date.toString() << ")"; + dbg.nospace() << "QDateTime(" << date.toString() << ')'; return dbg.space(); } #endif diff --git a/src/corelib/tools/qdumper.cpp b/src/corelib/tools/qdumper.cpp deleted file mode 100644 index c3b8524..0000000 --- a/src/corelib/tools/qdumper.cpp +++ /dev/null @@ -1,1157 +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 QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the 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 <qdatetime.h> -#include <qdebug.h> -#include <qdir.h> -#include <qfileinfo.h> -#include <qhash.h> -#include <qmap.h> -#include <qmetaobject.h> -#include <qobject.h> -#include <qstring.h> -#include <qvariant.h> -#include <qvector.h> - -#if !defined(Q_OS_WINCE) && !defined(QT_NO_DUMPER) - -#include <stdlib.h> -#include <stdio.h> - -#ifdef Q_OS_WIN -# include <windows.h> -#endif - -QT_BEGIN_NAMESPACE - -namespace { - -// This is used to abort evaluation of custom data dumpers in a "coordinated" -// way. Abortion will happen anyway when we try to access a non-initialized -// non-trivial object, so there is no way to prevent this from occuring at all -// conceptionally. Gdb will catch SIGSEGV and return to the calling frame. -// This is just fine provided we only _read_ memory in the custom handlers -// below. - -volatile int qProvokeSegFaultHelper; - -static void qCheckAccess(const void *d) -{ - // provoke segfault when address is not readable - qProvokeSegFaultHelper = *(char*)d; -} - -static void qCheckPointer(const void *d) -{ - if (!d) - return; - qProvokeSegFaultHelper = *(char*)d; -} - -static void qProvokeSegFault() -{ - // provoke segfault unconditionally - qCheckAccess(0); -} - -static char qDumpInBuffer[100]; -static char qDumpBuffer[1000]; -#ifdef Q_OS_WIN -static char qDumpBuffer2[sizeof(qDumpBuffer) + 100]; -#endif - -static char toHex(int n) -{ - return n < 10 ? '0' + n : 'a' - 10 + n; -} - - -struct QDumper -{ - explicit QDumper(); - ~QDumper(); - void flush(); - QDumper &operator<<(long c); - QDumper &operator<<(int i); - QDumper &operator<<(unsigned long c); - QDumper &operator<<(unsigned int i); - QDumper &operator<<(const void *p); - void put(char c); - void addCommaIfNeeded(); - void putEncoded(unsigned c); - QDumper &operator<<(const char *str); - QDumper &operator<<(const QString &str); - void disarm(); - - void beginHash(); // start of data hash output - void endHash(); // start of data hash output - - // the dumper arguments - int protocolVersion; // dumper protocol version - int token; // some token to show on success - const char *outertype; // object type - const char *iname; // object name used for display - const char *exp; // object expression - const char *innertype; // 'inner type' for class templates - const void *data; // pointer to raw data - bool dumpChildren; // do we want to see children? - - // handling of nested templates - void setupTemplateParameters(); - enum { maxTemplateParameters = 10 }; - const char *templateParameters[maxTemplateParameters + 1]; - int templateParametersCount; - - // internal state - bool success; // are we finished? - size_t pos; -}; - - -QDumper::QDumper() -{ - success = false; - pos = 0; -} - -QDumper::~QDumper() -{ - flush(); - put(0); // our end marker -#ifdef Q_OS_WIN - sprintf(qDumpBuffer2, "@@CDD/%d/done\n", token); - OutputDebugStringA(qDumpBuffer2); -#else - fprintf(stderr, "%d/done\n", token); -#endif - qDumpInBuffer[0] = 0; -} - -void QDumper::flush() -{ - qDumpBuffer[pos++] = 0; -#ifdef Q_OS_WIN - sprintf(qDumpBuffer2, "@@CDD#%d#%d,%s\n", token, int(pos - 1), qDumpBuffer); - OutputDebugStringA(qDumpBuffer2); -#else - fprintf(stderr, "%d#%d,%s\n", token, int(pos - 1), qDumpBuffer); -#endif - pos = 0; -} - -void QDumper::setupTemplateParameters() -{ - char *s = const_cast<char *>(innertype); - - templateParametersCount = 1; - templateParameters[0] = s; - for (int i = 1; i != maxTemplateParameters + 1; ++i) - templateParameters[i] = 0; - - while (*s) { - while (*s && *s != '@') - ++s; - if (*s) { - *s = '\0'; - ++s; - templateParameters[templateParametersCount++] = s; - } - } -} - -QDumper &QDumper::operator<<(unsigned long c) -{ - static char buf[100]; - sprintf(buf, "%lu", c); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(unsigned int i) -{ - static char buf[100]; - sprintf(buf, "%u", i); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(long c) -{ - static char buf[100]; - sprintf(buf, "%ld", c); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(int i) -{ - static char buf[100]; - sprintf(buf, "%d", i); - return (*this) << buf; -} - -QDumper &QDumper::operator<<(const void *p) -{ - static char buf[100]; - sprintf(buf, "%p", p); - // we get a '0x' prefix only on some implementations. - // if it isn't there, write it out manually. - if (buf[1] != 'x') { - put('0'); - put('x'); - } - return (*this) << buf; -} - -void QDumper::put(char c) -{ - if (pos >= sizeof(qDumpBuffer) - 100) - flush(); - qDumpBuffer[pos++] = c; -} - -void QDumper::addCommaIfNeeded() -{ - if (pos == 0) - return; - if (qDumpBuffer[pos - 1] == '}' || qDumpBuffer[pos - 1] == '"') - put(','); -} - -void QDumper::putEncoded(unsigned c) -{ - if (c >= 32 && c <= 126 && c != '"' && c != '\\') { - put(c); - } else { - put('\\'); - put('u'); - put(toHex((c >> 12) & 0xf)); - put(toHex((c >> 8) & 0xf)); - put(toHex((c >> 4) & 0xf)); - put(toHex( c & 0xf)); - } -} - -QDumper &QDumper::operator<<(const char *str) -{ - while (*str) - put(*(str++)); - return *this; -} - -QDumper &QDumper::operator<<(const QString &str) -{ - int n = str.size(); - if (n < 0) { - qProvokeSegFault(); - } else { - //(*this) << "[" << n << "]"; - if (n > 1000000) - n = 1000000; - //put(' '); - put('\\'); - put('"'); - for (int i = 0; i != n; ++i) - putEncoded(str[i].unicode()); - put('\\'); - put('"'); - if (n < str.size()) - (*this) << "<incomplete string>"; - } - return *this; -} - -void QDumper::disarm() -{ - flush(); - success = true; -} - -void QDumper::beginHash() -{ - addCommaIfNeeded(); - put('{'); -} - -void QDumper::endHash() -{ - put('}'); -} - - -// -// Some helpers to keep the dumper code short -// - -// dump property=value pair -#undef P -#define P(dumper,name,value) \ - do { \ - dumper.addCommaIfNeeded(); \ - dumper << (name) << "=\"" << value << "\""; \ - } while (0) - -// simple string property -#undef S -#define S(dumper, name, value) \ - dumper.beginHash(); \ - P(dumper, "name", name); \ - P(dumper, "value", value); \ - P(dumper, "type", "QString"); \ - P(dumper, "numchild", "0"); \ - dumper.endHash(); - -// simple integer property -#undef I -#define I(dumper, name, value) \ - dumper.beginHash(); \ - P(dumper, "name", name); \ - P(dumper, "value", value); \ - P(dumper, "type", "int"); \ - P(dumper, "numchild", "0"); \ - dumper.endHash(); - -// simple boolean property -#undef BL -#define BL(dumper, name, value) \ - dumper.beginHash(); \ - P(dumper, "name", name); \ - P(dumper, "value", (value ? "true" : "false")); \ - P(dumper, "type", "bool"); \ - P(dumper, "numchild", "0"); \ - dumper.endHash(); - -#undef TT -#define TT(type, value) \ - "<tr><td>" << type << "</td><td> : </td><td>" << value << "</td></tr>" - -static void qDumpUnknown(QDumper &d) -{ - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<internal error>"); - P(d, "type", d.outertype); - P(d, "numchild", "0"); - d.disarm(); -} - -static void qDumpQPropertyList(QDumper &d) -{ - const QObject *ob = (const QObject *)d.data; - const QMetaObject *mo = ob->metaObject(); - P(d, "iname", d.iname); - P(d, "addr", "<synthetic>"); - P(d, "type", "QObject"); - P(d, "numchild", mo->propertyCount()); - if (d.dumpChildren) { - d << ",children=["; - for (int i = mo->propertyCount(); --i >= 0; ) { - const QMetaProperty & prop = mo->property(i); - d.beginHash(); - P(d, "name", prop.name()); - if (QLatin1String(prop.typeName()) == QLatin1String("QString")) { - P(d, "value", prop.read(ob).toString()); - P(d, "numchild", "0"); - } else if (QLatin1String(prop.typeName()) == QLatin1String("bool")) { - P(d, "value", (prop.read(ob).toBool() ? "true" : "false")); - P(d, "numchild", "0"); - } else if (QLatin1String(prop.typeName()) == QLatin1String("int")) { - P(d, "value", prop.read(ob).toInt()); - P(d, "numchild", "0"); - } else { - P(d, "exp", "((" << mo->className() << "*)" << ob - << ")->" << prop.name() << "()"); - } - P(d, "type", prop.typeName()); - P(d, "numchild", "1"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQObject(QDumper &d) -{ - const QObject *ob = reinterpret_cast<const QObject *>(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", (void*)d.data); - P(d, "type", "QObject"); - P(d, "numchild", 4); - if (d.dumpChildren) { - const QMetaObject *mo = ob->metaObject(); - const QObjectList &children = ob->children(); - d << ",children=["; - S(d, "objectName", ob->objectName()); - d.beginHash(); - P(d, "name", "properties"); - // FIXME: Note that when simply using '(QObject*)' - // in the cast below, Gdb/MI _sometimes misparses - // expressions further down in the tree. - P(d, "exp", "*(class QObject*)" << d.data); - P(d, "type", "QPropertyList"); - P(d, "value", "<" << mo->propertyCount() << " items>"); - P(d, "numchild", mo->propertyCount()); - d.endHash(); - d.beginHash(); - P(d, "name", "children"); - P(d, "exp", "((class QObject*)" << d.data << ")->children()"); - P(d, "type", "QList<QObject *>"); - P(d, "value", "<" << children.size() << " items>"); - P(d, "numchild", children.size()); - d.endHash(); - d.beginHash(); - P(d, "name", "parent"); - P(d, "exp", "((class QObject*)" << d.data << ")->parent()"); - P(d, "type", "QObject *"); - P(d, "numchild", (ob->parent() ? "1" : "0")); - d.endHash(); - d << "]"; - } - d.disarm(); -} - -static void qDumpQDir(QDumper &d) -{ - const QDir &dir = *reinterpret_cast<const QDir *>(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", dir.path()); - P(d, "type", "QDir"); - P(d, "numchild", "3"); - if (d.dumpChildren) { - d << ",children=["; - S(d, "absolutePath", dir.absolutePath()); - S(d, "canonicalPath", dir.canonicalPath()); - d << "]"; - } - d.disarm(); -} - -static void qDumpQFileInfo(QDumper &d) -{ - const QFileInfo &info = *reinterpret_cast<const QFileInfo *>(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", info.filePath()); - P(d, "type", "QDir"); - P(d, "numchild", "3"); - if (d.dumpChildren) { - d << ",children=["; - S(d, "absolutePath", info.absolutePath()); - S(d, "absoluteFilePath", info.absoluteFilePath()); - S(d, "canonicalPath", info.canonicalPath()); - S(d, "canonicalFilePath", info.canonicalFilePath()); - S(d, "completeBaseName", info.completeBaseName()); - S(d, "completeSuffix", info.completeSuffix()); - S(d, "baseName", info.baseName()); -#ifdef Q_OS_MACX - BL(d, "isBundle", info.isBundle()); - S(d, "bundleName", info.bundleName()); -#endif - S(d, "completeSuffix", info.completeSuffix()); - S(d, "fileName", info.fileName()); - S(d, "filePath", info.filePath()); - S(d, "group", info.group()); - S(d, "owner", info.owner()); - S(d, "path", info.path()); - - I(d, "groupid", (long)info.groupId()); - I(d, "ownerid", (long)info.ownerId()); - //QFile::Permissions permissions () const - I(d, "permissions", info.permissions()); - - //QDir absoluteDir () const - //QDir dir () const - - BL(d, "caching", info.caching()); - BL(d, "exists", info.exists()); - BL(d, "isAbsolute", info.isAbsolute()); - BL(d, "isDir", info.isDir()); - BL(d, "isExecutable", info.isExecutable()); - BL(d, "isFile", info.isFile()); - BL(d, "isHidden", info.isHidden()); - BL(d, "isReadable", info.isReadable()); - BL(d, "isRelative", info.isRelative()); - BL(d, "isRoot", info.isRoot()); - BL(d, "isSymLink", info.isSymLink()); - BL(d, "isWritable", info.isWritable()); - -#ifndef QT_NO_DATESTRING - d.beginHash(); - P(d, "name", "created"); - P(d, "value", info.created().toString()); - P(d, "exp", "((QFileInfo*)" << d.data << ")->created()"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d.beginHash(); - P(d, "name", "lastModified"); - P(d, "value", info.lastModified().toString()); - P(d, "exp", "((QFileInfo*)" << d.data << ")->lastModified()"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d.beginHash(); - P(d, "name", "lastRead"); - P(d, "value", info.lastRead().toString()); - P(d, "exp", "((QFileInfo*)" << d.data << ")->lastRead()"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); -#endif - - d << "]"; - } - d.disarm(); -} - -static void qDumpQDateTime(QDumper &d) -{ -#ifdef QT_NO_DATESTRING - qDumpUnknown(d); -#else - const QDateTime &date = *reinterpret_cast<const QDateTime *>(d.data); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", date.toString()); - P(d, "type", "QDateTime"); - P(d, "numchild", "3"); - if (d.dumpChildren) { - d << ",children=["; - BL(d, "isNull", date.isNull()); - I(d, "toTime_t", (long)date.toTime_t()); - S(d, "toString", date.toString()); - S(d, "toString_(ISO)", date.toString(Qt::ISODate)); - S(d, "toString_(SystemLocale)", date.toString(Qt::SystemLocaleDate)); - S(d, "toString_(Locale)", date.toString(Qt::LocaleDate)); - S(d, "toString", date.toString()); - - d.beginHash(); - P(d, "name", "toUTC"); - P(d, "exp", "((QDateTime*)" << d.data << ")->toTimeSpec(Qt::UTC)"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d.beginHash(); - P(d, "name", "toLocalTime"); - P(d, "exp", "((QDateTime*)" << d.data << ")->toTimeSpec(Qt::LocalTime)"); - P(d, "type", "QDateTime"); - P(d, "numchild", "1"); - d.endHash(); - - d << "]"; - } - d.disarm(); -#endif // ifdef QT_NO_DATESTRING -} - -static void qDumpQString(QDumper &d) -{ - const QString &str = *reinterpret_cast<const QString *>(d.data); - - // Try to provoke segfaults early to prevent the frontend - // from asking for unavailable child details - if (!str.isEmpty()) { - volatile ushort dummy = 0; - dummy += str.at(0).unicode(); - dummy += str.at(str.size() - 1).unicode(); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", str); - P(d, "type", "QString"); - P(d, "numchild", "0"); - d.disarm(); -} - -static void qDumpQStringList(QDumper &d) -{ - const QStringList &list = *reinterpret_cast<const QStringList *>(d.data); - int n = list.size(); - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckAccess(&list.front()); - qCheckAccess(&list.back()); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - for (int i = 0; i != n; ++i) { - S(d, "[" << i << "]", list[i]); - } - if (n < list.size()) { - d.beginHash(); - P(d, "value", "<incomplete>"); - P(d, "type", " "); - P(d, "numchild", "0"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQVariantHelper(const void *data, QString *value, - QString *exp, int *numchild) -{ - const QVariant &v = *reinterpret_cast<const QVariant *>(data); - switch (v.type()) { - case QVariant::Invalid: - *value = QLatin1String("<invalid>"); - *numchild = 0; - break; - case QVariant::String: - *value = QLatin1Char('"') + v.toString() + QLatin1Char('"'); - *numchild = 0; - break; - case QVariant::StringList: - *exp = QString(QLatin1String("((QVariant*)%1)->d.data.c")) - .arg((qulonglong)data); - *numchild = v.toStringList().size(); - break; - case QVariant::Int: - *value = QString::number(v.toInt()); - *numchild= 0; - break; - case QVariant::Double: - *value = QString::number(v.toDouble()); - *numchild = 0; - break; - default: - // FIXME - //*exp = QString("qVariantValue<" << v.typeName() << ">" - // << "(*(QVariant*)" << data << ")"); - break; - } -} - -static void qDumpQVariant(QDumper &d) -{ - const QVariant &v = *reinterpret_cast<const QVariant *>(d.data); - QString value; - QString exp; - int numchild = 0; - qDumpQVariantHelper(d.data, &value, &exp, &numchild); - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "(" << v.typeName() << ") " << qPrintable(value)); - P(d, "type", "QVariant"); - P(d, "numchild", 1); - if (d.dumpChildren) { - d << ",children=["; - d.beginHash(); - P(d, "name", "value"); - if (!exp.isEmpty()) - P(d, "exp", qPrintable(exp)); - if (!value.isEmpty()) - P(d, "value", qPrintable(value)); - P(d, "type", v.typeName()); - P(d, "numchild", numchild); - d.endHash(); - d << "]"; - } - d.disarm(); -} - -static void qDumpQList(QDumper &d) -{ - // This uses the knowledge that QList<T> has only a single member - // of type union { QListData p; QListData::Data *d; }; - const QListData &ldata = *reinterpret_cast<const QListData*>(d.data); - const QListData::Data *pdata = *reinterpret_cast<const QListData::Data* const*>(d.data); - int nn = ldata.size(); - if (nn < 0) - qProvokeSegFault(); - if (nn > 0) { - qCheckAccess(ldata.d->array); - //qCheckAccess(ldata.d->array[0]); - //qCheckAccess(ldata.d->array[nn - 1]); - } - - int n = nn; - P(d, "iname", d.iname); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - for (int i = 0; i != n; ++i) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - // The exact condition here is: - // QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic - // but this data is not available in the compiled binary. - // So as first approximation only do the 'isLarge' check: - void *p = &(ldata.d->array[i + pdata->begin]); - unsigned long voidpsize = sizeof(void*); - P(d, "exp", "(sizeof(" << d.innertype << ")>" << voidpsize << - "?(**(" << d.innertype << "**)(" << p << "))" - ":(*(" << d.innertype << "*)(" << p << ")))"); - P(d, "type", d.innertype); - d.endHash(); - } - if (n < nn) { - d << ",{"; - P(d, "value", "<incomplete>"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQVector(QDumper &d) -{ - // Use 'int' as representative value. No way (and no need) - // to deduce proper type here. - const QVector<int> &vec = *reinterpret_cast<const QVector<int> *>(d.data); - const int nn = vec.size(); - - // Try to provoke segfaults early to prevent the frontend - // from asking for unavailable child details - if (nn < 0) - qProvokeSegFault(); - if (nn > 0) { - qCheckAccess(&vec.front()); - qCheckAccess(&vec.back()); - } - - //int innersize = 0; - //scanf(qDumpInBuffer, "%d", &innersize); - - int n = nn; - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - for (int i = 0; i != n; ++i) { - if (i) - d << ","; - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "exp", "(" << d.exp << ".d->array[" << i << "])"); - P(d, "type", d.innertype); - d.endHash(); - } - if (n < nn) { - d << ",{"; - P(d, "value", "<incomplete>"); - d.endHash(); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQHashNode(QDumper &d) -{ - struct NodeOS { void *next; uint k; uint v; } nodeOS; // int-key optimization, small value - struct NodeOL { void *next; uint k; void *v; } nodeOL; // int-key optimiatzion, large value - struct NodeNS { void *next; uint h; uint k; uint v; } nodeNS; // no optimization, small value - struct NodeNL { void *next; uint h; uint k; void *v; } nodeNL; // no optimization, large value - struct NodeL { void *next; uint h; void *k; void *v; } nodeL; // complex key - - // offsetof(...,...) not yet in Standard C++ - const ulong nodeOSk ( (char *)&nodeOS.k - (char *)&nodeOS ); - const ulong nodeOSv ( (char *)&nodeOS.v - (char *)&nodeOS ); - const ulong nodeOLk ( (char *)&nodeOL.k - (char *)&nodeOL ); - const ulong nodeOLv ( (char *)&nodeOL.v - (char *)&nodeOL ); - const ulong nodeNSk ( (char *)&nodeNS.k - (char *)&nodeNS ); - const ulong nodeNSv ( (char *)&nodeNS.v - (char *)&nodeNS ); - const ulong nodeNLk ( (char *)&nodeNL.k - (char *)&nodeNL ); - const ulong nodeNLv ( (char *)&nodeNL.v - (char *)&nodeNL ); - const ulong nodeLk ( (char *)&nodeL.k - (char *)&nodeL ); - const ulong nodeLv ( (char *)&nodeL.v - (char *)&nodeL ); - - const QHashData *h = reinterpret_cast<const QHashData *>(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", ""); - P(d, "numchild", 2); - if (d.dumpChildren) { - // there is a hash specialization in cast the key are integers or shorts - bool isOptimizedIntKey = qstrcmp(keyType, "int") == 0 -#if defined(Q_BYTE_ORDER) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN - || qstrcmp(keyType, "short") == 0 - || qstrcmp(keyType, "ushort") == 0 -#endif - || qstrcmp(keyType, "uint") == 0; - - d << ",children=["; - d.beginHash(); - P(d, "name", "key"); - P(d, "type", keyType); - unsigned long intsize = sizeof(int); - if (isOptimizedIntKey) { - P(d, "exp", "*(" << keyType << "*)" - "(((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeOLk << ":" << nodeOSk << - ")+(char*)" << h << ")"); - } else { - P(d, "exp", "*(" << keyType << "*)" - "(((sizeof(" << keyType << ")>" << intsize << ")?" - << nodeLk << ":" - "((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeNLk << ":" << nodeNSk << "))+(char*)" << h << ")"); - } - d.endHash(); - d.beginHash(); - P(d, "name", "value"); - P(d, "type", valueType); - if (isOptimizedIntKey) { - P(d, "exp", "*(" << valueType << "*)" - "(((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeOLv << ":" << nodeOSv << ")+(char*)" << h << ")"); - } else { - P(d, "exp", "*(" << valueType << "*)" - "(((sizeof(" << keyType << ")>" << intsize << ")?" << nodeLv << ":" - "((sizeof(" << valueType << ")>" << intsize << ")?" - << nodeNLv << ":" << nodeNSv << "))+(char*)" << h << ")"); - } - d.endHash(); - d << "]"; - } - d.disarm(); -} - -static void qDumpQHash(QDumper &d) -{ - QHashData *h = *reinterpret_cast<QHashData *const*>(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - qCheckPointer(h->fakeNext); - qCheckPointer(h->buckets); - - int n = h->size; - - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckPointer(h->fakeNext); - qCheckPointer(*h->buckets); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - - QHashData::Node *node = h->firstNode(); - QHashData::Node *end = reinterpret_cast<QHashData::Node *>(h); - int i = 0; - - while (node != end) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "type", "QHashNode<" << keyType << "," << valueType << " >"); - P(d, "exp", "*(QHashNode<" << keyType << "," << valueType << " >*)" << node); - d.endHash(); - - ++i; - node = QHashData::nextNode(node); - } - d << "]"; - } - d.disarm(); -} - -static void qDumpQMapNode(QDumper &d) -{ - const QMapData *h = reinterpret_cast<const QMapData *>(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - qCheckAccess(h->backward); - qCheckAccess(h->forward[0]); - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", ""); - P(d, "numchild", 2); - if (d.dumpChildren) { - unsigned long voidpsize = sizeof(void*); - d << ",children=["; - d.beginHash(); - P(d, "name", "key"); - P(d, "type", keyType); - P(d, "exp", "*(" << keyType << "*)" - << "(" - << 2 * voidpsize - << "-sizeof('QMap<" << keyType << "," << valueType << ">::Node')" - << "+(char*)" << h - << ")"); - d.endHash(); - d.beginHash(); - P(d, "name", "value"); - P(d, "type", valueType); - P(d, "exp", "*(" << valueType << "*)" - << "(" - << "(size_t)&(('QMap<" << keyType << "," << valueType << ">::Node'*)0)->value" - << "+" << 2 * voidpsize - << "-sizeof('QMap<" << keyType << "," << valueType << ">::Node')" - << "+(char*)" << h - << ")"); - d.endHash(); - d << "]"; - } - - d.disarm(); -} - -static void qDumpQMap(QDumper &d) -{ - QMapData *h = *reinterpret_cast<QMapData *const*>(d.data); - const char *keyType = d.templateParameters[0]; - const char *valueType = d.templateParameters[1]; - - int n = h->size; - - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckAccess(h->backward); - qCheckAccess(h->forward[0]); - qCheckPointer(h->backward->backward); - qCheckPointer(h->forward[0]->backward); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "numchild", n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - - QMapData::Node *node = reinterpret_cast<QMapData::Node *>(h->forward[0]); - QMapData::Node *end = reinterpret_cast<QMapData::Node *>(h); - int i = 0; - - while (node != end) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "type", "QMap<" << keyType << "," << valueType << ">::Node"); - P(d, "exp", "*('QMap<" << keyType << "," << valueType << ">::Node'*)" << node); - d.endHash(); - - ++i; - node = node->forward[0]; - } - d << "]"; - } - - d.disarm(); -} - -static void qDumpQSet(QDumper &d) -{ - // This uses the knowledge that QHash<T> has only a single member - // of union { QHashData *d; QHashNode<Key, T> *e; }; - QHashData *hd = *(QHashData**)d.data; - QHashData::Node *node = hd->firstNode(); - - int n = hd->size; - if (n < 0) - qProvokeSegFault(); - if (n > 0) { - qCheckAccess(node); - qCheckPointer(node->next); - } - - P(d, "iname", d.iname); - P(d, "addr", d.data); - P(d, "value", "<" << n << " items>"); - P(d, "valuedisabled", "true"); - P(d, "numchild", 2 * n); - if (d.dumpChildren) { - if (n > 100) - n = 100; - d << ",children=["; - int i = 0; - for (int bucket = 0; bucket != hd->numBuckets; ++bucket) { - for (node = hd->buckets[bucket]; node->next; node = node->next) { - d.beginHash(); - P(d, "name", "[" << i << "]"); - P(d, "type", d.innertype); - P(d, "exp", "(('QHashNode<" << d.innertype - << ",QHashDummyValue>'*)" - << static_cast<const void*>(node) << ")->key" - ); - d.endHash(); - ++i; - } - } - d << "]"; - } - d.disarm(); -} - -static void handleProtocolVersion2(QDumper & d) -{ - if (!d.outertype[0]) { - qDumpUnknown(d); - return; - } - - d.setupTemplateParameters(); - // d.outertype[0] is usally 'Q', so don't use it - switch (d.outertype[1]) { - case 'D': - if (qstrcmp(d.outertype, "QDateTime") == 0) - qDumpQDateTime(d); - else if (qstrcmp(d.outertype, "QDir") == 0) - qDumpQDir(d); - break; - case 'F': - if (qstrcmp(d.outertype, "QFileInfo") == 0) - qDumpQFileInfo(d); - break; - case 'H': - if (qstrcmp(d.outertype, "QHash") == 0) - qDumpQHash(d); - else if (qstrcmp(d.outertype, "QHashNode") == 0) - qDumpQHashNode(d); - break; - case 'L': - if (qstrcmp(d.outertype, "QList") == 0) - qDumpQList(d); - break; - case 'M': - if (qstrcmp(d.outertype, "QMap") == 0) - qDumpQMap(d); - else if (qstrcmp(d.outertype, "QMap::Node") == 0) - qDumpQMapNode(d); - break; - case 'O': - if (qstrcmp(d.outertype, "QObject") == 0) - qDumpQObject(d); - break; - case 'P': - if (qstrcmp(d.outertype, "QPropertyList") == 0) - qDumpQPropertyList(d); - break; - case 'S': - if (qstrcmp(d.outertype, "QSet") == 0) - qDumpQSet(d); - else if (qstrcmp(d.outertype, "QString") == 0) - qDumpQString(d); - else if (qstrcmp(d.outertype, "QStringList") == 0) - qDumpQStringList(d); - break; - case 'V': - if (qstrcmp(d.outertype, "QVariant") == 0) - qDumpQVariant(d); - else if (qstrcmp(d.outertype, "QVector") == 0) - qDumpQVector(d); - break; - } - - if (!d.success) - qDumpUnknown(d); -} - -} // anonymous namespace - - -extern "C" Q_CORE_EXPORT void qDumpObjectData( - int protocolVersion, - int token, - const char *outertype, - const char *iname, - const char *exp, - const char *innertype, - const void *data, - bool dumpChildren) -{ - if (protocolVersion == 1) { - // used to test whether error output gets through - //fprintf(stderr, "using stderr, qDebug follows: %d\n", token); - //qDebug() << "using qDebug, stderr already used: " << token; - } - - else if (protocolVersion == 2) { - QDumper d; - d.protocolVersion = protocolVersion; - d.token = token; - d.outertype = outertype ? outertype : ""; - d.iname = iname ? iname : ""; - d.exp = exp ? exp : ""; - d.innertype = innertype ? innertype : ""; - d.data = data ? data : ""; - d.dumpChildren = dumpChildren; - handleProtocolVersion2(d); - } - - else { - qDebug() << "Unsupported protocol version" << protocolVersion; - } -} - -QT_END_NAMESPACE - -#endif // !Q_OS_WINCE && !QT_NO_QDUMPER diff --git a/src/corelib/tools/qeasingcurve.cpp b/src/corelib/tools/qeasingcurve.cpp new file mode 100644 index 0000000..2b027fc --- /dev/null +++ b/src/corelib/tools/qeasingcurve.cpp @@ -0,0 +1,846 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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$ +** +****************************************************************************/ + +/* + +| *property* | *Used for type* | +| period | QEasingCurve::{In,Out,InOut,OutIn}Elastic | +| amplitude | QEasingCurve::{In,Out,InOut,OutIn}Bounce, QEasingCurve::{In,Out,InOut,OutIn}Elastic | +| overshoot | QEasingCurve::{In,Out,InOut,OutIn}Back | + +*/ + + + + +/*! + \class QEasingCurve + \since 4.6 + \ingroup animation + \brief The QEasingCurve class provides easing curves for controlling animation. + + Easing curves describe a function that controls how the speed of the interpolation + between 0 and 1 should be. Easing curves allow transitions from + one value to another to appear more natural than a simple constant speed would allow. + The QEasingCurve class is usually used in conjunction with the QAnimation class, + but can be used on its own. + + To calculate the speed of the interpolation, the easing curve provides the function + valueForProgress(), where the \a progress argument specifies the progress of the + interpolation: 0 is the start value of the interpolation, 1 is the end value of the + interpolation. The returned value is the effective progress of the interpolation. + If the returned value is the same as the input value for all input values the easing + curve is a linear curve. This is the default behaviour. + + For example, + \code + QEasingCurve easing(QEasingCurve::InOutQuad); + + for(qreal t = 0.0; t < 1.0; t+=0.1) + qWarning() << "Effective progress" << t << " is + << easing.valueForProgress(t); + \endcode + will print the effective progress of the interpolation between 0 and 1. + + When using a QAnimation, the easing curve will be used to control the + progress of the interpolation between startValue and endValue: + \code + QAnimation animation; + animation.setStartValue(0); + animation.setEndValue(1000); + animation.setDuration(1000); + animation.setEasingCurve(QEasingCurve::InOutQuad); + \endcode + */ + +/*! + \enum QEasingCurve::Type + + The type of easing curve. + + \value Linear \inlineimage qeasingcurve-linear.png + \br + Easing equation function for a simple linear tweening, + with no easing. + \value InQuad \inlineimage qeasingcurve-inquad.png + \br + Easing equation function for a quadratic (t^2) easing + in: accelerating from zero velocity. + \value OutQuad \inlineimage qeasingcurve-outquad.png + \br + Easing equation function for a quadratic (t^2) easing + out: decelerating to zero velocity. + \value InOutQuad \inlineimage qeasingcurve-inoutquad.png + \br + Easing equation function for a quadratic (t^2) easing + in/out: acceleration until halfway, then deceleration. + \value OutInQuad \inlineimage qeasingcurve-outinquad.png + \br + Easing equation function for a quadratic (t^2) easing + out/in: deceleration until halfway, then acceleration. + \value InCubic \inlineimage qeasingcurve-incubic.png + \br + Easing equation function for a cubic (t^3) easing + in: accelerating from zero velocity. + \value OutCubic \inlineimage qeasingcurve-outcubic.png + \br + Easing equation function for a cubic (t^3) easing + out: decelerating from zero velocity. + \value InOutCubic \inlineimage qeasingcurve-inoutcubic.png + \br + Easing equation function for a cubic (t^3) easing + in/out: acceleration until halfway, then deceleration. + \value OutInCubic \inlineimage qeasingcurve-outincubic.png + \br + Easing equation function for a cubic (t^3) easing + out/in: deceleration until halfway, then acceleration. + \value InQuart \inlineimage qeasingcurve-inquart.png + \br + Easing equation function for a quartic (t^4) easing + in: accelerating from zero velocity. + \value OutQuart \inlineimage qeasingcurve-outquart.png + \br + Easing equation function for a quartic (t^4) easing + out: decelerating from zero velocity. + \value InOutQuart \inlineimage qeasingcurve-inoutquart.png + \br + Easing equation function for a quartic (t^4) easing + in/out: acceleration until halfway, then deceleration. + \value OutInQuart \inlineimage qeasingcurve-outinquart.png + \br + Easing equation function for a quartic (t^4) easing + out/in: deceleration until halfway, then acceleration. + \value InQuint \inlineimage qeasingcurve-inquint.png + \br + Easing equation function for a quintic (t^5) easing + in: accelerating from zero velocity. + \value OutQuint \inlineimage qeasingcurve-outquint.png + \br + Easing equation function for a quintic (t^5) easing + out: decelerating from zero velocity. + \value InOutQuint \inlineimage qeasingcurve-inoutquint.png + \br + Easing equation function for a quintic (t^5) easing + in/out: acceleration until halfway, then deceleration. + \value OutInQuint \inlineimage qeasingcurve-outinquint.png + \br + Easing equation function for a quintic (t^5) easing + out/in: deceleration until halfway, then acceleration. + \value InSine \inlineimage qeasingcurve-insine.png + \br + Easing equation function for a sinusoidal (sin(t)) easing + in: accelerating from zero velocity. + \value OutSine \inlineimage qeasingcurve-outsine.png + \br + Easing equation function for a sinusoidal (sin(t)) easing + out: decelerating from zero velocity. + \value InOutSine \inlineimage qeasingcurve-inoutsine.png + \br + Easing equation function for a sinusoidal (sin(t)) easing + in/out: acceleration until halfway, then deceleration. + \value OutInSine \inlineimage qeasingcurve-outinsine.png + \br + Easing equation function for a sinusoidal (sin(t)) easing + out/in: deceleration until halfway, then acceleration. + \value InExpo \inlineimage qeasingcurve-inexpo.png + \br + Easing equation function for an exponential (2^t) easing + in: accelerating from zero velocity. + \value OutExpo \inlineimage qeasingcurve-outexpo.png + \br + Easing equation function for an exponential (2^t) easing + out: decelerating from zero velocity. + \value InOutExpo \inlineimage qeasingcurve-inoutexpo.png + \br + Easing equation function for an exponential (2^t) easing + in/out: acceleration until halfway, then deceleration. + \value OutInExpo \inlineimage qeasingcurve-outinexpo.png + \br + Easing equation function for an exponential (2^t) easing + out/in: deceleration until halfway, then acceleration. + \value InCirc \inlineimage qeasingcurve-incirc.png + \br + Easing equation function for a circular (sqrt(1-t^2)) easing + in: accelerating from zero velocity. + \value OutCirc \inlineimage qeasingcurve-outcirc.png + \br + Easing equation function for a circular (sqrt(1-t^2)) easing + out: decelerating from zero velocity. + \value InOutCirc \inlineimage qeasingcurve-inoutcirc.png + \br + Easing equation function for a circular (sqrt(1-t^2)) easing + in/out: acceleration until halfway, then deceleration. + \value OutInCirc \inlineimage qeasingcurve-outincirc.png + \br + Easing equation function for a circular (sqrt(1-t^2)) easing + out/in: deceleration until halfway, then acceleration. + \value InElastic \inlineimage qeasingcurve-inelastic.png + \br + Easing equation function for an elastic + (exponentially decaying sine wave) easing in: + accelerating from zero velocity. The peak amplitude + can be set with the \e amplitude parameter, and the + period of decay by the \e period parameter. + \value OutElastic \inlineimage qeasingcurve-outelastic.png + \br + Easing equation function for an elastic + (exponentially decaying sine wave) easing out: + decelerating from zero velocity. The peak amplitude + can be set with the \e amplitude parameter, and the + period of decay by the \e period parameter. + \value InOutElastic \inlineimage qeasingcurve-inoutelastic.png + \br + Easing equation function for an elastic + (exponentially decaying sine wave) easing in/out: + acceleration until halfway, then deceleration. + \value OutInElastic \inlineimage qeasingcurve-outinelastic.png + \br + Easing equation function for an elastic + (exponentially decaying sine wave) easing out/in: + deceleration until halfway, then acceleration. + \value InBack \inlineimage qeasingcurve-inback.png + \br + Easing equation function for a back (overshooting + cubic easing: (s+1)*t^3 - s*t^2) easing in: + accelerating from zero velocity. + \value OutBack \inlineimage qeasingcurve-outback.png + \br + Easing equation function for a back (overshooting + cubic easing: (s+1)*t^3 - s*t^2) easing out: + decelerating from zero velocity. + \value InOutBack \inlineimage qeasingcurve-inoutback.png + \br + Easing equation function for a back (overshooting + cubic easing: (s+1)*t^3 - s*t^2) easing in/out: + acceleration until halfway, then deceleration. + \value OutInBack \inlineimage qeasingcurve-outinback.png + \br + Easing equation function for a back (overshooting + cubic easing: (s+1)*t^3 - s*t^2) easing out/in: + deceleration until halfway, then acceleration. + \value InBounce \inlineimage qeasingcurve-inbounce.png + \br + Easing equation function for a bounce (exponentially + decaying parabolic bounce) easing in: accelerating + from zero velocity. + \value OutBounce \inlineimage qeasingcurve-outbounce.png + \br + Easing equation function for a bounce (exponentially + decaying parabolic bounce) easing out: decelerating + from zero velocity. + \value InOutBounce \inlineimage qeasingcurve-inoutbounce.png + \br + Easing equation function for a bounce (exponentially + decaying parabolic bounce) easing in/out: + acceleration until halfway, then deceleration. + \value OutInBounce \inlineimage qeasingcurve-outinbounce.png + \br + Easing equation function for a bounce (exponentially + decaying parabolic bounce) easing out/in: + deceleration until halfway, then acceleration. + \omitvalue InCurve + \omitvalue OutCurve + \omitvalue SineCurve + \omitvalue CosineCurve + \value Custom This is returned if the user have specified a custom curve type with setCustomType(). Note that you cannot call setType() with this value, but type() can return it. + \omitvalue NCurveTypes +*/ + +/*! + \typedef QEasingCurve::EasingFunction + + This is a typedef for a pointer to a function with the following + signature: + + \snippet doc/src/snippets/code/src_corelib_tools_qeasingcurve.cpp 0 +*/ + +#include "qeasingcurve.h" + +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> +#include <QtCore/QString> +#endif + +QT_BEGIN_NAMESPACE + +static bool isConfigFunction(QEasingCurve::Type type) +{ + return type >= QEasingCurve::InElastic + && type <= QEasingCurve::OutInBounce; +} + +class QEasingCurveFunction +{ +public: + enum Type { In, Out, InOut, OutIn }; + + QEasingCurveFunction(QEasingCurveFunction::Type type = In, qreal period = 0.3, qreal amplitude = 1.0, + qreal overshoot = 1.70158f) + : _t(type), _p(period), _a(amplitude), _o(overshoot) + { } + virtual ~QEasingCurveFunction() {} + virtual qreal value(qreal t); + virtual QEasingCurveFunction *copy() const; + bool operator==(const QEasingCurveFunction& other); + + Type _t; + qreal _p; + qreal _a; + qreal _o; +}; + +qreal QEasingCurveFunction::value(qreal t) +{ + return t; +} + +QEasingCurveFunction *QEasingCurveFunction::copy() const +{ + return new QEasingCurveFunction(_t, _p, _a, _o); +} + +bool QEasingCurveFunction::operator==(const QEasingCurveFunction& other) +{ + return _t == other._t && + _p == other._p && + _a == other._a && + _o == other._o; +} + +QT_BEGIN_INCLUDE_NAMESPACE +#include "../../3rdparty/easing/easing.cpp" +QT_END_INCLUDE_NAMESPACE + +class QEasingCurvePrivate +{ +public: + QEasingCurvePrivate() + : type(QEasingCurve::Linear), + config(0), + func(&easeNone) + { } + ~QEasingCurvePrivate() { delete config; } + void setType_helper(QEasingCurve::Type); + + QEasingCurve::Type type; + QEasingCurveFunction *config; + QEasingCurve::EasingFunction func; +}; + +struct ElasticEase : public QEasingCurveFunction +{ + ElasticEase(Type type) + : QEasingCurveFunction(type, qreal(0.3), qreal(1.0)) + { } + + QEasingCurveFunction *copy() const + { + ElasticEase *rv = new ElasticEase(_t); + rv->_p = _p; + rv->_a = _a; + return rv; + } + + qreal value(qreal t) + { + qreal p = (_p < 0) ? 0.3f : _p; + qreal a = (_a < 0) ? 1.0f : _a; + switch(_t) { + case In: + return easeInElastic(t, a, p); + case Out: + return easeOutElastic(t, a, p); + case InOut: + return easeInOutElastic(t, a, p); + case OutIn: + return easeOutInElastic(t, a, p); + default: + return t; + } + } +}; + +struct BounceEase : public QEasingCurveFunction +{ + BounceEase(Type type) + : QEasingCurveFunction(type, 0.3f, 1.0f) + { } + + QEasingCurveFunction *copy() const + { + BounceEase *rv = new BounceEase(_t); + rv->_a = _a; + return rv; + } + + qreal value(qreal t) + { + qreal a = (_a < 0) ? 1.0f : _a; + switch(_t) { + case In: + return easeInBounce(t, a); + case Out: + return easeOutBounce(t, a); + case InOut: + return easeInOutBounce(t, a); + case OutIn: + return easeOutInBounce(t, a); + default: + return t; + } + } +}; + +struct BackEase : public QEasingCurveFunction +{ + BackEase(Type type) + : QEasingCurveFunction(type, 0.3f, 1.0f, 1.70158f) + { } + + QEasingCurveFunction *copy() const + { + BackEase *rv = new BackEase(_t); + rv->_o = _o; + return rv; + } + + qreal value(qreal t) + { + qreal o = (_o < 0) ? 1.70158f : _o; + switch(_t) { + case In: + return easeInBack(t, o); + case Out: + return easeOutBack(t, o); + case InOut: + return easeInOutBack(t, o); + case OutIn: + return easeOutInBack(t, o); + default: + return t; + } + } +}; + +static QEasingCurve::EasingFunction curveToFunc(QEasingCurve::Type curve) +{ + switch(curve) { + case QEasingCurve::Linear: + return &easeNone; + case QEasingCurve::InQuad: + return &easeInQuad; + case QEasingCurve::OutQuad: + return &easeOutQuad; + case QEasingCurve::InOutQuad: + return &easeInOutQuad; + case QEasingCurve::OutInQuad: + return &easeOutInQuad; + case QEasingCurve::InCubic: + return &easeInCubic; + case QEasingCurve::OutCubic: + return &easeOutCubic; + case QEasingCurve::InOutCubic: + return &easeInOutCubic; + case QEasingCurve::OutInCubic: + return &easeOutInCubic; + case QEasingCurve::InQuart: + return &easeInQuart; + case QEasingCurve::OutQuart: + return &easeOutQuart; + case QEasingCurve::InOutQuart: + return &easeInOutQuart; + case QEasingCurve::OutInQuart: + return &easeOutInQuart; + case QEasingCurve::InQuint: + return &easeInQuint; + case QEasingCurve::OutQuint: + return &easeOutQuint; + case QEasingCurve::InOutQuint: + return &easeInOutQuint; + case QEasingCurve::OutInQuint: + return &easeOutInQuint; + case QEasingCurve::InSine: + return &easeInSine; + case QEasingCurve::OutSine: + return &easeOutSine; + case QEasingCurve::InOutSine: + return &easeInOutSine; + case QEasingCurve::OutInSine: + return &easeOutInSine; + case QEasingCurve::InExpo: + return &easeInExpo; + case QEasingCurve::OutExpo: + return &easeOutExpo; + case QEasingCurve::InOutExpo: + return &easeInOutExpo; + case QEasingCurve::OutInExpo: + return &easeOutInExpo; + case QEasingCurve::InCirc: + return &easeInCirc; + case QEasingCurve::OutCirc: + return &easeOutCirc; + case QEasingCurve::InOutCirc: + return &easeInOutCirc; + case QEasingCurve::OutInCirc: + return &easeOutInCirc; + // Internal for, compatibility with QTimeLine only ?? + case QEasingCurve::InCurve: + return &easeInCurve; + case QEasingCurve::OutCurve: + return &easeOutCurve; + case QEasingCurve::SineCurve: + return &easeSineCurve; + case QEasingCurve::CosineCurve: + return &easeCosineCurve; + default: + return 0; + }; +} + +static QEasingCurveFunction *curveToFunctionObject(QEasingCurve::Type type) +{ + QEasingCurveFunction *curveFunc = 0; + switch(type) { + case QEasingCurve::InElastic: + curveFunc = new ElasticEase(ElasticEase::In); + break; + case QEasingCurve::OutElastic: + curveFunc = new ElasticEase(ElasticEase::Out); + break; + case QEasingCurve::InOutElastic: + curveFunc = new ElasticEase(ElasticEase::InOut); + break; + case QEasingCurve::OutInElastic: + curveFunc = new ElasticEase(ElasticEase::OutIn); + break; + case QEasingCurve::OutBounce: + curveFunc = new BounceEase(BounceEase::Out); + break; + case QEasingCurve::InBounce: + curveFunc = new BounceEase(BounceEase::In); + break; + case QEasingCurve::OutInBounce: + curveFunc = new BounceEase(BounceEase::OutIn); + break; + case QEasingCurve::InOutBounce: + curveFunc = new BounceEase(BounceEase::InOut); + break; + case QEasingCurve::InBack: + curveFunc = new BackEase(BackEase::In); + break; + case QEasingCurve::OutBack: + curveFunc = new BackEase(BackEase::Out); + break; + case QEasingCurve::InOutBack: + curveFunc = new BackEase(BackEase::InOut); + break; + case QEasingCurve::OutInBack: + curveFunc = new BackEase(BackEase::OutIn); + break; + default: + curveFunc = new QEasingCurveFunction(QEasingCurveFunction::In, 0.3f, 1.0f, 1.70158f); // ### + } + + return curveFunc; +} + +/*! + Constructs an easing curve of the given \a type. + */ +QEasingCurve::QEasingCurve(Type type) + : d_ptr(new QEasingCurvePrivate) +{ + setType(type); +} + +/*! + Construct a copy of \a other. + */ +QEasingCurve::QEasingCurve(const QEasingCurve &other) +: d_ptr(new QEasingCurvePrivate) +{ + // ### non-atomic, requires malloc on shallow copy + *d_ptr = *other.d_ptr; + if(other.d_ptr->config) + d_ptr->config = other.d_ptr->config->copy(); +} + +/*! + Destructor. + */ + +QEasingCurve::~QEasingCurve() +{ + delete d_ptr; +} + +/*! + Copy \a other. + */ +QEasingCurve &QEasingCurve::operator=(const QEasingCurve &other) +{ + // ### non-atomic, requires malloc on shallow copy + if (d_ptr->config) { + delete d_ptr->config; + d_ptr->config = 0; + } + + *d_ptr = *other.d_ptr; + if(other.d_ptr->config) + d_ptr->config = other.d_ptr->config->copy(); + + return *this; +} + +/*! + Compare this easing curve with \a other and returns true if they are + equal. It will also compare the properties of a curve. + */ +bool QEasingCurve::operator==(const QEasingCurve &other) const +{ + bool res = d_ptr->func == other.d_ptr->func + && d_ptr->type == other.d_ptr->type; + if (res && d_ptr->config && other.d_ptr->config) { + // catch the config content + res = d_ptr->config->operator==(*(other.d_ptr->config)); + } + return res; +} + +/*! + \fn bool QEasingCurve::operator!=(const QEasingCurve &other) const + Compare this easing curve with \a other and returns true if they are not equal. + It will also compare the properties of a curve. + + \sa operator==() +*/ + +/*! + Returns the amplitude. This is not applicable for all curve types. + It is only applicable for bounce and elastic curves (curves of type() + QEasingCurve::InBounce, QEasingCurve::OutBounce, QEasingCurve::InOutBounce, + QEasingCurve::OutInBounce, QEasingCurve::InElastic, QEasingCurve::OutElastic, + QEasingCurve::InOutElastic or QEasingCurve::OutInElastic). + */ +qreal QEasingCurve::amplitude() const +{ + return d_ptr->config ? d_ptr->config->_a : 1.0; +} + +/*! + Sets the amplitude to \a amplitude. + + This will set the amplitude of the bounce or the amplitude of the + elastic "spring" effect. The higher the number, the higher the amplitude. + \sa amplitude() +*/ +void QEasingCurve::setAmplitude(qreal amplitude) +{ + if (!d_ptr->config) + d_ptr->config = curveToFunctionObject(d_ptr->type); + d_ptr->config->_a = amplitude; +} + +/*! + Returns the period. This is not applicable for all curve types. + It is only applicable if type() is QEasingCurve::InElastic, QEasingCurve::OutElastic, + QEasingCurve::InOutElastic or QEasingCurve::OutInElastic. + */ +qreal QEasingCurve::period() const +{ + return d_ptr->config ? d_ptr->config->_p : 0.3; +} + +/*! + Sets the period to \a period. + Setting a small period value will give a high frequency of the curve. A + large period will give it a small frequency. + + \sa period() +*/ +void QEasingCurve::setPeriod(qreal period) +{ + if (!d_ptr->config) + d_ptr->config = curveToFunctionObject(d_ptr->type); + d_ptr->config->_p = period; +} + +/*! + Returns the overshoot. This is not applicable for all curve types. + It is only applicable if type() is QEasingCurve::InBack, QEasingCurve::OutBack, + QEasingCurve::InOutBack or QEasingCurve::OutInBack. + */ +qreal QEasingCurve::overshoot() const +{ + return d_ptr->config ? d_ptr->config->_o : 1.70158f; +} + +/*! + Sets the overshoot to \a overshoot. + + 0 produces no overshoot, and the default value of 1.70158 produces an overshoot of 10 percent. + + \sa overshoot() +*/ +void QEasingCurve::setOvershoot(qreal overshoot) +{ + if (!d_ptr->config) + d_ptr->config = curveToFunctionObject(d_ptr->type); + d_ptr->config->_o = overshoot; +} + +/*! + Returns the type of the easing curve. +*/ +QEasingCurve::Type QEasingCurve::type() const +{ + return d_ptr->type; +} + +void QEasingCurvePrivate::setType_helper(QEasingCurve::Type newType) +{ + qreal amp = -1.0; + qreal period = -1.0; + qreal overshoot = -1.0; + + if (config) { + amp = config->_a; + period = config->_p; + overshoot = config->_o; + delete config; + config = 0; + } + + if (isConfigFunction(newType) || (amp != -1.0) || (period != -1.0) || (overshoot != -1.0)) { + config = curveToFunctionObject(newType); + if (amp != -1.0) + config->_a = amp; + if (period != -1.0) + config->_p = period; + if (overshoot != -1.0) + config->_o = overshoot; + func = 0; + } else if (newType != QEasingCurve::Custom) { + func = curveToFunc(newType); + } + Q_ASSERT((func == 0) == (config != 0)); + type = newType; +} + +/*! + Sets the type of the easing curve to \a type. +*/ +void QEasingCurve::setType(Type type) +{ + if (d_ptr->type == type) + return; + if (type < Linear || type >= NCurveTypes - 1) { + qWarning("QEasingCurve: Invalid curve type %d", type); + return; + } + + d_ptr->setType_helper(type); +} + +/*! + Sets a custom easing curve that is defined by the user in the function \a func. + The signature of the function is qreal myEasingFunction(qreal progress), + where \e progress and the return value is considered to be normalized between 0 and 1. + (In some cases the return value can be outside that range) + After calling this function type() will return QEasingCurve::Custom. + \a func cannot be zero. + + \sa customType() + \sa valueForProgress() +*/ +void QEasingCurve::setCustomType(EasingFunction func) +{ + if (!func) { + qWarning("Function pointer must not be null"); + return; + } + d_ptr->func = func; + d_ptr->setType_helper(Custom); +} + +/*! + Returns the function pointer to the custom easing curve. + If type() does not return QEasingCurve::Custom, this function + will return 0. +*/ +QEasingCurve::EasingFunction QEasingCurve::customType() const +{ + return d_ptr->type == Custom ? d_ptr->func : 0; +} + +/*! + Return the effective progress for the easing curve at \a progress. + While \a progress must be between 0 and 1, the returned effective progress + can be outside those bounds. For instance, QEasingCurve::InBack will + return negative values in the beginning of the function. + */ +qreal QEasingCurve::valueForProgress(qreal progress) const +{ + progress = qBound<qreal>(0, progress, 1); + if (d_ptr->func) + return d_ptr->func(progress); + else if (d_ptr->config) + return d_ptr->config->value(progress); + else + return progress; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QEasingCurve &item) +{ + debug << "type:" << item.d_ptr->type + << "func:" << item.d_ptr->func; + if (item.d_ptr->config) { + debug << QString::fromAscii("period:%1").arg(item.d_ptr->config->_p, 0, 'f', 20) + << QString::fromAscii("amp:%1").arg(item.d_ptr->config->_a, 0, 'f', 20) + << QString::fromAscii("overshoot:%1").arg(item.d_ptr->config->_o, 0, 'f', 20); + } + return debug; +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qeasingcurve.h b/src/corelib/tools/qeasingcurve.h new file mode 100644 index 0000000..a240bc0 --- /dev/null +++ b/src/corelib/tools/qeasingcurve.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QEASINGCURVE_H +#define QEASINGCURVE_H + +#include <QtCore/qglobal.h> +#include <QtCore/qobjectdefs.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEasingCurvePrivate; +class Q_CORE_EXPORT QEasingCurve +{ + Q_GADGET + Q_ENUMS(Type) +public: + enum Type { + Linear, + InQuad, OutQuad, InOutQuad, OutInQuad, + InCubic, OutCubic, InOutCubic, OutInCubic, + InQuart, OutQuart, InOutQuart, OutInQuart, + InQuint, OutQuint, InOutQuint, OutInQuint, + InSine, OutSine, InOutSine, OutInSine, + InExpo, OutExpo, InOutExpo, OutInExpo, + InCirc, OutCirc, InOutCirc, OutInCirc, + InElastic, OutElastic, InOutElastic, OutInElastic, + InBack, OutBack, InOutBack, OutInBack, + InBounce, OutBounce, InOutBounce, OutInBounce, + InCurve, OutCurve, SineCurve, CosineCurve, + Custom, NCurveTypes + }; + + QEasingCurve(Type type = Linear); + QEasingCurve(const QEasingCurve &other); + ~QEasingCurve(); + + QEasingCurve &operator=(const QEasingCurve &other); + bool operator==(const QEasingCurve &other) const; + inline bool operator!=(const QEasingCurve &other) const + { return !(this->operator==(other)); } + + qreal amplitude() const; + void setAmplitude(qreal amplitude); + + qreal period() const; + void setPeriod(qreal period); + + qreal overshoot() const; + void setOvershoot(qreal overshoot); + + Type type() const; + void setType(Type type); + typedef qreal (*EasingFunction)(qreal progress); + void setCustomType(EasingFunction func); + EasingFunction customType() const; + + qreal valueForProgress(qreal progress) const; +private: + QEasingCurvePrivate *d_ptr; + friend Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QEasingCurve &item); +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QEasingCurve &item); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/tools/qline.cpp b/src/corelib/tools/qline.cpp index f615dcd..88825a3 100644 --- a/src/corelib/tools/qline.cpp +++ b/src/corelib/tools/qline.cpp @@ -260,7 +260,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QLine &p) { - d << "QLine(" << p.p1() << "," << p.p2() << ")"; + d << "QLine(" << p.p1() << ',' << p.p2() << ')'; return d; } #endif @@ -822,7 +822,7 @@ qreal QLineF::angle(const QLineF &l) const #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QLineF &p) { - d << "QLineF(" << p.p1() << "," << p.p2() << ")"; + d << "QLineF(" << p.p1() << ',' << p.p2() << ')'; return d; } #endif diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index ab26945..248137a 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -120,7 +120,7 @@ static char *_qdtoa( NEEDS_VOLATILE double d, int mode, int ndigits, int *decpt, Q_CORE_EXPORT char *qdtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve, char **digits_str); Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); -Q_CORE_EXPORT qlonglong qstrtoll(const char *nptr, const char **endptr, register int base, bool *ok); +static qlonglong qstrtoll(const char *nptr, const char **endptr, register int base, bool *ok); static qulonglong qstrtoull(const char *nptr, const char **endptr, register int base, bool *ok); /****************************************************************************** @@ -1136,7 +1136,7 @@ static QString macToQtFormat(const QString &sys_fmt) break; case 'S': // fractional second if (repeat < 3) - result += QLatin1String("z"); + result += QLatin1Char('z'); else result += QLatin1String("zzz"); break; @@ -1150,7 +1150,7 @@ static QString macToQtFormat(const QString &sys_fmt) if (repeat >= 2) result += QLatin1String("dd"); else - result += QLatin1String("d"); + result += QLatin1Char('d'); break; case 'a': result += QLatin1String("AP"); @@ -4671,7 +4671,7 @@ static qulonglong qstrtoull(const char *nptr, const char **endptr, register int * Ignores `locale' stuff. Assumes that the upper and lower case * alphabets and digits are each contiguous. */ -Q_CORE_EXPORT qlonglong qstrtoll(const char *nptr, const char **endptr, register int base, bool *ok) +static qlonglong qstrtoll(const char *nptr, const char **endptr, register int base, bool *ok) { register const char *s; register qulonglong acc; @@ -5367,6 +5367,14 @@ static Bigint *mult(Bigint *a, Bigint *b) static Bigint *p5s; +struct p5s_deleter +{ + ~p5s_deleter() + { + Bfree(p5s); + } +}; + static Bigint *pow5mult(Bigint *b, int k) { Bigint *b1, *p5, *p51; @@ -5388,6 +5396,7 @@ static Bigint *pow5mult(Bigint *b, int k) return b; if (!(p5 = p5s)) { /* first time */ + static p5s_deleter deleter; p5 = p5s = i2b(625); p5->next = 0; } diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index 5f13e5a..32c8d17 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -990,7 +990,7 @@ Q_INLINE_TEMPLATE int QMultiMap<Key, T>::remove(const Key &key, const T &value) { int n = 0; typename QMap<Key, T>::iterator i(find(key)); - typename QMap<Key, T>::const_iterator end(QMap<Key, T>::constEnd()); + typename QMap<Key, T>::iterator end(QMap<Key, T>::end()); while (i != end && !qMapLessThanKey<Key>(key, i.key())) { if (i.value() == value) { i = erase(i); diff --git a/src/corelib/tools/qpoint.cpp b/src/corelib/tools/qpoint.cpp index feea473..90b44b1 100644 --- a/src/corelib/tools/qpoint.cpp +++ b/src/corelib/tools/qpoint.cpp @@ -373,7 +373,7 @@ QDebug operator<<(QDebug dbg, const QPoint &p) { QDebug operator<<(QDebug d, const QPointF &p) { - d.nospace() << "QPointF(" << p.x() << ", " << p.y() << ")"; + d.nospace() << "QPointF(" << p.x() << ", " << p.y() << ')'; return d; } #endif @@ -442,6 +442,19 @@ QDebug operator<<(QDebug d, const QPointF &p) otherwise returns false. */ + +/*! + Returns the sum of the absolute values of x() and y(), + traditionally known as the "Manhattan length" of the vector from + the origin to the point. + + \sa QPoint::manhattanLength() +*/ +qreal QPointF::manhattanLength() const +{ + return qAbs(x())+qAbs(y()); +} + /*! \fn qreal QPointF::x() const diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h index 1dab7e2..e716bf7 100644 --- a/src/corelib/tools/qpoint.h +++ b/src/corelib/tools/qpoint.h @@ -192,6 +192,8 @@ public: QPointF(const QPoint &p); QPointF(qreal xpos, qreal ypos); + qreal manhattanLength() const; + bool isNull() const; qreal x() const; diff --git a/src/corelib/tools/qringbuffer_p.h b/src/corelib/tools/qringbuffer_p.h index 3a0901d..02cc497 100644 --- a/src/corelib/tools/qringbuffer_p.h +++ b/src/corelib/tools/qringbuffer_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -class Q_CORE_EXPORT QRingBuffer +class QRingBuffer { public: inline QRingBuffer(int growth = 4096) : basicBlockSize(growth) { @@ -74,6 +74,52 @@ public: return buffers.isEmpty() ? 0 : (buffers.first().constData() + head); } + // access the bytes at a specified position + // the out-variable length will contain the amount of bytes readable + // from there, e.g. the amount still the same QByteArray + inline const char *readPointerAtPosition(qint64 pos, qint64 &length) const { + if (buffers.isEmpty()) { + length = 0; + return 0; + } + + if (pos >= bufferSize) { + length = 0; + return 0; + } + + // special case: it is in the first buffer + int nextDataBlockSizeValue = nextDataBlockSize(); + if (pos - head < nextDataBlockSizeValue) { + length = nextDataBlockSizeValue - pos; + return buffers.at(0).constData() + head + pos; + } + + // special case: we only had one buffer and tried to read over it + if (buffers.length() == 1) { + length = 0; + return 0; + } + + // skip the first + pos -= nextDataBlockSizeValue; + + // normal case: it is somewhere in the second to the-one-before-the-tailBuffer + for (int i = 1; i < tailBuffer; i++) { + if (pos >= buffers[i].size()) { + pos -= buffers[i].size(); + continue; + } + + length = buffers[i].length() - pos; + return buffers[i].constData() + pos; + } + + // it is in the tail buffer + length = tail - pos; + return buffers[tailBuffer].constData() + pos; + } + inline void free(int bytes) { bufferSize -= bytes; if (bufferSize < 0) 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/tools/qsize.h b/src/corelib/tools/qsize.h index 57c55ca..34a6c99 100644 --- a/src/corelib/tools/qsize.h +++ b/src/corelib/tools/qsize.h @@ -171,14 +171,14 @@ inline const QSize operator*(qreal c, const QSize &s) inline QSize &QSize::operator/=(qreal c) { - Q_ASSERT(!qFuzzyCompare(c + 1, 1)); + Q_ASSERT(!qFuzzyIsNull(c)); wd = qRound(wd/c); ht = qRound(ht/c); return *this; } inline const QSize operator/(const QSize &s, qreal c) { - Q_ASSERT(!qFuzzyCompare(c + 1, 1)); + Q_ASSERT(!qFuzzyIsNull(c)); return QSize(qRound(s.wd/c), qRound(s.ht/c)); } @@ -327,14 +327,14 @@ inline const QSizeF operator*(qreal c, const QSizeF &s) inline QSizeF &QSizeF::operator/=(qreal c) { - Q_ASSERT(!qFuzzyCompare(c + 1, 1)); + Q_ASSERT(!qFuzzyIsNull(c)); wd = wd/c; ht = ht/c; return *this; } inline const QSizeF operator/(const QSizeF &s, qreal c) { - Q_ASSERT(!qFuzzyCompare(c + 1, 1)); + Q_ASSERT(!qFuzzyIsNull(c)); return QSizeF(s.wd/c, s.ht/c); } diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index c3649e3..29509c5 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -195,6 +195,68 @@ static int ucstrnicmp(const ushort *a, const ushort *b, int l) return ucstricmp(a, a + l, b, b + l); } +static bool qMemEquals(const quint16 *a, const quint16 *b, int length) +{ + // Benchmarking indicates that doing memcmp is much slower than + // executing the comparison ourselves. + // To make it even faster, we do a 32-bit comparison, comparing + // twice the amount of data as a normal word-by-word comparison. + // + // Benchmarking results on a 2.33 GHz Core2 Duo, with a 64-QChar + // block of data, with 4194304 iterations (per iteration): + // operation usec cpu ticks + // memcmp 330 710 + // 16-bit 79 167-171 + // 32-bit aligned 49 105-109 + // + // Testing also indicates that unaligned 32-bit loads are as + // performant as 32-bit aligned. + if (a == b || !length) + return true; + + register union { + const quint16 *w; + const quint32 *d; + quintptr value; + } sa, sb; + sa.w = a; + sb.w = b; + + // check alignment + if ((sa.value & 2) == (sb.value & 2)) { + // both addresses have the same alignment + if (sa.value & 2) { + // both addresses are not aligned to 4-bytes boundaries + // compare the first character + if (*sa.w != *sb.w) + return false; + --length; + ++sa.w; + ++sb.w; + + // now both addresses are 4-bytes aligned + } + + // both addresses are 4-bytes aligned + // do a fast 32-bit comparison + register const quint32 *e = sa.d + (length >> 1); + for ( ; sa.d != e; ++sa.d, ++sb.d) { + if (*sa.d != *sb.d) + return false; + } + + // do we have a tail? + return (length & 1) ? *sa.w == *sb.w : true; + } else { + // one of the addresses isn't 4-byte aligned but the other is + register const quint16 *e = sa.w + length; + for ( ; sa.w != e; ++sa.w, ++sb.w) { + if (*sa.w != *sb.w) + return false; + } + } + return true; +} /*! \internal @@ -1908,8 +1970,10 @@ QString &QString::replace(QChar c, const QLatin1String &after, Qt::CaseSensitivi */ bool QString::operator==(const QString &other) const { - return (size() == other.size()) && - (memcmp((char*)unicode(),(char*)other.unicode(), size()*sizeof(QChar))==0); + if (d->size != other.d->size) + return false; + + return qMemEquals(d->data, other.d->data, d->size); } /*! @@ -3136,7 +3200,7 @@ bool QString::startsWith(const QString& s, Qt::CaseSensitivity cs) const if (s.d->size > d->size) return false; if (cs == Qt::CaseSensitive) { - return memcmp((char*)d->data, (char*)s.d->data, s.d->size*sizeof(QChar)) == 0; + return qMemEquals(d->data, s.d->data, s.d->size); } else { uint last = 0; uint olast = 0; @@ -3207,7 +3271,7 @@ bool QString::endsWith(const QString& s, Qt::CaseSensitivity cs) const if (pos < 0) return false; if (cs == Qt::CaseSensitive) { - return memcmp((char*)&d->data[pos], (char*)s.d->data, s.d->size*sizeof(QChar)) == 0; + return qMemEquals(d->data + pos, s.d->data, s.d->size); } else { uint last = 0; uint olast = 0; @@ -7692,7 +7756,8 @@ QString QStringRef::toString() const { */ bool operator==(const QStringRef &s1,const QStringRef &s2) { return (s1.size() == s2.size() && - (memcmp((char*)s1.unicode(), (char*)s2.unicode(), s1.size()*sizeof(QChar))==0)); } + qMemEquals((const ushort *)s1.unicode(), (const ushort *)s2.unicode(), s1.size())); +} /*! \relates QStringRef @@ -7701,7 +7766,8 @@ bool operator==(const QStringRef &s1,const QStringRef &s2) */ bool operator==(const QString &s1,const QStringRef &s2) { return (s1.size() == s2.size() && - (memcmp((char*)s1.unicode(), (char*)s2.unicode(), s1.size()*sizeof(QChar))==0)); } + qMemEquals((const ushort *)s1.unicode(), (const ushort *)s2.unicode(), s1.size())); +} /*! \relates QStringRef diff --git a/src/corelib/tools/qtimeline.cpp b/src/corelib/tools/qtimeline.cpp index 3a03558..ee4f73c 100644 --- a/src/corelib/tools/qtimeline.cpp +++ b/src/corelib/tools/qtimeline.cpp @@ -48,20 +48,6 @@ QT_BEGIN_NAMESPACE -static const qreal pi = qreal(3.14159265359); -static const qreal halfPi = pi / qreal(2.0); - - -static inline qreal qt_sinProgress(qreal value) -{ - return qSin((value * pi) - halfPi) / 2 + qreal(0.5); -} - -static inline qreal qt_smoothBeginEndMixFactor(qreal value) -{ - return qMin(qMax(1 - value * 2 + qreal(0.3), qreal(0.0)), qreal(1.0)); -} - class QTimeLinePrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QTimeLine) @@ -70,7 +56,7 @@ public: : startTime(0), duration(1000), startFrame(0), endFrame(0), updateInterval(1000 / 25), totalLoopCount(1), currentLoopCount(0), currentTime(0), timerId(0), - direction(QTimeLine::Forward), curveShape(QTimeLine::EaseInOutCurve), + direction(QTimeLine::Forward), easingCurve(QEasingCurve::InOutSine), state(QTimeLine::NotRunning) { } @@ -88,7 +74,7 @@ public: QTime timer; QTimeLine::Direction direction; - QTimeLine::CurveShape curveShape; + QEasingCurve easingCurve; QTimeLine::State state; inline void setState(QTimeLine::State newState) { @@ -523,12 +509,68 @@ void QTimeLine::setUpdateInterval(int interval) QTimeLine::CurveShape QTimeLine::curveShape() const { Q_D(const QTimeLine); - return d->curveShape; + switch (d->easingCurve.type()) { + default: + case QEasingCurve::InOutSine: + return EaseInOutCurve; + case QEasingCurve::InCurve: + return EaseInCurve; + case QEasingCurve::OutCurve: + return EaseOutCurve; + case QEasingCurve::Linear: + return LinearCurve; + case QEasingCurve::SineCurve: + return SineCurve; + case QEasingCurve::CosineCurve: + return CosineCurve; + } + return EaseInOutCurve; } + void QTimeLine::setCurveShape(CurveShape shape) { + switch (shape) { + default: + case EaseInOutCurve: + setEasingCurve(QEasingCurve(QEasingCurve::InOutSine)); + break; + case EaseInCurve: + setEasingCurve(QEasingCurve(QEasingCurve::InCurve)); + break; + case EaseOutCurve: + setEasingCurve(QEasingCurve(QEasingCurve::OutCurve)); + break; + case LinearCurve: + setEasingCurve(QEasingCurve(QEasingCurve::Linear)); + break; + case SineCurve: + setEasingCurve(QEasingCurve(QEasingCurve::SineCurve)); + break; + case CosineCurve: + setEasingCurve(QEasingCurve(QEasingCurve::CosineCurve)); + break; + } +} + +/*! + \property QTimeLine::easingCurve + + Specifies the easing curve that the timeline will use. + If both easing curve and curveShape are set, the last set property will + override the previous one. (If valueForTime() is reimplemented it will + override both) +*/ + +QEasingCurve QTimeLine::easingCurve() const +{ + Q_D(const QTimeLine); + return d->easingCurve; +} + +void QTimeLine::setEasingCurve(const QEasingCurve& curve) +{ Q_D(QTimeLine); - d->curveShape = shape; + d->easingCurve = curve; } /*! @@ -608,42 +650,8 @@ qreal QTimeLine::valueForTime(int msec) const Q_D(const QTimeLine); msec = qMin(qMax(msec, 0), d->duration); - // Simple linear interpolation qreal value = msec / qreal(d->duration); - - switch (d->curveShape) { - case EaseInOutCurve: - value = qt_sinProgress(value); - break; - // SmoothBegin blends Smooth and Linear Interpolation. - // Progress 0 - 0.3 : Smooth only - // Progress 0.3 - ~ 0.5 : Mix of Smooth and Linear - // Progress ~ 0.5 - 1 : Linear only - case EaseInCurve: { - const qreal sinProgress = qt_sinProgress(value); - const qreal linearProgress = value; - const qreal mix = qt_smoothBeginEndMixFactor(value); - value = sinProgress * mix + linearProgress * (1 - mix); - break; - } - case EaseOutCurve: { - const qreal sinProgress = qt_sinProgress(value); - const qreal linearProgress = value; - const qreal mix = qt_smoothBeginEndMixFactor(1 - value); - value = sinProgress * mix + linearProgress * (1 - mix); - break; - } - case SineCurve: - value = (qSin(((msec * pi * 2) / d->duration) - pi/2) + 1) / 2; - break; - case CosineCurve: - value = (qCos(((msec * pi * 2) / d->duration) - pi/2) + 1) / 2; - break; - default: - break; - } - - return value; + return d->easingCurve.valueForProgress(value); } /*! diff --git a/src/corelib/tools/qtimeline.h b/src/corelib/tools/qtimeline.h index 18c3980..48c9232 100644 --- a/src/corelib/tools/qtimeline.h +++ b/src/corelib/tools/qtimeline.h @@ -42,6 +42,7 @@ #ifndef QTIMELINE_H #define QTIMELINE_H +#include <QtCore/qeasingcurve.h> #include <QtCore/qobject.h> QT_BEGIN_HEADER @@ -60,6 +61,7 @@ class Q_CORE_EXPORT QTimeLine : public QObject Q_PROPERTY(Direction direction READ direction WRITE setDirection) Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount) Q_PROPERTY(CurveShape curveShape READ curveShape WRITE setCurveShape) + Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve) public: enum State { NotRunning, @@ -105,6 +107,9 @@ public: CurveShape curveShape() const; void setCurveShape(CurveShape shape); + QEasingCurve easingCurve() const; + void setEasingCurve(const QEasingCurve &curve); + int currentTime() const; int currentFrame() const; qreal currentValue() const; diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 1f047b8..7bdcba0 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -82,19 +82,9 @@ struct Q_CORE_EXPORT QVectorData }; template <typename T> -struct QVectorTypedData -{ - QBasicAtomicInt ref; - int alloc; - int size; -#if defined(QT_ARCH_SPARC) && defined(Q_CC_GNU) && defined(__LP64__) && defined(QT_BOOTSTRAPPED) - // workaround for bug in gcc 3.4.2 - uint sharable; - uint capacity; -#else - uint sharable : 1; - uint capacity : 1; -#endif +struct QVectorTypedData : private QVectorData +{ // private inheritance as we must not access QVectorData member thought QVectorTypedData + // as this would break strict aliasing rules. (in the case of shared_null) T array[1]; }; @@ -104,14 +94,14 @@ template <typename T> class QVector { typedef QVectorTypedData<T> Data; - union { QVectorData *p; QVectorTypedData<T> *d; }; + union { QVectorData *d; Data *p; }; public: - inline QVector() : p(&QVectorData::shared_null) { d->ref.ref(); } + inline QVector() : d(&QVectorData::shared_null) { d->ref.ref(); } explicit QVector(int size); QVector(int size, const T &t); inline QVector(const QVector<T> &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } - inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(d); } + inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(p); } QVector<T> &operator=(const QVector<T> &v); bool operator==(const QVector<T> &v) const; inline bool operator!=(const QVector<T> &v) const { return !(*this == v); } @@ -130,9 +120,9 @@ public: inline bool isDetached() const { return d->ref == 1; } inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } - inline T *data() { detach(); return d->array; } - inline const T *data() const { return d->array; } - inline const T *constData() const { return d->array; } + inline T *data() { detach(); return p->array; } + inline const T *data() const { return p->array; } + inline const T *constData() const { return p->array; } void clear(); const T &at(int i) const; @@ -225,12 +215,12 @@ public: typedef T* iterator; typedef const T* const_iterator; #endif - inline iterator begin() { detach(); return d->array; } - inline const_iterator begin() const { return d->array; } - inline const_iterator constBegin() const { return d->array; } - inline iterator end() { detach(); return d->array + d->size; } - inline const_iterator end() const { return d->array + d->size; } - inline const_iterator constEnd() const { return d->array + d->size; } + inline iterator begin() { detach(); return p->array; } + inline const_iterator begin() const { return p->array; } + inline const_iterator constBegin() const { return p->array; } + inline iterator end() { detach(); return p->array + d->size; } + inline const_iterator end() const { return p->array + d->size; } + inline const_iterator constEnd() const { return p->array + d->size; } iterator insert(iterator before, int n, const T &x); inline iterator insert(iterator before, const T &x) { return insert(before, 1, x); } iterator erase(iterator begin, iterator end); @@ -327,11 +317,11 @@ inline void QVector<T>::clear() template <typename T> inline const T &QVector<T>::at(int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::at", "index out of range"); - return d->array[i]; } + return p->array[i]; } template <typename T> inline const T &QVector<T>::operator[](int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); - return d->array[i]; } + return p->array[i]; } template <typename T> inline T &QVector<T>::operator[](int i) { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); @@ -369,7 +359,7 @@ QVector<T> &QVector<T>::operator=(const QVector<T> &v) { v.d->ref.ref(); if (!d->ref.deref()) - free(d); + free(p); d = v.d; if (!d->sharable) detach_helper(); @@ -385,31 +375,31 @@ inline QVectorData *QVector<T>::malloc(int aalloc) template <typename T> QVector<T>::QVector(int asize) { - p = malloc(asize); + d = malloc(asize); d->ref = 1; d->alloc = d->size = asize; d->sharable = true; d->capacity = false; if (QTypeInfo<T>::isComplex) { - T* b = d->array; - T* i = d->array + d->size; + T* b = p->array; + T* i = p->array + d->size; while (i != b) new (--i) T; } else { - qMemSet(d->array, 0, asize * sizeof(T)); + qMemSet(p->array, 0, asize * sizeof(T)); } } template <typename T> QVector<T>::QVector(int asize, const T &t) { - p = malloc(asize); + d = malloc(asize); d->ref = 1; d->alloc = d->size = asize; d->sharable = true; d->capacity = false; - T* i = d->array + d->size; - while (i != d->array) + T* i = p->array + d->size; + while (i != p->array) new (--i) T(t); } @@ -418,7 +408,7 @@ void QVector<T>::free(Data *x) { if (QTypeInfo<T>::isComplex) { T* b = x->array; - T* i = b + x->size; + T* i = b + reinterpret_cast<QVectorData *>(x)->size; while (i-- != b) i->~T(); } @@ -429,13 +419,13 @@ template <typename T> void QVector<T>::realloc(int asize, int aalloc) { T *j, *i, *b; - union { QVectorData *p; Data *d; } x; + union { QVectorData *d; Data *p; } x; x.d = d; if (QTypeInfo<T>::isComplex && aalloc == d->alloc && d->ref == 1) { // pure resize - i = d->array + d->size; - j = d->array + asize; + i = p->array + d->size; + j = p->array + asize; if (i > j) { while (i-- != j) i->~T(); @@ -450,22 +440,22 @@ void QVector<T>::realloc(int asize, int aalloc) if (aalloc != d->alloc || d->ref != 1) { // (re)allocate memory if (QTypeInfo<T>::isStatic) { - x.p = malloc(aalloc); + x.d = malloc(aalloc); } else if (d->ref != 1) { - x.p = QVectorData::malloc(sizeOfTypedData(), aalloc, sizeof(T), p); + x.d = QVectorData::malloc(sizeOfTypedData(), aalloc, sizeof(T), d); } else { if (QTypeInfo<T>::isComplex) { // call the destructor on all objects that need to be // destroyed when shrinking if (asize < d->size) { - j = d->array + asize; - i = d->array + d->size; + j = p->array + asize; + i = p->array + d->size; while (i-- != j) i->~T(); - i = d->array + asize; + i = p->array + asize; } } - x.p = p = static_cast<QVectorData *>(qRealloc(p, sizeOfTypedData() + (aalloc - 1) * sizeof(T))); + x.d = d = static_cast<QVectorData *>(qRealloc(d, sizeOfTypedData() + (aalloc - 1) * sizeof(T))); } x.d->ref = 1; x.d->sharable = true; @@ -474,31 +464,31 @@ void QVector<T>::realloc(int asize, int aalloc) } if (QTypeInfo<T>::isComplex) { if (asize < d->size) { - j = d->array + asize; - i = x.d->array + asize; + j = p->array + asize; + i = x.p->array + asize; } else { // construct all new objects when growing - i = x.d->array + asize; - j = x.d->array + d->size; + i = x.p->array + asize; + j = x.p->array + d->size; while (i != j) new (--i) T; - j = d->array + d->size; + j = p->array + d->size; } if (i != j) { // copy objects from the old array into the new array - b = x.d->array; + b = x.p->array; while (i != b) new (--i) T(*--j); } } else if (asize > d->size) { // initialize newly allocated memory to 0 - qMemSet(x.d->array + d->size, 0, (asize - d->size) * sizeof(T)); + qMemSet(x.p->array + d->size, 0, (asize - d->size) * sizeof(T)); } x.d->size = asize; x.d->alloc = aalloc; if (d != x.d) { if (!d->ref.deref()) - free(d); + free(p); d = x.d; } } @@ -506,15 +496,15 @@ void QVector<T>::realloc(int asize, int aalloc) template<typename T> Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i) const { - if (i < 0 || i >= p->size) { + if (i < 0 || i >= d->size) { return T(); } - return d->array[i]; + return p->array[i]; } template<typename T> Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i, const T &defaultValue) const { - return ((i < 0 || i >= p->size) ? defaultValue : d->array[i]); + return ((i < 0 || i >= d->size) ? defaultValue : p->array[i]); } template <typename T> @@ -525,14 +515,14 @@ void QVector<T>::append(const T &t) realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + 1, sizeof(T), QTypeInfo<T>::isStatic)); if (QTypeInfo<T>::isComplex) - new (d->array + d->size) T(copy); + new (p->array + d->size) T(copy); else - d->array[d->size] = copy; + p->array[d->size] = copy; } else { if (QTypeInfo<T>::isComplex) - new (d->array + d->size) T(t); + new (p->array + d->size) T(t); else - d->array[d->size] = t; + p->array[d->size] = t; } ++d->size; } @@ -540,27 +530,27 @@ void QVector<T>::append(const T &t) template <typename T> Q_TYPENAME QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, const T &t) { - int offset = before - d->array; + int offset = before - p->array; if (n != 0) { const T copy(t); if (d->ref != 1 || d->size + n > d->alloc) realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T), QTypeInfo<T>::isStatic)); if (QTypeInfo<T>::isStatic) { - T *b = d->array + d->size; - T *i = d->array + d->size + n; + T *b = p->array + d->size; + T *i = p->array + d->size + n; while (i != b) new (--i) T; - i = d->array + d->size; + i = p->array + d->size; T *j = i + n; - b = d->array + offset; + b = p->array + offset; while (i != b) *--j = *--i; i = b+n; while (i != b) *--i = copy; } else { - T *b = d->array + offset; + T *b = p->array + offset; T *i = b + n; memmove(i, b, (d->size - offset) * sizeof(T)); while (i != b) @@ -568,29 +558,29 @@ Q_TYPENAME QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, } d->size += n; } - return d->array + offset; + return p->array + offset; } template <typename T> Q_TYPENAME QVector<T>::iterator QVector<T>::erase(iterator abegin, iterator aend) { - int f = abegin - d->array; - int l = aend - d->array; + int f = abegin - p->array; + int l = aend - p->array; int n = l - f; detach(); if (QTypeInfo<T>::isComplex) { - qCopy(d->array+l, d->array+d->size, d->array+f); - T *i = d->array+d->size; - T* b = d->array+d->size-n; + qCopy(p->array+l, p->array+d->size, p->array+f); + T *i = p->array+d->size; + T* b = p->array+d->size-n; while (i != b) { --i; i->~T(); } } else { - memmove(d->array + f, d->array + l, (d->size-l)*sizeof(T)); + memmove(p->array + f, p->array + l, (d->size-l)*sizeof(T)); } d->size -= n; - return d->array + f; + return p->array + f; } template <typename T> @@ -600,9 +590,9 @@ bool QVector<T>::operator==(const QVector<T> &v) const return false; if (d == v.d) return true; - T* b = d->array; + T* b = p->array; T* i = b + d->size; - T* j = v.d->array + d->size; + T* j = v.p->array + d->size; while (i != b) if (!(*--i == *--j)) return false; @@ -615,8 +605,8 @@ QVector<T> &QVector<T>::fill(const T &from, int asize) const T copy(from); resize(asize < 0 ? d->size : asize); if (d->size) { - T *i = d->array + d->size; - T *b = d->array; + T *i = p->array + d->size; + T *b = p->array; while (i != b) *--i = copy; } @@ -629,9 +619,9 @@ QVector<T> &QVector<T>::operator+=(const QVector &l) int newSize = d->size + l.d->size; realloc(d->size, newSize); - T *w = d->array + newSize; - T *i = l.d->array + l.d->size; - T *b = l.d->array; + T *w = p->array + newSize; + T *i = l.p->array + l.d->size; + T *b = l.p->array; while (i != b) { if (QTypeInfo<T>::isComplex) new (--w) T(*--i); @@ -648,11 +638,11 @@ int QVector<T>::indexOf(const T &t, int from) const if (from < 0) from = qMax(from + d->size, 0); if (from < d->size) { - T* n = d->array + from - 1; - T* e = d->array + d->size; + T* n = p->array + from - 1; + T* e = p->array + d->size; while (++n != e) if (*n == t) - return n - d->array; + return n - p->array; } return -1; } @@ -665,8 +655,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const else if (from >= d->size) from = d->size-1; if (from >= 0) { - T* b = d->array; - T* n = d->array + from + 1; + T* b = p->array; + T* n = p->array + from + 1; while (n != b) { if (*--n == t) return n - b; @@ -678,8 +668,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const template <typename T> bool QVector<T>::contains(const T &t) const { - T* b = d->array; - T* i = d->array + d->size; + T* b = p->array; + T* i = p->array + d->size; while (i != b) if (*--i == t) return true; @@ -690,8 +680,8 @@ template <typename T> int QVector<T>::count(const T &t) const { int c = 0; - T* b = d->array; - T* i = d->array + d->size; + T* b = p->array; + T* i = p->array + d->size; while (i != b) if (*--i == t) ++c; diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index e5bf7e4..90287cb 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -11,6 +11,7 @@ HEADERS += \ tools/qcryptographichash.h \ tools/qdatetime.h \ tools/qdatetime_p.h \ + tools/qeasingcurve.h \ tools/qhash.h \ tools/qline.h \ tools/qlinkedlist.h \ @@ -19,6 +20,7 @@ HEADERS += \ tools/qlocale_p.h \ tools/qlocale_data_p.h \ tools/qmap.h \ + tools/qcontiguouscache.h \ tools/qpodlist_p.h \ tools/qpoint.h \ tools/qqueue.h \ @@ -45,7 +47,7 @@ SOURCES += \ tools/qbytearraymatcher.cpp \ tools/qcryptographichash.cpp \ tools/qdatetime.cpp \ - tools/qdumper.cpp \ + tools/qeasingcurve.cpp \ tools/qhash.cpp \ tools/qline.cpp \ tools/qlinkedlist.cpp \ @@ -53,6 +55,7 @@ SOURCES += \ tools/qlocale.cpp \ tools/qpoint.cpp \ tools/qmap.cpp \ + tools/qcontiguouscache.cpp \ tools/qrect.cpp \ tools/qregexp.cpp \ tools/qshareddata.cpp \ 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/accessible/qaccessible_win.cpp b/src/gui/accessible/qaccessible_win.cpp index 99cc272..f287874 100644 --- a/src/gui/accessible/qaccessible_win.cpp +++ b/src/gui/accessible/qaccessible_win.cpp @@ -50,7 +50,7 @@ #include <winuser.h> #if !defined(WINABLEAPI) -# if defined(Q_OS_WINCE) +# if defined(Q_WS_WINCE) # include <bldver.h> # endif # include <winable.h> @@ -61,7 +61,7 @@ #include <comdef.h> #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #include "qguifunctions_wince.h" #endif @@ -239,7 +239,7 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason) typedef void (WINAPI *PtrNotifyWinEvent)(DWORD, HWND, LONG, LONG); -#if defined(Q_OS_WINCE) // ### TODO: check for NotifyWinEvent in CE 6.0 +#if defined(Q_WS_WINCE) // ### TODO: check for NotifyWinEvent in CE 6.0 // There is no user32.lib nor NotifyWinEvent for CE return; #else @@ -289,7 +289,7 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason) if (reason != MenuCommand) { // MenuCommand is faked ptrNotifyWinEvent(reason, w->winId(), OBJID_CLIENT, who); } -#endif // Q_OS_WINCE +#endif // Q_WS_WINCE } void QAccessible::setRootObject(QObject *o) diff --git a/src/gui/accessible/qaccessiblewidget.cpp b/src/gui/accessible/qaccessiblewidget.cpp index 753ac57..1c2fcef 100644 --- a/src/gui/accessible/qaccessiblewidget.cpp +++ b/src/gui/accessible/qaccessiblewidget.cpp @@ -102,24 +102,7 @@ static QString buddyString(const QWidget *widget) QString Q_GUI_EXPORT qt_accStripAmp(const QString &text) { - if (text.isEmpty()) - return text; - - const QChar *ch = text.unicode(); - int length = text.length(); - QString str; - while (length > 0) { - if (*ch == QLatin1Char('&')) { - ++ch; - --length; - if (!ch) - --ch; - } - str += *ch; - ++ch; - --length; - } - return str; + return QString(text).remove(QLatin1Char('&')); } QString Q_GUI_EXPORT qt_accHotKey(const QString &text) diff --git a/src/gui/animation/animation.pri b/src/gui/animation/animation.pri new file mode 100644 index 0000000..27763ca --- /dev/null +++ b/src/gui/animation/animation.pri @@ -0,0 +1,3 @@ +# Qt gui animation module + +SOURCES += animation/qguivariantanimation.cpp diff --git a/src/gui/animation/qguivariantanimation.cpp b/src/gui/animation/qguivariantanimation.cpp new file mode 100644 index 0000000..37ca6a1 --- /dev/null +++ b/src/gui/animation/qguivariantanimation.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QT_NO_ANIMATION + +#include <QtCore/qvariantanimation.h> +#include <private/qvariantanimation_p.h> + +#include <QtGui/qcolor.h> + +QT_BEGIN_NAMESPACE + +template<> Q_INLINE_TEMPLATE QColor _q_interpolate(const QColor &f,const QColor &t, qreal progress) +{ + return QColor(_q_interpolate(f.red(), t.red(), progress), + _q_interpolate(f.green(), t.green(), progress), + _q_interpolate(f.blue(), t.blue(), progress), + _q_interpolate(f.alpha(), t.alpha(), progress)); +} + +static int qRegisterGuiGetInterpolator() +{ + qRegisterAnimationInterpolator<QColor>(_q_interpolateVariant<QColor>); + return 1; +} +Q_CONSTRUCTOR_FUNCTION(qRegisterGuiGetInterpolator) + +static int qUnregisterGuiGetInterpolator() +{ + qRegisterAnimationInterpolator<QColor>(0); + return 1; +} +Q_DESTRUCTOR_FUNCTION(qUnregisterGuiGetInterpolator) + +QT_END_NAMESPACE + +#endif //QT_NO_ANIMATION diff --git a/src/gui/dialogs/qcolordialog.cpp b/src/gui/dialogs/qcolordialog.cpp index 3aa04f6..7e885da 100644 --- a/src/gui/dialogs/qcolordialog.cpp +++ b/src/gui/dialogs/qcolordialog.cpp @@ -1066,7 +1066,7 @@ QColorShower::QColorShower(QColorDialog *parent) QGridLayout *gl = new QGridLayout(this); gl->setMargin(gl->spacing()); lab = new QColorShowLabel(this); -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE lab->setMinimumWidth(60); #else lab->setMinimumWidth(20); @@ -1369,7 +1369,7 @@ void QColorDialogPrivate::init(const QColor &initial) leftLay = 0; -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) smallDisplay = true; const int lumSpace = 20; #else @@ -1409,7 +1409,7 @@ void QColorDialogPrivate::init(const QColor &initial) leftLay->addWidget(lblBasicColors); leftLay->addWidget(standard); -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) leftLay->addStretch(); #endif @@ -1578,10 +1578,11 @@ void QColorDialog::setCurrentColor(const QColor &color) { Q_D(QColorDialog); d->setCurrentColor(color.rgb()); - d->selectColor(color.rgb()); + d->selectColor(color); d->setCurrentAlpha(color.alpha()); #ifdef Q_WS_MAC + d->setCurrentQColor(color); if (d->delegate) QColorDialogPrivate::setColor(d->delegate, color); #endif diff --git a/src/gui/dialogs/qcolordialog_mac.mm b/src/gui/dialogs/qcolordialog_mac.mm index 2556265..9862c1c 100644 --- a/src/gui/dialogs/qcolordialog_mac.mm +++ b/src/gui/dialogs/qcolordialog_mac.mm @@ -234,16 +234,22 @@ QT_USE_NAMESPACE CGFloat cyan, magenta, yellow, black, alpha; [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha]; mQtColor->setCmykF(cyan, magenta, yellow, black, alpha); + } else if (colorSpace == NSCalibratedRGBColorSpace || colorSpace == NSDeviceRGBColorSpace) { + CGFloat red, green, blue, alpha; + [color getRed:&red green:&green blue:&blue alpha:&alpha]; + mQtColor->setRgbF(red, green, blue, alpha); } else { - NSColor *tmpColor; - if (colorSpace == NSCalibratedRGBColorSpace || colorSpace == NSDeviceRGBColorSpace) { - tmpColor = color; + NSColorSpace *colorSpace = [color colorSpace]; + if ([colorSpace colorSpaceModel] == NSCMYKColorSpaceModel && [color numberOfComponents] == 5){ + CGFloat components[5]; + [color getComponents:components]; + mQtColor->setCmykF(components[0], components[1], components[2], components[3], components[4]); } else { - tmpColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + NSColor *tmpColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + CGFloat red, green, blue, alpha; + [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha]; + mQtColor->setRgbF(red, green, blue, alpha); } - CGFloat red, green, blue, alpha; - [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha]; - mQtColor->setRgbF(red, green, blue, alpha); } if (mPriv) @@ -414,10 +420,20 @@ void QColorDialogPrivate::setColor(void *delegate, const QColor &color) { QMacCocoaAutoReleasePool pool; QCocoaColorPanelDelegate *theDelegate = static_cast<QCocoaColorPanelDelegate *>(delegate); - NSColor *nsColor = [NSColor colorWithCalibratedRed:color.red() / 255.0 - green:color.green() / 255.0 - blue:color.blue() / 255.0 - alpha:color.alpha() / 255.0]; + NSColor *nsColor; + const QColor::Spec spec = color.spec(); + if (spec == QColor::Cmyk) { + nsColor = [NSColor colorWithDeviceCyan:color.cyanF() + magenta:color.magentaF() + yellow:color.yellowF() + black:color.blackF() + alpha:color.alphaF()]; + } else { + nsColor = [NSColor colorWithCalibratedRed:color.redF() + green:color.greenF() + blue:color.blueF() + alpha:color.alphaF()]; + } [[theDelegate colorPanel] setColor:nsColor]; } diff --git a/src/gui/dialogs/qdialog.cpp b/src/gui/dialogs/qdialog.cpp index 14d8162..b3f3c5b 100644 --- a/src/gui/dialogs/qdialog.cpp +++ b/src/gui/dialogs/qdialog.cpp @@ -55,7 +55,7 @@ #ifndef QT_NO_ACCESSIBILITY #include "qaccessible.h" #endif -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) #include "qt_windows.h" #include "qmenubar.h" #include "qpointer.h" @@ -251,7 +251,7 @@ QDialog::QDialog(QWidget *parent, Qt::WindowFlags f) : QWidget(*new QDialogPrivate, parent, f | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0)) { -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (!qt_wince_is_smartphone()) setWindowFlags(windowFlags() | Qt::WindowOkButtonHint | QFlag(qt_wince_is_mobile() ? 0 : Qt::WindowCancelButtonHint)); #endif @@ -280,7 +280,7 @@ QDialog::QDialog(QWidget *parent, const char *name, bool modal, Qt::WindowFlags QDialog::QDialog(QDialogPrivate &dd, QWidget *parent, Qt::WindowFlags f) : QWidget(dd, parent, f | QFlag((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : 0)) { -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (!qt_wince_is_smartphone()) setWindowFlags(windowFlags() | Qt::WindowOkButtonHint | QFlag(qt_wince_is_mobile() ? 0 : Qt::WindowCancelButtonHint)); #endif @@ -364,8 +364,8 @@ void QDialogPrivate::resetModalitySetByOpen() resetModalityTo = -1; } -#ifdef Q_OS_WINCE -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE +#ifdef Q_WS_WINCE_WM void QDialogPrivate::_q_doneAction() { //Done... @@ -473,7 +473,7 @@ int QDialog::exec() setResult(0); //On Windows Mobile we create an empty menu to hide the current menu -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM #ifndef QT_NO_MENUBAR QMenuBar *menuBar = 0; if (!findChild<QMenuBar *>()) @@ -484,7 +484,7 @@ int QDialog::exec() connect(doneAction, SIGNAL(triggered()), this, SLOT(_q_doneAction())); } #endif //QT_NO_MENUBAR -#endif //Q_OS_WINCE_WM +#endif //Q_WS_WINCE_WM show(); @@ -505,12 +505,12 @@ int QDialog::exec() int res = result(); if (deleteOnClose) delete this; -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM #ifndef QT_NO_MENUBAR else if (menuBar) delete menuBar; #endif //QT_NO_MENUBAR -#endif //Q_OS_WINCE_WM +#endif //Q_WS_WINCE_WM return res; } diff --git a/src/gui/dialogs/qdialog.h b/src/gui/dialogs/qdialog.h index ee1997a..bd86251 100644 --- a/src/gui/dialogs/qdialog.h +++ b/src/gui/dialogs/qdialog.h @@ -107,7 +107,7 @@ public Q_SLOTS: protected: QDialog(QDialogPrivate &, QWidget *parent, Qt::WindowFlags f = 0); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE bool event(QEvent *e); #endif void keyPressEvent(QKeyEvent *); @@ -124,7 +124,7 @@ private: Q_DECLARE_PRIVATE(QDialog) Q_DISABLE_COPY(QDialog) -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM Q_PRIVATE_SLOT(d_func(), void _q_doneAction()) #endif }; diff --git a/src/gui/dialogs/qdialog_p.h b/src/gui/dialogs/qdialog_p.h index 81a7b19..e4f551a 100644 --- a/src/gui/dialogs/qdialog_p.h +++ b/src/gui/dialogs/qdialog_p.h @@ -93,7 +93,7 @@ public: void hideDefault(); void resetModalitySetByOpen(); -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM void _q_doneAction(); #endif diff --git a/src/gui/dialogs/qerrormessage.cpp b/src/gui/dialogs/qerrormessage.cpp index f79c6b2..ca39d31 100644 --- a/src/gui/dialogs/qerrormessage.cpp +++ b/src/gui/dialogs/qerrormessage.cpp @@ -61,7 +61,7 @@ #include <stdio.h> #include <stdlib.h> -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp extern bool qt_wince_is_high_dpi(); //defined in qguifunctions_wince.cpp @@ -100,7 +100,7 @@ public: QSize QErrorMessageTextView::minimumSizeHint() const { -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (qt_wince_is_mobile()) if (qt_wince_is_high_dpi()) return QSize(200, 200); @@ -115,7 +115,7 @@ QSize QErrorMessageTextView::minimumSizeHint() const QSize QErrorMessageTextView::sizeHint() const { -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (qt_wince_is_mobile()) if (qt_wince_is_high_dpi()) return QSize(400, 200); @@ -240,7 +240,7 @@ QErrorMessage::QErrorMessage(QWidget * parent) d->again->setChecked(true); grid->addWidget(d->again, 1, 1, Qt::AlignTop); d->ok = new QPushButton(this); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE d->ok->setFixedSize(0,0); #endif connect(d->ok, SIGNAL(clicked()), this, SLOT(accept())); diff --git a/src/gui/dialogs/qfiledialog.cpp b/src/gui/dialogs/qfiledialog.cpp index d42775a..56b89f0 100644 --- a/src/gui/dialogs/qfiledialog.cpp +++ b/src/gui/dialogs/qfiledialog.cpp @@ -58,7 +58,7 @@ #include <qdebug.h> #include <qapplication.h> #include <qstylepainter.h> -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE #include "ui_qfiledialog.h" #else #include "ui_qfiledialog_wince.h" @@ -838,8 +838,8 @@ QStringList QFileDialogPrivate::addDefaultSuffixToFiles(const QStringList filesT // This check is needed since we might be at the root directory // and on Windows it already ends with slash. QString path = rootPath(); - if (!path.endsWith(QLatin1String("/"))) - path += QLatin1String("/"); + if (!path.endsWith(QLatin1Char('/'))) + path += QLatin1Char('/'); path += name; files.append(path); } @@ -1862,7 +1862,7 @@ QString QFileDialog::getExistingDirectory(QWidget *parent, #if defined(Q_WS_WIN) if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog) && (options & ShowDirsOnly) -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) && qt_priv_ptr_valid #endif ) { @@ -2076,7 +2076,7 @@ void QFileDialogPrivate::init(const QString &directory, const QString &nameFilte q->restoreState(settings.value(QLatin1String("filedialog")).toByteArray()); #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE qFileDialogUi->lookInLabel->setVisible(false); qFileDialogUi->fileNameLabel->setVisible(false); qFileDialogUi->fileTypeLabel->setVisible(false); @@ -2650,7 +2650,7 @@ void QFileDialogPrivate::_q_deleteCurrent() void QFileDialogPrivate::_q_autoCompleteFileName(const QString &text) { - if (text.startsWith(QLatin1String("//")) || text.startsWith(QLatin1String("\\"))) { + if (text.startsWith(QLatin1String("//")) || text.startsWith(QLatin1Char('\\'))) { qFileDialogUi->listView->selectionModel()->clearSelection(); return; } @@ -2692,7 +2692,7 @@ void QFileDialogPrivate::_q_updateOkButton() QStringList files = q->selectedFiles(); QString lineEditText = lineEdit()->text(); - if (lineEditText.startsWith(QLatin1String("//")) || lineEditText.startsWith(QLatin1String("\\"))) { + if (lineEditText.startsWith(QLatin1String("//")) || lineEditText.startsWith(QLatin1Char('\\'))) { button->setEnabled(true); if (acceptMode == QFileDialog::AcceptSave) button->setText(isOpenDirectory ? QFileDialog::tr("&Open") : acceptLabel); @@ -3198,7 +3198,7 @@ QStringList QFSCompletor::splitPath(const QString &path) const doubleSlash.clear(); #endif - QRegExp re(QLatin1String("[") + QRegExp::escape(sep) + QLatin1String("]")); + QRegExp re(QLatin1Char('[') + QRegExp::escape(sep) + QLatin1Char(']')); #ifdef Q_OS_WIN QStringList parts = pathCopy.split(re, QString::SkipEmptyParts); diff --git a/src/gui/dialogs/qfiledialog_mac.mm b/src/gui/dialogs/qfiledialog_mac.mm index 90af9fc..39a231b 100644 --- a/src/gui/dialogs/qfiledialog_mac.mm +++ b/src/gui/dialogs/qfiledialog_mac.mm @@ -911,8 +911,9 @@ void QFileDialogPrivate::createNavServicesDialog() navOptions.windowTitle = QCFString::toCFStringRef(q->windowTitle()); - static const int w = 450, h = 350; - navOptions.location.h = navOptions.location.v = -1; + navOptions.location.h = -1; + navOptions.location.v = -1; + QWidget *parent = q->parentWidget(); if (parent && parent->isVisible()) { WindowClass wclass; @@ -920,20 +921,6 @@ void QFileDialogPrivate::createNavServicesDialog() parent = parent->window(); QString s = parent->windowTitle(); navOptions.clientName = QCFString::toCFStringRef(s); - navOptions.location.h = (parent->x() + (parent->width() / 2)) - (w / 2); - navOptions.location.v = (parent->y() + (parent->height() / 2)) - (h / 2); - - QRect r = QApplication::desktop()->screenGeometry( - QApplication::desktop()->screenNumber(parent)); - const int border = 10; - if (navOptions.location.h + w > r.right()) - navOptions.location.h -= (navOptions.location.h + w) - r.right() + border; - if (navOptions.location.v + h > r.bottom()) - navOptions.location.v -= (navOptions.location.v + h) - r.bottom() + border; - if (navOptions.location.h < r.left()) - navOptions.location.h = r.left() + border; - if (navOptions.location.v < r.top()) - navOptions.location.v = r.top() + border; } filterInfo.currentSelection = 0; diff --git a/src/gui/dialogs/qfiledialog_win.cpp b/src/gui/dialogs/qfiledialog_win.cpp index 8431488..75a1820 100644 --- a/src/gui/dialogs/qfiledialog_win.cpp +++ b/src/gui/dialogs/qfiledialog_win.cpp @@ -60,7 +60,7 @@ #include <shlobj.h> -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #include <commdlg.h> # ifndef BFFM_SETSELECTION # define BFFM_SETSELECTION (WM_USER + 102) @@ -112,7 +112,7 @@ static void qt_win_resolve_libs() triedResolve = true; if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) QLibrary lib(QLatin1String("shell32")); ptrSHBrowseForFolder = (PtrSHBrowseForFolder) lib.resolve("SHBrowseForFolderW"); ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList) lib.resolve("SHGetPathFromIDListW"); @@ -186,7 +186,7 @@ static QString qt_win_selected_filter(const QString &filter, DWORD idx) return qt_win_make_filters_list(filter).at((int)idx - 1); } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE // Static vars for OFNA funcs: static QByteArray aInitDir; static QByteArray aInitSel; @@ -214,10 +214,10 @@ static OPENFILENAMEA *qt_win_make_OFNA(QWidget *parent, aInitSel = ""; } else { aInitSel = QDir::toNativeSeparators(initialSelection).toLocal8Bit(); - aInitSel.replace("<", ""); - aInitSel.replace(">", ""); - aInitSel.replace("\"", ""); - aInitSel.replace("|", ""); + aInitSel.replace('<', ""); + aInitSel.replace('>', ""); + aInitSel.replace('\"', ""); + aInitSel.replace('|', ""); } int maxLen = mode == QFileDialog::ExistingFiles ? maxMultiLen : maxNameLen; aInitSel.resize(maxLen + 1); // make room for return value @@ -286,10 +286,10 @@ static OPENFILENAME* qt_win_make_OFN(QWidget *parent, tTitle = title; QString initSel = QDir::toNativeSeparators(initialSelection); if (!initSel.isEmpty()) { - initSel.replace(QLatin1String("<"), QLatin1String("")); - initSel.replace(QLatin1String(">"), QLatin1String("")); - initSel.replace(QLatin1String("\""), QLatin1String("")); - initSel.replace(QLatin1String("|"), QLatin1String("")); + initSel.remove(QLatin1Char('<')); + initSel.remove(QLatin1Char('>')); + initSel.remove(QLatin1Char('\"')); + initSel.remove(QLatin1Char('|')); } int maxLen = mode == QFileDialog::ExistingFiles ? maxMultiLen : maxNameLen; @@ -505,7 +505,7 @@ QString qt_win_get_save_file_name(const QFileDialogArgs &args, } qt_win_clean_up_OFNA(&ofn); }); -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) int semIndex = result.indexOf(QLatin1Char(';')); if (semIndex >= 0) result = result.left(semIndex); @@ -700,7 +700,7 @@ QString qt_win_get_existing_directory(const QFileDialogArgs &args) modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true); modal_widget.setParent(parent, Qt::Window); QApplicationPrivate::enterModal(&modal_widget); -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) QT_WA({ qt_win_resolve_libs(); QString initDir = QDir::toNativeSeparators(args.directory); @@ -811,7 +811,7 @@ QString qt_win_get_existing_directory(const QFileDialogArgs &args) QDir::setCurrent(currentDir); if (!result.isEmpty()) - result.replace(QLatin1String("\\"), QLatin1String("/")); + result.replace(QLatin1Char('\\'), QLatin1Char('/')); return result; } diff --git a/src/gui/dialogs/qfilesystemmodel.cpp b/src/gui/dialogs/qfilesystemmodel.cpp index 012d3a1..825f8b6 100644 --- a/src/gui/dialogs/qfilesystemmodel.cpp +++ b/src/gui/dialogs/qfilesystemmodel.cpp @@ -337,7 +337,7 @@ QFileSystemModelPrivate::QFileSystemNode *QFileSystemModelPrivate::node(const QS { Q_Q(const QFileSystemModel); Q_UNUSED(q); - if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1String(":"))) + if (path.isEmpty() || path == myComputer() || path.startsWith(QLatin1Char(':'))) return const_cast<QFileSystemModelPrivate::QFileSystemNode*>(&root); // Construct the nodes up to the new root path if they need to be built diff --git a/src/gui/dialogs/qfontdialog.cpp b/src/gui/dialogs/qfontdialog.cpp index aa1c553..c7484e7 100644 --- a/src/gui/dialogs/qfontdialog.cpp +++ b/src/gui/dialogs/qfontdialog.cpp @@ -311,11 +311,11 @@ void QFontDialogPrivate::init() buttonBox->addButton(QDialogButtonBox::Cancel); QObject::connect(buttonBox, SIGNAL(rejected()), q, SLOT(reject())); -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) q->resize(180, 120); #else q->resize(500, 360); -#endif // Q_OS_WINCE +#endif // Q_WS_WINCE sizeEdit->installEventFilter(q); familyList->installEventFilter(q); diff --git a/src/gui/dialogs/qmessagebox.cpp b/src/gui/dialogs/qmessagebox.cpp index fb3bcb8..ae5b4a3 100644 --- a/src/gui/dialogs/qmessagebox.cpp +++ b/src/gui/dialogs/qmessagebox.cpp @@ -63,7 +63,7 @@ #include <QtGui/qfontmetrics.h> #include <QtGui/qclipboard.h> -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp extern bool qt_wince_is_smartphone();//defined in qguifunctions_wince.cpp extern bool qt_wince_is_pocket_pc(); //defined in qguifunctions_wince.cpp @@ -152,7 +152,7 @@ public: int layoutMinimumWidth(); void retranslateStrings(); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE void hideSpecial(); #endif @@ -272,10 +272,7 @@ void QMessageBoxPrivate::updateSize() return; QSize screenSize = QApplication::desktop()->availableGeometry(QCursor::pos()).size(); -#ifdef Q_WS_QWS - // the width of the screen, less the window border. - int hardLimit = screenSize.width() - (q->frameGeometry().width() - q->geometry().width()); -#elif defined(Q_OS_WINCE) +#if defined(Q_WS_QWS) || defined(Q_WS_WINCE) // the width of the screen, less the window border. int hardLimit = screenSize.width() - (q->frameGeometry().width() - q->geometry().width()); #else @@ -290,11 +287,11 @@ void QMessageBoxPrivate::updateSize() int softLimit = qMin(hardLimit, 500); #else // note: ideally on windows, hard and soft limits but it breaks compat -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE int softLimit = qMin(screenSize.width()/2, 500); #else int softLimit = qMin(screenSize.width() * 3 / 4, 500); -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE #endif if (informativeLabel) @@ -351,7 +348,7 @@ void QMessageBoxPrivate::updateSize() } -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE /*! \internal Hides special buttons which are rather shown in the title bar @@ -1208,7 +1205,7 @@ bool QMessageBox::event(QEvent *e) case QEvent::LanguageChange: d_func()->retranslateStrings(); break; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE case QEvent::OkRequest: case QEvent::HelpRequest: { QString bName = @@ -1313,7 +1310,7 @@ void QMessageBox::keyPressEvent(QKeyEvent *e) if (e == QKeySequence::Copy) { QString separator = QString::fromLatin1("---------------------------\n"); QString textToCopy = separator; - separator.prepend(QLatin1String("\n")); + separator.prepend(QLatin1Char('\n')); textToCopy += windowTitle() + separator; // title textToCopy += d->label->text() + separator; // text @@ -1351,7 +1348,7 @@ void QMessageBox::keyPressEvent(QKeyEvent *e) QDialog::keyPressEvent(e); } -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE /*! \reimp */ @@ -1420,7 +1417,7 @@ void QMessageBox::showEvent(QShowEvent *e) Q_D(QMessageBox); if (d->autoAddOkButton) { addButton(Ok); -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) d->hideSpecial(); #endif } @@ -1728,7 +1725,7 @@ void QMessageBox::aboutQt(QWidget *parent, const QString &title) QPixmap pm(QLatin1String(":/trolltech/qmessagebox/images/qtlogo-64.png")); if (!pm.isNull()) msgBox->setIconPixmap(pm); -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) msgBox->setDefaultButton(msgBox->addButton(QMessageBox::Ok)); #endif diff --git a/src/gui/dialogs/qmessagebox.h b/src/gui/dialogs/qmessagebox.h index e1667d6..c6fb3bc 100644 --- a/src/gui/dialogs/qmessagebox.h +++ b/src/gui/dialogs/qmessagebox.h @@ -145,7 +145,7 @@ public: QPushButton *addButton(StandardButton button); void removeButton(QAbstractButton *button); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE void setVisible(bool visible); #endif @@ -191,6 +191,8 @@ public: static StandardButton information(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton); + // ### Qt 5: Replace Ok with Yes|No in question() function. + // Also consider if Ok == Yes and Cancel == No. static StandardButton question(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton); diff --git a/src/gui/dialogs/qprintpreviewdialog.cpp b/src/gui/dialogs/qprintpreviewdialog.cpp index c00bd14..29a2738 100644 --- a/src/gui/dialogs/qprintpreviewdialog.cpp +++ b/src/gui/dialogs/qprintpreviewdialog.cpp @@ -54,6 +54,9 @@ #include <QtGui/qtoolbutton.h> #include <QtGui/qvalidator.h> #include <QtGui/qfiledialog.h> +#include <QtGui/qmainwindow.h> +#include <QtGui/qtoolbar.h> +#include <QtGui/qformlayout.h> #include <QtCore/QCoreApplication> #include <math.h> @@ -63,6 +66,13 @@ QT_BEGIN_NAMESPACE namespace { +class QPrintPreviewMainWindow : public QMainWindow +{ +public: + QPrintPreviewMainWindow(QWidget *parent) : QMainWindow(parent) {} + QMenu *createPopupMenu() { return 0; } +}; + class ZoomFactorValidator : public QDoubleValidator { public: @@ -197,7 +207,6 @@ public: QActionGroup *printerGroup; QAction *printAction; QAction *pageSetupAction; - QAction *closeAction; QPointer<QObject> receiverToDisconnectOnClose; QByteArray memberToDisconnectOnClose; @@ -219,27 +228,12 @@ void QPrintPreviewDialogPrivate::init(QPrinter *_printer) QObject::connect(preview, SIGNAL(previewChanged()), q, SLOT(_q_previewChanged())); setupActions(); - // Navigation - QToolButton* nextPageButton = new QToolButton; - nextPageButton->setDefaultAction(nextPageAction); - QToolButton* prevPageButton = new QToolButton; - prevPageButton->setDefaultAction(prevPageAction); - QToolButton* firstPageButton = new QToolButton; - firstPageButton->setDefaultAction(firstPageAction); - QToolButton* lastPageButton = new QToolButton; - lastPageButton->setDefaultAction(lastPageAction); - pageNumEdit = new LineEdit; pageNumEdit->setAlignment(Qt::AlignRight); - pageNumEdit->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding)); + pageNumEdit->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); pageNumLabel = new QLabel; QObject::connect(pageNumEdit, SIGNAL(editingFinished()), q, SLOT(_q_pageNumEdited())); - QToolButton* fitWidthButton = new QToolButton; - fitWidthButton->setDefaultAction(fitWidthAction); - QToolButton* fitPageButton = new QToolButton; - fitPageButton->setDefaultAction(fitPageAction); - zoomFactor = new QComboBox; zoomFactor->setEditable(true); zoomFactor->setMinimumContentsLength(7); @@ -255,77 +249,66 @@ void QPrintPreviewDialogPrivate::init(QPrinter *_printer) QObject::connect(zoomFactor, SIGNAL(currentIndexChanged(int)), q, SLOT(_q_zoomFactorChanged())); - QToolButton* zoomInButton = new QToolButton; - zoomInButton->setDefaultAction(zoomInAction); + QPrintPreviewMainWindow *mw = new QPrintPreviewMainWindow(q); + QToolBar *toolbar = new QToolBar(mw); + toolbar->addAction(fitWidthAction); + toolbar->addAction(fitPageAction); + toolbar->addSeparator(); + toolbar->addWidget(zoomFactor); + toolbar->addAction(zoomOutAction); + toolbar->addAction(zoomInAction); + toolbar->addSeparator(); + toolbar->addAction(portraitAction); + toolbar->addAction(landscapeAction); + toolbar->addSeparator(); + toolbar->addAction(firstPageAction); + toolbar->addAction(prevPageAction); + + // this is to ensure the label text and the editor text are + // aligned in all styles - the extra QVBoxLayout is a workaround + // for bug in QFormLayout + QWidget *pageEdit = new QWidget(toolbar); + QVBoxLayout *vboxLayout = new QVBoxLayout; + vboxLayout->setContentsMargins(0, 0, 0, 0); + QFormLayout *formLayout = new QFormLayout; + formLayout->setWidget(0, QFormLayout::LabelRole, pageNumEdit); + formLayout->setWidget(0, QFormLayout::FieldRole, pageNumLabel); + vboxLayout->addLayout(formLayout); + vboxLayout->setAlignment(Qt::AlignVCenter); + pageEdit->setLayout(vboxLayout); + toolbar->addWidget(pageEdit); + + toolbar->addAction(nextPageAction); + toolbar->addAction(lastPageAction); + toolbar->addSeparator(); + toolbar->addAction(singleModeAction); + toolbar->addAction(facingModeAction); + toolbar->addAction(overviewModeAction); + toolbar->addSeparator(); + toolbar->addAction(pageSetupAction); + toolbar->addAction(printAction); + + // Cannot use the actions' triggered signal here, since it doesn't autorepeat + QToolButton *zoomInButton = static_cast<QToolButton *>(toolbar->widgetForAction(zoomInAction)); + QToolButton *zoomOutButton = static_cast<QToolButton *>(toolbar->widgetForAction(zoomOutAction)); zoomInButton->setAutoRepeat(true); zoomInButton->setAutoRepeatInterval(200); zoomInButton->setAutoRepeatDelay(200); - - QToolButton* zoomOutButton = new QToolButton; - zoomOutButton->setDefaultAction(zoomOutAction); zoomOutButton->setAutoRepeat(true); zoomOutButton->setAutoRepeatInterval(200); zoomOutButton->setAutoRepeatDelay(200); - - //Cannot use the actions' triggered signal here, since it doesnt autorepeat QObject::connect(zoomInButton, SIGNAL(clicked()), q, SLOT(_q_zoomIn())); QObject::connect(zoomOutButton, SIGNAL(clicked()), q, SLOT(_q_zoomOut())); - QToolButton* portraitButton = new QToolButton; - portraitButton->setDefaultAction(portraitAction); - QToolButton* landscapeButton = new QToolButton; - landscapeButton->setDefaultAction(landscapeAction); - - QToolButton* singleModeButton = new QToolButton; - singleModeButton->setDefaultAction(singleModeAction); - QToolButton* facingModeButton = new QToolButton; - facingModeButton->setDefaultAction(facingModeAction); - QToolButton* overviewModeButton = new QToolButton; - overviewModeButton->setDefaultAction(overviewModeAction); - - QToolButton *printButton = new QToolButton; - printButton->setDefaultAction(printAction); - QToolButton *pageSetupButton = new QToolButton; - pageSetupButton->setDefaultAction(pageSetupAction); - QToolButton *closeButton = new QToolButton; - closeButton->setDefaultAction(closeAction); - - QHBoxLayout* modeLayout = new QHBoxLayout; - modeLayout->setSpacing(0); - modeLayout->addWidget(singleModeButton); - modeLayout->addWidget(facingModeButton); - modeLayout->addWidget(overviewModeButton); - - QHBoxLayout *barLayout = new QHBoxLayout; - barLayout->addWidget(fitWidthButton); - barLayout->addWidget(fitPageButton); - barLayout->addWidget(zoomFactor); - barLayout->addWidget(zoomOutButton); - barLayout->addWidget(zoomInButton); - barLayout->addWidget(portraitButton); - barLayout->addWidget(landscapeButton); - barLayout->addStretch(); - barLayout->addWidget(firstPageButton); - barLayout->addWidget(prevPageButton); - barLayout->addWidget(pageNumEdit); - barLayout->addWidget(pageNumLabel); - barLayout->addWidget(nextPageButton); - barLayout->addWidget(lastPageButton); - barLayout->addStretch(); - barLayout->addLayout(modeLayout); - barLayout->addStretch(); - barLayout->addWidget(pageSetupButton); - barLayout->addWidget(printButton); - barLayout->addWidget(closeButton); - - QWidget* buttonBar = new QWidget; - buttonBar->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum)); - barLayout->setMargin(0); - buttonBar->setLayout(barLayout); + mw->addToolBar(toolbar); + mw->setCentralWidget(preview); + // QMainWindows are always created as top levels, force it to be a + // plain widget + mw->setParent(q, Qt::Widget); QVBoxLayout *topLayout = new QVBoxLayout; - topLayout->addWidget(buttonBar); - topLayout->addWidget(preview); + topLayout->addWidget(mw); + topLayout->setMargin(0); q->setLayout(topLayout); QString caption = QCoreApplication::translate("QPrintPreviewDialog", "Print Preview"); @@ -338,7 +321,8 @@ void QPrintPreviewDialogPrivate::init(QPrinter *_printer) || printer->outputFormat() != QPrinter::NativeFormat #endif ) - pageSetupButton->setEnabled(false); + pageSetupAction->setEnabled(false); + preview->setFocus(); } static inline void qt_setupActionIcon(QAction *action, const QLatin1String &name) @@ -418,12 +402,10 @@ void QPrintPreviewDialogPrivate::setupActions() printerGroup = new QActionGroup(q); printAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Print")); pageSetupAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Page setup")); - closeAction = printerGroup->addAction(QCoreApplication::translate("QPrintPreviewDialog", "Close")); qt_setupActionIcon(printAction, QLatin1String("print")); qt_setupActionIcon(pageSetupAction, QLatin1String("page-setup")); QObject::connect(printAction, SIGNAL(triggered(bool)), q, SLOT(_q_print())); QObject::connect(pageSetupAction, SIGNAL(triggered(bool)), q, SLOT(_q_pageSetup())); - QObject::connect(closeAction, SIGNAL(triggered(bool)), q, SLOT(reject())); // Initial state: fitPageAction->setChecked(true); @@ -576,7 +558,7 @@ void QPrintPreviewDialogPrivate::_q_print() suffix = QLatin1String(".ps"); } QString fileName = QFileDialog::getSaveFileName(q, title, printer->outputFileName(), - QLatin1String("*") + suffix); + QLatin1Char('*') + suffix); if (!fileName.isEmpty()) { if (QFileInfo(fileName).suffix().isEmpty()) fileName.append(suffix); diff --git a/src/gui/dialogs/qwizard.cpp b/src/gui/dialogs/qwizard.cpp index 298f23f..b2ed983 100644 --- a/src/gui/dialogs/qwizard.cpp +++ b/src/gui/dialogs/qwizard.cpp @@ -69,7 +69,7 @@ #include "private/qdialog_p.h" #include <qdebug.h> -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE extern bool qt_wince_is_mobile(); //defined in qguifunctions_wce.cpp #endif @@ -1241,8 +1241,10 @@ void QWizardPrivate::updateMinMaxSizes(const QWizardLayoutInfo &info) #endif QSize minimumSize = mainLayout->totalMinimumSize() + QSize(0, extraHeight); QSize maximumSize; + bool skipMaxSize = false; #if defined(Q_WS_WIN) - if (QSysInfo::WindowsVersion > QSysInfo::WV_Me) // ### See Tasks 164078 and 161660 + if (QSysInfo::WindowsVersion <= QSysInfo::WV_Me) // ### See Tasks 164078 and 161660 + skipMaxSize = true; #endif maximumSize = mainLayout->totalMaximumSize(); if (info.header && headerWidget->maximumWidth() != QWIDGETSIZE_MAX) { @@ -1263,11 +1265,13 @@ void QWizardPrivate::updateMinMaxSizes(const QWizardLayoutInfo &info) } if (q->maximumWidth() == maximumWidth) { maximumWidth = maximumSize.width(); - q->setMaximumWidth(maximumWidth); + if (!skipMaxSize) + q->setMaximumWidth(maximumWidth); } if (q->maximumHeight() == maximumHeight) { maximumHeight = maximumSize.height(); - q->setMaximumHeight(maximumHeight); + if (!skipMaxSize) + q->setMaximumHeight(maximumHeight); } } @@ -2120,7 +2124,7 @@ QWizard::QWizard(QWidget *parent, Qt::WindowFlags flags) { Q_D(QWizard); d->init(); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (!qt_wince_is_mobile()) setWindowFlags(windowFlags() & ~Qt::WindowOkButtonHint); #endif 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..0d6d77d --- /dev/null +++ b/src/gui/embedded/qkbd_defaultmap_qws_p.h @@ -0,0 +1,796 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSKEYBOARDHANDLER_DEFAULTMAP_H +#define QWSKEYBOARDHANDLER_DEFAULTMAP_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. +// + +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..3224da2 --- /dev/null +++ b/src/gui/embedded/qkbd_qws_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWSKEYBOARD_P_H +#define QWSKEYBOARD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <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..1ef2696 --- /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/qkbdpc101_qws.cpp b/src/gui/embedded/qkbdpc101_qws.cpp deleted file mode 100644 index 3173645..0000000 --- a/src/gui/embedded/qkbdpc101_qws.cpp +++ /dev/null @@ -1,485 +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 "qkbdpc101_qws.h" - -#ifndef QT_NO_QWS_KEYBOARD - -#include "qscreen_qws.h" -#include "qwindowsystem_qws.h" -#include "qnamespace.h" -#include "qapplication.h" - -#include <unistd.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> - -#ifdef Q_OS_LINUX -#include <sys/kd.h> -#include <sys/vt.h> -#endif - -QT_BEGIN_NAMESPACE - -static const QWSKeyMap pc101KeyM[] = { - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Escape, 27 , 27 , 0xffff }, - { Qt::Key_1, '1' , '!' , 0xffff }, - { Qt::Key_2, '2' , '@' , 0xffff }, - { Qt::Key_3, '3' , '#' , 0xffff }, - { Qt::Key_4, '4' , '$' , 0xffff }, - { Qt::Key_5, '5' , '%' , 0xffff }, - { Qt::Key_6, '6' , '^' , 0xffff }, - { Qt::Key_7, '7' , '&' , 0xffff }, - { Qt::Key_8, '8' , '*' , 0xffff }, - { Qt::Key_9, '9' , '(' , 0xffff }, // 10 - { Qt::Key_0, '0' , ')' , 0xffff }, - { Qt::Key_Minus, '-' , '_' , 0xffff }, - { Qt::Key_Equal, '=' , '+' , 0xffff }, - { Qt::Key_Backspace, 8 , 8 , 0xffff }, - { Qt::Key_Tab, 9 , 9 , 0xffff }, - { Qt::Key_Q, 'q' , 'Q' , 'Q'-64 }, - { Qt::Key_W, 'w' , 'W' , 'W'-64 }, - { Qt::Key_E, 'e' , 'E' , 'E'-64 }, - { Qt::Key_R, 'r' , 'R' , 'R'-64 }, - { Qt::Key_T, 't' , 'T' , 'T'-64 }, // 20 - { Qt::Key_Y, 'y' , 'Y' , 'Y'-64 }, - { Qt::Key_U, 'u' , 'U' , 'U'-64 }, - { Qt::Key_I, 'i' , 'I' , 'I'-64 }, - { Qt::Key_O, 'o' , 'O' , 'O'-64 }, - { Qt::Key_P, 'p' , 'P' , 'P'-64 }, - { Qt::Key_BraceLeft, '[' , '{' , 0xffff }, - { Qt::Key_BraceRight, ']' , '}' , 0xffff }, - { Qt::Key_Return, 13 , 13 , 0xffff }, - { Qt::Key_Control, 0xffff , 0xffff , 0xffff }, - { Qt::Key_A, 'a' , 'A' , 'A'-64 }, // 30 - { Qt::Key_S, 's' , 'S' , 'S'-64 }, - { Qt::Key_D, 'd' , 'D' , 'D'-64 }, - { Qt::Key_F, 'f' , 'F' , 'F'-64 }, - { Qt::Key_G, 'g' , 'G' , 'G'-64 }, - { Qt::Key_H, 'h' , 'H' , 'H'-64 }, - { Qt::Key_J, 'j' , 'J' , 'J'-64 }, - { Qt::Key_K, 'k' , 'K' , 'K'-64 }, - { Qt::Key_L, 'l' , 'L' , 'L'-64 }, - { Qt::Key_Semicolon, ';' , ':' , 0xffff }, - { Qt::Key_Apostrophe, '\'' , '"' , 0xffff }, // 40 - { Qt::Key_QuoteLeft, '`' , '~' , 0xffff }, - { Qt::Key_Shift, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Backslash, '\\' , '|' , 0xffff }, - { Qt::Key_Z, 'z' , 'Z' , 'Z'-64 }, - { Qt::Key_X, 'x' , 'X' , 'X'-64 }, - { Qt::Key_C, 'c' , 'C' , 'C'-64 }, - { Qt::Key_V, 'v' , 'V' , 'V'-64 }, - { Qt::Key_B, 'b' , 'B' , 'B'-64 }, - { Qt::Key_N, 'n' , 'N' , 'N'-64 }, - { Qt::Key_M, 'm' , 'M' , 'M'-64 }, // 50 - { Qt::Key_Comma, ',' , '<' , 0xffff }, - { Qt::Key_Period, '.' , '>' , 0xffff }, - { Qt::Key_Slash, '/' , '?' , 0xffff }, - { Qt::Key_Shift, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Asterisk, '*' , '*' , 0xffff }, - { Qt::Key_Alt, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Space, ' ' , ' ' , 0xffff }, - { Qt::Key_CapsLock, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F1, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F2, 0xffff , 0xffff , 0xffff }, // 60 - { Qt::Key_F3, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F4, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F5, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F6, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F7, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F8, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F9, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F10, 0xffff , 0xffff , 0xffff }, - { Qt::Key_NumLock, 0xffff , 0xffff , 0xffff }, - { Qt::Key_ScrollLock, 0xffff , 0xffff , 0xffff }, // 70 - { Qt::Key_7, '7' , '7' , 0xffff }, - { Qt::Key_8, '8' , '8' , 0xffff }, - { Qt::Key_9, '9' , '9' , 0xffff }, - { Qt::Key_Minus, '-' , '-' , 0xffff }, - { Qt::Key_4, '4' , '4' , 0xffff }, - { Qt::Key_5, '5' , '5' , 0xffff }, - { Qt::Key_6, '6' , '6' , 0xffff }, - { Qt::Key_Plus, '+' , '+' , 0xffff }, - { Qt::Key_1, '1' , '1' , 0xffff }, - { Qt::Key_2, '2' , '2' , 0xffff }, // 80 - { Qt::Key_3, '3' , '3' , 0xffff }, - { Qt::Key_0, '0' , '0' , 0xffff }, - { Qt::Key_Period, '.' , '.' , 0xffff }, - { Qt::Key_SysReq, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Less, '<' , '>' , 0xffff }, - { Qt::Key_F11, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F12, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // 90 - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Enter, 13 , 13 , 0xffff }, - { Qt::Key_Control, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Slash, '/' , '/' , 0xffff }, - { Qt::Key_SysReq, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Meta, 0xffff , 0xffff , 0xffff }, // 100 - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // break - { Qt::Key_Home, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Up, 0xffff , 0xffff , 0xffff }, - { Qt::Key_PageUp, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Left, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Right, 0xffff , 0xffff , 0xffff }, - { Qt::Key_End, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Down, 0xffff , 0xffff , 0xffff }, - { Qt::Key_PageDown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Insert, 0xffff , 0xffff , 0xffff }, // 110 - { Qt::Key_Delete, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // macro - { Qt::Key_F13, 0xffff , 0xffff , 0xffff }, - { Qt::Key_F14, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Help, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, // do - { Qt::Key_F17, 0xffff , 0xffff , 0xffff }, - { Qt::Key_Plus, '+' , '-' , 0xffff }, - { Qt::Key_Pause, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { Qt::Key_unknown, 0xffff , 0xffff , 0xffff }, - { 0, 0xffff , 0xffff , 0xffff } -}; - -static const int keyMSize = sizeof(pc101KeyM)/sizeof(QWSKeyMap)-1; - -//=========================================================================== - -// -// PC-101 type keyboards -// - -/*! - \class QWSPC101KeyboardHandler - \ingroup qws - - \internal -*/ - -QWSPC101KeyboardHandler::QWSPC101KeyboardHandler(const QString&) -{ - shift = false; - alt = false; - ctrl = false; - extended = 0; - prevuni = 0; - prevkey = 0; - caps = false; -#if defined(QT_QWS_IPAQ) - // iPAQ Action Key has ScanCode 0x60: 0x60|0x80 = 0xe0 == extended mode 1 ! - ipaq_return_pressed = false; -#endif -} - -QWSPC101KeyboardHandler::~QWSPC101KeyboardHandler() -{ -} - -const QWSKeyMap *QWSPC101KeyboardHandler::keyMap() const -{ - return pc101KeyM; -} - -void QWSPC101KeyboardHandler::doKey(uchar code) -{ - - int keyCode = Qt::Key_unknown; - bool release = false; - int keypad = 0; - bool softwareRepeat = false; - -#ifndef QT_QWS_USE_KEYCODES - // extended? - if (code == 224 -#if defined(QT_QWS_IPAQ) - && !ipaq_return_pressed -#endif - ) { - extended = 1; - return; - } else if (code == 225) { - extended = 2; - return; - } -#endif - - if (code & 0x80) { - release = true; - code &= 0x7f; - } - -#ifndef QT_QWS_USE_KEYCODES - if (extended == 1) { - switch (code) { - case 72: - keyCode = Qt::Key_Up; - break; - case 75: - keyCode = Qt::Key_Left; - break; - case 77: - keyCode = Qt::Key_Right; - break; - case 80: - keyCode = Qt::Key_Down; - break; - case 82: - keyCode = Qt::Key_Insert; - break; - case 71: - keyCode = Qt::Key_Home; - break; - case 73: - keyCode = Qt::Key_PageUp; - break; - case 83: - keyCode = Qt::Key_Delete; - break; - case 79: - keyCode = Qt::Key_End; - break; - case 81: - keyCode = Qt::Key_PageDown; - break; - case 28: - keyCode = Qt::Key_Enter; - break; - case 53: - keyCode = Qt::Key_Slash; - break; - case 0x1d: - keyCode = Qt::Key_Control; - break; - case 0x2a: - keyCode = Qt::Key_Print; - break; - case 0x38: - keyCode = Qt::Key_Alt; - break; - case 0x5b: - keyCode = Qt::Key_Super_L; - break; - case 0x5c: - keyCode = Qt::Key_Super_R; - break; - case 0x5d: - keyCode = Qt::Key_Menu; - break; -#if 0 - default: - qDebug("extended1 code %x release %d", code, release); - break; -#endif - } - } else if (extended == 2) { - switch (code) { - case 0x1d: - return; - case 0x45: - keyCode = Qt::Key_Pause; - break; - } - } else -#endif - { - if (code < keyMSize) { - keyCode = pc101KeyM[code].key_code; - } - -#if defined(QT_QWS_IPAQ) || defined(QT_QWS_EBX) - softwareRepeat = true; - - switch (code) { - case 0x7a: case 0x7b: case 0x7c: case 0x7d: - keyCode = code - 0x7a + Qt::Key_F9; - softwareRepeat = false; - break; - case 0x79: - keyCode = Qt::Key_SysReq; - softwareRepeat = false; - break; - case 0x78: -# ifdef QT_QWS_IPAQ - keyCode = Qt::Key_F24; // record -# else - keyCode = Qt::Key_Escape; -# endif - softwareRepeat = false; - break; - case 0x60: - keyCode = Qt::Key_Return; -# ifdef QT_QWS_IPAQ - ipaq_return_pressed = !release; -# endif - break; - case 0x67: - keyCode = Qt::Key_Right; - break; - case 0x69: - keyCode = Qt::Key_Up; - break; - case 0x6a: - keyCode = Qt::Key_Down; - break; - case 0x6c: - keyCode = Qt::Key_Left; - break; - } - - if (qt_screen->isTransformed() - && keyCode >= Qt::Key_Left && keyCode <= Qt::Key_Down) - { - keyCode = transformDirKey(keyCode); - } -#endif - /* - Translate shift+Qt::Key_Tab to Qt::Key_Backtab - */ - if ((keyCode == Qt::Key_Tab) && shift) - keyCode = Qt::Key_Backtab; - } - -#ifndef QT_QWS_USE_KEYCODES - /* - Qt::Keypad consists of extended keys 53 and 28, - and non-extended keys 55 and 71 through 83. - */ - if ((extended == 1) ? (code == 53 || code == 28) : - (code == 55 || (code >= 71 && code <= 83))) -#else - if (code == 55 || code >= 71 && code <= 83 || code == 96 - || code == 98 || code == 118) -#endif - { - keypad = Qt::KeypadModifier; - } - - // Ctrl-Alt-Backspace exits qws - if (ctrl && alt && keyCode == Qt::Key_Backspace) { - qApp->quit(); - } - - if (keyCode == Qt::Key_Alt) { - alt = !release; - } else if (keyCode == Qt::Key_Control) { - ctrl = !release; - } else if (keyCode == Qt::Key_Shift) { - shift = !release; - } else if (keyCode == Qt::Key_CapsLock && release) { - caps = !caps; -#if defined(Q_OS_LINUX) - char leds; - ioctl(0, KDGETLED, &leds); - leds = leds & ~LED_CAP; - if (caps) leds |= LED_CAP; - ioctl(0, KDSETLED, leds); -#endif - } - if (keyCode != Qt::Key_unknown) { - bool bAlt = alt; - bool bCtrl = ctrl; - bool bShift = shift; - int unicode = 0; - if (code < keyMSize) { - if (!extended) { - bool bCaps = shift || - (caps ? QChar(keyMap()[code].unicode).isLetter() : false); - if (bCtrl) - unicode = keyMap()[code].ctrl_unicode ? keyMap()[code].ctrl_unicode : 0xffff; - else if (bCaps) - unicode = keyMap()[code].shift_unicode ? keyMap()[code].shift_unicode : 0xffff; - else - unicode = keyMap()[code].unicode ? keyMap()[code].unicode : 0xffff; -#ifndef QT_QWS_USE_KEYCODES - } else if (extended==1) { - if (code == 53) - unicode = '/'; -#endif - } - } - - modifiers = 0; - if (bAlt) modifiers |= Qt::AltModifier; - if (bCtrl) modifiers |= Qt::ControlModifier; - if (bShift) modifiers |= Qt::ShiftModifier; - if (keypad) modifiers |= Qt::KeypadModifier; - - // looks wrong -- WWA - bool repeat = false; - if (prevuni == unicode && prevkey == keyCode && !release) - repeat = true; - - processKeyEvent(unicode, keyCode, modifiers, !release, repeat); - - if (!release) { - prevuni = unicode; - prevkey = keyCode; - } else { - prevkey = prevuni = 0; - } - } - - if (softwareRepeat && !release) - beginAutoRepeat(prevuni, prevkey, modifiers); - else - endAutoRepeat(); - - extended = 0; -} - -QT_END_NAMESPACE - -#endif // QT_NO_QWS_KEYBOARD 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..5c0dec8 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,145 @@ 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]; + + 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 +328,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 +340,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 39a74d5..91121e7 100644 --- a/src/gui/embedded/qscreen_qws.cpp +++ b/src/gui/embedded/qscreen_qws.cpp @@ -49,6 +49,7 @@ #include "qpixmap.h" #include "qvarlengtharray.h" #include "qwsdisplay_qws.h" +#include "qpainter.h" #include <private/qdrawhelper_p.h> #include <private/qpaintengine_raster_p.h> #include <private/qpixmap_raster_p.h> @@ -198,10 +199,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); + } } @@ -2708,7 +2711,7 @@ void QScreen::compose(int level, const QRegion &exposed, QRegion &blend, default: break; } - spanData.setup(qwsServer->backgroundBrush(), 256); + spanData.setup(qwsServer->backgroundBrush(), 256, QPainter::CompositionMode_SourceOver); spanData.dx = off.x(); spanData.dy = off.y(); } else if (!surface->isBuffered()) { @@ -2773,7 +2776,7 @@ void QScreen::paintBackground(const QRegion &r) rb.prepare(&img); QSpanData spanData; spanData.init(&rb, 0); - spanData.setup(bg, 256); + spanData.setup(bg, 256, QPainter::CompositionMode_Source); spanData.dx = off.x(); spanData.dy = off.y(); Q_ASSERT(spanData.blend); 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 e8ace65..b9e462b 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -130,12 +130,18 @@ \img graphicsview-parentchild.png + \section1 Transformation + QGraphicsItem supports affine transformations in addition to its base position, pos(). To change the item's transformation, you can either pass - a transformation matrix to setTransform(), or call one of the convenience - functions rotate(), scale(), translate(), or shear(). Item transformations - accumulate from parent to child, so if both a parent and child item are - rotated 90 degrees, the child's total transformation will be 180 degrees. + a transformation matrix to setTransform(), or set the different transformation + properties (transformOrigin, x/y/zRotation, x/yScale, horizontal/verticalShear). + Note that setting the transformation matrix conflicts with using the properties. + Setting the properties will overwrite the transformation set with setTransform, + and using setTransform will reset the properties. + + Item transformations accumulate from parent to child, so if both a parent and child + item are rotated 90 degrees, the child's total transformation will be 180 degrees. Similarly, if the item's parent is scaled to 2x its original size, its children will also be twice as large. An item's transformation does not affect its own local geometry; all geometry functions (e.g., contains(), @@ -146,6 +152,22 @@ and scenePos(), which returns its position in scene coordinates. To reset an item's matrix, call resetTransform(). + The order you set the transformation properties does not affect the resulting transformation + The resulting transformation is always computed in the following order + + \code + [Origin] [RotateX] [RotateY] [RotateZ] [Shear] [Scale] [-Origin] + \endcode + + So the transformation is equivalent to the following code + + \code + QTransform().translate(xOrigin, yOrigin).rotate(xRotation, Qt::XAxis).rotate(yRotation, Qt::YAxis).rotate(zRotation, Qt::ZAxis) + .shear(horizontalShear, verticalShear).scale(xScale, yScale).translate(-xOrigin, -yOrigin); + \endcode + + \section1 Painting + The paint() function is called by QGraphicsView to paint the item's contents. The item has no background or default fill of its own; whatever is behind the item will shine through all areas that are not explicitly @@ -161,6 +183,8 @@ high z-values. Stacking order applies to sibling items; parents are always drawn before their children. + \section1 Events + QGraphicsItem receives events from QGraphicsScene through the virtual function sceneEvent(). This function distributes the most common events to a set of convenience event handlers: @@ -186,6 +210,8 @@ by the virtual function sceneEventFilter(). You can remove item event filters by calling removeSceneEventFilter(). + \section1 Custom Data + Sometimes it's useful to register custom data with an item, be it a custom item, or a standard item. You can call setData() on any item to store data in it using a key-value pair (the key being an integer, and the value is a @@ -274,6 +300,15 @@ this flag, the child will be stacked behind it. This flag is useful for drop shadow effects and for decoration objects that follow the parent item's geometry without drawing on top of it. + + \value ItemUsesExtendedStyleOption The item makes use of either + QStyleOptionGraphicsItem::exposedRect or QStyleOptionGraphicsItem::matrix. + By default, the exposedRect is initialized to the item's boundingRect and + the matrix is untransformed. Enable this flag for more fine-grained values. + Note that QStyleOptionGraphicsItem::levelOfDetail is unaffected by this flag + and is always initialized to 1. + Use QStyleOptionGraphicsItem::levelOfDetailFromTransform for a more + fine-grained value. */ /*! @@ -329,16 +364,17 @@ \value ItemTransformChange The item's transformation matrix changes. This notification is only sent when the item's local transformation matrix - changes (i.e., as a result of calling setTransform(), or one of the - convenience transformation functions, such as rotate()). The value + changes (i.e., as a result of calling setTransform(). The value argument is the new matrix (i.e., a QTransform); to get the old matrix, - call transform(). Do not call setTransform() or any of the transformation - convenience functions in itemChange() as this notification is delivered; + call transform(). Do not call setTransform() or set any of the transformation + properties in itemChange() as this notification is delivered; instead, you can return the new matrix from itemChange(). + This notification is not sent if you change the transformation properties. \value ItemTransformHasChanged The item's transformation matrix has - changed. This notification is only sent after the item's local - trasformation matrix has changed. The value argument is the new matrix + changed either because setTransform is called, or one of the transformation + properties is changed. This notification is only sent after the item's local + transformation matrix has changed. The value argument is the new matrix (same as transform()), and QGraphicsItem ignores the return value for this notification (i.e., a read-only notification). @@ -522,6 +558,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 @@ -559,29 +600,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. */ @@ -781,11 +799,213 @@ 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; + + const QVariant newParentVariant(q->itemChange(QGraphicsItem::ItemParentChange, + qVariantFromValue<QGraphicsItem *>(newParent))); + newParent = qVariantValue<QGraphicsItem *>(newParentVariant); + 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(); + + const QVariant thisPointerVariant(qVariantFromValue<QGraphicsItem *>(q)); + if (parent) { + // Remove from current parent + parent->d_ptr->removeChild(q); + parent->itemChange(QGraphicsItem::ItemChildRemovedChange, thisPointerVariant); + } + + // 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); + parent->itemChange(QGraphicsItem::ItemChildAddedChange, thisPointerVariant); + if (!implicitUpdate) + updateHelper(QRectF(), false, true); + + // 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(QRectF(), false, true); + } + } + + if (scene) { + // Invalidate any sort caching; arrival of a new item means we need to + // resort. + scene->d_func()->invalidateSortCache(); + } + + // Resolve opacity. + updateEffectiveOpacity(); + + // Resolve depth. + resolveDepth(parent ? parent->d_ptr->depth : -1); + + // Invalidate transform cache. + invalidateSceneTransformCache(); + + // Deliver post-change notification + q->itemChange(QGraphicsItem::ItemParentHasChanged, newParentVariant); +} + +/*! + \internal + + Returns the bounding rect of this item's children (excluding itself). +*/ +void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rect) +{ + for (int i = 0; i < children.size(); ++i) { + QGraphicsItem *child = children.at(i); + QGraphicsItemPrivate *childd = child->d_ptr; + bool hasX = childd->hasTransform; + bool hasPos = !childd->pos.isNull(); + if (hasPos || hasX) { + QTransform matrix; + if (hasX) + matrix = child->transform(); + if (hasPos) { + const QPointF &p = childd->pos; + matrix *= QTransform::fromTranslate(p.x(), p.y()); + } + matrix *= *x; + *rect |= matrix.mapRect(child->boundingRect()); + if (!childd->children.isEmpty()) + childd->childrenBoundingRectHelper(&matrix, rect); + } else { + *rect |= x->mapRect(child->boundingRect()); + if (!childd->children.isEmpty()) + childd->childrenBoundingRectHelper(x, rect); + } + } +} + +void QGraphicsItemPrivate::initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform, + const QRegion &exposedRegion, bool allItems) const +{ + Q_ASSERT(option); + Q_Q(const QGraphicsItem); + + // Initialize standard QStyleOption values. + const QRectF brect = q->boundingRect(); + option->state = QStyle::State_None; + option->rect = brect.toRect(); + option->levelOfDetail = 1; + option->exposedRect = brect; + if (selected) + option->state |= QStyle::State_Selected; + if (enabled) + option->state |= QStyle::State_Enabled; + if (q->hasFocus()) + option->state |= QStyle::State_HasFocus; + if (scene) { + if (scene->d_func()->hoverItems.contains(q_ptr)) + option->state |= QStyle::State_MouseOver; + if (q == scene->mouseGrabberItem()) + option->state |= QStyle::State_Sunken; + } + + if (!(flags & QGraphicsItem::ItemUsesExtendedStyleOption)) + return; + + // Initialize QStyleOptionGraphicsItem specific values (matrix, exposedRect). + + const QTransform itemToViewportTransform = q->deviceTransform(worldTransform); + option->matrix = itemToViewportTransform.toAffine(); //### discards perspective + + if (!allItems) { + // Determine the item's exposed area + option->exposedRect = QRectF(); + const QTransform reverseMap = itemToViewportTransform.inverted(); + const QVector<QRect> exposedRects(exposedRegion.rects()); + for (int i = 0; i < exposedRects.size(); ++i) { + option->exposedRect |= reverseMap.mapRect(exposedRects.at(i)); + if (option->exposedRect.contains(brect)) + break; + } + option->exposedRect &= brect; + } +} + +/*! + \internal + Empty all cached pixmaps from the pixmap cache. */ void QGraphicsItemCache::purge() { QPixmapCache::remove(key); + key = QPixmapCache::Key(); QMutableMapIterator<QPaintDevice *, DeviceData> it(deviceData); while (it.hasNext()) { DeviceData &data = it.next().value(); @@ -853,24 +1073,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); @@ -1015,98 +1228,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; - const QVariant newParentVariant(itemChange(ItemParentChange, qVariantFromValue<QGraphicsItem *>(parent))); - parent = qVariantValue<QGraphicsItem *>(newParentVariant); - 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(); - - const QVariant thisPointerVariant(qVariantFromValue<QGraphicsItem *>(this)); - if (d_ptr->parent) { - // Remove from current parent - qt_graphicsitem_removeChild(this, &d_ptr->parent->d_func()->children); - d_ptr->parent->itemChange(ItemChildRemovedChange, thisPointerVariant); - } - - 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; - d_ptr->parent->itemChange(ItemChildAddedChange, thisPointerVariant); - if (!implicitUpdate) - d_ptr->updateHelper(QRectF(), false, true); - - // 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(QRectF(), false, true); - } - - 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. - d_ptr->updateEffectiveOpacity(); - - // Resolve depth. - d_ptr->resolveDepth(parent ? parent->d_ptr->depth : -1); - - // Invalidate transform cache. - d_ptr->invalidateSceneTransformCache(); - - // Deliver post-change notification - itemChange(QGraphicsItem::ItemParentHasChanged, newParentVariant); + d_ptr->setParentItemHelper(parent, /* deleting = */ false); } /*! @@ -1332,12 +1454,6 @@ void QGraphicsItem::setCacheMode(CacheMode mode, const QSize &logicalCacheSize) cache->purge(); if (mode == ItemCoordinateCache) { - if (cache->key.isEmpty()) { - // Generate new simple pixmap cache key. - QString tmp; - tmp.sprintf("qgv-%p", this); - cache->key = tmp; - } if (lastMode == mode && cache->fixedSize == logicalCacheSize) noVisualChange = true; cache->fixedSize = logicalCacheSize; @@ -1415,7 +1531,9 @@ void QGraphicsItem::setCursor(const QCursor &cursor) d_ptr->setExtra(QGraphicsItemPrivate::ExtraCursor, qVariantValue<QCursor>(cursorVariant)); d_ptr->hasCursor = 1; if (d_ptr->scene) { + d_ptr->scene->d_func()->allItemsUseDefaultCursor = false; foreach (QGraphicsView *view, d_ptr->scene->views()) { + view->viewport()->setMouseTracking(true); // Note: Some of this logic is duplicated in QGraphicsView's mouse events. if (view->underMouse()) { foreach (QGraphicsItem *itemUnderCursor, view->items(view->mapFromGlobal(QCursor::pos()))) { @@ -1902,11 +2020,11 @@ void QGraphicsItem::setOpacity(qreal opacity) newOpacity = qBound<qreal>(0.0, newOpacity, 1.0); // No change? Done. - if (qFuzzyCompare(newOpacity, this->opacity())) + if (qFuzzyIsNull(newOpacity - this->opacity())) return; // Assign local opacity. - if (qFuzzyCompare(newOpacity, qreal(1.0))) { + if (qFuzzyIsNull(newOpacity - 1)) { // Opaque, unset opacity. d_ptr->hasOpacity = 0; d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraOpacity); @@ -2051,7 +2169,13 @@ bool QGraphicsItem::acceptsHoverEvents() const */ void QGraphicsItem::setAcceptHoverEvents(bool enabled) { + if (d_ptr->acceptsHover == quint32(enabled)) + return; d_ptr->acceptsHover = quint32(enabled); + if (d_ptr->acceptsHover && d_ptr->scene && d_ptr->scene->d_func()->allItemsIgnoreHoverEvents) { + d_ptr->scene->d_func()->allItemsIgnoreHoverEvents = false; + d_ptr->scene->d_func()->enableMouseTrackingOnViews(); + } } /*! @@ -2061,7 +2185,7 @@ void QGraphicsItem::setAcceptHoverEvents(bool enabled) */ void QGraphicsItem::setAcceptsHoverEvents(bool enabled) { - d_ptr->acceptsHover = quint32(enabled); + setAcceptHoverEvents(enabled); } /*! @@ -2466,8 +2590,13 @@ QMatrix QGraphicsItem::matrix() const /*! \since 4.3 - Returns this item's transformation matrix. If no matrix has been set, the - identity matrix is returned. + Returns this item's transformation matrix. + + Either the one set by setTransform, or the resulting transformation from + all the transfmation properties + + If no matrix or transformation property has been set, the + identity matrix is returned. \sa setTransform(), sceneTransform() */ @@ -2475,10 +2604,404 @@ QTransform QGraphicsItem::transform() const { if (!d_ptr->hasTransform) return QTransform(); + if (d_ptr->hasDecomposedTransform && d_ptr->dirtyTransform) { + QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + QTransform x; + decomposed->generateTransform(&x); + QVariant v(x); + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, v); + d_ptr->dirtyTransform = 0; + const_cast<QGraphicsItem *>(this)->itemChange(ItemTransformHasChanged, v); + return x; + } return qVariantValue<QTransform>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform)); } /*! + \since 4.6 + + Returns the rotation around the X axis. + + The default is 0 + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, this function return the default value. + + \sa setXRotation(), {Transformations} +*/ +qreal QGraphicsItem::xRotation() const +{ + return d_ptr->decomposedTransform()->xRotation; +} + +/*! + \since 4.6 + + Sets the rotation around the X axis to \a angle degrees. + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, it will be overwritten. + + \sa xRotation(), {Transformations} +*/ +void QGraphicsItem::setXRotation(qreal angle) +{ + if (!d_ptr->dirtyTransform) { + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + } + QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + decomposed->xRotation = angle; + if (!d_ptr->dirtyTransform) + d_ptr->invalidateSceneTransformCache(); + d_ptr->dirtyTransform = 1; + d_ptr->hasTransform = 1; +} + +/*! + \since 4.6 + + Returns the rotation around the Y axis. + + The default is 0 + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, this function return the default value. + + \sa setYRotation(), {Transformations} +*/ +qreal QGraphicsItem::yRotation() const +{ + return d_ptr->decomposedTransform()->yRotation; +} + +/*! + \since 4.6 + + Sets the rotation around the Y axis to \a angle degrees. + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, it will be overwritten. + + \sa yRotation(), {Transformations} +*/ +void QGraphicsItem::setYRotation(qreal angle) +{ + if (!d_ptr->dirtyTransform) { + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + } + QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + decomposed->yRotation = angle; + if (!d_ptr->dirtyTransform) + d_ptr->invalidateSceneTransformCache(); + d_ptr->dirtyTransform = 1; + d_ptr->hasTransform = 1; +} + +/*! + \since 4.6 + + Returns the rotation around the Z axis. + + The default is 0 + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, this function return the default value. + + \sa setZRotation(), {Transformations} +*/ +qreal QGraphicsItem::zRotation() const +{ + return d_ptr->decomposedTransform()->zRotation; +} + +/*! + \since 4.6 + + Sets the rotation around the Z axis to \a angle degrees. + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, it will be overwritten. + + \sa zRotation(), {Transformations} +*/ +void QGraphicsItem::setZRotation(qreal angle) +{ + if (!d_ptr->dirtyTransform) { + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + } + QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + decomposed->zRotation = angle; + if (!d_ptr->dirtyTransform) + d_ptr->invalidateSceneTransformCache(); + d_ptr->dirtyTransform = 1; + d_ptr->hasTransform = 1; +} + +/*! + \since 4.6 + + This convenience function set the rotation angles around the 3 axes + to \a x, \a y and \a z. + + \sa setXRotation(), setYRotation(), setZRotation() +*/ +void QGraphicsItem::setRotation(qreal x, qreal y, qreal z) +{ + setXRotation(x); + setYRotation(y); + setZRotation(z); +} + +/*! + \since 4.6 + + Returns the scale factor on the X axis. + + The default is 1 + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, this function return the default value. + + \sa setXScale(), {Transformations} +*/ +qreal QGraphicsItem::xScale() const +{ + return d_ptr->decomposedTransform()->xScale; +} + +/*! + \since 4.6 + + Sets the scale factor on the X axis to \a factor. + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, it will be overwritten. + + \sa xScale(), {Transformations} +*/ +void QGraphicsItem::setXScale(qreal factor) +{ + if (!d_ptr->dirtyTransform) { + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + } + QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + decomposed->xScale = factor; + if (!d_ptr->dirtyTransform) + d_ptr->invalidateSceneTransformCache(); + d_ptr->dirtyTransform = 1; + d_ptr->hasTransform = 1; +} + +/*! + \since 4.6 + + Returns the scale factor on the Y axis. + + The default is 1 + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, this function return the default value. + + \sa setYScale(), {Transformations} +*/ +qreal QGraphicsItem::yScale() const +{ + return d_ptr->decomposedTransform()->yScale; +} + +/*! + \since 4.6 + + Sets the scale factor on the Y axis to \a factor. + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, it will be overwritten. + + \sa yScale(), {Transformations} +*/ +void QGraphicsItem::setYScale(qreal factor) +{ + if (!d_ptr->dirtyTransform) { + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + } + QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + decomposed->yScale = factor; + if (!d_ptr->dirtyTransform) + d_ptr->invalidateSceneTransformCache(); + d_ptr->dirtyTransform = 1; + d_ptr->hasTransform = 1; +} + +/*! + \since 4.6 + + This convenience function set the scaling factors on X and Y axis to \a sx and \a sy. + + \sa setXScale(), setYScale() +*/ +void QGraphicsItem::setScale(qreal sx, qreal sy) +{ + setXScale(sx); + setYScale(sy); +} + +/*! + \since 4.6 + + Returns the horizontal shear. + + The default is 0 + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, this function return the default value. + + \sa setHorizontalShear(), {Transformations} +*/ +qreal QGraphicsItem::horizontalShear() const +{ + return d_ptr->decomposedTransform()->horizontalShear; +} + +/*! + \since 4.6 + + Sets the horizontal shear to \a shear. + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, it will be overwritten. + + \sa horizontalShear(), {Transformations} +*/ +void QGraphicsItem::setHorizontalShear(qreal shear) +{ + if (!d_ptr->dirtyTransform) { + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + } + QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + decomposed->horizontalShear = shear; + if (!d_ptr->dirtyTransform) + d_ptr->invalidateSceneTransformCache(); + d_ptr->dirtyTransform = 1; + d_ptr->hasTransform = 1; +} + +/*! + \since 4.6 + + Returns the vertical shear. + + The default is 0 + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, this function return the default value. + + \sa setHorizontalShear(), {Transformations} +*/ +qreal QGraphicsItem::verticalShear() const +{ + return d_ptr->decomposedTransform()->verticalShear; +} + +/*! + \since 4.6 + + Sets the vertical shear to \a shear. + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, it will be overwritten. + + \sa verticalShear(), {Transformations} +*/ +void QGraphicsItem::setVerticalShear(qreal shear) +{ + if (!d_ptr->dirtyTransform) { + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + } + QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + decomposed->verticalShear = shear; + if (!d_ptr->dirtyTransform) + d_ptr->invalidateSceneTransformCache(); + d_ptr->dirtyTransform = 1; + d_ptr->hasTransform = 1; +} + +/*! + \since 4.6 + + This convenience function sets the horizontal shear to \a sh and the vertical shear to \a sv. + + \sa setHorizontalShear(), setVerticalShear() +*/ +void QGraphicsItem::setShear(qreal sh, qreal sv) +{ + setHorizontalShear(sh); + setVerticalShear(sv); +} + +/*! + \since 4.6 + + Returns the transformation origin for the transformation properties. + + The default is QPointF(0,0). + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, this function return the default value. + + \sa setTransformOrigin(), {Transformations} +*/ +QPointF QGraphicsItem::transformOrigin() const +{ + const QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + return QPointF(decomposed->xOrigin, decomposed->yOrigin); +} + +/*! + \fn inline void setTransformOrigin(qreal x, qreal y) + + \since 4.6 + + This is an overloaded member function, provided for convenience. + Sets the transformation origin for the transformation + properties to the point(\a x, \a y). + + \sa setTransformOrigin(), {Transformations} +*/ + +/*! + \since 4.6 + + Sets the transformation origin for the transformation properties to \a origin. + This does not apply to the transformation set by setTransform. + + \warning setting this property is conflicting with calling setTransform. + If a transform has been set, it will be overwritten. + + \sa transformOrigin(), {Transformations} +*/ +void QGraphicsItem::setTransformOrigin(const QPointF &origin) +{ + if (!d_ptr->dirtyTransform) { + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + } + QGraphicsItemPrivate::DecomposedTransform *decomposed = d_ptr->decomposedTransform(); + decomposed->xOrigin = origin.x(); + decomposed->yOrigin = origin.y(); + if (!d_ptr->dirtyTransform) + d_ptr->invalidateSceneTransformCache(); + d_ptr->dirtyTransform = 1; + d_ptr->hasTransform = 1; +} + +/*! \obsolete Use sceneTransform() instead. @@ -2753,7 +3276,7 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co Use setTransform() instead. - \sa transform(), rotate(), scale(), shear(), translate(), {The Graphics View Coordinate System} + \sa transform(), {The Graphics View Coordinate System} */ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) { @@ -2778,6 +3301,8 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) prepareGeometryChange(); d_ptr->hasTransform = !newTransform.isIdentity(); d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); + d_ptr->dirtyTransformComponents = 1; + d_ptr->dirtyTransform = 0; d_ptr->invalidateSceneTransformCache(); // Send post-notification. @@ -2801,7 +3326,10 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) to map an item coordiate to a scene coordinate, or mapFromScene() to map from scene coordinates to item coordinates. - \sa transform(), rotate(), scale(), shear(), translate(), {The Graphics View Coordinate System} + \warning using this function conflicts with using the transformation properties. + If you set a transformation, getting the properties will return default values. + + \sa transform(), setRotation(), setScale(), setShear(), setTransformOrigin() {The Graphics View Coordinate System} */ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) { @@ -2826,6 +3354,8 @@ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) prepareGeometryChange(); d_ptr->hasTransform = !newTransform.isIdentity(); d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); + d_ptr->dirtyTransformComponents = 1; + d_ptr->dirtyTransform = 0; d_ptr->invalidateSceneTransformCache(); // Send post-notification. @@ -2845,8 +3375,9 @@ void QGraphicsItem::resetMatrix() /*! \since 4.3 - Resets this item's transformation matrix to the identity matrix. This is - equivalent to calling \c setTransform(QTransform()). + Resets this item's transformation matrix to the identity matrix or + all the transformation properties to their default values. + This is equivalent to calling \c setTransform(QTransform()). \sa setTransform(), transform() */ @@ -2856,6 +3387,9 @@ void QGraphicsItem::resetTransform() } /*! + \obsolete + Use setZRotation() instead + Rotates the current item transformation \a angle degrees clockwise around its origin. To translate around an arbitrary point (x, y), you need to combine translation and rotation with setTransform(). @@ -2864,6 +3398,9 @@ void QGraphicsItem::resetTransform() \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 6 + \warning using this function conflicts with using the transformation properties. + Getting those properties after using this function will return default values. + \sa setTransform(), transform(), scale(), shear(), translate() */ void QGraphicsItem::rotate(qreal angle) @@ -2872,6 +3409,9 @@ void QGraphicsItem::rotate(qreal angle) } /*! + \obsolete + Use setScale() instead + Scales the current item transformation by (\a sx, \a sy) around its origin. To scale from an arbitrary point (x, y), you need to combine translation and scaling with setTransform(). @@ -2880,7 +3420,10 @@ void QGraphicsItem::rotate(qreal angle) \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 7 - \sa setTransform(), transform(), rotate(), shear(), translate() + \warning using this function conflicts with using the transformation properties. + Getting those properties after using this function will return default values. + + \sa setTransform(), transform() */ void QGraphicsItem::scale(qreal sx, qreal sy) { @@ -2888,9 +3431,15 @@ void QGraphicsItem::scale(qreal sx, qreal sy) } /*! + \obsolete + Use setShear instead. + Shears the current item transformation by (\a sh, \a sv). - \sa setTransform(), transform(), rotate(), scale(), translate() + \warning using this function conflicts with using the transformation properties. + Getting those properties after using this function will return default values. + + \sa setTransform(), transform() */ void QGraphicsItem::shear(qreal sh, qreal sv) { @@ -2898,13 +3447,19 @@ void QGraphicsItem::shear(qreal sh, qreal sv) } /*! + \obsolete + Use setPos() or setTransformOrigin() instead. + Translates the current item transformation by (\a dx, \a dy). If all you want is to move an item, you should call moveBy() or setPos() instead; this function changes the item's translation, which is conceptually separate from its position. - \sa setTransform(), transform(), rotate(), scale(), shear() + \warning using this function conflicts with using the transformation properties. + Getting those properties after using this function will return default values. + + \sa setTransform(), transform() */ void QGraphicsItem::translate(qreal dx, qreal dy) { @@ -2949,11 +3504,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 @@ -2981,7 +3536,7 @@ void QGraphicsItem::setZValue(qreal z) qreal newZ = qreal(newZVariant.toDouble()); if (newZ == d_ptr->z) return; - d_ptr->z = z; + d_ptr->z = newZ; d_ptr->fullUpdateHelper(); if (d_ptr->scene) { @@ -3013,13 +3568,8 @@ void QGraphicsItem::setZValue(qreal z) QRectF QGraphicsItem::childrenBoundingRect() const { QRectF childRect; - foreach (QGraphicsItem *child, children()) { - QPointF childPos = child->pos(); - QTransform matrix = child->transform(); - if (!childPos.isNull()) - matrix *= QTransform::fromTranslate(childPos.x(), childPos.y()); - childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect()); - } + QTransform x; + d_ptr->childrenBoundingRectHelper(&x, &childRect); return childRect; } @@ -3033,7 +3583,7 @@ QRectF QGraphicsItem::childrenBoundingRect() const Although the item's shape can be arbitrary, the bounding rect is always rectangular, and it is unaffected by the items' - transformation (scale(), rotate(), etc.). + transformation. If you want to change the item's bounding rectangle, you must first call prepareGeometryChange(). This notifies the scene of the imminent change, @@ -3634,7 +4184,7 @@ void QGraphicsItem::setBoundingRegionGranularity(qreal granularity) All painting is done in local coordinates. - \sa setCacheMode(), QPen::width(), {Item Coordinates} + \sa setCacheMode(), QPen::width(), {Item Coordinates}, ItemUsesExtendedStyleOption */ /*! @@ -3725,7 +4275,7 @@ void QGraphicsItemPrivate::fullUpdateHelper(bool childrenOnly, bool maybeDirtyCl dirtyChildren = 1; } -static inline bool qt_allChildrenCombineOpacity(QGraphicsItem *parent) +static inline bool allChildrenCombineOpacityHelper(QGraphicsItem *parent) { Q_ASSERT(parent); if (parent->flags() & QGraphicsItem::ItemDoesntPropagateOpacityToChildren) @@ -3744,11 +4294,11 @@ void QGraphicsItemPrivate::updateEffectiveOpacity() Q_Q(QGraphicsItem); if (parent) { resolveEffectiveOpacity(parent->effectiveOpacity()); - parent->d_ptr->allChildrenCombineOpacity = qt_allChildrenCombineOpacity(parent); + parent->d_ptr->allChildrenCombineOpacity = allChildrenCombineOpacityHelper(parent); } else { resolveEffectiveOpacity(1.0); } - allChildrenCombineOpacity = qt_allChildrenCombineOpacity(q); + allChildrenCombineOpacity = allChildrenCombineOpacityHelper(q); } /*! @@ -3775,7 +4325,7 @@ void QGraphicsItemPrivate::resolveEffectiveOpacity(qreal parentEffectiveOpacity) } // Set this item's resolved opacity. - if (qFuzzyCompare(myEffectiveOpacity, qreal(1.0))) { + if (qFuzzyIsNull(myEffectiveOpacity - 1)) { // Opaque, unset effective opacity. hasEffectiveOpacity = 0; unsetExtra(ExtraEffectiveOpacity); @@ -3814,6 +4364,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)); @@ -3825,6 +4410,9 @@ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const return c; } +/*! + \internal +*/ void QGraphicsItemPrivate::removeExtraItemCache() { QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); @@ -3980,11 +4568,13 @@ void QGraphicsItem::update(const QRectF &rect) } // Invalidate cache. - if (rect.isNull()) { - cache->allExposed = true; - cache->exposed.clear(); - } else { - cache->exposed.append(rect); + if (!cache->allExposed) { + if (rect.isNull()) { + cache->allExposed = true; + cache->exposed.clear(); + } else { + cache->exposed.append(rect); + } } // Only invalidate cache; item is already dirty. if (d_ptr->dirty) @@ -3999,6 +4589,45 @@ void QGraphicsItem::update(const QRectF &rect) d_ptr->scene->itemUpdated(this, rect); } +/*! + \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 @@ -4027,11 +4656,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 = qFuzzyIsNull(dx - int(dx)) && qFuzzyIsNull(dy - int(dy)) + && (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::replace(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; } @@ -4188,7 +4851,9 @@ QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPointF &point */ QPointF QGraphicsItem::mapToParent(const QPointF &point) const { - return d_ptr->pos + (d_ptr->hasTransform ? transform().map(point) : point); + if (!d_ptr->hasTransform) + return point + d_ptr->pos; + return transform().map(point) + d_ptr->pos; } /*! @@ -4253,9 +4918,9 @@ QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QRectF &rect */ QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const { - QPolygonF p = !d_ptr->hasTransform ? rect : transform().map(rect); - p.translate(d_ptr->pos); - return p; + if (!d_ptr->hasTransform) + return rect.translated(d_ptr->pos); + return transform().map(rect).translated(d_ptr->pos); } /*! @@ -4454,9 +5119,9 @@ QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPolygonF &p */ QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const { - QPolygonF p = !d_ptr->hasTransform ? polygon : transform().map(polygon); - p.translate(d_ptr->pos); - return p; + if (!d_ptr->hasTransform) + return polygon.translated(d_ptr->pos); + return transform().map(polygon).translated(d_ptr->pos); } /*! @@ -4498,10 +5163,9 @@ QPainterPath QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPainterP */ QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const { - QTransform x = QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y()); - if (d_ptr->hasTransform) - x = transform() * x; - return x.map(path); + if (!d_ptr->hasTransform) + return path.translated(d_ptr->pos); + return transform().map(path).translated(d_ptr->pos); } /*! @@ -4716,9 +5380,9 @@ QPainterPath QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPainte */ QPainterPath QGraphicsItem::mapFromParent(const QPainterPath &path) const { - if (d_ptr->parent) - return d_ptr->parent->itemTransform(this).map(path); - return mapFromScene(path); + QPainterPath p(path); + p.translate(-d_ptr->pos); + return d_ptr->hasTransform ? transform().inverted().map(p) : p; } /*! @@ -5757,7 +6421,7 @@ static void qt_graphicsItem_highlightSelected( QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option) { const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1)); - if (qFuzzyCompare(qMax(murect.width(), murect.height()) + 1, 1)) + if (qFuzzyIsNull(qMax(murect.width(), murect.height()))) return; const QRectF mbrect = painter->transform().mapRect(item->boundingRect()); @@ -7484,9 +8148,7 @@ void QGraphicsPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsIte painter->setRenderHint(QPainter::SmoothPixmapTransform, (d->transformationMode == Qt::SmoothTransformation)); - QRectF exposed = option->exposedRect.adjusted(-1, -1, 1, 1); - exposed &= QRectF(d->offset.x(), d->offset.y(), d->pixmap.width(), d->pixmap.height()); - painter->drawPixmap(exposed, d->pixmap, exposed.translated(-d->offset)); + painter->drawPixmap(d->offset, d->pixmap); if (option->state & QStyle::State_Selected) qt_graphicsItem_highlightSelected(this, painter, option); @@ -7652,6 +8314,7 @@ QGraphicsTextItem::QGraphicsTextItem(const QString &text, QGraphicsItem *parent setPlainText(text); setAcceptDrops(true); setAcceptHoverEvents(true); + setFlags(ItemUsesExtendedStyleOption); } /*! @@ -7671,6 +8334,7 @@ QGraphicsTextItem::QGraphicsTextItem(QGraphicsItem *parent dd->qq = this; setAcceptDrops(true); setAcceptHoverEvents(true); + setFlag(ItemUsesExtendedStyleOption); } /*! @@ -7941,19 +8605,19 @@ bool QGraphicsTextItem::sceneEvent(QEvent *event) void QGraphicsTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { if ((QGraphicsItem::d_ptr->flags & (ItemIsSelectable | ItemIsMovable)) - && (event->buttons() & Qt::LeftButton) && dd->_q_mouseOnEdge(event)) { - // User left-pressed on edge of selectable/movable item, use - // base impl. - dd->useDefaultImpl = true; + && (event->buttons() & Qt::LeftButton) && dd->_q_mouseOnEdge(event)) { + // User left-pressed on edge of selectable/movable item, use + // base impl. + dd->useDefaultImpl = true; } else if (event->buttons() == event->button() - && dd->control->textInteractionFlags() == Qt::NoTextInteraction) { - // User pressed first button on non-interactive item. - dd->useDefaultImpl = true; + && dd->control->textInteractionFlags() == Qt::NoTextInteraction) { + // User pressed first button on non-interactive item. + dd->useDefaultImpl = true; } if (dd->useDefaultImpl) { QGraphicsItem::mousePressEvent(event); - if (!event->isAccepted()) - dd->useDefaultImpl = false; + if (!event->isAccepted()) + dd->useDefaultImpl = false; return; } dd->sendControlEvent(event); @@ -7978,14 +8642,14 @@ void QGraphicsTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (dd->useDefaultImpl) { QGraphicsItem::mouseReleaseEvent(event); - if (dd->control->textInteractionFlags() == Qt::NoTextInteraction - && !event->buttons()) { - // User released last button on non-interactive item. + if (dd->control->textInteractionFlags() == Qt::NoTextInteraction + && !event->buttons()) { + // User released last button on non-interactive item. dd->useDefaultImpl = false; - } else if ((event->buttons() & Qt::LeftButton) == 0) { - // User released the left button on an interactive item. + } else if ((event->buttons() & Qt::LeftButton) == 0) { + // User released the left button on an interactive item. dd->useDefaultImpl = false; - } + } return; } dd->sendControlEvent(event); @@ -8275,9 +8939,9 @@ bool QGraphicsTextItemPrivate::_q_mouseOnEdge(QGraphicsSceneMouseEvent *event) Sets the flags \a flags to specify how the text item should react to user input. - The default for a QGraphicsTextItem is Qt::NoTextInteraction. Setting a - value different to Qt::NoTextInteraction will also set the ItemIsFocusable - QGraphicsItem flag. + The default for a QGraphicsTextItem is Qt::NoTextInteraction. This function + also affects the ItemIsFocusable QGraphicsItem flag by setting it if \a flags + is different from Qt::NoTextInteraction and clearing it otherwise. By default, the text is read-only. To transform the item into an editor, set the Qt::TextEditable flag. @@ -9016,6 +9680,9 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) case QGraphicsItem::ItemStacksBehindParent: str = "ItemStacksBehindParent"; break; + case QGraphicsItem::ItemUsesExtendedStyleOption: + str = "ItemUsesExtendedStyleOption"; + break; } debug << str; return debug; @@ -9023,17 +9690,17 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags) { - debug << "("; + debug << '('; bool f = false; for (int i = 0; i < 9; ++i) { if (flags & (1 << i)) { if (f) - debug << "|"; + debug << '|'; f = true; debug << QGraphicsItem::GraphicsItemFlag(int(flags & (1 << i))); } } - debug << ")"; + debug << ')'; return debug; } diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index b98882d..f6ee197 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -94,7 +94,9 @@ public: ItemIgnoresTransformations = 0x20, ItemIgnoresParentOpacity = 0x40, ItemDoesntPropagateOpacityToChildren = 0x80, - ItemStacksBehindParent = 0x100 + ItemStacksBehindParent = 0x100, + ItemUsesExtendedStyleOption = 0x200 + // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag. }; Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag) @@ -239,11 +241,41 @@ public: QTransform itemTransform(const QGraphicsItem *other, bool *ok = 0) const; void setTransform(const QTransform &matrix, bool combine = false); void resetTransform(); - - void rotate(qreal angle); - void scale(qreal sx, qreal sy); - void shear(qreal sh, qreal sv); - void translate(qreal dx, qreal dy); + + void rotate(qreal angle); // ### obsolete + void scale(qreal sx, qreal sy); // ### obsolete + void shear(qreal sh, qreal sv); // ### obsolete + void translate(qreal dx, qreal dy); // ### obsolete + + qreal xRotation() const; + void setXRotation(qreal angle); + + qreal yRotation() const; + void setYRotation(qreal angle); + + qreal zRotation() const; + void setZRotation(qreal angle); + void setRotation(qreal x, qreal y, qreal z); + + qreal xScale() const; + void setXScale(qreal factor); + + qreal yScale() const; + void setYScale(qreal factor); + void setScale(qreal sx, qreal sy); + + qreal horizontalShear() const; + void setHorizontalShear(qreal shear); + + qreal verticalShear() const; + void setVerticalShear(qreal shear); + void setShear(qreal sh, qreal sv); + + QPointF transformOrigin() const; + void setTransformOrigin(const QPointF &origin); + inline void setTransformOrigin(qreal x, qreal y) + { setTransformOrigin(QPointF(x,y)); } + virtual void advance(int phase); // Stacking order @@ -1013,4 +1045,3 @@ QT_END_NAMESPACE QT_END_HEADER #endif // QGRAPHICSITEM_H - diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 9ce1bbf..62e0411 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -54,6 +54,9 @@ // #include "qgraphicsitem.h" +#include "qpixmapcache.h" + +#include <QtCore/qpoint.h> #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW @@ -69,13 +72,14 @@ public: // ItemCoordinateCache only QRect boundingRect; QSize fixedSize; - QString key; + QPixmapCache::Key key; // DeviceCoordinateCache only struct DeviceData { + DeviceData() {} QTransform lastTransform; QPoint cacheIndent; - QString key; + QPixmapCache::Key key; }; QMap<QPaintDevice *, DeviceData> deviceData; @@ -91,6 +95,15 @@ class Q_AUTOTEST_EXPORT QGraphicsItemPrivate { Q_DECLARE_PUBLIC(QGraphicsItem) public: + struct TransformData + { + TransformData() : rotationX(0),rotationY(0),rotationZ(0),scaleX(1),scaleY(1), dirty(true) {} + QTransform baseTransform; + QTransform transform; + QPointF transformCenter; + qreal rotationX,rotationY,rotationZ,scaleX,scaleY; + bool dirty; + }; enum Extra { ExtraTransform, ExtraToolTip, @@ -99,7 +112,8 @@ public: ExtraMaxDeviceCoordCacheSize, ExtraBoundingRegionGranularity, ExtraOpacity, - ExtraEffectiveOpacity + ExtraEffectiveOpacity, + ExtraDecomposedTransform }; enum AncestorFlag { @@ -113,6 +127,7 @@ public: : z(0), scene(0), parent(0), + siblingIndex(-1), index(-1), depth(0), acceptedMouseButtons(0x1f), @@ -131,7 +146,6 @@ public: ancestorFlags(0), cacheMode(0), hasBoundingRegionGranularity(0), - flags(0), hasOpacity(0), hasEffectiveOpacity(0), isWidget(0), @@ -141,7 +155,11 @@ public: dirtyClipPath(1), emptyClipPath(0), inSetPosHelper(0), + flags(0), allChildrenCombineOpacity(1), + hasDecomposedTransform(0), + dirtyTransform(0), + dirtyTransformComponents(0), globalStackingOrder(-1), sceneTransformIndex(-1), q_ptr(0) @@ -173,6 +191,12 @@ public: void resolveEffectiveOpacity(qreal effectiveParentOpacity); void resolveDepth(int parentDepth); void invalidateSceneTransformCache(); + void addChild(QGraphicsItem *child); + void removeChild(QGraphicsItem *child); + void setParentItemHelper(QGraphicsItem *parent, bool deleting); + void childrenBoundingRectHelper(QTransform *x, QRectF *rect); + void initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform, + const QRegion &exposedRegion, bool allItems = false) const; virtual void resolveFont(uint inheritedMask) { @@ -224,7 +248,7 @@ public: } } } - + struct ExtraStruct { ExtraStruct(Extra type, QVariant value) : type(type), value(value) @@ -236,8 +260,10 @@ public: bool operator<(Extra extra) const { return type < extra; } }; + QList<ExtraStruct> extras; + QGraphicsItemCache *maybeExtraItemCache() const; QGraphicsItemCache *extraItemCache() const; void removeExtraItemCache(); @@ -263,7 +289,7 @@ public: void updateCachedClipPathFromSetPosHelper(const QPointF &newPos); inline bool isFullyTransparent() const - { return hasEffectiveOpacity && qFuzzyCompare(q_func()->effectiveOpacity() + 1, qreal(1.0)); } + { return hasEffectiveOpacity && qFuzzyIsNull(q_func()->effectiveOpacity()); } inline bool childrenCombineOpacity() const { return allChildrenCombineOpacity || children.isEmpty(); } @@ -287,10 +313,11 @@ public: QGraphicsScene *scene; QGraphicsItem *parent; QList<QGraphicsItem *> children; + int siblingIndex; int index; int depth; - // Packed 32 bytes + // Packed 32 bits quint32 acceptedMouseButtons : 5; quint32 visible : 1; quint32 explicitlyHidden : 1; @@ -307,9 +334,6 @@ public: quint32 ancestorFlags : 3; quint32 cacheMode : 2; quint32 hasBoundingRegionGranularity : 1; - quint32 flags : 9; - - // New 32 bytes quint32 hasOpacity : 1; quint32 hasEffectiveOpacity : 1; quint32 isWidget : 1; @@ -319,15 +343,88 @@ public: quint32 dirtyClipPath : 1; quint32 emptyClipPath : 1; quint32 inSetPosHelper : 1; + + // New 32 bits + quint32 flags : 10; quint32 allChildrenCombineOpacity : 1; + quint32 hasDecomposedTransform : 1; + quint32 dirtyTransform : 1; + quint32 dirtyTransformComponents : 1; + quint32 padding : 18; // feel free to use // Optional stacking order int globalStackingOrder; int sceneTransformIndex; + struct DecomposedTransform; + DecomposedTransform *decomposedTransform() const + { + QGraphicsItemPrivate *that = const_cast<QGraphicsItemPrivate *>(this); + DecomposedTransform *decomposed; + if (hasDecomposedTransform) { + decomposed = qVariantValue<DecomposedTransform *>(extra(ExtraDecomposedTransform)); + } else { + decomposed = new DecomposedTransform; + that->setExtra(ExtraDecomposedTransform, qVariantFromValue<DecomposedTransform *>(decomposed)); + that->hasDecomposedTransform = 1; + if (!dirtyTransformComponents) + decomposed->reset(); + } + if (dirtyTransformComponents) { + decomposed->initFrom(q_ptr->transform()); + that->dirtyTransformComponents = 0; + } + return decomposed; + } + + struct DecomposedTransform { + qreal xScale; + qreal yScale; + qreal xRotation; + qreal yRotation; + qreal zRotation; + qreal horizontalShear; + qreal verticalShear; + qreal xOrigin; + qreal yOrigin; + + inline void reset() + { + xScale = 1.0; + yScale = 1.0; + xRotation = 0.0; + yRotation = 0.0; + zRotation = 0.0; + horizontalShear = 0.0; + verticalShear = 0.0; + xOrigin = 0.0; + yOrigin = 0.0; + } + + inline void initFrom(const QTransform &x) + { + reset(); + // ### decompose transform + Q_UNUSED(x); + } + + inline void generateTransform(QTransform *x) const + { + x->translate(xOrigin, yOrigin); + x->rotate(xRotation, Qt::XAxis); + x->rotate(yRotation, Qt::YAxis); + x->rotate(zRotation, Qt::ZAxis); + x->shear(horizontalShear, verticalShear); + x->scale(xScale, yScale); + x->translate(-xOrigin, -yOrigin); + } + }; + QGraphicsItem *q_ptr; }; +Q_DECLARE_METATYPE(QGraphicsItemPrivate::DecomposedTransform *) + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicslayoutitem.cpp b/src/gui/graphicsview/qgraphicslayoutitem.cpp index b46e05e..b58eb53 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem.cpp +++ b/src/gui/graphicsview/qgraphicslayoutitem.cpp @@ -108,13 +108,22 @@ static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qre \internal */ QGraphicsLayoutItemPrivate::QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *par, bool layout) - : parent(par), isLayout(layout), ownedByLayout(false), graphicsItem(0) + : parent(par), userSizeHints(0), isLayout(layout), ownedByLayout(false), graphicsItem(0) { } /*! \internal */ +QGraphicsLayoutItemPrivate::~QGraphicsLayoutItemPrivate() +{ + // Remove any lazily allocated data + delete[] userSizeHints; +} + +/*! + \internal +*/ void QGraphicsLayoutItemPrivate::init() { sizeHintCacheDirty = true; @@ -132,7 +141,8 @@ QSizeF *QGraphicsLayoutItemPrivate::effectiveSizeHints(const QSizeF &constraint) for (int i = 0; i < Qt::NSizeHints; ++i) { cachedSizeHints[i] = constraint; - combineSize(cachedSizeHints[i], userSizeHints[i]); + if (userSizeHints) + combineSize(cachedSizeHints[i], userSizeHints[i]); } QSizeF &minS = cachedSizeHints[Qt::MinimumSize]; @@ -198,6 +208,58 @@ QGraphicsItem *QGraphicsLayoutItemPrivate::parentItem() const } /*! + \internal + + Ensures that userSizeHints is allocated. + This function must be called before any dereferencing. +*/ +void QGraphicsLayoutItemPrivate::ensureUserSizeHints() +{ + if (!userSizeHints) + userSizeHints = new QSizeF[Qt::NSizeHints]; +} + +/*! + \internal + + Sets the user size hint \a which to \a size. Use an invalid size to unset the size hint. + */ +void QGraphicsLayoutItemPrivate::setSize(Qt::SizeHint which, const QSizeF &size) +{ + Q_Q(QGraphicsLayoutItem); + + if (userSizeHints) { + if (size == userSizeHints[which]) + return; + } else if (!size.isValid()) { + return; + } + + ensureUserSizeHints(); + userSizeHints[which] = size; + q->updateGeometry(); +} + +/*! + \internal + + Sets the width of the user size hint \a which to \a width. + */ +void QGraphicsLayoutItemPrivate::setSizeComponent( + Qt::SizeHint which, SizeComponent component, qreal value) +{ + Q_Q(QGraphicsLayoutItem); + ensureUserSizeHints(); + qreal &userValue = (component == Width) + ? userSizeHints[which].rwidth() + : userSizeHints[which].rheight(); + if (value == userValue) + return; + userValue = value; + q->updateGeometry(); +} + +/*! \class QGraphicsLayoutItem \brief The QGraphicsLayoutItem class can be inherited to allow your custom items to be managed by layouts. @@ -381,12 +443,7 @@ QSizePolicy QGraphicsLayoutItem::sizePolicy() const */ void QGraphicsLayoutItem::setMinimumSize(const QSizeF &size) { - Q_D(QGraphicsLayoutItem); - if (size == d->userSizeHints[Qt::MinimumSize]) - return; - - d->userSizeHints[Qt::MinimumSize] = size; - updateGeometry(); + d_ptr->setSize(Qt::MinimumSize, size); } /*! @@ -416,12 +473,7 @@ QSizeF QGraphicsLayoutItem::minimumSize() const */ void QGraphicsLayoutItem::setMinimumWidth(qreal width) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::MinimumSize].rwidth(); - if (width == userSizeHint) - return; - userSizeHint = width; - updateGeometry(); + d_ptr->setSizeComponent(Qt::MinimumSize, d_ptr->Width, width); } /*! @@ -431,12 +483,7 @@ void QGraphicsLayoutItem::setMinimumWidth(qreal width) */ void QGraphicsLayoutItem::setMinimumHeight(qreal height) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::MinimumSize].rheight(); - if (height == userSizeHint) - return; - userSizeHint = height; - updateGeometry(); + d_ptr->setSizeComponent(Qt::MinimumSize, d_ptr->Height, height); } @@ -450,12 +497,7 @@ void QGraphicsLayoutItem::setMinimumHeight(qreal height) */ void QGraphicsLayoutItem::setPreferredSize(const QSizeF &size) { - Q_D(QGraphicsLayoutItem); - if (size == d->userSizeHints[Qt::PreferredSize]) - return; - - d->userSizeHints[Qt::PreferredSize] = size; - updateGeometry(); + d_ptr->setSize(Qt::PreferredSize, size); } /*! @@ -485,12 +527,7 @@ QSizeF QGraphicsLayoutItem::preferredSize() const */ void QGraphicsLayoutItem::setPreferredHeight(qreal height) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::PreferredSize].rheight(); - if (height == userSizeHint) - return; - userSizeHint = height; - updateGeometry(); + d_ptr->setSizeComponent(Qt::PreferredSize, d_ptr->Height, height); } /*! @@ -500,12 +537,7 @@ void QGraphicsLayoutItem::setPreferredHeight(qreal height) */ void QGraphicsLayoutItem::setPreferredWidth(qreal width) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::PreferredSize].rwidth(); - if (width == userSizeHint) - return; - userSizeHint = width; - updateGeometry(); + d_ptr->setSizeComponent(Qt::PreferredSize, d_ptr->Width, width); } /*! @@ -519,12 +551,7 @@ void QGraphicsLayoutItem::setPreferredWidth(qreal width) */ void QGraphicsLayoutItem::setMaximumSize(const QSizeF &size) { - Q_D(QGraphicsLayoutItem); - if (size == d->userSizeHints[Qt::MaximumSize]) - return; - - d->userSizeHints[Qt::MaximumSize] = size; - updateGeometry(); + d_ptr->setSize(Qt::MaximumSize, size); } /*! @@ -554,12 +581,7 @@ QSizeF QGraphicsLayoutItem::maximumSize() const */ void QGraphicsLayoutItem::setMaximumWidth(qreal width) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::MaximumSize].rwidth(); - if (width == userSizeHint) - return; - userSizeHint = width; - updateGeometry(); + d_ptr->setSizeComponent(Qt::MaximumSize, d_ptr->Width, width); } /*! @@ -569,12 +591,7 @@ void QGraphicsLayoutItem::setMaximumWidth(qreal width) */ void QGraphicsLayoutItem::setMaximumHeight(qreal height) { - Q_D(QGraphicsLayoutItem); - qreal &userSizeHint = d->userSizeHints[Qt::MaximumSize].rheight(); - if (height == userSizeHint) - return; - userSizeHint = height; - updateGeometry(); + d_ptr->setSizeComponent(Qt::MaximumSize, d_ptr->Height, height); } /*! @@ -732,6 +749,11 @@ QRectF QGraphicsLayoutItem::contentsRect() const */ QSizeF QGraphicsLayoutItem::effectiveSizeHint(Qt::SizeHint which, const QSizeF &constraint) const { + Q_D(const QGraphicsLayoutItem); + + if (!d->userSizeHints && constraint.isValid()) + return constraint; + // ### should respect size policy??? return d_ptr->effectiveSizeHints(constraint)[which]; } diff --git a/src/gui/graphicsview/qgraphicslayoutitem_p.h b/src/gui/graphicsview/qgraphicslayoutitem_p.h index fab0f39..dc044e6 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem_p.h +++ b/src/gui/graphicsview/qgraphicslayoutitem_p.h @@ -63,16 +63,20 @@ class Q_AUTOTEST_EXPORT QGraphicsLayoutItemPrivate { Q_DECLARE_PUBLIC(QGraphicsLayoutItem) public: - virtual ~QGraphicsLayoutItemPrivate() {} + virtual ~QGraphicsLayoutItemPrivate(); QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *parent, bool isLayout); void init(); QSizeF *effectiveSizeHints(const QSizeF &constraint) const; QGraphicsItem *parentItem() const; + void ensureUserSizeHints(); + void setSize(Qt::SizeHint which, const QSizeF &size); + enum SizeComponent { Width, Height }; + void setSizeComponent(Qt::SizeHint which, SizeComponent component, qreal value); QSizePolicy sizePolicy; QGraphicsLayoutItem *parent; - QSizeF userSizeHints[Qt::NSizeHints]; + QSizeF *userSizeHints; mutable QSizeF cachedSizeHints[Qt::NSizeHints]; mutable QSizeF cachedConstraint; diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index e660879..a5b11ff 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -460,7 +460,7 @@ void QGraphicsProxyWidgetPrivate::embedSubWindow(QWidget *subWin) { QWExtra *extra; if (!((extra = subWin->d_func()->extra) && extra->proxyWidget)) { - QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func()); + QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func(), subWin->windowFlags()); subProxy->d_func()->setWidget_helper(subWin, false); } } @@ -544,6 +544,9 @@ QGraphicsProxyWidget::~QGraphicsProxyWidget() hidden or disabled after embedding is complete. The class documentation has a full overview over the shared state. + QGraphicsProxyWidget's window flags determine whether the widget, after + embedding, will be given window decorations or not. + After this function returns, QGraphicsProxyWidget will keep its state synchronized with that of \a widget whenever possible. @@ -661,10 +664,6 @@ void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool auto if (newWidget->testAttribute(Qt::WA_SetCursor)) q->setCursor(widget->cursor()); #endif - Qt::WFlags flags = newWidget->windowFlags(); - if (newWidget->windowType() == Qt::Window) - flags &= ~Qt::Window; - q->setWindowFlags(flags); q->setEnabled(newWidget->isEnabled()); q->setVisible(newWidget->isVisible()); q->setLayoutDirection(newWidget->layoutDirection()); @@ -977,6 +976,7 @@ void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *even } #endif // QT_NO_CONTEXTMENU +#ifndef QT_NO_DRAGANDDROP /*! \reimp */ @@ -1097,6 +1097,7 @@ void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event) } #endif } +#endif /*! \reimp diff --git a/src/gui/graphicsview/qgraphicsproxywidget.h b/src/gui/graphicsview/qgraphicsproxywidget.h index b2c3c8f..ab8c9da 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.h +++ b/src/gui/graphicsview/qgraphicsproxywidget.h @@ -90,10 +90,12 @@ protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); #endif +#ifndef QT_NO_DRAGANDDROP void dragEnterEvent(QGraphicsSceneDragDropEvent *event); void dragLeaveEvent(QGraphicsSceneDragDropEvent *event); void dragMoveEvent(QGraphicsSceneDragDropEvent *event); void dropEvent(QGraphicsSceneDragDropEvent *event); +#endif void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index b89e352..1fc4567 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -351,6 +351,8 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() dragDropItem(0), enterWidget(0), lastDropAction(Qt::IgnoreAction), + allItemsIgnoreHoverEvents(true), + allItemsUseDefaultCursor(true), painterStateProtection(true), sortCacheEnabled(false), updatingSortCache(false), @@ -622,6 +624,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. @@ -756,10 +782,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); @@ -880,11 +902,18 @@ void QGraphicsScenePrivate::grabMouse(QGraphicsItem *item, bool implicit) { // Append to list of mouse grabber items, and send a mouse grab event. if (mouseGrabberItems.contains(item)) { - if (mouseGrabberItems.last() == item) - qWarning("QGraphicsItem::grabMouse: already a mouse grabber"); - else + if (mouseGrabberItems.last() == item) { + Q_ASSERT(!implicit); + if (!lastMouseGrabberItemHasImplicitMouseGrab) { + qWarning("QGraphicsItem::grabMouse: already a mouse grabber"); + } else { + // Upgrade to an explicit mouse grab + lastMouseGrabberItemHasImplicitMouseGrab = false; + } + } else { qWarning("QGraphicsItem::grabMouse: already blocked by mouse grabber: %p", mouseGrabberItems.last()); + } return; } @@ -1036,6 +1065,12 @@ void QGraphicsScenePrivate::clearKeyboardGrabber() ungrabKeyboard(keyboardGrabberItems.first()); } +void QGraphicsScenePrivate::enableMouseTrackingOnViews() +{ + foreach (QGraphicsView *view, views) + view->viewport()->setMouseTracking(true); +} + /*! Returns all items for the screen position in \a event. */ @@ -1294,7 +1329,8 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou // check if the item we are sending to are disabled (before we send the event) bool disabled = !item->isEnabled(); bool isWindow = item->isWindow(); - if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick && item != lastMouseGrabberItem) { + if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick + && item != lastMouseGrabberItem && lastMouseGrabberItem) { // If this item is different from the item that received the last // mouse event, and mouseEvent is a doubleclick event, then the // event is converted to a press. Known limitation: @@ -1831,7 +1867,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; } /*! @@ -2211,8 +2247,6 @@ void QGraphicsScene::setSceneRect(const QRectF &rect) void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRectF &source, Qt::AspectRatioMode aspectRatioMode) { - Q_D(QGraphicsScene); - // Default source rect = scene rect QRectF sourceRect = source; if (sourceRect.isNull()) @@ -2269,36 +2303,8 @@ void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRect // Generate the style options QStyleOptionGraphicsItem *styleOptionArray = new QStyleOptionGraphicsItem[numItems]; - for (int i = 0; i < numItems; ++i) { - QGraphicsItem *item = itemArray[i]; - - QStyleOptionGraphicsItem option; - option.state = QStyle::State_None; - option.rect = item->boundingRect().toRect(); - if (item->isSelected()) - option.state |= QStyle::State_Selected; - if (item->isEnabled()) - option.state |= QStyle::State_Enabled; - if (item->hasFocus()) - option.state |= QStyle::State_HasFocus; - if (d->hoverItems.contains(item)) - option.state |= QStyle::State_MouseOver; - if (item == mouseGrabberItem()) - option.state |= QStyle::State_Sunken; - - // Calculate a simple level-of-detail metric. - // ### almost identical code in QGraphicsView::paintEvent() - // and QGraphicsView::render() - consider refactoring - QTransform itemToDeviceTransform = item->deviceTransform(painterTransform); - - option.levelOfDetail = qSqrt(itemToDeviceTransform.map(v1).length() * itemToDeviceTransform.map(v2).length()); - option.matrix = itemToDeviceTransform.toAffine(); //### discards perspective - - option.exposedRect = item->boundingRect(); - option.exposedRect &= itemToDeviceTransform.inverted().mapRect(targetRect); - - styleOptionArray[i] = option; - } + for (int i = 0; i < numItems; ++i) + itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], painterTransform, targetRect.toRect()); // Render the scene. drawBackground(painter, sourceRect); @@ -2742,12 +2748,13 @@ void QGraphicsScene::clear() } d->unindexedItems.clear(); qDeleteAll(unindexedParents); - d->indexedItems.clear(); d->freeItemIndexes.clear(); d->lastItemCount = 0; d->bspTree.clear(); d->largestUntransformableItem = QRectF(); + d->allItemsIgnoreHoverEvents = true; + d->allItemsUseDefaultCursor = true; } /*! @@ -2851,7 +2858,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); @@ -2892,6 +2898,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(); @@ -2909,6 +2919,17 @@ void QGraphicsScene::addItem(QGraphicsItem *item) ++d->selectionChanging; int oldSelectedItemSize = d->selectedItems.size(); + // Enable mouse tracking if the item accepts hover events or has a cursor set. + if (d->allItemsIgnoreHoverEvents && d->itemAcceptsHoverEvents_helper(item)) { + d->allItemsIgnoreHoverEvents = false; + d->enableMouseTrackingOnViews(); + } + if (d->allItemsUseDefaultCursor && item->hasCursor()) { + d->allItemsUseDefaultCursor = false; + if (d->allItemsIgnoreHoverEvents) // already enabled otherwise + d->enableMouseTrackingOnViews(); + } + // Update selection lists if (item->isSelected()) d->selectedItems << item; @@ -3244,13 +3265,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. @@ -4209,6 +4232,9 @@ bool QGraphicsScenePrivate::itemAcceptsHoverEvents_helper(const QGraphicsItem *i */ bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent) { + if (allItemsIgnoreHoverEvents) + return false; + // Find the first item that accepts hover events, reusing earlier // calculated data is possible. if (cachedItemsUnderMouse.isEmpty()) { @@ -4608,7 +4634,7 @@ static void _q_paintItem(QGraphicsItem *item, QPainter *painter, ? proxy->widget()->windowOpacity() : 1.0; const qreal oldPainterOpacity = painter->opacity(); - if (qFuzzyCompare(windowOpacity + 1, qreal(1.0))) + if (qFuzzyIsNull(windowOpacity)) return; // Set new painter opacity. if (windowOpacity < 1.0) @@ -4717,8 +4743,9 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte return; // Fetch the off-screen transparent buffer and exposed area info. - QString pixmapKey; + QPixmapCache::Key pixmapKey; QPixmap pix; + bool pixmapFound; QGraphicsItemCache *itemCache = itemd->extraItemCache(); if (cacheMode == QGraphicsItem::ItemCoordinateCache) { if (itemCache->boundingRect != brect.toRect()) { @@ -4728,17 +4755,11 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte } pixmapKey = itemCache->key; } else { - if ((pixmapKey = itemCache->deviceData.value(widget).key).isEmpty()) { - pixmapKey.sprintf("qgv-%p-%p", item, widget); - QGraphicsItemCache::DeviceData data; - data.key = pixmapKey; - itemCache->deviceData.insert(widget, data); - } + pixmapKey = itemCache->deviceData.value(widget).key; } // Find pixmap in cache. - if (!itemCache->allExposed) - QPixmapCache::find(pixmapKey, pix); + pixmapFound = QPixmapCache::find(pixmapKey, &pix); // Render using item coordinate cache mode. if (cacheMode == QGraphicsItem::ItemCoordinateCache) { @@ -4763,6 +4784,12 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte // Redraw any newly exposed areas. if (itemCache->allExposed || !itemCache->exposed.isEmpty()) { + + //We know that we will modify the pixmap, removing it from the cache + //will detach the one we have and avoid a deep copy + if (pixmapFound) + QPixmapCache::remove(pixmapKey); + // Fit the item's bounding rect into the pixmap's coordinates. QTransform itemToPixmap; if (fixedCacheSize) { @@ -4791,8 +4818,8 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(), &cacheOption, painterStateProtection); - // Reinsert this pixmap into the cache. - QPixmapCache::insert(pixmapKey, pix); + // insert this pixmap into the cache. + itemCache->key = QPixmapCache::insert(pix); // Reset expose data. itemCache->allExposed = false; @@ -4919,6 +4946,11 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte // Check for newly invalidated areas. if (itemCache->allExposed || !itemCache->exposed.isEmpty() || !scrollExposure.isEmpty()) { + //We know that we will modify the pixmap, removing it from the cache + //will detach the one we have and avoid a deep copy + if (pixmapFound) + QPixmapCache::remove(pixmapKey); + // Construct an item-to-pixmap transform. QPointF p = deviceRect.topLeft(); QTransform itemToPixmap = painter->worldTransform(); @@ -4959,8 +4991,8 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte } if (pixModified) { - // Reinsert this pixmap into the cache - QPixmapCache::insert(pixmapKey, pix); + // Insert this pixmap into the cache. + deviceData->key = QPixmapCache::insert(pix); } // Redraw the exposed area using an untransformed painter. This @@ -5242,7 +5274,7 @@ void QGraphicsScene::itemUpdated(QGraphicsItem *item, const QRectF &rect) // Deliver the actual update. if (!d->updateAll) { if (d->views.isEmpty() || ((d->connectedSignals & d->changedSignalMask) && !item->d_ptr->itemIsUntransformable() - && qFuzzyCompare(item->boundingRegionGranularity(), qreal(0.0)))) { + && qFuzzyIsNull(item->boundingRegionGranularity()))) { // This block of code is kept for compatibility. Since 4.5, by default // QGraphicsView does not connect the signal and we use the below // method of delivering updates. diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index befbbd8..9ace725 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(); @@ -165,6 +168,9 @@ public: Qt::DropAction lastDropAction; QList<QGraphicsItem *> cachedItemsUnderMouse; QList<QGraphicsItem *> hoverItems; + bool allItemsIgnoreHoverEvents; + bool allItemsUseDefaultCursor; + void enableMouseTrackingOnViews(); QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownPos; QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownScenePos; QMap<Qt::MouseButton, QPoint> mouseGrabberButtonDownScreenPos; diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index a795fb4..501aef1 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -614,6 +614,16 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) } #ifndef QT_NO_CURSOR + // If all the items ignore hover events, we don't look-up any items + // in QGraphicsScenePrivate::dispatchHoverEvent, hence the + // cachedItemsUnderMouse list will be empty. We therefore do the look-up + // for cursor items here if not all items use the default cursor. + if (scene->d_func()->allItemsIgnoreHoverEvents && !scene->d_func()->allItemsUseDefaultCursor + && scene->d_func()->cachedItemsUnderMouse.isEmpty()) { + scene->d_func()->cachedItemsUnderMouse = scene->d_func()->itemsAtPosition(mouseEvent.screenPos(), + mouseEvent.scenePos(), + mouseEvent.widget()); + } // Find the topmost item under the mouse with a cursor. foreach (QGraphicsItem *item, scene->d_func()->cachedItemsUnderMouse) { if (item->hasCursor()) { @@ -1061,7 +1071,7 @@ QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedReg // Step 1) If all items are contained within the expose region, then // return a list of all visible items. - const QRectF exposedRegionSceneBounds = q->mapToScene(exposedRegion.boundingRect().adjusted(-1, -1, 2, 2)) + const QRectF exposedRegionSceneBounds = q->mapToScene(exposedRegion.boundingRect().adjusted(-1, -1, 1, 1)) .boundingRect(); if (exposedRegionSceneBounds.contains(scene->d_func()->growingItemsBoundingRect)) { Q_ASSERT(allItems); @@ -1117,69 +1127,6 @@ QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedReg return itemsInArea(exposedPath, Qt::IntersectsItemBoundingRect, Qt::DescendingOrder); } -void QGraphicsViewPrivate::generateStyleOptions(const QList<QGraphicsItem *> &itemList, - QGraphicsItem **itemArray, - QStyleOptionGraphicsItem *styleOptionArray, - const QTransform &worldTransform, - bool allItems, - const QRegion &exposedRegion) const -{ - // Two unit vectors. - QLineF v1(0, 0, 1, 0); - QLineF v2(0, 0, 0, 1); - QTransform itemToViewportTransform; - QRectF brect; - QTransform reverseMap; - - for (int i = 0; i < itemList.size(); ++i) { - QGraphicsItem *item = itemArray[i] = itemList[i]; - - QStyleOptionGraphicsItem &option = styleOptionArray[i]; - brect = item->boundingRect(); - option.state = QStyle::State_None; - option.rect = brect.toRect(); - option.exposedRect = QRectF(); - if (item->d_ptr->selected) - option.state |= QStyle::State_Selected; - if (item->d_ptr->enabled) - option.state |= QStyle::State_Enabled; - if (item->hasFocus()) - option.state |= QStyle::State_HasFocus; - if (scene->d_func()->hoverItems.contains(item)) - option.state |= QStyle::State_MouseOver; - if (item == scene->mouseGrabberItem()) - option.state |= QStyle::State_Sunken; - - // Calculate a simple level-of-detail metric. - // ### almost identical code in QGraphicsScene::render() - // and QGraphicsView::render() - consider refactoring - itemToViewportTransform = item->deviceTransform(worldTransform); - - if (itemToViewportTransform.type() <= QTransform::TxTranslate) { - // Translation and rotation only? The LOD is 1. - option.levelOfDetail = 1; - } else { - // LOD is the transformed area of a 1x1 rectangle. - option.levelOfDetail = qSqrt(itemToViewportTransform.map(v1).length() * itemToViewportTransform.map(v2).length()); - } - option.matrix = itemToViewportTransform.toAffine(); //### discards perspective - - if (!allItems) { - // Determine the item's exposed area - reverseMap = itemToViewportTransform.inverted(); - foreach (const QRect &rect, exposedRegion.rects()) { - option.exposedRect |= reverseMap.mapRect(QRectF(rect.adjusted(-1, -1, 1, 1))); - if (option.exposedRect.contains(brect)) - break; - } - option.exposedRect &= brect; - } else { - // The whole item is exposed - option.exposedRect = brect; - } - } -} - /*! Constructs a QGraphicsView. \a parent is passed to QWidget's constructor. */ @@ -1689,6 +1636,12 @@ void QGraphicsView::setScene(QGraphicsScene *scene) d->recalculateContentSize(); d->lastCenterPoint = sceneRect().center(); d->keepLastCenterPoint = true; + // We are only interested in mouse tracking if items accept + // hover events or use non-default cursors. + if (!d->scene->d_func()->allItemsIgnoreHoverEvents + || !d->scene->d_func()->allItemsUseDefaultCursor) { + d->viewport->setMouseTracking(true); + } } else { d->recalculateContentSize(); } @@ -2130,40 +2083,10 @@ void QGraphicsView::render(QPainter *painter, const QRectF &target, const QRect .scale(xratio, yratio) .translate(-sourceRect.left(), -sourceRect.top()); - // Two unit vectors. - QLineF v1(0, 0, 1, 0); - QLineF v2(0, 0, 0, 1); - // Generate the style options QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems); - QStyleOptionGraphicsItem* option = styleOptionArray; - for (int i = 0; i < numItems; ++i, ++option) { - QGraphicsItem *item = itemArray[i]; - - option->state = QStyle::State_None; - option->rect = item->boundingRect().toRect(); - if (item->isSelected()) - option->state |= QStyle::State_Selected; - if (item->isEnabled()) - option->state |= QStyle::State_Enabled; - if (item->hasFocus()) - option->state |= QStyle::State_HasFocus; - if (d->scene->d_func()->hoverItems.contains(item)) - option->state |= QStyle::State_MouseOver; - if (item == d->scene->mouseGrabberItem()) - option->state |= QStyle::State_Sunken; - - // Calculate a simple level-of-detail metric. - // ### almost identical code in QGraphicsScene::render() - // and QGraphicsView::paintEvent() - consider refactoring - QTransform itemToViewportTransform = item->deviceTransform(painterMatrix); - - option->levelOfDetail = qSqrt(itemToViewportTransform.map(v1).length() * itemToViewportTransform.map(v2).length()); - option->matrix = itemToViewportTransform.toAffine(); - - option->exposedRect = item->boundingRect(); - option->exposedRect &= itemToViewportTransform.inverted().mapRect(targetRect); - } + for (int i = 0; i < numItems; ++i) + itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], painterMatrix, targetRect.toRect()); painter->save(); @@ -2289,7 +2212,7 @@ QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const QTransform xinv = viewportTransform().inverted(); return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1))); } - return d->scene->items(mapToScene(pos.x(), pos.y(), 2, 2)); + return d->scene->items(mapToScene(pos.x(), pos.y(), 1, 1)); } QPainterPath path; @@ -2451,10 +2374,11 @@ QPolygonF QGraphicsView::mapToScene(const QRect &rect) const return QPolygonF(); QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll()); - QPointF tl = scrollOffset + rect.topLeft(); - QPointF tr = scrollOffset + rect.topRight(); - QPointF br = scrollOffset + rect.bottomRight(); - QPointF bl = scrollOffset + rect.bottomLeft(); + QRect r = rect.adjusted(0, 0, 1, 1); + QPointF tl = scrollOffset + r.topLeft(); + QPointF tr = scrollOffset + r.topRight(); + QPointF br = scrollOffset + r.bottomRight(); + QPointF bl = scrollOffset + r.bottomLeft(); QPolygonF poly; poly.resize(4); @@ -2784,9 +2708,7 @@ void QGraphicsView::setupViewport(QWidget *widget) const bool isGLWidget = widget->inherits("QGLWidget"); - d->accelerateScrolling = !(isGLWidget - || widget->testAttribute(Qt::WA_MSWindowsUseDirect3D) - || qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault)); + d->accelerateScrolling = !(isGLWidget); widget->setFocusPolicy(Qt::StrongFocus); @@ -2795,7 +2717,12 @@ void QGraphicsView::setupViewport(QWidget *widget) widget->setAutoFillBackground(true); } - widget->setMouseTracking(true); + // We are only interested in mouse tracking if items + // accept hover events or use non-default cursors. + if (d->scene && (!d->scene->d_func()->allItemsIgnoreHoverEvents + || !d->scene->d_func()->allItemsUseDefaultCursor)) { + widget->setMouseTracking(true); + } widget->setAcceptDrops(acceptDrops()); } @@ -3442,7 +3369,7 @@ void QGraphicsView::paintEvent(QPaintEvent *event) exposedRegion = viewport()->rect(); else if (d->viewportUpdateMode == BoundingRectViewportUpdate) exposedRegion = event->rect(); - QRectF exposedSceneRect = mapToScene(exposedRegion.boundingRect().adjusted(0, 0, 1, 1)).boundingRect(); + QRectF exposedSceneRect = mapToScene(exposedRegion.boundingRect()).boundingRect(); // Set up the painter QPainter painter(viewport()); @@ -3462,7 +3389,7 @@ void QGraphicsView::paintEvent(QPaintEvent *event) #ifdef QGRAPHICSVIEW_DEBUG QTime stopWatch; stopWatch.start(); - qDebug() << "QGraphicsView::paintEvent(" << exposedRegion << ")"; + qDebug() << "QGraphicsView::paintEvent(" << exposedRegion << ')'; #endif // Find all exposed items @@ -3496,6 +3423,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(); } @@ -3517,15 +3445,17 @@ void QGraphicsView::paintEvent(QPaintEvent *event) int backgroundTime = stopWatch.elapsed() - exposedTime; #endif - // Generate the style options - QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()]; - QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(itemList.size()); - - d->generateStyleOptions(itemList, itemArray, styleOptionArray, viewTransform, - allItems, exposedRegion); - - // Items - drawItems(&painter, itemList.size(), itemArray, styleOptionArray); + if (!itemList.isEmpty()) { + // Generate the style options. + const int numItems = itemList.size(); + QGraphicsItem **itemArray = &itemList[0]; // Relies on QList internals, but is perfectly valid. + QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems); + for (int i = 0; i < numItems; ++i) + itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], viewTransform, exposedRegion, allItems); + // Draw the items. + drawItems(&painter, numItems, itemArray, styleOptionArray); + d->freeStyleOptionsArray(styleOptionArray); + } #ifdef QGRAPHICSVIEW_DEBUG int itemsTime = stopWatch.elapsed() - exposedTime - backgroundTime; @@ -3534,9 +3464,6 @@ void QGraphicsView::paintEvent(QPaintEvent *event) // Foreground drawForeground(&painter, exposedSceneRect); - delete [] itemArray; - d->freeStyleOptionsArray(styleOptionArray); - #ifdef QGRAPHICSVIEW_DEBUG int foregroundTime = stopWatch.elapsed() - exposedTime - backgroundTime - itemsTime; #endif @@ -3648,31 +3575,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/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h index a76279e..c18f85d 100644 --- a/src/gui/graphicsview/qgraphicsview_p.h +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE -class Q_GUI_EXPORT QGraphicsViewPrivate : public QAbstractScrollAreaPrivate +class QGraphicsViewPrivate : public QAbstractScrollAreaPrivate { Q_DECLARE_PUBLIC(QGraphicsView) public: @@ -174,13 +174,6 @@ public: bool updateSceneSlotReimplementedChecked; QList<QGraphicsItem *> findItems(const QRegion &exposedRegion, bool *allItems) const; - - void generateStyleOptions(const QList<QGraphicsItem *> &itemList, - QGraphicsItem **itemArray, - QStyleOptionGraphicsItem *styleOptionArray, - const QTransform &worldTransform, - bool allItems, - const QRegion &exposedRegion) const; }; QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp index 7f02fb9..341f9a2 100644 --- a/src/gui/graphicsview/qgraphicswidget.cpp +++ b/src/gui/graphicsview/qgraphicswidget.cpp @@ -200,7 +200,55 @@ QT_BEGIN_NAMESPACE /*! \property QGraphicsWidget::pos \brief the position of the widget -*/ +*/ + +/*! + \property QGraphicsWidget::xRotation + \since 4.6 + \brief the rotation angle in degrees around the X axis +*/ + +/*! + \property QGraphicsWidget::yRotation + \since 4.6 + \brief the rotation angle in degrees around the Y axis +*/ + +/*! + \property QGraphicsWidget::zRotation + \since 4.6 + \brief the rotation angle in degrees around the Z axis +*/ + +/*! + \property QGraphicsWidget::xScale + \since 4.6 + \brief the scale factor on the X axis. +*/ + +/*! + \property QGraphicsWidget::yScale + \since 4.6 + \brief the scale factor on the Y axis. +*/ + +/*! + \property QGraphicsWidget::horizontalShear + \since 4.6 + \brief the horizontal shear. +*/ + +/*! + \property QGraphicsWidget::verticalShear + \since 4.6 + \brief the vertical shear. +*/ + +/*! + \property QGraphicsWidget::transformOrigin + \since 4.6 + \brief the transformation origin for the transformation properties. +*/ /*! Constructs a QGraphicsWidget instance. The optional \a parent argument is @@ -459,17 +507,19 @@ void QGraphicsWidget::setContentsMargins(qreal left, qreal top, qreal right, qre { Q_D(QGraphicsWidget); - if (left == d->leftMargin - && top == d->topMargin - && right == d->rightMargin - && bottom == d->bottomMargin) { + if (!d->margins && left == 0 && top == 0 && right == 0 && bottom == 0) + return; + d->ensureMargins(); + if (left == d->margins[d->Left] + && top == d->margins[d->Top] + && right == d->margins[d->Right] + && bottom == d->margins[d->Bottom]) return; - } - d->leftMargin = left; - d->topMargin = top; - d->rightMargin = right; - d->bottomMargin = bottom; + d->margins[d->Left] = left; + d->margins[d->Top] = top; + d->margins[d->Right] = right; + d->margins[d->Bottom] = bottom; if (QGraphicsLayout *l = d->layout) l->invalidate(); @@ -490,14 +540,16 @@ void QGraphicsWidget::setContentsMargins(qreal left, qreal top, qreal right, qre void QGraphicsWidget::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const { Q_D(const QGraphicsWidget); + if (left || top || right || bottom) + d->ensureMargins(); if (left) - *left = d->leftMargin; + *left = d->margins[d->Left]; if (top) - *top = d->topMargin; + *top = d->margins[d->Top]; if (right) - *right = d->rightMargin; + *right = d->margins[d->Right]; if (bottom) - *bottom = d->bottomMargin; + *bottom = d->margins[d->Bottom]; } /*! @@ -513,16 +565,23 @@ void QGraphicsWidget::getContentsMargins(qreal *left, qreal *top, qreal *right, void QGraphicsWidget::setWindowFrameMargins(qreal left, qreal top, qreal right, qreal bottom) { Q_D(QGraphicsWidget); - bool unchanged = left == d->leftWindowFrameMargin && top == d->topWindowFrameMargin - && right == d->rightWindowFrameMargin && bottom == d->bottomWindowFrameMargin; + + if (!d->windowFrameMargins && left == 0 && top == 0 && right == 0 && bottom == 0) + return; + d->ensureWindowFrameMargins(); + bool unchanged = + d->windowFrameMargins[d->Left] == left + && d->windowFrameMargins[d->Top] == top + && d->windowFrameMargins[d->Right] == right + && d->windowFrameMargins[d->Bottom] == bottom; if (d->setWindowFrameMargins && unchanged) return; if (!unchanged) prepareGeometryChange(); - d->leftWindowFrameMargin = left; - d->topWindowFrameMargin = top; - d->rightWindowFrameMargin = right; - d->bottomWindowFrameMargin = bottom; + d->windowFrameMargins[d->Left] = left; + d->windowFrameMargins[d->Top] = top; + d->windowFrameMargins[d->Right] = right; + d->windowFrameMargins[d->Bottom] = bottom; d->setWindowFrameMargins = true; } @@ -536,14 +595,16 @@ void QGraphicsWidget::setWindowFrameMargins(qreal left, qreal top, qreal right, void QGraphicsWidget::getWindowFrameMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const { Q_D(const QGraphicsWidget); + if (left || top || right || bottom) + d->ensureWindowFrameMargins(); if (left) - *left = d->leftWindowFrameMargin; + *left = d->windowFrameMargins[d->Left]; if (top) - *top = d->topWindowFrameMargin; + *top = d->windowFrameMargins[d->Top]; if (right) - *right = d->rightWindowFrameMargin; + *right = d->windowFrameMargins[d->Right]; if (bottom) - *bottom = d->bottomWindowFrameMargin; + *bottom = d->windowFrameMargins[d->Bottom]; } /*! @@ -577,8 +638,10 @@ void QGraphicsWidget::unsetWindowFrameMargins() QRectF QGraphicsWidget::windowFrameGeometry() const { Q_D(const QGraphicsWidget); - return geometry().adjusted(-d->leftWindowFrameMargin, -d->topWindowFrameMargin, - d->rightWindowFrameMargin, d->bottomWindowFrameMargin); + return d->windowFrameMargins + ? geometry().adjusted(-d->windowFrameMargins[d->Left], -d->windowFrameMargins[d->Top], + d->windowFrameMargins[d->Right], d->windowFrameMargins[d->Bottom]) + : geometry(); } /*! @@ -589,8 +652,10 @@ QRectF QGraphicsWidget::windowFrameGeometry() const QRectF QGraphicsWidget::windowFrameRect() const { Q_D(const QGraphicsWidget); - return rect().adjusted(-d->leftWindowFrameMargin, -d->topWindowFrameMargin, - d->rightWindowFrameMargin, d->bottomWindowFrameMargin); + return d->windowFrameMargins + ? rect().adjusted(-d->windowFrameMargins[d->Left], -d->windowFrameMargins[d->Top], + d->windowFrameMargins[d->Right], d->windowFrameMargins[d->Bottom]) + : rect(); } /*! @@ -699,7 +764,10 @@ QSizeF QGraphicsWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) c QSizeF sh; if (d->layout) { sh = d->layout->effectiveSizeHint(which, constraint); - sh += QSizeF(d->leftMargin + d->rightMargin, d->topMargin + d->bottomMargin); + if (d->margins) { + sh += QSizeF(d->margins[d->Left] + d->margins[d->Right], + d->margins[d->Top] + d->margins[d->Bottom]); + } } else { switch (which) { case Qt::MinimumSize: @@ -1039,10 +1107,6 @@ QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant & break; } case ItemParentHasChanged: { - // reset window type on parent change in order to automagically remove decorations etc. - Qt::WindowFlags wflags = d->windowFlags & ~Qt::WindowType_Mask; - d->adjustWindowFlags(&wflags); - setWindowFlags(wflags); // Deliver ParentChange. QEvent event(QEvent::ParentChange); QApplication::sendEvent(this, &event); @@ -1131,7 +1195,8 @@ bool QGraphicsWidget::windowFrameEvent(QEvent *event) d->windowFrameMousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); break; case QEvent::GraphicsSceneMouseMove: - if (d->grabbedSection != Qt::NoSection) { + d->ensureWindowData(); + if (d->windowData->grabbedSection != Qt::NoSection) { d->windowFrameMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); event->accept(); } @@ -1186,7 +1251,8 @@ Qt::WindowFrameSection QGraphicsWidget::windowFrameSectionAt(const QPointF &pos) const qreal cornerMargin = 20; //### Not sure of this one, it should be the same value for all edges. - const qreal windowFrameWidth = d->leftWindowFrameMargin; + const qreal windowFrameWidth = d->windowFrameMargins + ? d->windowFrameMargins[d->Left] : 0; Qt::WindowFrameSection s = Qt::NoSection; if (x <= left + cornerMargin) { @@ -1212,7 +1278,8 @@ Qt::WindowFrameSection QGraphicsWidget::windowFrameSectionAt(const QPointF &pos) } if (s == Qt::NoSection) { QRectF r1 = r; - r1.setHeight(d->topWindowFrameMargin); + r1.setHeight(d->windowFrameMargins + ? d->windowFrameMargins[d->Top] : 0); if (r1.contains(pos)) s = Qt::TitleBarArea; } @@ -1320,7 +1387,8 @@ bool QGraphicsWidget::event(QEvent *event) case QEvent::GraphicsSceneMouseMove: case QEvent::GraphicsSceneMouseRelease: case QEvent::GraphicsSceneMouseDoubleClick: - if (d->hasDecoration() && d->grabbedSection != Qt::NoSection) + d->ensureWindowData(); + if (d->hasDecoration() && d->windowData->grabbedSection != Qt::NoSection) return windowFrameEvent(event); break; case QEvent::GraphicsSceneHoverEnter: @@ -1623,6 +1691,7 @@ void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags) return; bool wasPopup = (d->windowFlags & Qt::WindowType_Mask) == Qt::Popup; + d->adjustWindowFlags(&wFlags); d->windowFlags = wFlags; if (!d->setWindowFrameMargins) unsetWindowFrameMargins(); @@ -1635,6 +1704,11 @@ void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags) else d->scene->d_func()->addPopup(this); } + + if (d->scene && d->scene->d_func()->allItemsIgnoreHoverEvents && d->hasDecoration()) { + d->scene->d_func()->allItemsIgnoreHoverEvents = false; + d->scene->d_func()->enableMouseTrackingOnViews(); + } } /*! @@ -1667,12 +1741,13 @@ bool QGraphicsWidget::isActiveWindow() const void QGraphicsWidget::setWindowTitle(const QString &title) { Q_D(QGraphicsWidget); - d->windowTitle = title; + d->ensureWindowData(); + d->windowData->windowTitle = title; } QString QGraphicsWidget::windowTitle() const { Q_D(const QGraphicsWidget); - return d->windowTitle; + return d->windowData ? d->windowData->windowTitle : QString(); } /*! @@ -2108,11 +2183,12 @@ void QGraphicsWidget::paintWindowFrame(QPainter *painter, const QStyleOptionGrap QStyleOptionTitleBar bar; bar.QStyleOption::operator=(*option); d->initStyleOptionTitleBar(&bar); // this clear flags in bar.state - if (d->buttonMouseOver) + d->ensureWindowData(); + if (d->windowData->buttonMouseOver) bar.state |= QStyle::State_MouseOver; else bar.state &= ~QStyle::State_MouseOver; - if (d->buttonSunken) + if (d->windowData->buttonSunken) bar.state |= QStyle::State_Sunken; else bar.state &= ~QStyle::State_Sunken; @@ -2267,7 +2343,7 @@ void QGraphicsWidget::dumpFocusChain() qWarning("Found a focus chain that is not circular, (next == 0)"); break; } - qDebug() << i++ << QString::number(uint(next), 16) << next->className() << next->data(0) << QString::fromAscii("focusItem:%1").arg(next->hasFocus() ? "1" : "0") << QLatin1String("next:") << next->d_func()->focusNext->data(0) << QLatin1String("prev:") << next->d_func()->focusPrev->data(0); + qDebug() << i++ << QString::number(uint(next), 16) << next->className() << next->data(0) << QString::fromAscii("focusItem:%1").arg(next->hasFocus() ? '1' : '0') << QLatin1String("next:") << next->d_func()->focusNext->data(0) << QLatin1String("prev:") << next->d_func()->focusPrev->data(0); if (visited.contains(next)) { qWarning("Already visited this node. However, I expected to dump until I found myself."); break; diff --git a/src/gui/graphicsview/qgraphicswidget.h b/src/gui/graphicsview/qgraphicswidget.h index 34f1c5f..a5c9068 100644 --- a/src/gui/graphicsview/qgraphicswidget.h +++ b/src/gui/graphicsview/qgraphicswidget.h @@ -81,7 +81,14 @@ class Q_GUI_EXPORT QGraphicsWidget : public QObject, public QGraphicsItem, publi Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) Q_PROPERTY(QPointF pos READ pos WRITE setPos) Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry) - + Q_PROPERTY(QPointF transformOrigin READ transformOrigin WRITE setTransformOrigin) + Q_PROPERTY(qreal xRotation READ xRotation WRITE setXRotation) + Q_PROPERTY(qreal yRotation READ yRotation WRITE setYRotation) + Q_PROPERTY(qreal zRotation READ zRotation WRITE setZRotation) + Q_PROPERTY(qreal xScale READ xScale WRITE setXScale) + Q_PROPERTY(qreal yScale READ yScale WRITE setYScale) + Q_PROPERTY(qreal horizontalShear READ horizontalShear WRITE setHorizontalShear) + Q_PROPERTY(qreal verticalShear READ verticalShear WRITE setVerticalShear) public: QGraphicsWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); ~QGraphicsWidget(); diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp index 789f8da..a435758 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.cpp +++ b/src/gui/graphicsview/qgraphicswidget_p.cpp @@ -66,16 +66,17 @@ void QGraphicsWidgetPrivate::init(QGraphicsItem *parentItem, Qt::WindowFlags wFl isWidget = 1; // QGraphicsItem::isWidget() returns true. focusNext = focusPrev = q; focusPolicy = Qt::NoFocus; + + adjustWindowFlags(&wFlags); + windowFlags = wFlags; + q->setParentItem(parentItem); q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType)); q->setGraphicsItem(q); resolveLayoutDirection(); - - if (!parentItem) - adjustWindowFlags(&wFlags); - windowFlags = wFlags; q->unsetWindowFrameMargins(); + q->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption); } qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const { @@ -89,56 +90,57 @@ qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options return (qreal)height; } -void QGraphicsWidgetPrivate::getLayoutItemMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const +/*! + \internal +*/ +QGraphicsWidgetPrivate::~QGraphicsWidgetPrivate() { - if (left) - *left = leftLayoutItemMargin; - if (top) - *top = topLayoutItemMargin; - if (right) - *right = rightLayoutItemMargin; - if (bottom) - *bottom = bottomLayoutItemMargin; + // Remove any lazily allocated data + delete[] margins; + delete[] windowFrameMargins; + delete windowData; } -void QGraphicsWidgetPrivate::setLayoutItemMargins(qreal left, qreal top, qreal right, qreal bottom) -{ - if (leftLayoutItemMargin == left - && topLayoutItemMargin == top - && rightLayoutItemMargin == right - && bottomLayoutItemMargin == bottom) - return; +/*! + \internal - Q_Q(QGraphicsWidget); - leftLayoutItemMargin = left; - topLayoutItemMargin = top; - rightLayoutItemMargin = right; - bottomLayoutItemMargin = bottom; - q->updateGeometry(); + Ensures that margins is allocated. + This function must be called before any dereferencing. +*/ +void QGraphicsWidgetPrivate::ensureMargins() const +{ + if (!margins) { + margins = new qreal[4]; + for (int i = 0; i < 4; ++i) + margins[i] = 0; + } } -void QGraphicsWidgetPrivate::setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt) +/*! + \internal + + Ensures that windowFrameMargins is allocated. + This function must be called before any dereferencing. +*/ +void QGraphicsWidgetPrivate::ensureWindowFrameMargins() const { - Q_Q(QGraphicsWidget); - QStyleOption myOpt; - if (!opt) { - q->initStyleOption(&myOpt); - myOpt.rect.setRect(0, 0, 32768, 32768); // arbitrary - opt = &myOpt; + if (!windowFrameMargins) { + windowFrameMargins = new qreal[4]; + for (int i = 0; i < 4; ++i) + windowFrameMargins[i] = 0; } +} - QRect liRect = q->style()->subElementRect(element, opt, /* q */ 0); - if (liRect.isValid()) { - leftLayoutItemMargin = (opt->rect.left() - liRect.left()); - topLayoutItemMargin = (opt->rect.top() - liRect.top()); - rightLayoutItemMargin = (liRect.right() - opt->rect.right()); - bottomLayoutItemMargin = (liRect.bottom() - opt->rect.bottom()); - } else { - leftLayoutItemMargin = 0; - topLayoutItemMargin = 0; - rightLayoutItemMargin = 0; - bottomLayoutItemMargin = 0; - } +/*! + \internal + + Ensures that windowData is allocated. + This function must be called before any dereferencing. +*/ +void QGraphicsWidgetPrivate::ensureWindowData() +{ + if (!windowData) + windowData = new WindowData; } void QGraphicsWidgetPrivate::setPalette_helper(const QPalette &palette) @@ -297,11 +299,12 @@ QFont QGraphicsWidgetPrivate::naturalWidgetFont() const void QGraphicsWidgetPrivate::initStyleOptionTitleBar(QStyleOptionTitleBar *option) { Q_Q(QGraphicsWidget); + ensureWindowData(); q->initStyleOption(option); option->rect.setHeight(titleBarHeight(*option)); option->titleBarFlags = windowFlags; option->subControls = QStyle::SC_TitleBarCloseButton | QStyle::SC_TitleBarLabel | QStyle::SC_TitleBarSysMenu; - option->activeSubControls = hoveredSubControl; + option->activeSubControls = windowData->hoveredSubControl; bool isActive = q->isActiveWindow(); if (isActive) { option->state |= QStyle::State_Active; @@ -313,7 +316,8 @@ void QGraphicsWidgetPrivate::initStyleOptionTitleBar(QStyleOptionTitleBar *optio } QFont windowTitleFont = QApplication::font("QWorkspaceTitleBar"); QRect textRect = q->style()->subControlRect(QStyle::CC_TitleBar, option, QStyle::SC_TitleBarLabel, 0); - option->text = QFontMetrics(windowTitleFont).elidedText(windowTitle, Qt::ElideRight, textRect.width()); + option->text = QFontMetrics(windowTitleFont).elidedText( + windowData->windowTitle, Qt::ElideRight, textRect.width()); } void QGraphicsWidgetPrivate::adjustWindowFlags(Qt::WindowFlags *flags) @@ -341,9 +345,10 @@ void QGraphicsWidgetPrivate::adjustWindowFlags(Qt::WindowFlags *flags) void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent *event) { Q_Q(QGraphicsWidget); - if (grabbedSection != Qt::NoSection) { - if (grabbedSection == Qt::TitleBarArea) { - buttonSunken = false; + ensureWindowData(); + if (windowData->grabbedSection != Qt::NoSection) { + if (windowData->grabbedSection == Qt::TitleBarArea) { + windowData->buttonSunken = false; QStyleOptionTitleBar bar; initStyleOptionTitleBar(&bar); // make sure that the coordinates (rect and pos) we send to the style are positive. @@ -351,8 +356,10 @@ void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEve bar.rect.moveTo(0,0); bar.rect.setHeight(q->style()->pixelMetric(QStyle::PM_TitleBarHeight, &bar)); QPointF pos = event->pos(); - pos.rx() += leftWindowFrameMargin; - pos.ry() += topWindowFrameMargin; + if (windowFrameMargins) { + pos.rx() += windowFrameMargins[Left]; + pos.ry() += windowFrameMargins[Top]; + } bar.subControls = QStyle::SC_TitleBarCloseButton; if (q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, @@ -361,7 +368,7 @@ void QGraphicsWidgetPrivate::windowFrameMouseReleaseEvent(QGraphicsSceneMouseEve } } if (!(static_cast<QGraphicsSceneMouseEvent *>(event)->buttons())) - grabbedSection = Qt::NoSection; + windowData->grabbedSection = Qt::NoSection; event->accept(); } } @@ -372,35 +379,16 @@ void QGraphicsWidgetPrivate::windowFrameMousePressEvent(QGraphicsSceneMouseEvent if (event->button() != Qt::LeftButton) return; - startGeometry = q->geometry(); - grabbedSection = q->windowFrameSectionAt(event->pos()); - switch (grabbedSection) { - case Qt::LeftSection: - case Qt::TopLeftSection: - mouseDelta = event->pos() - q->rect().topLeft(); - break; - case Qt::TopSection: - case Qt::TopRightSection: - mouseDelta = event->pos() - q->rect().topRight(); - break; - case Qt::RightSection: - case Qt::BottomRightSection: - mouseDelta = event->pos() - q->rect().bottomRight(); - break; - case Qt::BottomSection: - case Qt::BottomLeftSection: - mouseDelta = event->pos() - q->rect().bottomLeft(); - break; - case Qt::TitleBarArea: - if (hoveredSubControl == QStyle::SC_TitleBarCloseButton) { - buttonSunken = true; - q->update(); - } - break; - case Qt::NoSection: - break; + ensureWindowData(); + windowData->startGeometry = q->geometry(); + windowData->grabbedSection = q->windowFrameSectionAt(event->pos()); + ensureWindowData(); + if (windowData->grabbedSection == Qt::TitleBarArea + && windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton) { + windowData->buttonSunken = true; + q->update(); } - event->setAccepted(grabbedSection != Qt::NoSection); + event->setAccepted(windowData->grabbedSection != Qt::NoSection); } static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry, @@ -455,7 +443,8 @@ static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry, void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent *event) { Q_Q(QGraphicsWidget); - if (!(event->buttons() & Qt::LeftButton) || hoveredSubControl != QStyle::SC_TitleBarLabel) + ensureWindowData(); + if (!(event->buttons() & Qt::LeftButton) || windowData->hoveredSubControl != QStyle::SC_TitleBarLabel) return; QLineF delta(q->mapFromScene(event->buttonDownScenePos(Qt::LeftButton)), event->pos()); @@ -464,49 +453,56 @@ void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent QLineF parentYDelta(q->mapToParent(QPointF(0, delta.p1().y())), q->mapToParent(QPointF(0, delta.p2().y()))); QRectF newGeometry; - switch (grabbedSection) { + switch (windowData->grabbedSection) { case Qt::LeftSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()), - startGeometry.size() - QSizeF(delta.dx(), delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentXDelta.dx(), parentXDelta.dy()), + windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy())); break; case Qt::TopLeftSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()), - startGeometry.size() - QSizeF(delta.dx(), delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentDelta.dx(), parentDelta.dy()), + windowData->startGeometry.size() - QSizeF(delta.dx(), delta.dy())); break; case Qt::TopSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()), - startGeometry.size() - QSizeF(0, delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentYDelta.dx(), parentYDelta.dy()), + windowData->startGeometry.size() - QSizeF(0, delta.dy())); break; case Qt::TopRightSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()), - startGeometry.size() - QSizeF(-delta.dx(), delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentYDelta.dx(), parentYDelta.dy()), + windowData->startGeometry.size() - QSizeF(-delta.dx(), delta.dy())); break; case Qt::RightSection: - newGeometry = QRectF(startGeometry.topLeft(), - startGeometry.size() + QSizeF(delta.dx(), 0)); + newGeometry = QRectF(windowData->startGeometry.topLeft(), + windowData->startGeometry.size() + QSizeF(delta.dx(), 0)); break; case Qt::BottomRightSection: - newGeometry = QRectF(startGeometry.topLeft(), - startGeometry.size() + QSizeF(delta.dx(), delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft(), + windowData->startGeometry.size() + QSizeF(delta.dx(), delta.dy())); break; case Qt::BottomSection: - newGeometry = QRectF(startGeometry.topLeft(), - startGeometry.size() + QSizeF(0, delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft(), + windowData->startGeometry.size() + QSizeF(0, delta.dy())); break; case Qt::BottomLeftSection: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()), - startGeometry.size() - QSizeF(delta.dx(), -delta.dy())); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentXDelta.dx(), parentXDelta.dy()), + windowData->startGeometry.size() - QSizeF(delta.dx(), -delta.dy())); break; case Qt::TitleBarArea: - newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()), - startGeometry.size()); + newGeometry = QRectF(windowData->startGeometry.topLeft() + + QPointF(parentDelta.dx(), parentDelta.dy()), + windowData->startGeometry.size()); break; case Qt::NoSection: break; } - if (grabbedSection != Qt::NoSection) { - _q_boundGeometryToSizeConstraints(startGeometry, &newGeometry, grabbedSection, + if (windowData->grabbedSection != Qt::NoSection) { + _q_boundGeometryToSizeConstraints(windowData->startGeometry, &newGeometry, + windowData->grabbedSection, q->effectiveSizeHint(Qt::MinimumSize), q->effectiveSizeHint(Qt::MaximumSize)); q->setGeometry(newGeometry); @@ -519,21 +515,25 @@ void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent if (!hasDecoration()) return; + ensureWindowData(); + if (q->rect().contains(event->pos())) { - if (buttonMouseOver || hoveredSubControl != QStyle::SC_None) + if (windowData->buttonMouseOver || windowData->hoveredSubControl != QStyle::SC_None) windowFrameHoverLeaveEvent(event); return; } - bool wasMouseOver = buttonMouseOver; - QRect oldButtonRect = buttonRect; - buttonRect = QRect(); - buttonMouseOver = false; + bool wasMouseOver = windowData->buttonMouseOver; + QRect oldButtonRect = windowData->buttonRect; + windowData->buttonRect = QRect(); + windowData->buttonMouseOver = false; QPointF pos = event->pos(); QStyleOptionTitleBar bar; // make sure that the coordinates (rect and pos) we send to the style are positive. - pos.rx() += leftWindowFrameMargin; - pos.ry() += topWindowFrameMargin; + if (windowFrameMargins) { + pos.rx() += windowFrameMargins[Left]; + pos.ry() += windowFrameMargins[Top]; + } initStyleOptionTitleBar(&bar); bar.rect = q->windowFrameRect().toRect(); bar.rect.moveTo(0,0); @@ -559,14 +559,17 @@ void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent cursorShape = Qt::SizeVerCursor; break; case Qt::TitleBarArea: - buttonRect = q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, 0); + windowData->buttonRect = q->style()->subControlRect( + QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarCloseButton, 0); #ifdef Q_WS_MAC // On mac we should hover if we are in the 'area' of the buttons - buttonRect |= q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMinButton, 0); - buttonRect |= q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMaxButton, 0); + windowData->buttonRect |= q->style()->subControlRect( + QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMinButton, 0); + windowData->buttonRect |= q->style()->subControlRect( + QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMaxButton, 0); #endif - if (buttonRect.contains(pos.toPoint())) - buttonMouseOver = true; + if (windowData->buttonRect.contains(pos.toPoint())) + windowData->buttonMouseOver = true; event->ignore(); break; default: @@ -578,15 +581,15 @@ void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent q->setCursor(cursorShape); #endif // update buttons if we hover over them - hoveredSubControl = q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &bar, pos.toPoint(), 0); - if (hoveredSubControl != QStyle::SC_TitleBarCloseButton) - hoveredSubControl = QStyle::SC_TitleBarLabel; + windowData->hoveredSubControl = q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &bar, pos.toPoint(), 0); + if (windowData->hoveredSubControl != QStyle::SC_TitleBarCloseButton) + windowData->hoveredSubControl = QStyle::SC_TitleBarLabel; - if (buttonMouseOver != wasMouseOver) { + if (windowData->buttonMouseOver != wasMouseOver) { if (!oldButtonRect.isNull()) q->update(QRectF(oldButtonRect).translated(q->windowFrameRect().topLeft())); - if (!buttonRect.isNull()) - q->update(QRectF(buttonRect).translated(q->windowFrameRect().topLeft())); + if (!windowData->buttonRect.isNull()) + q->update(QRectF(windowData->buttonRect).translated(q->windowFrameRect().topLeft())); } } @@ -600,16 +603,19 @@ void QGraphicsWidgetPrivate::windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent q->unsetCursor(); #endif + ensureWindowData(); + bool needsUpdate = false; - if (hoveredSubControl == QStyle::SC_TitleBarCloseButton || buttonMouseOver) + if (windowData->hoveredSubControl == QStyle::SC_TitleBarCloseButton + || windowData->buttonMouseOver) needsUpdate = true; // update the hover state (of buttons etc...) - hoveredSubControl = QStyle::SC_None; - buttonMouseOver = false; - buttonRect = QRect(); + windowData->hoveredSubControl = QStyle::SC_None; + windowData->buttonMouseOver = false; + windowData->buttonRect = QRect(); if (needsUpdate) - q->update(buttonRect); + q->update(windowData->buttonRect); } } diff --git a/src/gui/graphicsview/qgraphicswidget_p.h b/src/gui/graphicsview/qgraphicswidget_p.h index 53eaa31..f4cdeca 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.h +++ b/src/gui/graphicsview/qgraphicswidget_p.h @@ -68,19 +68,12 @@ class QStyleOptionTitleBar; #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW -class Q_GUI_EXPORT QGraphicsWidgetPrivate : public QGraphicsItemPrivate +class QGraphicsWidgetPrivate : public QGraphicsItemPrivate { Q_DECLARE_PUBLIC(QGraphicsWidget) public: QGraphicsWidgetPrivate() - : leftMargin(0), - topMargin(0), - rightMargin(0), - bottomMargin(0), - leftLayoutItemMargin(0), - topLayoutItemMargin(0), - rightLayoutItemMargin(0), - bottomLayoutItemMargin(0), + : margins(0), layout(0), inheritedPaletteResolveMask(0), inheritedFontResolveMask(0), @@ -92,40 +85,23 @@ public: focusPrev(0), focusChild(0), windowFlags(0), - hoveredSubControl(QStyle::SC_None), - grabbedSection(Qt::NoSection), - buttonMouseOver(false), - buttonSunken(false), + windowData(0), setWindowFrameMargins(false), - leftWindowFrameMargin(0), - topWindowFrameMargin(0), - rightWindowFrameMargin(0), - bottomWindowFrameMargin(0) + windowFrameMargins(0) { } + virtual ~QGraphicsWidgetPrivate(); void init(QGraphicsItem *parentItem, Qt::WindowFlags wFlags); qreal titleBarHeight(const QStyleOptionTitleBar &options) const; // Margins - qreal leftMargin; - qreal topMargin; - qreal rightMargin; - qreal bottomMargin; - QRectF contentsRect; - - // Layout item margins - void getLayoutItemMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const; - void setLayoutItemMargins(qreal left, qreal top, qreal right, qreal bottom); - void setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt = 0); + enum {Left, Top, Right, Bottom}; + mutable qreal *margins; + void ensureMargins() const; void fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *newScene = 0); void setLayout_helper(QGraphicsLayout *l); - qreal leftLayoutItemMargin; - qreal topLayoutItemMargin; - qreal rightLayoutItemMargin; - qreal bottomLayoutItemMargin; - // Layouts QGraphicsLayout *layout; void setLayoutDirection_helper(Qt::LayoutDirection direction); @@ -208,20 +184,26 @@ public: // Windows Qt::WindowFlags windowFlags; - QString windowTitle; - QStyle::SubControl hoveredSubControl; - Qt::WindowFrameSection grabbedSection; - uint buttonMouseOver : 1; - uint buttonSunken : 1; - QPointF mouseDelta; // to compensate for small error when interactively resizing - QRectF startGeometry; - QRect buttonRect; + struct WindowData { + QString windowTitle; + QStyle::SubControl hoveredSubControl; + Qt::WindowFrameSection grabbedSection; + uint buttonMouseOver : 1; + uint buttonSunken : 1; + QRectF startGeometry; + QRect buttonRect; + WindowData() + : hoveredSubControl(QStyle::SC_None) + , grabbedSection(Qt::NoSection) + , buttonMouseOver(false) + , buttonSunken(false) + {} + } *windowData; + void ensureWindowData(); bool setWindowFrameMargins; - qreal leftWindowFrameMargin; - qreal topWindowFrameMargin; - qreal rightWindowFrameMargin; - qreal bottomWindowFrameMargin; + mutable qreal *windowFrameMargins; + void ensureWindowFrameMargins() const; #ifndef QT_NO_ACTION QList<QAction *> actions; diff --git a/src/gui/graphicsview/qgridlayoutengine.cpp b/src/gui/graphicsview/qgridlayoutengine.cpp index dc5ca21..88712c2 100644 --- a/src/gui/graphicsview/qgridlayoutengine.cpp +++ b/src/gui/graphicsview/qgridlayoutengine.cpp @@ -275,7 +275,7 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz if (hasIgnoreFlag) { factors[i] = (stretch < 0) ? 1.0 : 0.0; } else { - factors[i] = (stretch < 0) ? sizes[i] : 0.0; + factors[i] = (stretch < 0) ? sizes[i] : 0.0; } } else if (stretch == sumStretches) { factors[i] = 1.0; @@ -615,7 +615,7 @@ QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal heig QSizeF size = effectiveMaxSize().boundedTo(QSizeF(cellWidth, cellHeight)); width = size.width(); height = size.height(); - + Qt::Alignment align = q_engine->effectiveAlignment(this); switch (align & Qt::AlignHorizontal_Mask) { case Qt::AlignHCenter: @@ -717,7 +717,7 @@ void QGridLayoutItem::dump(int indent) const void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta) { count += delta; - + insertOrRemoveItems(stretches, row, delta); insertOrRemoveItems(spacings, row, delta); insertOrRemoveItems(alignments, row, delta); @@ -1076,7 +1076,7 @@ QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHi break; } return QSizeF(); -} +} QSizePolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const { @@ -1131,9 +1131,9 @@ void QGridLayoutEngine::dump(int indent) const QString message = QLatin1String("[ "); for (int column = 0; column < internalGridColumnCount(); ++column) { message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3); - message += QLatin1String(" "); + message += QLatin1Char(' '); } - message += QLatin1String("]"); + message += QLatin1Char(']'); qDebug("%*s %s", indent, "", qPrintable(message)); } @@ -1150,16 +1150,16 @@ void QGridLayoutEngine::dump(int indent) const q_rowData.dump(indent + 2); qDebug("%*s Geometries output", indent, ""); + QVector<qreal> *cellPos = &q_yy; for (int pass = 0; pass < 2; ++pass) { - QVector<qreal> &cellPos = q_yy; QString message; - for (i = 0; i < cellPos.count(); ++i) { + for (i = 0; i < cellPos->count(); ++i) { message += QLatin1String((message.isEmpty() ? "[" : ", ")); - message += QString::number(cellPos.at(i)); + message += QString::number(cellPos->at(i)); } - message += QLatin1String("]"); + message += QLatin1Char(']'); qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message)); - cellPos = q_xx; + cellPos = &q_xx; } } #endif @@ -1538,5 +1538,5 @@ void QGridLayoutEngine::ensureGeometries(const QLayoutStyleInfo &styleInfo, } QT_END_NAMESPACE - + #endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/gui.pro b/src/gui/gui.pro index f224e67..329fb01 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -19,6 +19,7 @@ win32:include(kernel/win.pri) embedded:include(embedded/embedded.pri) #modules +include(animation/animation.pri) include(kernel/kernel.pri) include(image/image.pri) include(painting/painting.pri) @@ -31,6 +32,8 @@ include(itemviews/itemviews.pri) include(inputmethod/inputmethod.pri) include(graphicsview/graphicsview.pri) include(util/util.pri) +include(statemachine/statemachine.pri) +include(math3d/math3d.pri) embedded: QT += network diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index ca52974..bf348af 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -22,6 +22,7 @@ HEADERS += \ image/qpixmap.h \ image/qpixmap_raster_p.h \ image/qpixmapcache.h \ + image/qpixmapcache_p.h \ image/qpixmapdata_p.h \ image/qpixmapdatafactory_p.h \ image/qpixmapfilter_p.h diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index a880a13..471062f 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -304,6 +304,8 @@ QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::St QString key = QLatin1String("$qt_icon_") + QString::number(pm.cacheKey()) + QString::number(pe->mode) + + QString::number(qApp->palette().cacheKey()) + + QLatin1Char('_') + QString::number(actualSize.width()) + QLatin1Char('_') + QString::number(actualSize.height()) diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 70d4e2c..c1c5631 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1691,8 +1691,12 @@ void QImage::setColorTable(const QVector<QRgb> colors) d->colortable = colors; d->has_alpha_clut = false; - for (int i = 0; i < d->colortable.size(); ++i) - d->has_alpha_clut |= (qAlpha(d->colortable.at(i)) != 255); + for (int i = 0; i < d->colortable.size(); ++i) { + if (qAlpha(d->colortable.at(i)) != 255) { + d->has_alpha_clut = true; + break; + } + } } /*! @@ -4849,8 +4853,6 @@ bool QImage::operator==(const QImage & i) const return false; if (d->format != Format_RGB32) { - if (d->colortable != i.d->colortable) - return false; if (d->format >= Format_ARGB32) { // all bits defined const int n = d->width * d->depth / 8; if (n == d->bytes_per_line && n == i.d->bytes_per_line) { @@ -4863,11 +4865,13 @@ bool QImage::operator==(const QImage & i) const } } } else { - int w = width(); - int h = height(); + const int w = width(); + const int h = height(); + const QVector<QRgb> &colortable = d->colortable; + const QVector<QRgb> &icolortable = i.d->colortable; for (int y=0; y<h; ++y) { for (int x=0; x<w; ++x) { - if (pixelIndex(x, y) != i.pixelIndex(x, y)) + if (colortable[pixelIndex(x, y)] != icolortable[i.pixelIndex(x, y)]) return false; } } diff --git a/src/gui/image/qnativeimage.cpp b/src/gui/image/qnativeimage.cpp index 33e565c..dc01fe4 100644 --- a/src/gui/image/qnativeimage.cpp +++ b/src/gui/image/qnativeimage.cpp @@ -65,7 +65,7 @@ typedef struct { QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool isTextBuffer, QWidget *) { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE Q_UNUSED(isTextBuffer); #endif BITMAPINFO_MASK bmi; @@ -78,7 +78,7 @@ QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool is if (format == QImage::Format_RGB16) { bmi.bmiHeader.biBitCount = 16; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (isTextBuffer) { bmi.bmiHeader.biCompression = BI_RGB; bmi.redMask = 0; @@ -116,7 +116,7 @@ QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool is Q_ASSERT(image.paintEngine()->type() == QPaintEngine::Raster); static_cast<QRasterPaintEngine *>(image.paintEngine())->setDC(hdc); -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE GdiFlush(); #endif } diff --git a/src/gui/image/qnativeimage_p.h b/src/gui/image/qnativeimage_p.h index 860485a..6402af2 100644 --- a/src/gui/image/qnativeimage_p.h +++ b/src/gui/image/qnativeimage_p.h @@ -70,7 +70,7 @@ QT_BEGIN_NAMESPACE class QWidget; -class Q_GUI_EXPORT QNativeImage +class QNativeImage { public: QNativeImage(int width, int height, QImage::Format format, bool isTextBuffer = false, QWidget *widget = 0); diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 7fcb2b9..8ed9e93 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. @@ -1309,14 +1359,6 @@ bool QPixmap::isDetached() const void QPixmap::deref() { if (data && !data->ref.deref()) { // Destroy image if last ref -#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN) - if (data->classId() == QPixmapData::RasterClass) { - QRasterPixmapData *rData = static_cast<QRasterPixmapData*>(data); - if (rData->texture) - rData->texture->Release(); - rData->texture = 0; - } -#endif if (data->is_cached && qt_pixmap_cleanup_hook_64) qt_pixmap_cleanup_hook_64(cacheKey()); delete data; @@ -1853,7 +1895,7 @@ int QPixmap::defaultDepth() return QScreen::instance()->depth(); #elif defined(Q_WS_X11) return QX11Info::appDepth(); -#elif defined(Q_OS_WINCE) +#elif defined(Q_WS_WINCE) return QColormap::instance().depth(); #elif defined(Q_WS_WIN) return 32; // XXX @@ -1888,12 +1930,6 @@ void QPixmap::detach() if (id == QPixmapData::RasterClass) { QRasterPixmapData *rasterData = static_cast<QRasterPixmapData*>(data); rasterData->image.detach(); -#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECT3D) - if (rasterData->texture) { - rasterData->texture->Release(); - rasterData->texture = 0; - } -#endif } if (data->is_cached && qt_pixmap_cleanup_hook_64 && data->ref == 1) diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index 1863273..a5609e4 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; @@ -251,8 +254,6 @@ private: friend class QWidgetPrivate; friend class QRasterPaintEngine; friend class QRasterBuffer; - friend class QDirect3DPaintEngine; - friend class QDirect3DPaintEnginePrivate; friend class QDetachedPixmap; #if !defined(QT_NO_DATASTREAM) friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPixmap &); @@ -274,6 +275,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..29e4f3f 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -50,12 +50,6 @@ #include <private/qwidget_p.h> #include <private/qdrawhelper_p.h> -#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN) -#include <private/qpaintengine_d3d_p.h> -#include <d3d9.h> -extern QDirect3DPaintEngine *qt_d3dEngine(); -#endif - QT_BEGIN_NAMESPACE const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, @@ -63,9 +57,6 @@ const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, QRasterPixmapData::QRasterPixmapData(PixelType type) : QPixmapData(type, RasterClass) -#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECT3D) - , texture(0) -#endif { } @@ -181,6 +172,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; @@ -321,25 +322,36 @@ extern int qt_defaultDpiY(); int QRasterPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const { + QImageData *d = image.d; + if (!d) + return 0; + // override the image dpi with the screen dpi when rendering to a pixmap - const int dpmX = qRound(qt_defaultDpiX() * 100 / qreal(2.54)); - const int dpmY = qRound(qt_defaultDpiY() * 100 / qreal(2.54)); switch (metric) { + case QPaintDevice::PdmWidth: + return d->width; + case QPaintDevice::PdmHeight: + return d->height; case QPaintDevice::PdmWidthMM: - return qRound(image.width() * 1000 / dpmX); + return qRound(d->width * 25.4 / qt_defaultDpiX()); case QPaintDevice::PdmHeightMM: - return qRound(image.height() * 1000 / dpmY); - case QPaintDevice::PdmDpiX: - return qRound(dpmX * qreal(0.0254)); - case QPaintDevice::PdmDpiY: - return qRound(dpmY * qreal(0.0254)); + return qRound(d->width * 25.4 / qt_defaultDpiY()); + case QPaintDevice::PdmNumColors: + return d->colortable.size(); + case QPaintDevice::PdmDepth: + return d->depth; + case QPaintDevice::PdmDpiX: // fall-through case QPaintDevice::PdmPhysicalDpiX: - return qRound(dpmX * qreal(0.0254)); + return qt_defaultDpiX(); + case QPaintDevice::PdmDpiY: // fall-through case QPaintDevice::PdmPhysicalDpiY: - return qRound(dpmY * qreal(0.0254)); + return qt_defaultDpiY(); default: - return image.metric(metric); + qWarning("QRasterPixmapData::metric(): Unhandled metric type %d", metric); + break; } + + return 0; } QImage* QRasterPixmapData::buffer() diff --git a/src/gui/image/qpixmap_raster_p.h b/src/gui/image/qpixmap_raster_p.h index 095f378..9d3bf72 100644 --- a/src/gui/image/qpixmap_raster_p.h +++ b/src/gui/image/qpixmap_raster_p.h @@ -58,9 +58,6 @@ #ifdef Q_WS_WIN # include "qt_windows.h" -# ifndef QT_NO_DIRECT3D -# include <d3d9.h> -# endif #endif QT_BEGIN_NAMESPACE @@ -75,6 +72,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; @@ -87,10 +85,6 @@ protected: int metric(QPaintDevice::PaintDeviceMetric metric) const; private: -#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECT3D) - friend class QDirect3DPaintEnginePrivate; - IDirect3DTexture9 *texture; -#endif friend class QPixmap; friend class QBitmap; friend class QDetachedPixmap; diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp index 6a8b38a..ac02f97 100644 --- a/src/gui/image/qpixmap_win.cpp +++ b/src/gui/image/qpixmap_win.cpp @@ -59,7 +59,7 @@ #include "qdebug.h" #include "qt_windows.h" -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) #include <winbase.h> #include "qguifunctions_wince.h" extern bool qt_wince_is_high_dpi(); @@ -80,7 +80,7 @@ QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h ) if (w < 0) w = r.right - r.left; if (h < 0) h = r.bottom - r.top; -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM if (qt_wince_is_pocket_pc()) { QWidget *widget = QWidget::find(winId); if (qobject_cast<QDesktopWidget *>(widget)) { @@ -101,7 +101,7 @@ QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h ) // copy data HDC window_dc = GetDC(winId); BitBlt(bitmap_dc, 0, 0, w, h, window_dc, x, y, SRCCOPY -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE | CAPTUREBLT #endif ); @@ -243,7 +243,7 @@ QPixmap QPixmap::fromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format) } #ifdef Q_WS_WIN -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE static QImage qt_fromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h) { @@ -347,7 +347,7 @@ QPixmap convertHIconToPixmap( const HICON icon) DeleteDC(hdc); return QPixmap::fromImage(image); } -#else //ifndef Q_OS_WINCE +#else //ifndef Q_WS_WINCE QPixmap convertHIconToPixmap( const HICON icon, bool large) { HDC screenDevice = GetDC(0); @@ -412,7 +412,7 @@ QPixmap convertHIconToPixmap( const HICON icon, bool large) DeleteDC(hdc); return QPixmap::fromImage(image); } -#endif //ifndef Q_OS_WINCE +#endif //ifndef Q_WS_WINCE QPixmap loadIconFromShell32( int resourceId, int size ) { diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp index d2e8d84..a0ae01f 100644 --- a/src/gui/image/qpixmap_x11.cpp +++ b/src/gui/image/qpixmap_x11.cpp @@ -2177,6 +2177,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/qpixmapcache.cpp b/src/gui/image/qpixmapcache.cpp index 458d6b9..82b42b4 100644 --- a/src/gui/image/qpixmapcache.cpp +++ b/src/gui/image/qpixmapcache.cpp @@ -40,13 +40,9 @@ ****************************************************************************/ #include "qpixmapcache.h" -#include "qcache.h" #include "qobject.h" #include "qdebug.h" - -#include "qpaintengine.h" -#include <private/qimage_p.h> -#include <private/qpixmap_raster_p.h> +#include "qpixmapcache_p.h" QT_BEGIN_NAMESPACE @@ -68,15 +64,17 @@ QT_BEGIN_NAMESPACE access the global pixmap cache. It creates an internal QCache object for caching the pixmaps. - The cache associates a pixmap with a string (key). If two pixmaps - are inserted into the cache using equal keys, then the last pixmap - will hide the first pixmap. The QHash and QCache classes do + The cache associates a pixmap with a string as a key or with a QPixmapCache::Key. + The QPixmapCache::Key is faster than using strings as key. + If two pixmaps are inserted into the cache using equal keys, then the + last pixmap will hide the first pixmap. The QHash and QCache classes do exactly the same. The cache becomes full when the total size of all pixmaps in the - cache exceeds cacheLimit(). The initial cache limit is 1024 KB (1 - MB); it is changed with setCacheLimit(). A pixmap takes roughly - (\e{width} * \e{height} * \e{depth})/8 bytes of memory. + cache exceeds cacheLimit(). The initial cache limit is + 2048 KB(2 MB) for Embedded, 10240 KB (10 + MB) for Desktops; it is changed with setCacheLimit(). + A pixmap takes roughly (\e{width} * \e{height} * \e{depth})/8 bytes of memory. The \e{Qt Quarterly} article \l{http://doc.trolltech.com/qq/qq12-qpixmapcache.html}{Optimizing @@ -86,58 +84,128 @@ QT_BEGIN_NAMESPACE \sa QCache, QPixmap */ -#if defined(Q_WS_QWS) || defined(Q_OS_WINCE) +#if defined(Q_WS_QWS) || defined(Q_WS_WINCE) static int cache_limit = 2048; // 2048 KB cache limit for embedded #else static int cache_limit = 10240; // 10 MB cache limit for desktop #endif -// XXX: hw: is this a general concept we need to abstract? -class QDetachedPixmap : public QPixmap +/*! + Constructs an empty Key object. +*/ +QPixmapCache::Key::Key() : d(0) { -public: - QDetachedPixmap(const QPixmap &pix) : QPixmap(pix) - { - if (data && data->classId() == QPixmapData::RasterClass) { - QRasterPixmapData *d = static_cast<QRasterPixmapData*>(data); - if (!d->image.isNull() && d->image.d->paintEngine - && !d->image.d->paintEngine->isActive()) - { - delete d->image.d->paintEngine; - d->image.d->paintEngine = 0; - } - } +} + +/*! + \internal + Constructs a copy of \a other. +*/ +QPixmapCache::Key::Key(const Key &other) +{ + if (other.d) + ++(other.d->ref); + d = other.d; +} + +/*! + Destructor; called immediately before the object is deleted. +*/ +QPixmapCache::Key::~Key() +{ + if (d && --(d->ref) == 0) + delete d; +} + +/*! + \internal + + Returns true if this key is the same as the given \a key. +*/ +bool QPixmapCache::Key::operator ==(const Key &key) const +{ + return (d == key.d); +} + +/*! + \fn bool QPixmapCache::Key::operator !=(const Key &key) const + \internal +*/ + +/*! + \internal +*/ +QPixmapCache::Key &QPixmapCache::Key::operator =(const Key &other) +{ + if (d != other.d) { + if (other.d) + ++(other.d->ref); + if (d && --(d->ref) == 0) + delete d; + d = other.d; } -}; + return *this; +} -class QPMCache : public QObject, public QCache<qint64, QDetachedPixmap> +class QPMCache : public QObject, public QCache<QPixmapCache::Key, QDetachedPixmap> { Q_OBJECT public: - QPMCache() - : QObject(0), - QCache<qint64, QDetachedPixmap>(cache_limit * 1024), - theid(0), ps(0), t(false) { } - ~QPMCache() { } + QPMCache(); + ~QPMCache(); void timerEvent(QTimerEvent *); bool insert(const QString& key, const QPixmap &pixmap, int cost); + QPixmapCache::Key insert(const QPixmap &pixmap, int cost); + bool replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost); bool remove(const QString &key); + bool remove(const QPixmapCache::Key &key); + + void resizeKeyArray(int size); + QPixmapCache::Key createKey(); + void releaseKey(const QPixmapCache::Key &key); + void clear(); QPixmap *object(const QString &key) const; + QPixmap *object(const QPixmapCache::Key &key) const; + + static inline QPixmapCache::KeyData *get(const QPixmapCache::Key &key) + {return key.d;} + + static QPixmapCache::KeyData* getKeyData(QPixmapCache::Key *key); private: - QHash<QString, qint64> cacheKeys; + int *keyArray; int theid; int ps; + int keyArraySize; + int freeKey; + QHash<QString, QPixmapCache::Key> cacheKeys; bool t; }; + QT_BEGIN_INCLUDE_NAMESPACE #include "qpixmapcache.moc" QT_END_INCLUDE_NAMESPACE +uint qHash(const QPixmapCache::Key &k) +{ + return qHash(QPMCache::get(k)->key); +} + +QPMCache::QPMCache() + : QObject(0), + QCache<QPixmapCache::Key, QDetachedPixmap>(cache_limit * 1024), + keyArray(0), theid(0), ps(0), keyArraySize(0), freeKey(0), t(false) +{ +} +QPMCache::~QPMCache() +{ + free(keyArray); +} + /* - This is supposed to cut the cache size down by about 80-90% in a + This is supposed to cut the cache size down by about 25% in a minute once the application becomes idle, to let any inserted pixmap remain in the cache for some time before it becomes a candidate for cleaning-up, and to not cut down the size of the cache while the @@ -146,23 +214,28 @@ QT_END_INCLUDE_NAMESPACE When the last pixmap has been deleted from the cache, kill the timer so Qt won't keep the CPU from going into sleep mode. */ - void QPMCache::timerEvent(QTimerEvent *) { int mc = maxCost(); bool nt = totalCost() == ps; + QList<QPixmapCache::Key> keys = QCache<QPixmapCache::Key, QDetachedPixmap>::keys(); setMaxCost(nt ? totalCost() * 3 / 4 : totalCost() -1); setMaxCost(mc); ps = totalCost(); - QHash<QString, qint64>::iterator it = cacheKeys.begin(); + QHash<QString, QPixmapCache::Key>::iterator it = cacheKeys.begin(); while (it != cacheKeys.end()) { if (!contains(it.value())) { + releaseKey(it.value()); it = cacheKeys.erase(it); } else { ++it; } } + for (int i = 0; i < keys.size(); ++i) { + if (!contains(keys.at(i))) + releaseKey(keys.at(i)); + } if (!size()) { killTimer(theid); @@ -176,38 +249,156 @@ void QPMCache::timerEvent(QTimerEvent *) QPixmap *QPMCache::object(const QString &key) const { - return QCache<qint64, QDetachedPixmap>::object(cacheKeys.value(key, -1)); + QPixmapCache::Key cacheKey = cacheKeys.value(key); + if (!cacheKey.d || !cacheKey.d->isValid) { + const_cast<QPMCache *>(this)->cacheKeys.remove(key); + return 0; + } + QPixmap *ptr = QCache<QPixmapCache::Key, QDetachedPixmap>::object(cacheKey); + //We didn't find the pixmap in the cache, the key is not valid anymore + if (!ptr) { + const_cast<QPMCache *>(this)->cacheKeys.remove(key); + const_cast<QPMCache *>(this)->releaseKey(cacheKey); + } + return ptr; } +QPixmap *QPMCache::object(const QPixmapCache::Key &key) const +{ + Q_ASSERT(key.d->isValid); + QPixmap *ptr = QCache<QPixmapCache::Key, QDetachedPixmap>::object(key); + //We didn't find the pixmap in the cache, the key is not valid anymore + if (!ptr) + const_cast<QPMCache *>(this)->releaseKey(key); + return ptr; +} bool QPMCache::insert(const QString& key, const QPixmap &pixmap, int cost) { - qint64 cacheKey = pixmap.cacheKey(); - if (QCache<qint64, QDetachedPixmap>::object(cacheKey)) { + QPixmapCache::Key cacheKey; + QPixmapCache::Key oldCacheKey = cacheKeys.value(key); + //If for the same key we add already a pixmap we should delete it + if (oldCacheKey.d) { + QCache<QPixmapCache::Key, QDetachedPixmap>::remove(oldCacheKey); + cacheKey = oldCacheKey; + } else { + cacheKey = createKey(); + } + + bool success = QCache<QPixmapCache::Key, QDetachedPixmap>::insert(cacheKey, new QDetachedPixmap(pixmap), cost); + if (success) { cacheKeys.insert(key, cacheKey); - return true; + if (!theid) { + theid = startTimer(30000); + t = false; + } + } else { + //Insertion failed we released the new allocated key + releaseKey(cacheKey); } - qint64 oldCacheKey = cacheKeys.value(key, -1); - //If for the same key we add already a pixmap we should delete it - if (oldCacheKey != -1) - QCache<qint64, QDetachedPixmap>::remove(oldCacheKey); + return success; +} - bool success = QCache<qint64, QDetachedPixmap>::insert(cacheKey, new QDetachedPixmap(pixmap), cost); +QPixmapCache::Key QPMCache::insert(const QPixmap &pixmap, int cost) +{ + QPixmapCache::Key cacheKey = createKey(); + bool success = QCache<QPixmapCache::Key, QDetachedPixmap>::insert(cacheKey, new QDetachedPixmap(pixmap), cost); if (success) { - cacheKeys.insert(key, cacheKey); if (!theid) { theid = startTimer(30000); t = false; } + } else { + //Insertion failed we released the key and return an invalid one + releaseKey(cacheKey); + } + return cacheKey; +} + +bool QPMCache::replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost) +{ + Q_ASSERT(key.d->isValid); + //If for the same key we add already a pixmap we should delete it + QCache<QPixmapCache::Key, QDetachedPixmap>::remove(key); + + bool success = QCache<QPixmapCache::Key, QDetachedPixmap>::insert(key, new QDetachedPixmap(pixmap), cost); + if (success && !theid) { + theid = startTimer(30000); + t = false; } return success; } bool QPMCache::remove(const QString &key) { - qint64 cacheKey = cacheKeys.value(key, -1); + QPixmapCache::Key cacheKey = cacheKeys.value(key); + //The key was not in the cache + if (!cacheKey.d) + return false; cacheKeys.remove(key); - return QCache<qint64, QDetachedPixmap>::remove(cacheKey); + releaseKey(cacheKey); + return QCache<QPixmapCache::Key, QDetachedPixmap>::remove(cacheKey); +} + +bool QPMCache::remove(const QPixmapCache::Key &key) +{ + bool result = QCache<QPixmapCache::Key, QDetachedPixmap>::remove(key); + //We release the key after we removed it from the cache + releaseKey(key); + return result; +} + +void QPMCache::resizeKeyArray(int size) +{ + if (size <= keyArraySize || size == 0) + return; + keyArray = reinterpret_cast<int *>(realloc(keyArray, size * sizeof(int))); + for (int i = keyArraySize; i != size; ++i) + keyArray[i] = i + 1; + keyArraySize = size; +} + +QPixmapCache::Key QPMCache::createKey() +{ + if (freeKey == keyArraySize) + resizeKeyArray(keyArraySize ? keyArraySize << 1 : 2); + int id = freeKey; + freeKey = keyArray[id]; + QPixmapCache::Key key; + QPixmapCache::KeyData *d = QPMCache::getKeyData(&key); + d->key = ++id; + return key; +} + +void QPMCache::releaseKey(const QPixmapCache::Key &key) +{ + if (key.d->key > keyArraySize || key.d->key <= 0) + return; + key.d->key--; + keyArray[key.d->key] = freeKey; + freeKey = key.d->key; + key.d->isValid = false; + key.d->key = 0; +} + +void QPMCache::clear() +{ + free(keyArray); + keyArray = 0; + freeKey = 0; + keyArraySize = 0; + //Mark all keys as invalid + QList<QPixmapCache::Key> keys = QCache<QPixmapCache::Key, QDetachedPixmap>::keys(); + for (int i = 0; i < keys.size(); ++i) + keys.at(i).d->isValid = false; + QCache<QPixmapCache::Key, QDetachedPixmap>::clear(); +} + +QPixmapCache::KeyData* QPMCache::getKeyData(QPixmapCache::Key *key) +{ + if (!key->d) + key->d = new QPixmapCache::KeyData; + return key->d; } Q_GLOBAL_STATIC(QPMCache, pm_cache) @@ -222,7 +413,7 @@ Q_GLOBAL_STATIC(QPMCache, pm_cache) \warning If valid, you should copy the pixmap immediately (this is fast). Subsequent insertions into the cache could cause the pointer to become invalid. For this reason, we recommend you use - find(const QString&, QPixmap&) instead. + bool find(const QString&, QPixmap*) instead. Example: \snippet doc/src/snippets/code/src_gui_image_qpixmapcache.cpp 0 @@ -235,25 +426,57 @@ QPixmap *QPixmapCache::find(const QString &key) /*! + \obsolete + + Use bool find(const QString&, QPixmap*) instead. +*/ + +bool QPixmapCache::find(const QString &key, QPixmap& pixmap) +{ + return find(key, &pixmap); +} + +/*! Looks for a cached pixmap associated with the \a key in the cache. - If the pixmap is found, the function sets \a pm to that pixmap and - returns true; otherwise it leaves \a pm alone and returns false. + If the pixmap is found, the function sets \a pixmap to that pixmap and + returns true; otherwise it leaves \a pixmap alone and returns false. + + \since 4.6 Example: \snippet doc/src/snippets/code/src_gui_image_qpixmapcache.cpp 1 */ -bool QPixmapCache::find(const QString &key, QPixmap& pm) +bool QPixmapCache::find(const QString &key, QPixmap* pixmap) { QPixmap *ptr = pm_cache()->object(key); - if (ptr) - pm = *ptr; + if (ptr && pixmap) + *pixmap = *ptr; return ptr != 0; } +/*! + Looks for a cached pixmap associated with the \a key in the cache. + If the pixmap is found, the function sets \a pixmap to that pixmap and + returns true; otherwise it leaves \a pixmap alone and returns false. If + the pixmap is not found, it means that the \a key is not valid anymore, + so it will be released for the next insertion. + + \since 4.6 +*/ +bool QPixmapCache::find(const Key &key, QPixmap* pixmap) +{ + //The key is not valid anymore, a flush happened before probably + if (!key.d || !key.d->isValid) + return false; + QPixmap *ptr = pm_cache()->object(key); + if (ptr && pixmap) + *pixmap = *ptr; + return ptr != 0; +} /*! - Inserts a copy of the pixmap \a pm associated with the \a key into + Inserts a copy of the pixmap \a pixmap associated with the \a key into the cache. All pixmaps inserted by the Qt library have a key starting with @@ -272,9 +495,46 @@ bool QPixmapCache::find(const QString &key, QPixmap& pm) \sa setCacheLimit() */ -bool QPixmapCache::insert(const QString &key, const QPixmap &pm) +bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap) { - return pm_cache()->insert(key, pm, pm.width() * pm.height() * pm.depth() / 8); + return pm_cache()->insert(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8); +} + +/*! + Inserts a copy of the pixmap \a pixmap into + the cache and return you the key. + + When a pixmap is inserted and the cache is about to exceed its + limit, it removes pixmaps until there is enough room for the + pixmap to be inserted. + + The oldest pixmaps (least recently accessed in the cache) are + deleted when more space is needed. + + \sa setCacheLimit(), replace() + + \since 4.6 +*/ +QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap) +{ + return pm_cache()->insert(pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8); +} + +/*! + Replace the pixmap associated to the \a key into + the cache. It return true if the pixmap \a pixmap has been correctly + inserted into the cache false otherwise. + + \sa setCacheLimit(), insert() + + \since 4.6 +*/ +bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap) +{ + //The key is not valid anymore, a flush happened before probably + if (!key.d || !key.d->isValid) + return false; + return pm_cache()->replace(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8); } /*! @@ -314,6 +574,19 @@ void QPixmapCache::remove(const QString &key) pm_cache()->remove(key); } +/*! + Removes the pixmap associated with \a key from the cache and release + the key for a future insertion. + + \since 4.6 +*/ +void QPixmapCache::remove(const Key &key) +{ + //The key is not valid anymore, a flush happened before probably + if (!key.d || !key.d->isValid) + return; + pm_cache()->remove(key); +} /*! Removes all pixmaps from the cache. diff --git a/src/gui/image/qpixmapcache.h b/src/gui/image/qpixmapcache.h index 2750a88..ae64310 100644 --- a/src/gui/image/qpixmapcache.h +++ b/src/gui/image/qpixmapcache.h @@ -53,12 +53,35 @@ QT_MODULE(Gui) class Q_GUI_EXPORT QPixmapCache { public: + class KeyData; + class Key + { + public: + Key(); + Key(const Key &other); + ~Key(); + bool operator ==(const Key &key) const; + inline bool operator !=(const Key &key) const + { return !operator==(key); } + Key &operator =(const Key &other); + + private: + KeyData *d; + friend class QPMCache; + friend class QPixmapCache; + }; + static int cacheLimit(); static void setCacheLimit(int); static QPixmap *find(const QString &key); - static bool find(const QString &key, QPixmap&); - static bool insert(const QString &key, const QPixmap&); + static bool find(const QString &key, QPixmap &pixmap); + static bool find(const QString &key, QPixmap *pixmap); + static bool find(const Key &key, QPixmap *pixmap); + static bool insert(const QString &key, const QPixmap &pixmap); + static Key insert(const QPixmap &pixmap); + static bool replace(const Key &key, const QPixmap &pixmap); static void remove(const QString &key); + static void remove(const Key &key); static void clear(); }; diff --git a/src/gui/image/qpixmapcache_p.h b/src/gui/image/qpixmapcache_p.h new file mode 100644 index 0000000..a35e0d2 --- /dev/null +++ b/src/gui/image/qpixmapcache_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QPIXMAPCACHE_P_H +#define QPIXMAPCACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qpixmapcache.h" +#include "qpaintengine.h" +#include <private/qimage_p.h> +#include <private/qpixmap_raster_p.h> +#include "qcache.h" + +QT_BEGIN_NAMESPACE + +uint qHash(const QPixmapCache::Key &k); + +class QPixmapCache::KeyData +{ +public: + KeyData() : isValid(true), key(0), ref(1) {} + KeyData(const KeyData &other) + : isValid(other.isValid), key(other.key), ref(1) {} + ~KeyData() {} + + bool isValid; + int key; + int ref; +}; + +// XXX: hw: is this a general concept we need to abstract? +class QDetachedPixmap : public QPixmap +{ +public: + QDetachedPixmap(const QPixmap &pix) : QPixmap(pix) + { + if (data && data->classId() == QPixmapData::RasterClass) { + QRasterPixmapData *d = static_cast<QRasterPixmapData*>(data); + if (!d->image.isNull() && d->image.d->paintEngine + && !d->image.d->paintEngine->isActive()) + { + delete d->image.d->paintEngine; + d->image.d->paintEngine = 0; + } + } + } +}; + +QT_END_NAMESPACE + +#endif // QPIXMAPCACHE_P_H 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..27bc70d 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; @@ -121,7 +122,7 @@ private: }; #ifdef Q_WS_WIN -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE QPixmap convertHIconToPixmap( const HICON icon); #else QPixmap convertHIconToPixmap( const HICON icon, bool large = false); diff --git a/src/gui/image/qpixmapdatafactory_p.h b/src/gui/image/qpixmapdatafactory_p.h index 65e3f11..9604346 100644 --- a/src/gui/image/qpixmapdatafactory_p.h +++ b/src/gui/image/qpixmapdatafactory_p.h @@ -65,7 +65,7 @@ QT_MODULE(Gui) class QPixmapData; -class Q_GUI_EXPORT QPixmapDataFactory +class QPixmapDataFactory { public: static QPixmapDataFactory* instance(int screen = 0); diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp index 8631c8c..4e6adc0 100644 --- a/src/gui/image/qpixmapfilter.cpp +++ b/src/gui/image/qpixmapfilter.cpp @@ -401,6 +401,7 @@ static void convolute( } yk++; } + delete[] fixedKernel; } /*! diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp index 90bd2ab..f4062ad 100644 --- a/src/gui/image/qxpmhandler.cpp +++ b/src/gui/image/qxpmhandler.cpp @@ -1146,7 +1146,7 @@ static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const QTextStream s(device); s << "/* XPM */" << endl << "static char *" << fbname(fileName) << "[]={" << endl - << "\"" << w << " " << h << " " << ncolors << " " << cpp << "\""; + << '\"' << w << ' ' << h << ' ' << ncolors << ' ' << cpp << '\"'; // write palette QMap<QRgb, int>::Iterator c = colorMap.begin(); @@ -1162,7 +1162,7 @@ static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const qGreen(color), qBlue(color)); ++c; - s << "," << endl << line; + s << ',' << endl << line; } // write pixels, limit to 4 characters per pixel @@ -1184,7 +1184,7 @@ static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const } } } - s << "," << endl << "\"" << line << "\""; + s << ',' << endl << '\"' << line << '\"'; } s << "};" << endl; return (s.status() == QTextStream::Ok); 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/qmacinputcontext_mac.cpp b/src/gui/inputmethod/qmacinputcontext_mac.cpp index f0e7ea9..86385fa 100644 --- a/src/gui/inputmethod/qmacinputcontext_mac.cpp +++ b/src/gui/inputmethod/qmacinputcontext_mac.cpp @@ -45,6 +45,7 @@ #include "qtextformat.h" #include <qdebug.h> #include <private/qapplication_p.h> +#include <private/qkeymapper_p.h> QT_BEGIN_NAMESPACE @@ -63,7 +64,8 @@ static QTextFormat qt_mac_compose_format() } QMacInputContext::QMacInputContext(QObject *parent) - : QInputContext(parent), composing(false), recursionGuard(false), textDocument(0) + : QInputContext(parent), composing(false), recursionGuard(false), textDocument(0), + keydownEvent(0) { // createTextDocument(); } @@ -183,6 +185,16 @@ QMacInputContext::cleanup() #endif } +void QMacInputContext::setLastKeydownEvent(EventRef event) +{ + EventRef tmpEvent = keydownEvent; + keydownEvent = event; + if (keydownEvent) + RetainEvent(keydownEvent); + if (tmpEvent) + ReleaseEvent(tmpEvent); +} + OSStatus QMacInputContext::globalEventProcessor(EventHandlerCallRef, EventRef event, void *) { @@ -335,6 +347,12 @@ QMacInputContext::globalEventProcessor(EventHandlerCallRef, EventRef event, void GetEventParameter(key_ev, kEventParamKeyMacCharCodes, typeChar, 0, sizeof(chr), 0, &chr); if(!chr || chr >= 128 || (text.length() > 0 && (text.length() > 1 || text.at(0) != QLatin1Char(chr)))) handled_event = !widget->testAttribute(Qt::WA_InputMethodEnabled); + QMacInputContext *context = qobject_cast<QMacInputContext*>(qApp->inputContext()); + if (context && context->lastKeydownEvent()) { + qt_keymapper_private()->translateKeyEvent(widget, 0, context->lastKeydownEvent(), + 0, false); + context->setLastKeydownEvent(0); + } } break; } default: diff --git a/src/gui/inputmethod/qmacinputcontext_p.h b/src/gui/inputmethod/qmacinputcontext_p.h index f708040..c3f245a 100644 --- a/src/gui/inputmethod/qmacinputcontext_p.h +++ b/src/gui/inputmethod/qmacinputcontext_p.h @@ -78,6 +78,10 @@ public: static OSStatus globalEventProcessor(EventHandlerCallRef, EventRef, void *); static void initialize(); static void cleanup(); + + EventRef lastKeydownEvent() { return keydownEvent; } + void setLastKeydownEvent(EventRef); + protected: void mouseHandler(int pos, QMouseEvent *); private: @@ -85,6 +89,7 @@ private: bool recursionGuard; TSMDocumentID textDocument; QString currentText; + EventRef keydownEvent; }; 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 e3e8aa4..dd966d5 100644 --- a/src/gui/inputmethod/qwininputcontext_win.cpp +++ b/src/gui/inputmethod/qwininputcontext_win.cpp @@ -59,7 +59,7 @@ #include "qdebug.h" #endif -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) extern void qt_wince_show_SIP(bool show); // defined in qguifunctions_wince.cpp #endif @@ -237,7 +237,7 @@ QWinInputContext::QWinInputContext(QObject *parent) aimmpump->Start(); } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE QSysInfo::WinVersion ver = QSysInfo::windowsVersion(); if (ver & QSysInfo::WV_NT_based && ver >= QSysInfo::WV_VISTA) { // Since the IsValidLanguageGroup/IsValidLocale functions always return true on @@ -349,7 +349,7 @@ static LONG getCompositionString(HIMC himc, DWORD dwIndex, LPVOID lpbuf, DWORD d if(QSysInfo::WindowsVersion != QSysInfo::WV_95) { len = ImmGetCompositionStringW(himc, dwIndex, lpbuf, dBufLen); } -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) else { len = ImmGetCompositionStringA(himc, dwIndex, lpbuf, dBufLen); if (unicode) @@ -738,7 +738,7 @@ inline void enableIme(QWidget *w, bool value) // enable ime if (defaultContext) ImmAssociateContext(w->effectiveWinId(), defaultContext); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (qApp->autoSipEnabled()) qt_wince_show_SIP(true); #endif @@ -747,7 +747,7 @@ inline void enableIme(QWidget *w, bool value) HIMC oldimc = ImmAssociateContext(w->effectiveWinId(), 0); if (!defaultContext) defaultContext = oldimc; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (qApp->autoSipEnabled()) qt_wince_show_SIP(false); #endif @@ -755,7 +755,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; @@ -824,6 +824,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 c77395a..c90216b 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -1426,10 +1426,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: { @@ -1642,7 +1642,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; } @@ -2165,11 +2165,12 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) } #endif bool modified = (event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)); - if (!event->text().isEmpty() && !modified) { - if (!edit(currentIndex(), AnyKeyPressed, event)) - keyboardSearch(event->text()); + if (!event->text().isEmpty() && !modified && !edit(currentIndex(), AnyKeyPressed, event)) { + keyboardSearch(event->text()); + event->accept(); + } else { + event->ignore(); } - event->ignore(); break; } } } @@ -2325,7 +2326,7 @@ bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEve } if (d->sendDelegateEvent(index, event)) { - d->viewport->update(visualRect(index)); + update(index); return true; } @@ -2844,9 +2845,9 @@ void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget d->persistent.insert(widget); d->addEditor(index, widget, true); widget->show(); + dataChanged(index, index); // update the geometry if (!d->delayedPendingLayout) widget->setGeometry(visualRect(index)); - dataChanged(index, index); // update the geometry } } @@ -2926,7 +2927,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; } @@ -3131,9 +3132,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)); } } @@ -3161,16 +3160,15 @@ void QAbstractItemView::currentChanged(const QModelIndex ¤t, const QModelI closeEditor(editor, QAbstractItemDelegate::NoHint); } if (isVisible()) { - d->setDirtyRegion(visualRect(previous)); - d->updateDirtyRegion(); + update(previous); } } + if (current.isValid() && !d->autoScrollTimer.isActive()) { if (isVisible()) { 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/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h index 16bd1ab..139d0b7 100644 --- a/src/gui/itemviews/qabstractitemview_p.h +++ b/src/gui/itemviews/qabstractitemview_p.h @@ -99,7 +99,7 @@ public: QVariant data(const QModelIndex &, int) const { return QVariant(); } }; -class Q_GUI_EXPORT QAbstractItemViewPrivate : public QAbstractScrollAreaPrivate +class QAbstractItemViewPrivate : public QAbstractScrollAreaPrivate { Q_DECLARE_PUBLIC(QAbstractItemView) diff --git a/src/gui/itemviews/qdirmodel.cpp b/src/gui/itemviews/qdirmodel.cpp index 7da7c7a..65e3032 100644 --- a/src/gui/itemviews/qdirmodel.cpp +++ b/src/gui/itemviews/qdirmodel.cpp @@ -44,6 +44,7 @@ #ifndef QT_NO_DIRMODEL #include <qstack.h> #include <qfile.h> +#include <qfilesystemmodel.h> #include <qurl.h> #include <qmime.h> #include <qpair.h> @@ -1335,14 +1336,14 @@ QString QDirModelPrivate::size(const QModelIndex &index) const const quint64 tb = 1024 * gb; quint64 bytes = n->info.size(); if (bytes >= tb) - return QLocale().toString(bytes / tb) + QString::fromLatin1(" TB"); + return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3)); if (bytes >= gb) - return QLocale().toString(bytes / gb) + QString::fromLatin1(" GB"); + return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2)); if (bytes >= mb) - return QLocale().toString(bytes / mb) + QString::fromLatin1(" MB"); + return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1)); if (bytes >= kb) - return QLocale().toString(bytes / kb) + QString::fromLatin1(" KB"); - return QLocale().toString(bytes) + QString::fromLatin1(" bytes"); + return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb)); + return QFileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes)); } QString QDirModelPrivate::type(const QModelIndex &index) const diff --git a/src/gui/itemviews/qfileiconprovider.cpp b/src/gui/itemviews/qfileiconprovider.cpp index ac62551..434bcde 100644 --- a/src/gui/itemviews/qfileiconprovider.cpp +++ b/src/gui/itemviews/qfileiconprovider.cpp @@ -215,8 +215,7 @@ QIcon QFileIconProvider::icon(IconType type) const QIcon QFileIconProviderPrivate::getWinIcon(const QFileInfo &fileInfo) const { QIcon retIcon; - QString fileExtension = fileInfo.suffix().toUpper(); - fileExtension.prepend( QLatin1String(".") ); + const QString fileExtension = QLatin1Char('.') + fileInfo.suffix().toUpper(); QString key; if (fileInfo.isFile() && !fileInfo.isExecutable() && !fileInfo.isSymLink()) @@ -356,7 +355,7 @@ QIcon QFileIconProvider::icon(const QFileInfo &info) const return icon; #endif if (info.isRoot()) -#if defined (Q_WS_WIN) && !defined(Q_OS_WINCE) +#if defined (Q_WS_WIN) && !defined(Q_WS_WINCE) { uint type = DRIVE_UNKNOWN; QT_WA({ type = GetDriveTypeW((wchar_t *)info.absoluteFilePath().utf16()); }, @@ -416,26 +415,22 @@ QString QFileIconProvider::type(const QFileInfo &info) const } if (info.isDir()) - return QApplication::translate("QFileDialog", #ifdef Q_WS_WIN - "File Folder", "Match Windows Explorer" + return QApplication::translate("QFileDialog", "File Folder", "Match Windows Explorer"); #else - "Folder", "All other platforms" + return QApplication::translate("QFileDialog", "Folder", "All other platforms"); #endif - ); // Windows - "File Folder" // OS X - "Folder" // Konqueror - "Folder" // Nautilus - "folder" if (info.isSymLink()) - return QApplication::translate("QFileDialog", #ifdef Q_OS_MAC - "Alias", "Mac OS X Finder" + return QApplication::translate("QFileDialog", "Alias", "Mac OS X Finder"); #else - "Shortcut", "All other platforms" + return QApplication::translate("QFileDialog", "Shortcut", "All other platforms"); #endif - ); // OS X - "Alias" // Windows - "Shortcut" // Konqueror - "Folder" or "TXT File" i.e. what it is pointing to diff --git a/src/gui/itemviews/qheaderview.cpp b/src/gui/itemviews/qheaderview.cpp index 86ece40..ad93922 100644 --- a/src/gui/itemviews/qheaderview.cpp +++ b/src/gui/itemviews/qheaderview.cpp @@ -1195,7 +1195,7 @@ QHeaderView::ResizeMode QHeaderView::resizeMode(int logicalIndex) const Q_D(const QHeaderView); int visual = visualIndex(logicalIndex); Q_ASSERT(visual != -1); - return d->visualIndexResizeMode(visual); + return d->headerSectionResizeMode(visual); } /*! @@ -1234,7 +1234,7 @@ void QHeaderView::setSortIndicatorShown(bool show) if (sortIndicatorSection() < 0 || sortIndicatorSection() > count()) return; - if (d->visualIndexResizeMode(sortIndicatorSection()) == ResizeToContents) + if (d->headerSectionResizeMode(sortIndicatorSection()) == ResizeToContents) resizeSections(); d->viewport->update(); @@ -1969,20 +1969,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(); } @@ -2936,22 +2935,25 @@ int QHeaderViewPrivate::lastVisibleVisualIndex() const void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool useGlobalMode) { Q_Q(QHeaderView); + //stop the timer in case it is delayed + delayedResize.stop(); executePostedLayout(); if (sectionCount == 0) return; + + if (resizeRecursionBlock) + return; + resizeRecursionBlock = true; + invalidateCachedSizeHint(); + const int lastVisibleSection = lastVisibleVisualIndex(); + // find stretchLastSection if we have it int stretchSection = -1; - if (stretchLastSection && !useGlobalMode) { - for (int i = sectionCount - 1; i >= 0; --i) { - if (!isVisualIndexHidden(i)) { - stretchSection = i; - break; - } - } - } + if (stretchLastSection && !useGlobalMode) + stretchSection = lastVisibleVisualIndex(); // count up the number of strected sections and how much space left for them int lengthToStrech = (orientation == Qt::Horizontal ? viewport->width() : viewport->height()); @@ -2965,7 +2967,7 @@ void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool if (useGlobalMode && (i != stretchSection)) resizeMode = globalMode; else - resizeMode = (i == stretchSection ? QHeaderView::Stretch : visualIndexResizeMode(i)); + resizeMode = (i == stretchSection ? QHeaderView::Stretch : headerSectionResizeMode(i)); if (resizeMode == QHeaderView::Stretch) { ++numberOfStretchedSections; @@ -2997,7 +2999,6 @@ void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool int spanStartSection = 0; int previousSectionLength = 0; - const int lastVisibleSection = lastVisibleVisualIndex(); QHeaderView::ResizeMode previousSectionResizeMode = QHeaderView::Interactive; @@ -3016,7 +3017,7 @@ void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool else resizeMode = (i == stretchSection ? QHeaderView::Stretch - : visualIndexResizeMode(i)); + : newSectionResizeMode); if (resizeMode == QHeaderView::Stretch && stretchSectionLength != -1) { if (i == lastVisibleSection) newSectionLength = qMax(stretchSectionLength, lastSectionSize); @@ -3053,7 +3054,7 @@ void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool (sectionCount - spanStartSection) * previousSectionLength, previousSectionResizeMode); //Q_ASSERT(headerLength() == length); - + resizeRecursionBlock = false; viewport->update(); } diff --git a/src/gui/itemviews/qheaderview_p.h b/src/gui/itemviews/qheaderview_p.h index 2889f08..cfb1c3e 100644 --- a/src/gui/itemviews/qheaderview_p.h +++ b/src/gui/itemviews/qheaderview_p.h @@ -90,6 +90,7 @@ public: highlightSelected(false), stretchLastSection(false), cascadingResizing(false), + resizeRecursionBlock(false), stretchSections(0), contentsSections(0), minimumSectionSize(-1), @@ -169,10 +170,6 @@ public: if (!sectionHidden.isEmpty()) sectionHidden.setBit(visual, hidden); } - inline QHeaderView::ResizeMode visualIndexResizeMode(int visual) const { - return headerSectionResizeMode(visual); - } - inline bool hasAutoResizeSections() const { return stretchSections || stretchLastSection || contentsSections; } @@ -210,7 +207,7 @@ public: } inline bool sectionIsCascadable(int visual) const { - return visualIndexResizeMode(visual) == QHeaderView::Interactive; + return headerSectionResizeMode(visual) == QHeaderView::Interactive; } inline int modelSectionCount() const { @@ -230,7 +227,6 @@ public: inline void executePostedResize() const { if (delayedResize.isActive() && state == NoState) { - delayedResize.stop(); const_cast<QHeaderView*>(q_func())->resizeSections(); } } @@ -274,6 +270,7 @@ public: bool highlightSelected; bool stretchLastSection; bool cascadingResizing; + bool resizeRecursionBlock; int stretchSections; int contentsSections; int defaultSectionSize; diff --git a/src/gui/itemviews/qitemselectionmodel.cpp b/src/gui/itemviews/qitemselectionmodel.cpp index 8baefdf..696b8fa 100644 --- a/src/gui/itemviews/qitemselectionmodel.cpp +++ b/src/gui/itemviews/qitemselectionmodel.cpp @@ -1553,7 +1553,7 @@ QDebug operator<<(QDebug dbg, const QItemSelectionRange &range) { #ifndef Q_BROKEN_DEBUG_STREAM dbg.nospace() << "QItemSelectionRange(" << range.topLeft() - << "," << range.bottomRight() << ")"; + << ',' << range.bottomRight() << ')'; return dbg.space(); #else qWarning("This compiler doesn't support streaming QItemSelectionRange to QDebug"); diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index 48f53a0..1071c1d 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 @@ -1608,6 +1608,10 @@ QRegion QListView::visualRegionForSelection(const QItemSelection &selection) con if (!selection.at(i).isValid()) continue; QModelIndex parent = selection.at(i).topLeft().parent(); + //we only display the children of the root in a listview + //we're not interested in the other model indexes + if (parent != d->root) + continue; int t = selection.at(i).topLeft().row(); int b = selection.at(i).bottomRight().row(); if (d->viewMode == IconMode || d->isWrapping()) { // in non-static mode, we have to go through all selected items @@ -1616,8 +1620,8 @@ QRegion QListView::visualRegionForSelection(const QItemSelection &selection) con } else { // in static mode, we can optimize a bit while (t <= b && d->isHidden(t)) ++t; while (b >= t && d->isHidden(b)) --b; - const QModelIndex top = d->model->index(t, c, d->root); - const QModelIndex bottom = d->model->index(b, c, d->root); + const QModelIndex top = d->model->index(t, c, parent); + const QModelIndex bottom = d->model->index(b, c, parent); QRect rect(visualRect(top).topLeft(), visualRect(bottom).bottomRight()); selectionRegion += QRegion(rect); diff --git a/src/gui/itemviews/qsortfilterproxymodel.cpp b/src/gui/itemviews/qsortfilterproxymodel.cpp index 92dfd19..96eb6f0 100644 --- a/src/gui/itemviews/qsortfilterproxymodel.cpp +++ b/src/gui/itemviews/qsortfilterproxymodel.cpp @@ -56,6 +56,15 @@ QT_BEGIN_NAMESPACE typedef QList<QPair<QModelIndex, QPersistentModelIndex> > QModelIndexPairList; +static inline QSet<int> qVectorToSet(const QVector<int> &vector) +{ + QSet<int> set; + set.reserve(vector.size()); + for(int i=0; i < vector.size(); ++i) + set << vector.at(i); + return set; +} + class QSortFilterProxyModelLessThan { public: @@ -224,8 +233,8 @@ public: QModelIndexPairList store_persistent_indexes(); void update_persistent_indexes(const QModelIndexPairList &source_indexes); - void filter_changed(); - void handle_filter_changed( + void filter_changed(const QModelIndex &source_parent = QModelIndex()); + QSet<int> handle_filter_changed( QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, const QModelIndex &source_parent, Qt::Orientation orient); @@ -937,27 +946,39 @@ void QSortFilterProxyModelPrivate::update_persistent_indexes( q->changePersistentIndexList(from, to); } + /*! \internal Updates the proxy model (adds/removes rows) based on the new filter. */ -void QSortFilterProxyModelPrivate::filter_changed() +void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_parent) { - QMap<QModelIndex, Mapping *>::const_iterator it; - for (it = source_index_mapping.constBegin(); it != source_index_mapping.constEnd(); ++it) { - QModelIndex source_parent = it.key(); - Mapping *m = it.value(); - handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical); - handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal); + IndexMap::const_iterator it = source_index_mapping.constFind(source_parent); + if (it == source_index_mapping.constEnd()) + return; + Mapping *m = it.value(); + QSet<int> rows_removed = handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical); + QSet<int> columns_removed = handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal); + QVector<QModelIndex>::iterator it2 = m->mapped_children.end(); + while (it2 != m->mapped_children.begin()) { + --it2; + const QModelIndex source_child_index = *it2; + if (rows_removed.contains(source_child_index.row()) || columns_removed.contains(source_child_index.column())) { + it2 = m->mapped_children.erase(it2); + remove_from_mapping(source_child_index); + } else { + filter_changed(source_child_index); + } } } /*! \internal + returns the removed items indexes */ -void QSortFilterProxyModelPrivate::handle_filter_changed( +QSet<int> QSortFilterProxyModelPrivate::handle_filter_changed( QVector<int> &source_to_proxy, QVector<int> &proxy_to_source, const QModelIndex &source_parent, Qt::Orientation orient) { @@ -994,6 +1015,7 @@ void QSortFilterProxyModelPrivate::handle_filter_changed( insert_source_items(source_to_proxy, proxy_to_source, source_items_insert, source_parent, orient); } + return qVectorToSet(source_items_remove); } void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &source_top_left, @@ -1044,15 +1066,14 @@ void QSortFilterProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &sourc if (!source_rows_remove.isEmpty()) { remove_source_items(m->proxy_rows, m->source_rows, source_rows_remove, source_parent, Qt::Vertical); - QSet<int> source_rows_remove_set = source_rows_remove.toList().toSet(); - QVector<QModelIndex>::iterator it = m->mapped_children.begin(); - while (it != m->mapped_children.end()) { + QSet<int> source_rows_remove_set = qVectorToSet(source_rows_remove); + QVector<QModelIndex>::iterator it = m->mapped_children.end(); + while (it != m->mapped_children.begin()) { + --it; const QModelIndex source_child_index = *it; if (source_rows_remove_set.contains(source_child_index.row())) { it = m->mapped_children.erase(it); remove_from_mapping(source_child_index); - } else { - ++it; } } } diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp index 2902768..0bded54 100644 --- a/src/gui/itemviews/qtableview.cpp +++ b/src/gui/itemviews/qtableview.cpp @@ -59,6 +59,138 @@ QT_BEGIN_NAMESPACE +/** \internal + Add a span to the collection. the collection takes the ownership. + */ +void QSpanCollection::addSpan(QSpanCollection::Span *span) +{ + spans.append(span); + Index::iterator it_y = index.lowerBound(-span->top()); + if (it_y == index.end() || it_y.key() != -span->top()) { + //there is no spans that starts with the row in the index, so create a sublist for it. + SubIndex sub_index; + if (it_y != index.end()) { + //the previouslist is the list of spans that sarts _before_ the row of the span. + // and which may intersect this row. + const SubIndex previousList = it_y.value(); + foreach(Span *s, previousList) { + //If a subspans intersect the row, we need to split it into subspans + if(s->bottom() >= span->top()) + sub_index.insert(-s->left(), s); + } + } + it_y = index.insert(-span->top(), sub_index); + //we will insert span to *it_y in the later loop + } + + //insert the span as supspan in all the lists that intesects the span + while(-it_y.key() <= span->bottom()) { + (*it_y).insert(-span->left(), span); + if(it_y == index.begin()) + break; + --it_y; + } +} + + +/** \internal +* Has to be called after the height and width of a span is changed. +* +* old_height is the height before the change +* +* if the size of the span is now 0x0 the span will be deleted. +*/ +void QSpanCollection::updateSpan(QSpanCollection::Span *span, int old_height) +{ + if (old_height < span->height()) { + //add the span as subspan in all the lists that intersect the new covered columns + Index::iterator it_y = index.lowerBound(-(span->top() + old_height - 1)); + Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list + while (-it_y.key() <= span->bottom()) { + (*it_y).insert(-span->left(), span); + if(it_y == index.begin()) + break; + --it_y; + } + } else if (old_height > span->height()) { + //remove the span from all the subspans lists that intersect the columns not covered anymore + Index::iterator it_y = index.lowerBound(-span->bottom()); + Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list + while (-it_y.key() <= span->top() + old_height -1) { + if(-it_y.key() != span->bottom()) { + (*it_y).remove(-span->left()); + if (it_y->isEmpty()) { + it_y = index.erase(it_y) - 1; + } + } + if(it_y == index.begin()) + break; + --it_y; + } + } + + if (span->width() == 0 && span->height() == 0) { + spans.removeOne(span); + delete span; + } +} + +/** \internal + * \return a spans that spans over cell x,y (column,row) or 0 if there is none. + */ +QSpanCollection::Span *QSpanCollection::spanAt(int x, int y) const +{ + Index::const_iterator it_y = index.lowerBound(-y); + if (it_y == index.end()) + return 0; + SubIndex::const_iterator it_x = (*it_y).lowerBound(-x); + if (it_x == (*it_y).end()) + return 0; + Span *span = *it_x; + if (span->right() >= x && span->bottom() >= y) + return span; + return 0; +} + + +/** \internal +* remove and deletes all spans inside the collection +*/ +void QSpanCollection::clear() +{ + qDeleteAll(spans); + index.clear(); + spans.clear(); +} + +/** \internal + * return a list to all the spans that spans over cells in the given rectangle + */ +QList<QSpanCollection::Span *> QSpanCollection::spansInRect(int x, int y, int w, int h) const +{ + QSet<Span *> list; + Index::const_iterator it_y = index.lowerBound(-y); + if(it_y == index.end()) + --it_y; + while(-it_y.key() <= y + h) { + SubIndex::const_iterator it_x = (*it_y).lowerBound(-x); + if (it_x == (*it_y).end()) + --it_x; + while(-it_x.key() <= x + w) { + Span *s = *it_x; + if (s->bottom() >= y && s->right() >= x) + list << s; + if (it_x == (*it_y).begin()) + break; + --it_x; + } + if(it_y == index.begin()) + break; + --it_y; + } + return list.toList(); +} + class QTableCornerButton : public QAbstractButton { Q_OBJECT @@ -149,35 +281,40 @@ void QTableViewPrivate::trimHiddenSelections(QItemSelectionRange *range) const */ void QTableViewPrivate::setSpan(int row, int column, int rowSpan, int columnSpan) { - if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0) + if (row < 0 || column < 0 || rowSpan <= 0 || columnSpan <= 0) { + qWarning() << "QTableView::setSpan: invalid span given: (" << row << ',' << column << ',' << rowSpan << ',' << columnSpan << ')'; return; - Span sp(row, column, rowSpan, columnSpan); - QList<Span>::iterator it; - for (it = spans.begin(); it != spans.end(); ++it) { - if (((*it).top() == sp.top()) && ((*it).left() == sp.left())) { - if ((sp.height() == 1) && (sp.width() == 1)) - spans.erase(it); // "Implicit" span (1, 1), no need to store it - else - *it = sp; // Replace + } + QSpanCollection::Span *sp = spans.spanAt(column, row); + if (sp) { + if (sp->top() != row || sp->left() != column) { + qWarning() << "QTableView::setSpan: span cannot overlap"; return; } + if (rowSpan == 1 && columnSpan == 1) { + rowSpan = columnSpan = 0; + } + const int old_height = sp->height(); + sp->m_bottom = row + rowSpan - 1; + sp->m_right = column + columnSpan - 1; + spans.updateSpan(sp, old_height); + return; } - spans.append(sp); + sp = new QSpanCollection::Span(row, column, rowSpan, columnSpan); + spans.addSpan(sp); } /*! \internal Gets the span information for the cell at (\a row, \a column). */ -QTableViewPrivate::Span QTableViewPrivate::span(int row, int column) const +QSpanCollection::Span QTableViewPrivate::span(int row, int column) const { - QList<Span>::const_iterator it; - for (it = spans.constBegin(); it != spans.constEnd(); ++it) { - Span span = *it; - if (isInSpan(row, column, span)) - return span; - } - return Span(row, column, 1, 1); + QSpanCollection::Span *sp = spans.spanAt(column, row); + if (sp) + return *sp; + + return QSpanCollection::Span(row, column, 1, 1); } /*! @@ -233,67 +370,9 @@ bool QTableViewPrivate::spanContainsSection(const QHeaderView *header, int logic /*! \internal - Returns true if one or more spans intersect column \a column. -*/ -bool QTableViewPrivate::spansIntersectColumn(int column) const -{ - QList<Span>::const_iterator it; - for (it = spans.constBegin(); it != spans.constEnd(); ++it) { - Span span = *it; - if (spanContainsColumn(column, span.left(), span.width())) - return true; - } - return false; -} - -/*! - \internal - Returns true if one or more spans intersect row \a row. -*/ -bool QTableViewPrivate::spansIntersectRow(int row) const -{ - QList<Span>::const_iterator it; - for (it = spans.constBegin(); it != spans.constEnd(); ++it) { - Span span = *it; - if (spanContainsRow(row, span.top(), span.height())) - return true; - } - return false; -} - -/*! - \internal - Returns true if one or more spans intersect one or more columns. -*/ -bool QTableViewPrivate::spansIntersectColumns(const QList<int> &columns) const -{ - QList<int>::const_iterator it; - for (it = columns.constBegin(); it != columns.constEnd(); ++it) { - if (spansIntersectColumn(*it)) - return true; - } - return false; -} - -/*! - \internal - Returns true if one or more spans intersect one or more rows. -*/ -bool QTableViewPrivate::spansIntersectRows(const QList<int> &rows) const -{ - QList<int>::const_iterator it; - for (it = rows.constBegin(); it != rows.constEnd(); ++it) { - if (spansIntersectRow(*it)) - return true; - } - return false; -} - -/*! - \internal Returns the visual rect for the given \a span. */ -QRect QTableViewPrivate::visualSpanRect(const Span &span) const +QRect QTableViewPrivate::visualSpanRect(const QSpanCollection::Span &span) const { Q_Q(const QTableView); // vertical @@ -319,42 +398,54 @@ QRect QTableViewPrivate::visualSpanRect(const Span &span) const preparation for the main drawing loop. \a drawn is a QBitArray of visualRowCountxvisualCoulumnCount which say if particular cell has been drawn */ -void QTableViewPrivate::drawAndClipSpans(const QRect &area, QPainter *painter, +void QTableViewPrivate::drawAndClipSpans(const QRegion &area, QPainter *painter, const QStyleOptionViewItemV4 &option, QBitArray *drawn, int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn) { bool alternateBase = false; QRegion region = viewport->rect(); - QList<Span>::const_iterator it; - for (it = spans.constBegin(); it != spans.constEnd(); ++it) { - Span span = *it; + QList<QSpanCollection::Span *> visibleSpans; + bool sectionMoved = verticalHeader->sectionsMoved() || horizontalHeader->sectionsMoved(); + + if (!sectionMoved) { + visibleSpans = spans.spansInRect(logicalColumn(firstVisualColumn), logicalRow(firstVisualRow), + lastVisualColumn - firstVisualColumn + 1, lastVisualRow - firstVisualRow + 1); + } else { + QSet<QSpanCollection::Span *> set; + for(int x = firstVisualColumn; x <= lastVisualColumn; x++) + for(int y = firstVisualRow; y <= lastVisualRow; y++) + set.insert(spans.spanAt(x,y)); + set.remove(0); + visibleSpans = set.toList(); + } - int row = span.top(); - int col = span.left(); + foreach (QSpanCollection::Span *span, visibleSpans) { + int row = span->top(); + int col = span->left(); if (isHidden(row, col)) continue; QModelIndex index = model->index(row, col, root); if (!index.isValid()) continue; - QRect rect = visualSpanRect(span); + QRect rect = visualSpanRect(*span); rect.translate(scrollDelayOffset); - if (!rect.intersects(area)) + if (!area.intersects(rect)) continue; QStyleOptionViewItemV4 opt = option; opt.rect = rect; - alternateBase = alternatingColors && (span.top() & 1); + alternateBase = alternatingColors && (span->top() & 1); if (alternateBase) opt.features |= QStyleOptionViewItemV2::Alternate; else opt.features &= ~QStyleOptionViewItemV2::Alternate; drawCell(painter, opt, index); region -= rect; - for (int r = span.top(); r <= span.bottom(); ++r) { + for (int r = span->top(); r <= span->bottom(); ++r) { const int vr = visualRow(r); if (vr < firstVisualRow || vr > lastVisualRow) continue; - for (int c = span.left(); c <= span.right(); ++c) { + for (int c = span->left(); c <= span->right(); ++c) { const int vc = visualColumn(c); if (vc < firstVisualColumn || vc > lastVisualColumn) continue; @@ -753,7 +844,8 @@ void QTableView::paintEvent(QPaintEvent *event) uint x = horizontalHeader->length() - horizontalHeader->offset() - (rightToLeft ? 0 : 1); uint y = verticalHeader->length() - verticalHeader->offset() - 1; - QVector<QRect> rects = event->region().rects(); + const QRegion region = event->region().translated(offset); + const QVector<QRect> rects = region.rects(); //firstVisualRow is the visual index of the first visible row. lastVisualRow is the visual index of the last visible Row. //same goes for ...VisualColumn @@ -773,9 +865,13 @@ void QTableView::paintEvent(QPaintEvent *event) QBitArray drawn((lastVisualRow - firstVisualRow + 1) * (lastVisualColumn - firstVisualColumn + 1)); + if (d->hasSpans()) { + d->drawAndClipSpans(region, &painter, option, &drawn, + firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn); + } + for (int i = 0; i < rects.size(); ++i) { QRect dirtyArea = rects.at(i); - dirtyArea.translate(offset); dirtyArea.setBottom(qMin(dirtyArea.bottom(), int(y))); if (rightToLeft) { dirtyArea.setLeft(qMax(dirtyArea.left(), d->viewport->width() - int(x))); @@ -783,10 +879,6 @@ void QTableView::paintEvent(QPaintEvent *event) dirtyArea.setRight(qMin(dirtyArea.right(), int(x))); } - if (d->hasSpans()) - d->drawAndClipSpans(dirtyArea, &painter, option, &drawn, - firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn); - // get the horizontal start and end visual sections int left = horizontalHeader->visualIndexAt(dirtyArea.left()); int right = horizontalHeader->visualIndexAt(dirtyArea.right()); @@ -913,7 +1005,7 @@ QModelIndex QTableView::indexAt(const QPoint &pos) const int c = columnAt(pos.x()); if (r >= 0 && c >= 0) { if (d->hasSpans()) { - QTableViewPrivate::Span span = d->span(r, c); + QSpanCollection::Span span = d->span(r, c); r = span.top(); c = span.left(); } @@ -1011,14 +1103,14 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi --visualRow; if (d->hasSpans()) { int row = d->logicalRow(visualRow); - QTableViewPrivate::Span span = d->span(row, current.column()); + QSpanCollection::Span span = d->span(row, current.column()); visualRow = d->visualRow(span.top()); visualColumn = d->visualColumn(span.left()); } break; case MoveDown: if (d->hasSpans()) { - QTableViewPrivate::Span span = d->span(current.row(), current.column()); + QSpanCollection::Span span = d->span(current.row(), current.column()); visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height())); } #ifdef QT_KEYPAD_NAVIGATION @@ -1030,7 +1122,7 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi ++visualRow; if (d->hasSpans()) { int row = d->logicalRow(visualRow); - QTableViewPrivate::Span span = d->span(row, current.column()); + QSpanCollection::Span span = d->span(row, current.column()); visualColumn = d->visualColumn(span.left()); } break; @@ -1058,7 +1150,7 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi --visualColumn; if (d->hasSpans()) { int column = d->logicalColumn(visualColumn); - QTableViewPrivate::Span span = d->span(current.row(), column); + QSpanCollection::Span span = d->span(current.row(), column); visualRow = d->visualRow(span.top()); visualColumn = d->visualColumn(span.left()); } @@ -1078,7 +1170,7 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi } // else MoveRight case MoveRight: if (d->hasSpans()) { - QTableViewPrivate::Span span = d->span(current.row(), current.column()); + QSpanCollection::Span span = d->span(current.row(), current.column()); visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width())); } ++visualColumn; @@ -1086,7 +1178,7 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi ++visualColumn; if (d->hasSpans()) { int column = d->logicalColumn(visualColumn); - QTableViewPrivate::Span span = d->span(current.row(), column); + QSpanCollection::Span span = d->span(current.row(), column); visualRow = d->visualRow(span.top()); } break; @@ -1161,9 +1253,8 @@ void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionF int right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column())); do { expanded = false; - QList<QTableViewPrivate::Span>::const_iterator it; - for (it = d->spans.constBegin(); it != d->spans.constEnd(); ++it) { - QTableViewPrivate::Span span = *it; + foreach (QSpanCollection::Span *it, d->spans.spans) { + const QSpanCollection::Span &span = *it; int t = d->visualRow(span.top()); int l = d->visualColumn(span.left()); int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height())); @@ -1253,7 +1344,7 @@ QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) co bool verticalMoved = verticalHeader()->sectionsMoved(); bool horizontalMoved = horizontalHeader()->sectionsMoved(); - if ((verticalMoved && horizontalMoved) || d->hasSpans()) { + if ((verticalMoved && horizontalMoved) || (d->hasSpans() && (verticalMoved || horizontalMoved))) { for (int i = 0; i < selection.count(); ++i) { QItemSelectionRange range = selection.at(i); if (range.parent() != d->root || !range.isValid()) @@ -1296,9 +1387,19 @@ QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) co if (range.parent() != d->root || !range.isValid()) continue; d->trimHiddenSelections(&range); - QRect tl = visualRect(range.topLeft()); - QRect br = visualRect(range.bottomRight()); - selectionRegion += QRegion(tl|br); + + const int rtop = rowViewportPosition(range.top()); + const int rbottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom()); + const int rleft = columnViewportPosition(range.left()); + const int rright = columnViewportPosition(range.right()) + columnWidth(range.right()); + selectionRegion += QRect(QPoint(rleft, rtop), QPoint(rright, rbottom)); + if (d->hasSpans()) { + foreach (QSpanCollection::Span *s, + d->spans.spansInRect(range.left(), range.top(), range.width(), range.height())) { + if (range.contains(s->top(), s->left(), range.parent())) + selectionRegion += d->visualSpanRect(*s); + } + } } } @@ -1874,7 +1975,7 @@ QRect QTableView::visualRect(const QModelIndex &index) const d->executePostedLayout(); if (d->hasSpans()) { - QTableViewPrivate::Span span = d->span(index.row(), index.column()); + QSpanCollection::Span span = d->span(index.row(), index.column()); return d->visualSpanRect(span); } @@ -1903,7 +2004,7 @@ void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint) || isIndexHidden(index)) return; - QTableViewPrivate::Span span; + QSpanCollection::Span span; if (d->hasSpans()) span = d->span(index.row(), index.column()); @@ -2010,7 +2111,7 @@ void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint) } } - d->setDirtyRegion(visualRect(index)); + update(index); } /*! @@ -2058,7 +2159,7 @@ void QTableView::timerEvent(QTimerEvent *event) QRect rect; int viewportHeight = d->viewport->height(); int viewportWidth = d->viewport->width(); - if (d->hasSpans() && d->spansIntersectColumns(d->columnsToUpdate)) { + if (d->hasSpans()) { rect = QRect(0, 0, viewportWidth, viewportHeight); } else { for (int i = d->columnsToUpdate.size()-1; i >= 0; --i) { @@ -2083,7 +2184,7 @@ void QTableView::timerEvent(QTimerEvent *event) int viewportHeight = d->viewport->height(); int viewportWidth = d->viewport->width(); int top; - if (d->hasSpans() && d->spansIntersectRows(d->rowsToUpdate)) { + if (d->hasSpans()) { top = 0; } else { top = viewportHeight; @@ -2114,7 +2215,7 @@ void QTableView::rowMoved(int, int oldIndex, int newIndex) updateGeometries(); int logicalOldIndex = d->verticalHeader->logicalIndex(oldIndex); int logicalNewIndex = d->verticalHeader->logicalIndex(newIndex); - if (d->hasSpans() && (d->spansIntersectRow(logicalOldIndex) || d->spansIntersectRow(logicalNewIndex))) { + if (d->hasSpans()) { d->viewport->update(); } else { int oldTop = rowViewportPosition(logicalOldIndex); @@ -2142,7 +2243,7 @@ void QTableView::columnMoved(int, int oldIndex, int newIndex) updateGeometries(); int logicalOldIndex = d->horizontalHeader->logicalIndex(oldIndex); int logicalNewIndex = d->horizontalHeader->logicalIndex(newIndex); - if (d->hasSpans() && (d->spansIntersectColumn(logicalOldIndex) || d->spansIntersectColumn(logicalNewIndex))) { + if (d->hasSpans()) { d->viewport->update(); } else { int oldLeft = columnViewportPosition(logicalOldIndex); @@ -2325,7 +2426,7 @@ bool QTableView::isIndexHidden(const QModelIndex &index) const if (isRowHidden(index.row()) || isColumnHidden(index.column())) return true; if (d->hasSpans()) { - QTableViewPrivate::Span span = d->span(index.row(), index.column()); + QSpanCollection::Span span = d->span(index.row(), index.column()); return !((span.top() == index.row()) && (span.left() == index.column())); } return false; diff --git a/src/gui/itemviews/qtableview_p.h b/src/gui/itemviews/qtableview_p.h index b08eabd..3f3dd36 100644 --- a/src/gui/itemviews/qtableview_p.h +++ b/src/gui/itemviews/qtableview_p.h @@ -53,12 +53,69 @@ // We mean it. // +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QSet> +#include <QtCore/QDebug> #include "private/qabstractitemview_p.h" #ifndef QT_NO_TABLEVIEW QT_BEGIN_NAMESPACE +/** \internal +* +* This is a list of span with a binary index to look up quickly a span at a certain index. +* +* The index is a map of map. +* spans are mentaly divided into sub spans so that the start of any subspans doesn't overlap +* with any other subspans. There is no real representation of the subspans. +* The key of the first map is the row where the subspan starts, the value of the first map is +* a list (map) of all subspans that starts at the same row. It is indexed with its row +*/ +class QSpanCollection +{ +public: + struct Span + { + int m_top; + int m_left; + int m_bottom; + int m_right; + Span() + : m_top(-1), m_left(-1), m_bottom(-1), m_right(-1) { } + Span(int row, int column, int rowCount, int columnCount) + : m_top(row), m_left(column), m_bottom(row+rowCount-1), m_right(column+columnCount-1) { } + inline int top() const { return m_top; } + inline int left() const { return m_left; } + inline int bottom() const { return m_bottom; } + inline int right() const { return m_right; } + inline int height() const { return m_bottom - m_top + 1; } + inline int width() const { return m_right - m_left + 1; } + }; + + ~QSpanCollection() + { + qDeleteAll(spans); + } + + void addSpan(Span *span); + void updateSpan(Span *span, int old_height); + Span *spanAt(int x, int y) const; + void clear(); + QList<Span *> spansInRect(int x, int y, int w, int h) const; + + QList<Span *> spans; //lists of all spans +private: + //the indexes are negative so the QMap::lowerBound do what i need. + typedef QMap<int, Span *> SubIndex; + typedef QMap<int, SubIndex> Index; + Index index; +}; + +Q_DECLARE_TYPEINFO ( QSpanCollection::Span, Q_MOVABLE_TYPE); + + class QTableViewPrivate : public QAbstractItemViewPrivate { Q_DECLARE_PUBLIC(QTableView) @@ -98,11 +155,7 @@ public: int sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const; int sectionSpanSize(const QHeaderView *header, int logical, int span) const; bool spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const; - bool spansIntersectColumn(int column) const; - bool spansIntersectRow(int row) const; - bool spansIntersectColumns(const QList<int> &columns) const; - bool spansIntersectRows(const QList<int> &rows) const; - void drawAndClipSpans(const QRect &area, QPainter *painter, + void drawAndClipSpans(const QRegion &area, QPainter *painter, const QStyleOptionViewItemV4 &option, QBitArray *drawn, int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn); void drawCell(QPainter *painter, const QStyleOptionViewItemV4 &option, const QModelIndex &index); @@ -121,27 +174,10 @@ public: bool sortingEnabled; bool geometryRecursionBlock; - struct Span - { - int m_top; - int m_left; - int m_bottom; - int m_right; - Span() - : m_top(-1), m_left(-1), m_bottom(-1), m_right(-1) { } - Span(int row, int column, int rowCount, int columnCount) - : m_top(row), m_left(column), m_bottom(row+rowCount-1), m_right(column+columnCount-1) { } - inline int top() const { return m_top; } - inline int left() const { return m_left; } - inline int bottom() const { return m_bottom; } - inline int right() const { return m_right; } - inline int height() const { return m_bottom - m_top + 1; } - inline int width() const { return m_right - m_left + 1; } - }; - QList<Span> spans; + QSpanCollection spans; void setSpan(int row, int column, int rowSpan, int columnSpan); - Span span(int row, int column) const; + QSpanCollection::Span span(int row, int column) const; inline int rowSpan(int row, int column) const { return span(row, column).height(); } @@ -149,17 +185,7 @@ public: return span(row, column).width(); } inline bool hasSpans() const { - return !spans.isEmpty(); - } - inline bool spanContainsRow(int row, int spanRow, int span) const { - return spanContainsSection(verticalHeader, row, spanRow, span); - } - inline bool spanContainsColumn(int column, int spanColumn, int span) const { - return spanContainsSection(horizontalHeader, column, spanColumn, span); - } - inline bool isInSpan(int row, int column, const Span &span) const { - return spanContainsRow(row, span.top(), span.height()) - && spanContainsColumn(column, span.left(), span.width()); + return !spans.spans.isEmpty(); } inline int rowSpanHeight(int row, int span) const { return sectionSpanSize(verticalHeader, row, span); @@ -194,7 +220,7 @@ public: return isColumnHidden(c) || !isCellEnabled(r, c); } - QRect visualSpanRect(const Span &span) const; + QRect visualSpanRect(const QSpanCollection::Span &span) const; void _q_selectRow(int row); void _q_selectColumn(int column); diff --git a/src/gui/itemviews/qtreeview.cpp b/src/gui/itemviews/qtreeview.cpp index 1070648..c3fca8e 100644 --- a/src/gui/itemviews/qtreeview.cpp +++ b/src/gui/itemviews/qtreeview.cpp @@ -680,10 +680,9 @@ void QTreeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &botto // refresh the height cache here; we don't really lose anything by getting the size hint, // since QAbstractItemView::dataChanged() will get the visualRect for the items anyway - QModelIndex top = topLeft.sibling(topLeft.row(), 0); - int topViewIndex = d->viewIndex(top); + int topViewIndex = d->viewIndex(topLeft); if (topViewIndex == 0) - d->defaultItemHeight = indexRowSizeHint(top); + d->defaultItemHeight = indexRowSizeHint(topLeft); bool sizeChanged = false; if (topViewIndex != -1) { if (topLeft == bottomRight) { @@ -691,8 +690,7 @@ void QTreeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &botto d->invalidateHeightCache(topViewIndex); sizeChanged = (oldHeight != d->itemHeight(topViewIndex)); } else { - QModelIndex bottom = bottomRight.sibling(bottomRight.row(), 0); - int bottomViewIndex = d->viewIndex(bottom); + int bottomViewIndex = d->viewIndex(bottomRight); for (int i = topViewIndex; i <= bottomViewIndex; ++i) { int oldHeight = d->itemHeight(i); d->invalidateHeightCache(i); @@ -1142,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 @@ -1548,7 +1546,7 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, ? logicalIndexBeforeLeft : logicalIndices.at(currentLogicalSection - 1); if (columnCount == 1 || (nextLogicalSection == 0 && prevLogicalSection == -1) - || (headerSection == 0 && nextLogicalSection == -1)) + || (headerSection == 0 && nextLogicalSection == -1) || spanning) opt.viewItemPosition = QStyleOptionViewItemV4::OnlyOne; else if (headerSection == 0 || (nextLogicalSection != 0 && prevLogicalSection == -1)) opt.viewItemPosition = QStyleOptionViewItemV4::Beginning; @@ -1817,10 +1815,10 @@ void QTreeView::mouseDoubleClickEvent(QMouseEvent *event) if (i == -1) return; // user clicked outside the items - const QModelIndex &index = d->viewItems.at(i).index; + const QPersistentModelIndex firstColumnIndex = d->viewItems.at(i).index; int column = d->header->logicalIndexAt(event->x()); - QPersistentModelIndex persistent = index.sibling(index.row(), column); + QPersistentModelIndex persistent = firstColumnIndex.sibling(firstColumnIndex.row(), column); if (d->pressedIndex != persistent) { mousePressEvent(event); @@ -1843,10 +1841,10 @@ void QTreeView::mouseDoubleClickEvent(QMouseEvent *event) if (d->itemsExpandable && d->expandsOnDoubleClick && d->hasVisibleChildren(persistent)) { - if (!((i < d->viewItems.count()) && (d->viewItems.at(i).index == persistent))) { + if (!((i < d->viewItems.count()) && (d->viewItems.at(i).index == firstColumnIndex))) { // find the new index of the item for (i = 0; i < d->viewItems.count(); ++i) { - if (d->viewItems.at(i).index == persistent) + if (d->viewItems.at(i).index == firstColumnIndex) break; } if (i == d->viewItems.count()) @@ -2424,14 +2422,10 @@ void QTreeView::rowsInserted(const QModelIndex &parent, int start, int end) ? d->viewItems.count() : d->viewItems.at(parentItem).total) - 1; - int firstColumn = 0; - while (isColumnHidden(firstColumn) && firstColumn < header()->count() - 1) - ++firstColumn; - const int delta = end - start + 1; QVector<QTreeViewItem> insertedItems(delta); for (int i = 0; i < delta; ++i) { - insertedItems[i].index = d->model->index(i + start, firstColumn, parent); + insertedItems[i].index = d->model->index(i + start, 0, parent); insertedItems[i].level = childLevel; } if (d->viewItems.isEmpty()) @@ -2614,7 +2608,7 @@ void QTreeView::expandAll() d->viewItems[i].expanded = true; d->layout(i); QModelIndex idx = d->viewItems.at(i).index; - d->expandedIndexes.insert(idx.sibling(idx.row(), 0)); + d->expandedIndexes.insert(idx); } updateGeometries(); d->viewport->update(); @@ -3094,10 +3088,6 @@ void QTreeViewPrivate::layout(int i) Q_Q(QTreeView); QModelIndex current; QModelIndex parent = (i < 0) ? (QModelIndex)root : modelIndex(i); - // modelIndex() will return an index that don't have a parent if column 0 is hidden, - // so we must make sure that parent points to the actual parent that has children. - if (parent != root) - parent = model->index(parent.row(), 0, parent.parent()); if (i>=0 && !parent.isValid()) { //modelIndex() should never return something invalid for the real items. @@ -3132,13 +3122,9 @@ void QTreeViewPrivate::layout(int i) int last = 0; int children = 0; - int firstColumn = 0; - while (header->isSectionHidden(firstColumn) && firstColumn < header->count()) - ++firstColumn; - for (int j = first; j < first + count; ++j) { - current = model->index(j - first, firstColumn, parent); - if (isRowHidden(current.sibling(current.row(), 0))) { + current = model->index(j - first, 0, parent); + if (isRowHidden(current)) { ++hidden; last = j - hidden + children; } else { @@ -3321,15 +3307,11 @@ int QTreeViewPrivate::itemAtCoordinate(int coordinate) const int QTreeViewPrivate::viewIndex(const QModelIndex &_index) const { - Q_Q(const QTreeView); if (!_index.isValid() || viewItems.isEmpty()) return -1; const int totalCount = viewItems.count(); - int firstColumn = 0; - while (q->isColumnHidden(firstColumn) && firstColumn < header->count()) - ++firstColumn; - const QModelIndex index = _index.sibling(_index.row(), firstColumn); + const QModelIndex index = _index.sibling(_index.row(), 0); // A quick check near the last item to see if we are just incrementing diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index cb2b014..a1b982a 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -193,7 +193,7 @@ embedded { MENU_NIB.path = Resources MENU_NIB.version = Versions QMAKE_BUNDLE_DATA += MENU_NIB - RESOURCES += mac/maccursors.qrc + RESOURCES += mac/macresources.qrc LIBS += -framework AppKit } diff --git a/src/gui/kernel/qaction.cpp b/src/gui/kernel/qaction.cpp index abb17d7..b2afbd0 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; } @@ -1368,7 +1370,7 @@ QAction::MenuRole QAction::menuRole() const void QAction::setIconVisibleInMenu(bool visible) { Q_D(QAction); - if (visible != (bool)d->iconVisibleInMenu) { + if (d->iconVisibleInMenu == -1 || visible != bool(d->iconVisibleInMenu)) { int oldValue = d->iconVisibleInMenu; d->iconVisibleInMenu = visible; // Only send data changed if we really need to. diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 4cf0ad7..a9424db 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -89,7 +89,7 @@ #include "qapplication.h" -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #include "qdatetime.h" #include "qguifunctions_wince.h" extern bool qt_wince_is_smartphone(); //qguifunctions_wince.cpp @@ -101,15 +101,12 @@ extern bool qt_wince_is_pocket_pc(); //qguifunctions_wince.cpp static void initResources() { -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE Q_INIT_RESOURCE(qstyle_wince); #else Q_INIT_RESOURCE(qstyle); #endif -#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN) - Q_INIT_RESOURCE(qpaintengine_d3d); -#endif Q_INIT_RESOURCE(qmessagebox); #if !defined(QT_NO_PRINTDIALOG) Q_INIT_RESOURCE(qprintdialog); @@ -130,7 +127,7 @@ QInputContext *QApplicationPrivate::inputContext; bool QApplicationPrivate::quitOnLastWindowClosed = true; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE int QApplicationPrivate::autoMaximizeThreshold = -1; bool QApplicationPrivate::autoSipEnabled = false; #endif @@ -442,7 +439,7 @@ bool QApplicationPrivate::fade_tooltip = false; bool QApplicationPrivate::animate_toolbox = false; bool QApplicationPrivate::widgetCount = false; QString* QApplicationPrivate::styleOverride = 0; -#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE) +#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) bool QApplicationPrivate::inSizeMove = false; #endif #ifdef QT_KEYPAD_NAVIGATION @@ -620,13 +617,6 @@ void QApplicationPrivate::process_cmdline() and QPixmaps. Available options are \c{raster} and \c{opengl}. \endlist - The Windows version of Qt supports an additional command line option, if - Direct3D support has been compiled into Qt: - \list - \o -direct3d will make the Direct3D paint engine the default widget - paint engine in Qt. \bold {This functionality is experimental.} - \endlist - The X11 version of Qt supports some traditional X11 command line options: \list \o -display \e display, sets the X display (default is $DISPLAY). @@ -848,6 +838,9 @@ void QApplicationPrivate::initialize() // trigger registering of QVariant's GUI types extern int qRegisterGuiVariant(); qRegisterGuiVariant(); + // trigger registering of QStateMachine's GUI types + extern int qRegisterGuiStateMachine(); + qRegisterGuiStateMachine(); is_app_running = true; // no longer starting up @@ -866,7 +859,7 @@ void QApplicationPrivate::initialize() q->setAttribute(Qt::AA_NativeWindows); #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #ifdef QT_AUTO_MAXIMIZE_THRESHOLD autoMaximizeThreshold = QT_AUTO_MAXIMIZE_THRESHOLD; #else @@ -875,7 +868,7 @@ void QApplicationPrivate::initialize() else autoMaximizeThreshold = -1; #endif //QT_AUTO_MAXIMIZE_THRESHOLD -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE // Set up which span functions should be used in raster engine... qInitDrawhelperAsm(); @@ -1069,6 +1062,9 @@ QApplication::~QApplication() QApplicationPrivate::fade_tooltip = false; QApplicationPrivate::widgetCount = false; + // trigger unregistering of QStateMachine's GUI types + extern int qUnregisterGuiStateMachine(); + qUnregisterGuiStateMachine(); // trigger unregistering of QVariant's GUI types extern int qUnregisterGuiVariant(); qUnregisterGuiVariant(); @@ -1227,7 +1223,7 @@ bool QApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventLis the WA_InputMethodEnabled attribute set. */ -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE void QApplication::setAutoMaximizeThreshold(const int threshold) { QApplicationPrivate::autoMaximizeThreshold = threshold; @@ -1302,7 +1298,7 @@ QStyle *QApplication::style() delete QApplicationPrivate::styleOverride; QApplicationPrivate::styleOverride = 0; } else { -#if defined(Q_WS_WIN) && defined(Q_OS_WINCE) +#if defined(Q_WS_WIN) && defined(Q_WS_WINCE) if (qt_wince_is_smartphone() || qt_wince_is_pocket_pc()) style = QLatin1String("WindowsMobile"); else @@ -2089,8 +2085,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; @@ -3496,7 +3493,7 @@ void QApplication::changeOverrideCursor(const QCursor &cursor) It is necessary to call this function to start event handling. The main event loop receives events from the window system and dispatches these to the application widgets. - + Generally, no user interaction can take place before calling exec(). As a special case, modal widgets like QMessageBox can be used before calling exec(), because modal widgets call exec() to start a local event loop. @@ -4034,7 +4031,7 @@ bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) if (receiver->isWidgetType()) { QWidget *widget = static_cast<QWidget *>(receiver); -#if !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !defined(QT_NO_CURSOR)) +#if !defined(Q_WS_WINCE) || (defined(GWES_ICONCURS) && !defined(QT_NO_CURSOR)) // toggle HasMouse widget state on enter and leave if ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) && (!qApp->activePopupWidget() || qApp->activePopupWidget() == widget->window())) diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index 1d6941d..2baf6dc 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -102,7 +102,7 @@ class Q_GUI_EXPORT QApplication : public QCoreApplication #ifndef QT_NO_STYLE_STYLESHEET Q_PROPERTY(QString styleSheet READ styleSheet WRITE setStyleSheet) #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE Q_PROPERTY(int autoMaximizeThreshold READ autoMaximizeThreshold WRITE setAutoMaximizeThreshold) Q_PROPERTY(bool autoSipEnabled READ autoSipEnabled WRITE setAutoSipEnabled) #endif @@ -281,7 +281,7 @@ public Q_SLOTS: #ifndef QT_NO_STYLE_STYLESHEET void setStyleSheet(const QString& sheet); #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE void setAutoMaximizeThreshold(const int threshold); int autoMaximizeThreshold() const; void setAutoSipEnabled(const bool enabled); diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index 69302ec..d5fa9ea 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -1196,10 +1196,6 @@ void qt_init(QApplicationPrivate *priv, int) [qtMenuLoader release]; } #endif - if (QApplication::testAttribute(Qt::AA_MacPluginApplication)) { - extern void qt_mac_set_native_menubar(bool); - qt_mac_set_native_menubar(false); - } // Register for Carbon tablet proximity events on the event monitor target. // This means that we should receive proximity events even when we aren't the active application. if (!tablet_proximity_handler) { @@ -1642,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_p.h b/src/gui/kernel/qapplication_p.h index 7487f0a..7e97418 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -219,7 +219,7 @@ public: #endif static bool quitOnLastWindowClosed; static void emitLastWindowClosed(); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE static int autoMaximizeThreshold; static bool autoSipEnabled; #endif @@ -358,7 +358,7 @@ public: #ifdef Q_WS_MAC static bool native_modal_dialog_active; #endif -#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE) +#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) static bool inSizeMove; #endif diff --git a/src/gui/kernel/qapplication_qws.cpp b/src/gui/kernel/qapplication_qws.cpp index 018440f..1125610 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/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 239ee14..39f7335 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #include "qguifunctions_wince.h" #include "qmenubar.h" extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp @@ -48,7 +48,7 @@ extern bool qt_wince_is_smartphone(); //defined in qguifunctions_wince.c extern bool qt_wince_is_pocket_pc(); //defined in qguifunctions_wince.cpp extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.cpp #endif -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM #include <windowsm.h> #include <tpcshell.h> #endif @@ -106,7 +106,7 @@ extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.c #include <winuser.h> #if !defined(WINABLEAPI) -# if defined(Q_OS_WINCE) +# if defined(Q_WS_WINCE) # include <bldver.h> # endif # include <winable.h> @@ -155,7 +155,7 @@ static PtrFlashWindowEx pFlashWindowEx = 0; QT_BEGIN_NAMESPACE -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #ifndef SHRG_RETURNCMD struct SHRGINFO { DWORD cbSize; @@ -266,7 +266,7 @@ Q_CORE_EXPORT bool winPostMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPa #define MK_XBUTTON2 0x0040 #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #define GET_KEYSTATE_WPARAM(wParam) (LOWORD(wParam)) #endif @@ -376,11 +376,6 @@ QRgb qt_colorref2qrgb(COLORREF col) Internal variables and functions *****************************************************************************/ -extern Q_CORE_EXPORT char theAppName[]; -extern Q_CORE_EXPORT char appFileName[]; -extern Q_CORE_EXPORT HINSTANCE appInst; // handle to app instance -extern Q_CORE_EXPORT HINSTANCE appPrevInst; // handle to prev app instance -extern Q_CORE_EXPORT int appCmdShow; // main window show command static HWND curWin = 0; // current window static HDC displayDC = 0; // display device context @@ -497,7 +492,7 @@ static void qt_set_windows_color_resources() pal.setColor(QPalette::HighlightedText, QColor(qt_colorref2qrgb(GetSysColor(COLOR_HIGHLIGHTTEXT)))); -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) // ### hardcoded until I find out how to get it from the system settings. pal.setColor(QPalette::LinkVisited, pal.highlight().color().dark(150)); pal.setColor(QPalette::Link, pal.highlight().color().light(130)); @@ -573,7 +568,7 @@ static void qt_set_windows_color_resources() static void qt_set_windows_font_resources() { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE QFont menuFont; QFont messageFont; QFont statusFont; @@ -625,7 +620,7 @@ static void qt_set_windows_font_resources() smallerFont.setPointSize(systemFont.pointSize()-1); QApplication::setFont(smallerFont, "QTabBar"); } -#endif// Q_OS_WINCE +#endif// Q_WS_WINCE } static void qt_win_read_cleartype_settings() @@ -745,9 +740,6 @@ void qt_init(QApplicationPrivate *priv, int) appNoGrab = !appNoGrab; else #endif // QT_DEBUG - if (qstrcmp(argv[i], "-direct3d") == 0) - QApplication::setAttribute(Qt::AA_MSWindowsUseDirect3DByDefault); - else argv[j++] = argv[i]; } if(j < priv->argc) { @@ -755,21 +747,12 @@ void qt_init(QApplicationPrivate *priv, int) priv->argc = j; } - // Get the application name/instance if qWinMain() was not invoked -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE // No message boxes but important ones SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); #endif - if (appInst == 0) { - QT_WA({ - appInst = GetModuleHandle(0); - }, { - appInst = GetModuleHandleA(0); - }); - } - -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE // Initialize OLE/COM // S_OK means success and S_FALSE means that it has already // been initialized @@ -781,7 +764,7 @@ void qt_init(QApplicationPrivate *priv, int) #endif // Misc. initialization -#if defined(QT_DEBUG) && !defined(Q_OS_WINCE) +#if defined(QT_DEBUG) && !defined(Q_WS_WINCE) GdiSetBatchLimit(1); #endif @@ -793,9 +776,9 @@ void qt_init(QApplicationPrivate *priv, int) #ifndef QT_NO_CURSOR QCursorData::initialize(); #endif - qApp->setObjectName(QLatin1String(theAppName)); + qApp->setObjectName(priv->appName()); -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) // default font HFONT hfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); QFont f(QLatin1String("MS Sans Serif"),8); @@ -815,7 +798,7 @@ void qt_init(QApplicationPrivate *priv, int) && f.family() == QLatin1String("MS Shell Dlg")) f.setFamily(QLatin1String("MS Shell Dlg 2")); QApplicationPrivate::setSystemFont(f); -#else //Q_OS_WINCE +#else //Q_WS_WINCE LOGFONT lf; HGDIOBJ stockFont = GetStockObject(SYSTEM_FONT); int result = 0; @@ -823,7 +806,7 @@ void qt_init(QApplicationPrivate *priv, int) QFont font = qt_LOGFONTtoQFont(lf, true); if (result) QApplicationPrivate::setSystemFont(font); -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE // QFont::locale_init(); ### Uncomment when it does something on Windows @@ -880,7 +863,7 @@ void qt_cleanup() delete QApplicationPrivate::inputContext; QApplicationPrivate::inputContext = 0; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE // Deinitialize OLE/COM OleUninitialize(); #endif @@ -891,12 +874,6 @@ void qt_cleanup() Platform specific global and internal functions *****************************************************************************/ -Q_GUI_EXPORT int qWinAppCmdShow() // get main window show command -{ - return appCmdShow; -} - - Q_GUI_EXPORT HDC qt_win_display_dc() // get display DC { Q_ASSERT(qApp && qApp->thread() == QThread::currentThread()); @@ -928,7 +905,7 @@ const QString qt_reg_winclass(QWidget *w) // register window class if (flags & Qt::MSWindowsOwnDC) { cname = QLatin1String("QWidgetOwnDC"); style = CS_DBLCLKS; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE style |= CS_OWNDC; #endif icon = true; @@ -943,14 +920,14 @@ const QString qt_reg_winclass(QWidget *w) // register window class } else { cname = QLatin1String("QTool"); } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE style |= CS_SAVEBITS; #endif icon = false; } else if (type == Qt::Popup) { cname = QLatin1String("QPopup"); style = CS_DBLCLKS; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE style |= CS_SAVEBITS; #endif if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP @@ -963,7 +940,7 @@ const QString qt_reg_winclass(QWidget *w) // register window class icon = true; } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE // force CS_OWNDC when the GL graphics system is // used as the default renderer if (qt_win_owndc_required) @@ -992,11 +969,11 @@ const QString qt_reg_winclass(QWidget *w) // register window class if (classExists == -1) { QT_WA({ WNDCLASS wcinfo; - classExists = GetClassInfo((HINSTANCE)qWinAppInst(), (TCHAR*)cname.utf16(), &wcinfo); + classExists = GetClassInfo(qWinAppInst(), (TCHAR*)cname.utf16(), &wcinfo); classExists = classExists && wcinfo.lpfnWndProc != QtWndProc; }, { WNDCLASSA wcinfo; - classExists = GetClassInfoA((HINSTANCE)qWinAppInst(), cname.toLatin1(), &wcinfo); + classExists = GetClassInfoA(qWinAppInst(), cname.toLatin1(), &wcinfo); classExists = classExists && wcinfo.lpfnWndProc != QtWndProc; }); } @@ -1008,7 +985,7 @@ const QString qt_reg_winclass(QWidget *w) // register window class return cname; ATOM atom; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE HBRUSH bgBrush = (HBRUSH)GetSysColorBrush(COLOR_WINDOW); QT_WA({ WNDCLASS wc; @@ -1016,9 +993,9 @@ const QString qt_reg_winclass(QWidget *w) // register window class wc.lpfnWndProc = (WNDPROC)QtWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; - wc.hInstance = (HINSTANCE)qWinAppInst(); + wc.hInstance = qWinAppInst(); if (icon) { - wc.hIcon = LoadIcon(appInst, L"IDI_ICON1"); + wc.hIcon = LoadIcon(qWinAppInst(), L"IDI_ICON1"); if (!wc.hIcon) wc.hIcon = LoadIcon(0, IDI_APPLICATION); } else { @@ -1035,9 +1012,9 @@ const QString qt_reg_winclass(QWidget *w) // register window class wc.lpfnWndProc = (WNDPROC)QtWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; - wc.hInstance = (HINSTANCE)qWinAppInst(); + wc.hInstance = qWinAppInst(); if (icon) { - wc.hIcon = LoadIconA(appInst, (char*)"IDI_ICON1"); + wc.hIcon = LoadIconA(qWinAppInst(), (char*)"IDI_ICON1"); if (!wc.hIcon) wc.hIcon = LoadIconA(0, (char*)IDI_APPLICATION); } else { @@ -1056,9 +1033,9 @@ const QString qt_reg_winclass(QWidget *w) // register window class wc.lpfnWndProc = (WNDPROC)QtWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; - wc.hInstance = (HINSTANCE)qWinAppInst(); + wc.hInstance = qWinAppInst(); if (icon) { - wc.hIcon = LoadIcon(appInst, L"IDI_ICON1"); + wc.hIcon = LoadIcon(qWinAppInst(), L"IDI_ICON1"); // if (!wc.hIcon) // wc.hIcon = LoadIcon(0, IDI_APPLICATION); } else { @@ -1092,9 +1069,9 @@ static void unregWinClasses() QHash<QString, int>::ConstIterator it = hash->constBegin(); while (it != hash->constEnd()) { QT_WA({ - UnregisterClass((TCHAR*)it.key().utf16(), (HINSTANCE)qWinAppInst()); + UnregisterClass((TCHAR*)it.key().utf16(), qWinAppInst()); } , { - UnregisterClassA(it.key().toLatin1(), (HINSTANCE)qWinAppInst()); + UnregisterClassA(it.key().toLatin1(), qWinAppInst()); }); ++it; } @@ -1129,7 +1106,7 @@ void qWinRequestConfig(WId id, int req, int x, int y, int w, int h) configRequests->append(r); // store request in queue } -Q_GUI_EXPORT void qWinProcessConfigRequests() // perform requests in queue +static void qWinProcessConfigRequests() // perform requests in queue { if (!configRequests) return; @@ -1381,7 +1358,7 @@ void QApplication::winFocus(QWidget *widget, bool gotFocus) && (QApplicationPrivate::active_window->windowType() == Qt::Dialog)) { // raise the entire application, not just the dialog QWidget* mw = QApplicationPrivate::active_window; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE while(mw->parentWidget() && (mw->windowType() == Qt::Dialog)) mw = mw->parentWidget()->window(); if (mw->testAttribute(Qt::WA_WState_Created) && mw != QApplicationPrivate::active_window) @@ -1432,7 +1409,7 @@ static bool qt_is_translatable_mouse_event(UINT message) message >= WM_XBUTTONDOWN && message <= WM_XBUTTONDBLCLK) && message != WM_MOUSEWHEEL -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE || message >= WM_NCMOUSEMOVE && message <= WM_NCMBUTTONDBLCLK #endif ; @@ -1472,7 +1449,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam msg.pt.y = GET_Y_LPARAM(lParam); // If it's a non-client-area message the coords are screen coords, otherwise they are // client coords. -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE if (message < WM_NCMOUSEMOVE || message > WM_NCMBUTTONDBLCLK) #endif ClientToScreen(msg.hwnd, &msg.pt); @@ -1502,7 +1479,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam if (imeParentWnd && QApplication::activePopupWidget() && (message == WM_MBUTTONDOWN || message == WM_XBUTTONDOWN || message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE || message == WM_NCMBUTTONDOWN || message == WM_NCLBUTTONDOWN || message == WM_NCRBUTTONDOWN)) { #else @@ -1512,7 +1489,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam } switch (message) { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE case WM_QUERYENDSESSION: { if (sm_smActive) // bogus message from windows RETURN(true); @@ -1563,7 +1540,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam #endif case WM_SETTINGCHANGE: -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE // CE SIP hide/show if (wParam == SPI_SETSIPINFO) { QResizeEvent re(QSize(0, 0), QSize(0, 0)); // Calculated by QDesktopWidget @@ -1665,7 +1642,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam if (!qt_tabletChokeMouse) { result = widget->translateMouseEvent(msg); // mouse event -#if defined(Q_OS_WINCE) && !defined(QT_NO_CONTEXTMENU) +#if defined(Q_WS_WINCE) && !defined(QT_NO_CONTEXTMENU) if (message == WM_LBUTTONDOWN && widget != qApp->activePopupWidget()) { QWidget* alienWidget = widget; if ((alienWidget != qApp->activePopupWidget()) && (alienWidget->contextMenuPolicy() != Qt::PreventContextMenu)) { @@ -1911,7 +1888,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam } break; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE case WM_NCHITTEST: if (widget->isWindow()) { QPoint pos = widget->mapFromGlobal(QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); @@ -1930,7 +1907,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam #endif case WM_SYSCOMMAND: { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE bool window_state_change = false; Qt::WindowStates oldstate = Qt::WindowStates(widget->dataPtr()->window_state); // MSDN:In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter are @@ -1958,7 +1935,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam QHideEvent e; qt_sendSpontaneousEvent(widget, &e); widget->hideChildren(true); -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE const QString title = widget->windowIconText(); if (!title.isEmpty()) widget->setWindowTitle_helper(title); @@ -1981,7 +1958,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam widget->showChildren(true); QShowEvent e; qt_sendSpontaneousEvent(widget, &e); -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE const QString title = widget->windowTitle(); if (!title.isEmpty()) widget->setWindowTitle_helper(title); @@ -2031,7 +2008,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam result = widget->translatePaintEvent(msg); break; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE case WM_ENTERSIZEMOVE: autoCaptureWnd = hwnd; QApplicationPrivate::inSizeMove = true; @@ -2088,7 +2065,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam // where it got it from; it would simply get a 0 value as the old focus widget. if (!(widget->windowState() & Qt::WindowMinimized)) { // Ignore the activate message send by WindowsXP to a minimized window -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM if (widget->windowState() & Qt::WindowFullScreen) qt_wince_hide_taskbar(widget->winId()); #endif @@ -2115,7 +2092,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam } break; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE case WM_MOUSEACTIVATE: if (widget->window()->windowType() == Qt::Tool) { QWidget *w = widget; @@ -2204,7 +2181,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam result = false; break; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE case WM_WINDOWPOSCHANGING: { result = false; @@ -2320,7 +2297,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam break; } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE case WM_CHANGECBCHAIN: case WM_DRAWCLIPBOARD: #endif @@ -2427,7 +2404,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam #endif // QT_NO_TABLETEVENT } break; -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM case WM_SETFOCUS: { HIMC hC; hC = ImmGetContext(hwnd); @@ -2473,7 +2450,7 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam widget->update(); break; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE case WM_INPUTLANGCHANGE: { char info[7]; if (!GetLocaleInfoA(MAKELCID(lParam, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, info, 6)) { @@ -2671,19 +2648,19 @@ bool qt_try_modal(QWidget *widget, MSG *msg, int& ret) int type = msg->message; bool block_event = false; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE if (type != WM_NCHITTEST) #endif if ((type >= WM_MOUSEFIRST && type <= WM_MOUSELAST) || type == WM_MOUSEWHEEL || type == (int)WM95_MOUSEWHEEL || type == WM_MOUSELEAVE || (type >= WM_KEYFIRST && type <= WM_KEYLAST) -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE || type == WM_NCMOUSEMOVE #endif ) { if (type == WM_MOUSEMOVE -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE || type == WM_NCMOUSEMOVE #endif ) { @@ -2701,7 +2678,7 @@ bool qt_try_modal(QWidget *widget, MSG *msg, int& ret) } else if (type == WM_CLOSE) { block_event = true; } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE else if (type == WM_MOUSEACTIVATE || type == WM_NCLBUTTONDOWN){ if (!top->isActiveWindow()) { top->activateWindow(); @@ -2859,7 +2836,7 @@ static const ushort mouseTbl[] = { WM_XBUTTONUP, QEvent::MouseButtonRelease, Qt::XButton1, WM_XBUTTONDBLCLK, QEvent::MouseButtonDblClick, Qt::XButton1, -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE WM_NCMOUSEMOVE, QEvent::NonClientAreaMouseMove, 0, WM_NCLBUTTONDOWN, QEvent::NonClientAreaMouseButtonPress, Qt::LeftButton, WM_NCLBUTTONUP, QEvent::NonClientAreaMouseButtonRelease, Qt::LeftButton, @@ -3176,10 +3153,7 @@ bool QETWidget::translateMouseEvent(const MSG &msg) if (popupButtonFocus) { target = popupButtonFocus; } else if (popupChild) { - // forward mouse events to the popup child. mouse move events - // are only forwarded to popup children that enable mouse tracking. - if (type != QEvent::MouseMove || popupChild->hasMouseTracking()) - target = popupChild; + target = popupChild; } pos = target->mapFromGlobal(globalPos); @@ -3661,7 +3635,7 @@ bool QETWidget::translateConfigEvent(const MSG &msg) WORD b = HIWORD(msg.lParam); QSize oldSize = size(); QSize newSize(a, b); -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM if (isFullScreen() && (oldSize.width() == newSize.height()) && (oldSize.height() == newSize.width())) qt_wince_hide_taskbar(internalWinId()); #endif @@ -3673,7 +3647,7 @@ bool QETWidget::translateConfigEvent(const MSG &msg) // Capture SIZE_MINIMIZED without preceding WM_SYSCOMMAND // (like Windows+M) if (msg.wParam == SIZE_MINIMIZED && !isMinimized()) { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE const QString title = windowIconText(); if (!title.isEmpty()) d_func()->setWindowTitle_helper(title); @@ -3685,7 +3659,7 @@ bool QETWidget::translateConfigEvent(const MSG &msg) hideChildren(true); } } else if (msg.wParam != SIZE_MINIMIZED && isMinimized()) { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE const QString title = windowTitle(); if (!title.isEmpty()) d_func()->setWindowTitle_helper(title); @@ -3731,7 +3705,7 @@ bool QETWidget::translateConfigEvent(const MSG &msg) QPoint oldPos = geometry().topLeft(); QPoint newCPos(a, b); // Ignore silly Windows move event to wild pos after iconify. -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) if (!IsIconic(internalWinId()) && newCPos != oldPos) { #endif cr.moveTopLeft(newCPos); @@ -3743,7 +3717,7 @@ bool QETWidget::translateConfigEvent(const MSG &msg) QMoveEvent * e = new QMoveEvent(newCPos, oldPos); QApplication::postEvent(this, e); } -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) } #endif } @@ -3785,7 +3759,7 @@ int QApplication::cursorFlashTime() void QApplication::setDoubleClickInterval(int ms) { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE SetDoubleClickTime(ms); #endif QApplicationPrivate::mouse_double_click_time = ms; diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index a3c9406..90376b3 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -294,6 +294,10 @@ static const char * x11_atomnames = { // XEMBED "_XEMBED\0" "_XEMBED_INFO\0" + + "Wacom Stylus\0" + "Wacom Cursor\0" + "Wacom Eraser\0" }; Q_GUI_EXPORT QX11Data *qt_x11Data = 0; @@ -1579,6 +1583,7 @@ static PtrWacomConfigOpenDevice ptrWacomConfigOpenDevice = 0; static PtrWacomConfigGetRawParam ptrWacomConfigGetRawParam = 0; static PtrWacomConfigCloseDevice ptrWacomConfigCloseDevice = 0; static PtrWacomConfigTerm ptrWacomConfigTerm = 0; +Q_GLOBAL_STATIC(QByteArray, wacomDeviceName) #endif #endif @@ -2356,13 +2361,6 @@ void qt_init(QApplicationPrivate *priv, int, XAxisInfoPtr a; XDevice *dev = 0; -#if !defined(Q_OS_IRIX) - // XFree86 divides a stylus and eraser into 2 devices, so we must do for both... - const QString XFREENAMESTYLUS = QLatin1String("stylus"); - const QString XFREENAMEPEN = QLatin1String("pen"); - const QString XFREENAMEERASER = QLatin1String("eraser"); -#endif - if (X11->ptrXListInputDevices) { devices = X11->ptrXListInputDevices(X11->display, &ndev); if (!devices) @@ -2377,18 +2375,19 @@ void qt_init(QApplicationPrivate *priv, int, gotStylus = false; gotEraser = false; - QString devName = QString::fromLocal8Bit(devs->name).toLower(); #if defined(Q_OS_IRIX) + QString devName = QString::fromLocal8Bit(devs->name).toLower(); if (devName == QLatin1String(WACOM_NAME)) { deviceType = QTabletEvent::Stylus; gotStylus = true; } #else - if (devName.startsWith(XFREENAMEPEN) - || devName.startsWith(XFREENAMESTYLUS)) { + if (devs->type == ATOM(XWacomStylus)) { deviceType = QTabletEvent::Stylus; + if (wacomDeviceName()->isEmpty()) + wacomDeviceName()->append(devs->name); gotStylus = true; - } else if (devName.startsWith(XFREENAMEERASER)) { + } else if (devs->type == ATOM(XWacomEraser)) { deviceType = QTabletEvent::XFreeEraser; gotEraser = true; } @@ -2979,7 +2978,7 @@ QWidget *QApplication::topLevelAt(const QPoint &p) void QApplication::syncX() { if (X11->display) - XSync(X11->display, False); // don't discard events + XSync(X11->display, False); // don't discard events } @@ -4484,8 +4483,7 @@ void fetchWacomToolId(int &deviceType, qint64 &serialId) WACOMCONFIG *config = ptrWacomConfigInit(X11->display, 0); if (config == 0) return; - const char *name = "stylus"; // TODO get this from the X config instead (users may have called it differently) - WACOMDEVICE *device = ptrWacomConfigOpenDevice (config, name); + WACOMDEVICE *device = ptrWacomConfigOpenDevice (config, wacomDeviceName()->constData()); if (device == 0) return; unsigned keys[1]; diff --git a/src/gui/kernel/qclipboard.cpp b/src/gui/kernel/qclipboard.cpp index bab3449..96e9580 100644 --- a/src/gui/kernel/qclipboard.cpp +++ b/src/gui/kernel/qclipboard.cpp @@ -50,6 +50,7 @@ #include "qvariant.h" #include "qbuffer.h" #include "qimage.h" +#include "qtextcodec.h" QT_BEGIN_NAMESPACE @@ -276,11 +277,12 @@ QClipboard::~QClipboard() */ QString QClipboard::text(QString &subtype, Mode mode) const { - const QMimeData *data = mimeData(mode); + const QMimeData *const data = mimeData(mode); if (!data) return QString(); + + const QStringList formats = data->formats(); if (subtype.isEmpty()) { - QStringList formats = data->formats(); if (formats.contains(QLatin1String("text/plain"))) subtype = QLatin1String("plain"); else { @@ -289,13 +291,21 @@ QString QClipboard::text(QString &subtype, Mode mode) const subtype = formats.at(i).mid(5); break; } + if (subtype.isEmpty()) + return QString(); } - } - if (subtype.isEmpty()) + } else if (!formats.contains(QLatin1String("text/") + subtype)) { return QString(); - if (subtype == QLatin1String("plain")) - return data->text(); - return QString::fromUtf8(data->data(QLatin1String("text/") + subtype)); + } + + const QByteArray rawData = data->data(QLatin1String("text/") + subtype); + + QTextCodec* codec = QTextCodec::codecForMib(106); // utf-8 is default + if (subtype == QLatin1String("html")) + codec = QTextCodec::codecForHtml(rawData, codec); + else + codec = QTextCodec::codecForUtfText(rawData, codec); + return codec->toUnicode(rawData); } /*! diff --git a/src/gui/kernel/qclipboard_x11.cpp b/src/gui/kernel/qclipboard_x11.cpp index 089cc43..d7eb111 100644 --- a/src/gui/kernel/qclipboard_x11.cpp +++ b/src/gui/kernel/qclipboard_x11.cpp @@ -786,7 +786,7 @@ static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom p QByteArray data; QByteArray fmt = X11->xdndAtomToString(target); - if (fmt.isEmpty() || !QInternalMimeData::hasFormatHelper(QString::fromAscii(fmt), d->source())) { // Not a MIME type we have + if (fmt.isEmpty()) { // Not a MIME type we have DEBUG("QClipboard: send_selection(): converting to type '%s' is not supported", fmt.data()); return XNone; } diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index f1a7f39..838f751 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -196,6 +196,7 @@ extern "C" { if (self) { [self finishInitWithQWidget:widget widgetPrivate:widgetprivate]; } + composingText = new QString(); composing = false; sendKeyEvents = true; currentCustomTypes = 0; @@ -419,6 +420,7 @@ extern "C" { - (void)dealloc { + delete composingText; [[NSNotificationCenter defaultCenter] removeObserver:self]; delete currentCustomTypes; [self unregisterDraggedTypes]; @@ -1009,7 +1011,7 @@ extern "C" { - (void) insertText:(id)aString { - if (composing) { + if ([aString length]) { // Send the commit string to the widget. QString commitText; if ([aString isKindOfClass:[NSAttributedString class]]) { @@ -1023,6 +1025,7 @@ extern "C" { e.setCommitString(commitText); qt_sendSpontaneousEvent(qwidget, &e); } + composingText->clear(); } - (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange @@ -1076,12 +1079,21 @@ extern "C" { attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, composingLength, format); } + *composingText = qtText; QInputMethodEvent e(qtText, attrs); qt_sendSpontaneousEvent(qwidget, &e); + if (!composingLength) + composing = false; } - (void) unmarkText { + if (composing) { + QInputMethodEvent e; + e.setCommitString(*composingText); + qt_sendSpontaneousEvent(qwidget, &e); + } + composingText->clear(); composing = false; } diff --git a/src/gui/kernel/qcocoaview_mac_p.h b/src/gui/kernel/qcocoaview_mac_p.h index 527b1a6..be4501f3 100644 --- a/src/gui/kernel/qcocoaview_mac_p.h +++ b/src/gui/kernel/qcocoaview_mac_p.h @@ -84,6 +84,7 @@ Q_GUI_EXPORT bool composing; int composingLength; bool sendKeyEvents; + QString *composingText; QStringList *currentCustomTypes; } - (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; diff --git a/src/gui/kernel/qcursor_win.cpp b/src/gui/kernel/qcursor_win.cpp index 85d5a11..a475882 100644 --- a/src/gui/kernel/qcursor_win.cpp +++ b/src/gui/kernel/qcursor_win.cpp @@ -66,7 +66,7 @@ QCursorData::~QCursorData() { delete bm; delete bmm; -#if !defined(Q_OS_WINCE) || defined(GWES_ICONCURS) +#if !defined(Q_WS_WINCE) || defined(GWES_ICONCURS) if (hcurs) DestroyCursor(hcurs); #endif @@ -129,7 +129,7 @@ extern HBITMAP qt_createIconMask(const QBitmap &bitmap); static HCURSOR create32BitCursor(const QPixmap &pixmap, int hx, int hy) { HCURSOR cur = 0; -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) QBitmap mask = pixmap.mask(); if (mask.isNull()) { mask = QBitmap(pixmap.size()); @@ -407,7 +407,7 @@ void QCursorData::update() } int n = qMax(1, bbits.width() / 8); int h = bbits.height(); -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) uchar* xBits = new uchar[h * n]; uchar* xMask = new uchar[h * n]; int x = 0; @@ -430,7 +430,7 @@ void QCursorData::update() xBits, xMask); delete [] xBits; delete [] xMask; -#elif defined(GWES_ICONCURS) // Q_OS_WINCE +#elif defined(GWES_ICONCURS) // Q_WS_WINCE // Windows CE only supports fixed cursor size. int sysW = GetSystemMetrics(SM_CXCURSOR); int sysH = GetSystemMetrics(SM_CYCURSOR); diff --git a/src/gui/kernel/qdesktopwidget_win.cpp b/src/gui/kernel/qdesktopwidget_win.cpp index 725e985..b9e2e1e 100644 --- a/src/gui/kernel/qdesktopwidget_win.cpp +++ b/src/gui/kernel/qdesktopwidget_win.cpp @@ -45,7 +45,7 @@ #include "qlibrary.h" #include <qvector.h> #include <limits.h> -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #include <sipapi.h> #endif #include "qwidget_p.h" @@ -92,7 +92,7 @@ QVector<QRect> *QDesktopWidgetPrivate::rects = 0; QVector<QRect> *QDesktopWidgetPrivate::workrects = 0; static int screen_number = 0; int QDesktopWidgetPrivate::refcount = 0; -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM // Use SIP information, if available // SipGetInfo is not supported by SSDK (no definition!). static inline void qt_get_sip_info(QRect &rect) @@ -208,7 +208,7 @@ void QDesktopWidgetPrivate::init(QDesktopWidget *that) SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); QRect qr = QRect(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1)); -#if defined(Q_OS_WINCE_WM) +#if defined(Q_WS_WINCE_WM) qt_get_sip_info(qr); #endif @@ -222,7 +222,7 @@ void QDesktopWidgetPrivate::init(QDesktopWidget *that) enumDisplayMonitors(0, 0, enumCallback, 0); enumDisplayMonitors = 0; getMonitorInfo = 0; -#endif // Q_OS_WINCE +#endif // Q_WS_WINCE } QDesktopWidgetPrivate::~QDesktopWidgetPrivate() @@ -303,7 +303,7 @@ QWidget *QDesktopWidget::screen(int /* screen */) const QRect QDesktopWidget::availableGeometry(int screen) const { Q_D(const QDesktopWidget); -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM for(int i=0; i < d->workrects->size(); ++i) qt_get_sip_info((*d->workrects)[i]); #endif @@ -385,7 +385,7 @@ void QDesktopWidget::resizeEvent(QResizeEvent *) QDesktopWidgetPrivate::cleanup(); QDesktopWidgetPrivate::init(this); -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM for(int i=0; i < d->workrects->size(); ++i) qt_get_sip_info((*d->workrects)[i]); #endif diff --git a/src/gui/kernel/qdnd_p.h b/src/gui/kernel/qdnd_p.h index 7a0cb7a..9871658 100644 --- a/src/gui/kernel/qdnd_p.h +++ b/src/gui/kernel/qdnd_p.h @@ -202,7 +202,7 @@ public: #endif }; -class Q_GUI_EXPORT QDragManager: public QObject { +class QDragManager: public QObject { Q_OBJECT QDragManager(); diff --git a/src/gui/kernel/qdnd_x11.cpp b/src/gui/kernel/qdnd_x11.cpp index 4c9c73c..9b2305d 100644 --- a/src/gui/kernel/qdnd_x11.cpp +++ b/src/gui/kernel/qdnd_x11.cpp @@ -542,6 +542,8 @@ bool QX11Data::xdndMimeDataForAtom(Atom a, QMimeData *mimeData, QByteArray *data dm->xdndMimeTransferedPixmapIndex = (dm->xdndMimeTransferedPixmapIndex + 1) % 2; } + } else { + DEBUG("QClipboard: xdndMimeDataForAtom(): converting to type '%s' is not supported", qPrintable(atomName)); } } return data; @@ -622,28 +624,12 @@ QVariant QX11Data::xdndMimeConvertToFormat(Atom a, const QByteArray &data, const if (format == QLatin1String("image/ppm")) { if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) { Pixmap xpm = *((Pixmap*)data.data()); - Display *dpy = display; - Window r; - int x,y; - uint w,h,bw,d; if (!xpm) return QByteArray(); - XGetGeometry(dpy,xpm, &r,&x,&y,&w,&h,&bw,&d); + QPixmap qpm = QPixmap::fromX11Pixmap(xpm); QImageWriter imageWriter; - GC gc = XCreateGC(dpy, xpm, 0, 0); - QImage imageToWrite; - if (d == 1) { - QBitmap qbm(w,h); - XCopyArea(dpy,xpm,qbm.handle(),gc,0,0,w,h,0,0); - imageWriter.setFormat("PBMRAW"); - imageToWrite = qbm.toImage(); - } else { - QPixmap qpm(w,h); - XCopyArea(dpy,xpm,qpm.handle(),gc,0,0,w,h,0,0); - imageWriter.setFormat("PPMRAW"); - imageToWrite = qpm.toImage(); - } - XFreeGC(dpy,gc); + imageWriter.setFormat("PPMRAW"); + QImage imageToWrite = qpm.toImage(); QBuffer buf; buf.open(QIODevice::WriteOnly); imageWriter.setDevice(&buf); diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 2aed287..a59e870 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -861,6 +861,17 @@ bool QKeyEvent::matches(QKeySequence::StandardKey matchKey) const uint searchkey = (modifiers() | key()) & ~(Qt::KeypadModifier); //The keypad modifier should not make a difference uint platform = QApplicationPrivate::currentPlatform(); +#ifdef Q_WS_MAC + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + uint oldSearchKey = searchkey; + searchkey &= ~(Qt::ControlModifier | Qt::MetaModifier); + if (oldSearchKey & Qt::ControlModifier) + searchkey |= Qt::MetaModifier; + if (oldSearchKey & Qt::MetaModifier) + searchkey |= Qt::ControlModifier; + } +#endif + uint N = QKeySequencePrivate::numberOfKeyBindings; int first = 0; int last = N - 1; @@ -3070,7 +3081,7 @@ QDebug operator<<(QDebug dbg, const QEvent *e) { << ", " << me->button() << ", " << hex << (int)me->buttons() << ", " << hex << (int)me->modifiers() - << ")"; + << ')'; } return dbg.space(); @@ -3091,7 +3102,7 @@ QDebug operator<<(QDebug dbg, const QEvent *e) { #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: dbg.nospace() << "QWheelEvent(" << static_cast<const QWheelEvent *>(e)->delta() - << ")"; + << ')'; return dbg.space(); #endif case QEvent::KeyPress: @@ -3117,7 +3128,7 @@ QDebug operator<<(QDebug dbg, const QEvent *e) { << ", \"" << ke->text() << "\", " << ke->isAutoRepeat() << ", " << ke->count() - << ")"; + << ')'; } return dbg.space(); case QEvent::FocusIn: diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index cc94aad..8e762d6 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE // // ### Qt 5: remove -class Q_GUI_EXPORT QKeyEventEx : public QKeyEvent +class QKeyEventEx : public QKeyEvent { public: QKeyEventEx(Type type, int key, Qt::KeyboardModifiers modifiers, @@ -76,7 +76,7 @@ protected: }; // ### Qt 5: remove -class Q_GUI_EXPORT QMouseEventEx : public QMouseEvent +class QMouseEventEx : public QMouseEvent { public: QMouseEventEx(Type type, const QPointF &pos, const QPoint &globalPos, diff --git a/src/gui/kernel/qformlayout.cpp b/src/gui/kernel/qformlayout.cpp index e2d6108..a665c89 100644 --- a/src/gui/kernel/qformlayout.cpp +++ b/src/gui/kernel/qformlayout.cpp @@ -689,12 +689,16 @@ void QFormLayoutPrivate::setupVerticalLayoutData(int width) // are split. maxLabelWidth = 0; if (!wrapAllRows) { + int maxFieldMinWidth = 0; //the maximum minimum size of the field for (int i = 0; i < rr; ++i) { const QFormLayoutItem *label = m_matrix(i, 0); const QFormLayoutItem *field = m_matrix(i, 1); - if (label && (label->sizeHint.width() + (field ? field->minSize.width() : 0) <= width)) + if (label && field && label->sideBySide) maxLabelWidth = qMax(maxLabelWidth, label->sizeHint.width()); + if (field) + maxFieldMinWidth = qMax(maxFieldMinWidth, field->minSize.width() + field->sbsHSpace); } + maxLabelWidth = qMin(maxLabelWidth, width - maxFieldMinWidth); } else { maxLabelWidth = width; } diff --git a/src/gui/kernel/qkeymapper.cpp b/src/gui/kernel/qkeymapper.cpp index 535d009..503a33b 100644 --- a/src/gui/kernel/qkeymapper.cpp +++ b/src/gui/kernel/qkeymapper.cpp @@ -116,6 +116,4 @@ QKeyMapperPrivate *qt_keymapper_private() return QKeyMapper::instance()->d_func(); } -Q_GUI_EXPORT QList<int> qt_keymapper_possibleKeys(QKeyEvent *e) { return QKeyMapper::instance()->possibleKeys(e); } - QT_END_NAMESPACE diff --git a/src/gui/kernel/qkeymapper_mac.cpp b/src/gui/kernel/qkeymapper_mac.cpp index 1a0fb08..01b2c13 100644 --- a/src/gui/kernel/qkeymapper_mac.cpp +++ b/src/gui/kernel/qkeymapper_mac.cpp @@ -48,6 +48,7 @@ #include <qinputcontext.h> #include <private/qkeymapper_p.h> #include <private/qapplication_p.h> +#include <private/qmacinputcontext_p.h> QT_BEGIN_NAMESPACE @@ -160,6 +161,14 @@ Qt::KeyboardModifiers qt_mac_get_modifiers(int keys) ret |= Qt::KeyboardModifier(qt_mac_modifier_symbols[i].qt_code); } } + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + Qt::KeyboardModifiers oldModifiers = ret; + ret &= ~(Qt::MetaModifier | Qt::ControlModifier); + if (oldModifiers & Qt::ControlModifier) + ret |= Qt::MetaModifier; + if (oldModifiers & Qt::MetaModifier) + ret |= Qt::ControlModifier; + } return ret; } static int qt_mac_get_mac_modifiers(Qt::KeyboardModifiers keys) @@ -176,6 +185,15 @@ static int qt_mac_get_mac_modifiers(Qt::KeyboardModifiers keys) ret |= qt_mac_modifier_symbols[i].mac_code; } } + + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + int oldModifiers = ret; + ret &= ~(controlKeyBit | cmdKeyBit); + if (oldModifiers & controlKeyBit) + ret |= cmdKeyBit; + if (oldModifiers & cmdKeyBit) + ret |= controlKeyBit; + } return ret; } void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object) @@ -480,7 +498,8 @@ static bool translateKeyEventInternal(EventHandlerCallRef er, EventRef keyEvent, #ifdef QT_MAC_USE_COCOA if (outHandled) { qt_mac_eat_unicode_key = false; - CallNextEventHandler(er, keyEvent); + if (er) + CallNextEventHandler(er, keyEvent); *outHandled = qt_mac_eat_unicode_key; } #endif @@ -692,8 +711,14 @@ bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, EventHandlerCallRef e return true; } - if (qApp->inputContext() && qApp->inputContext()->isComposing()) + if (qApp->inputContext() && qApp->inputContext()->isComposing()) { + if (ekind == kEventRawKeyDown) { + QMacInputContext *context = qobject_cast<QMacInputContext*>(qApp->inputContext()); + if (context) + context->setLastKeydownEvent(event); + } return false; + } //get modifiers Qt::KeyboardModifiers modifiers; int qtKey; @@ -721,7 +746,8 @@ bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, EventHandlerCallRef e //is it of use to text services? If so we won't bother //with a QKeyEvent. qt_mac_eat_unicode_key = false; - CallNextEventHandler(er, event); + if (er) + CallNextEventHandler(er, event); extern bool qt_mac_menubar_is_open(); if (qt_mac_eat_unicode_key || qt_mac_menubar_is_open()) { return true; diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp index 3bcf9e3..ee45969 100644 --- a/src/gui/kernel/qkeysequence.cpp +++ b/src/gui/kernel/qkeysequence.cpp @@ -105,20 +105,39 @@ static bool operator<(int key, const MacSpecialKey &entry) static const MacSpecialKey * const MacSpecialKeyEntriesEnd = entries + NumEntries; -static QChar macSymbolForQtKey(int key) +QChar qt_macSymbolForQtKey(int key) { const MacSpecialKey *i = qBinaryFind(entries, MacSpecialKeyEntriesEnd, key); if (i == MacSpecialKeyEntriesEnd) return QChar(); - return QChar(i->macSymbol); + ushort macSymbol = i->macSymbol; + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) + && (macSymbol == kControlUnicode || macSymbol == kCommandUnicode)) { + if (macSymbol == kControlUnicode) + macSymbol = kCommandUnicode; + else + macSymbol = kControlUnicode; + } + + return QChar(macSymbol); } static int qtkeyForMacSymbol(const QChar ch) { + const ushort unicode = ch.unicode(); for (int i = 0; i < NumEntries; ++i) { const MacSpecialKey &entry = entries[i]; - if (entry.macSymbol == ch.unicode()) - return entry.key; + if (entry.macSymbol == unicode) { + int key = entry.key; + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) + && (unicode == kControlUnicode || unicode == kCommandUnicode)) { + if (unicode == kControlUnicode) + key = Qt::Key_Control; + else + key = Qt::Key_Meta; + } + return key; + } } return -1; } @@ -126,7 +145,7 @@ static int qtkeyForMacSymbol(const QChar ch) #else static bool qt_sequence_no_mnemonics = false; #endif -void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemonics = !b; } +void Q_AUTOTEST_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemonics = !b; } /*! \class QKeySequence @@ -213,12 +232,14 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni \row \i Open \i Ctrl+O \i Ctrl+O \i Ctrl+O \i Ctrl+O \row \i Close \i Ctrl+F4, Ctrl+W \i Ctrl+W, Ctrl+F4 \i Ctrl+W \i Ctrl+W \row \i Save \i Ctrl+S \i Ctrl+S \i Ctrl+S \i Ctrl+S + \row \i Quit \i \i Ctrl+Q \i Qtrl+Q \i Qtrl+Q \row \i SaveAs \i \i Ctrl+Shift+S \i \i Ctrl+Shift+S \row \i New \i Ctrl+N \i Ctrl+N \i Ctrl+N \i Ctrl+N \row \i Delete \i Del \i Del, Meta+D \i Del, Ctrl+D \i Del, Ctrl+D \row \i Cut \i Ctrl+X, Shift+Del \i Ctrl+X \i Ctrl+X, F20, Shift+Del \i Ctrl+X, F20, Shift+Del \row \i Copy \i Ctrl+C, Ctrl+Ins \i Ctrl+C \i Ctrl+C, F16, Ctrl+Ins \i Ctrl+C, F16, Ctrl+Ins \row \i Paste \i Ctrl+V, Shift+Ins \i Ctrl+V \i Ctrl+V, F18, Shift+Ins \i Ctrl+V, F18, Shift+Ins + \row \i Preferences \i \i Ctrl+, \i \i \row \i Undo \i Ctrl+Z, Alt+Backspace \i Ctrl+Z \i Ctrl+Z, F14 \i Ctrl+Z, F14 \row \i Redo \i Ctrl+Y, Shift+Ctrl+Z, Alt+Shift+Backspace \i Ctrl+Shift+Z, Ctrl+Y \i Ctrl+Shift+Z \i Ctrl+Shift+Z \row \i Back \i Alt+Left, Backspace \i Ctrl+[ \i Alt+Left \i Alt+Left @@ -521,6 +542,7 @@ const QKeyBinding QKeySequencePrivate::keyBindings[] = { {QKeySequence::FindPrevious, 1, Qt::SHIFT | Qt::Key_F3, QApplicationPrivate::KB_Win}, {QKeySequence::ZoomIn, 1, Qt::CTRL | Qt::Key_Plus, QApplicationPrivate::KB_All}, {QKeySequence::NextChild, 0, Qt::CTRL | Qt::Key_Comma, QApplicationPrivate::KB_KDE}, + {QKeySequence::Preferences, 0, Qt::CTRL | Qt::Key_Comma, QApplicationPrivate::KB_Mac}, {QKeySequence::ZoomOut, 1, Qt::CTRL | Qt::Key_Minus, QApplicationPrivate::KB_All}, {QKeySequence::PreviousChild, 0, Qt::CTRL | Qt::Key_Period, QApplicationPrivate::KB_KDE}, {QKeySequence::HelpContents, 1, Qt::CTRL | Qt::Key_Question, QApplicationPrivate::KB_Mac}, @@ -538,6 +560,7 @@ const QKeyBinding QKeySequencePrivate::keyBindings[] = { {QKeySequence::New, 1, Qt::CTRL | Qt::Key_N, QApplicationPrivate::KB_All}, {QKeySequence::Open, 1, Qt::CTRL | Qt::Key_O, QApplicationPrivate::KB_All}, {QKeySequence::Print, 1, Qt::CTRL | Qt::Key_P, QApplicationPrivate::KB_All}, + {QKeySequence::Quit, 0, Qt::CTRL | Qt::Key_Q, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_KDE | QApplicationPrivate::KB_Mac}, {QKeySequence::Refresh, 1, Qt::CTRL | Qt::Key_R, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac}, {QKeySequence::Replace, 0, Qt::CTRL | Qt::Key_R, QApplicationPrivate::KB_KDE}, {QKeySequence::Save, 1, Qt::CTRL | Qt::Key_S, QApplicationPrivate::KB_All}, @@ -669,12 +692,14 @@ const uint QKeySequencePrivate::numberOfKeyBindings = sizeof(QKeySequencePrivate \value NextChild Navigate to next tab or child window. \value Open Open document. \value Paste Paste. + \value Preferences Open the preferences dialog. \value PreviousChild Navigate to previous tab or child window. \value Print Print document. + \value Quit Quit the application. \value Redo Redo. \value Refresh Refresh or reload current document. \value Replace Find and replace. - \value SaveAs Save document after prompting the user for a file name. + \value SaveAs Save document after prompting the user for a file name. \value Save Save document. \value SelectAll Select all text. \value SelectEndOfBlock Extend selection to the end of a text block. This shortcut is only used on OS X. @@ -780,6 +805,21 @@ QKeySequence::QKeySequence(const QKeySequence& keysequence) d->ref.ref(); } +#ifdef Q_WS_MAC +static inline int maybeSwapShortcut(int shortcut) +{ + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + uint oldshortcut = shortcut; + shortcut &= ~(Qt::CTRL | Qt::META); + if (oldshortcut & Qt::CTRL) + shortcut |= Qt::META; + if (oldshortcut & Qt::META) + shortcut |= Qt::CTRL; + } + return shortcut; +} +#endif + /*! \since 4.2 @@ -796,10 +836,16 @@ QList<QKeySequence> QKeySequence::keyBindings(StandardKey key) for (uint i = 0; i < QKeySequencePrivate::numberOfKeyBindings ; ++i) { QKeyBinding keyBinding = QKeySequencePrivate::keyBindings[i]; if (keyBinding.standardKey == key && (keyBinding.platform & platform)) { - if (keyBinding.priority > 0) - list.prepend(QKeySequence(QKeySequencePrivate::keyBindings[i].shortcut)); - else - list.append(QKeySequence(QKeySequencePrivate::keyBindings[i].shortcut)); + uint shortcut = +#ifdef Q_WS_MAC + maybeSwapShortcut(QKeySequencePrivate::keyBindings[i].shortcut); +#else + QKeySequencePrivate::keyBindings[i].shortcut; +#endif + if (keyBinding.priority > 0) + list.prepend(QKeySequence(shortcut)); + else + list.append(QKeySequence(shortcut)); } } return list; @@ -967,9 +1013,16 @@ int QKeySequencePrivate::decodeString(const QString &str, QKeySequence::Sequence gmodifs = globalModifs(); if (gmodifs->isEmpty()) { #ifdef Q_WS_MAC - *gmodifs << QModifKeyName(Qt::CTRL, QChar(kCommandUnicode)); + const bool dontSwap = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta); + if (dontSwap) + *gmodifs << QModifKeyName(Qt::META, QChar(kCommandUnicode)); + else + *gmodifs << QModifKeyName(Qt::CTRL, QChar(kCommandUnicode)); *gmodifs << QModifKeyName(Qt::ALT, QChar(kOptionUnicode)); - *gmodifs << QModifKeyName(Qt::META, QChar(kControlUnicode)); + if (dontSwap) + *gmodifs << QModifKeyName(Qt::CTRL, QChar(kControlUnicode)); + else + *gmodifs << QModifKeyName(Qt::META, QChar(kControlUnicode)); *gmodifs << QModifKeyName(Qt::SHIFT, QChar(kShiftUnicode)); #endif *gmodifs << QModifKeyName(Qt::CTRL, QLatin1String("ctrl+")) @@ -1067,8 +1120,6 @@ int QKeySequencePrivate::decodeString(const QString &str, QKeySequence::Sequence if (found) break; } -#ifdef Q_WS_MAC -#endif } return ret; } @@ -1097,15 +1148,30 @@ QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat QString s; #if defined(Q_WS_MAC) if (nativeText) { - // On MAC the order is Meta, Alt, Shift, Control. - if ((key & Qt::META) == Qt::META) - s += macSymbolForQtKey(Qt::Key_Meta); - if ((key & Qt::ALT) == Qt::ALT) - s += macSymbolForQtKey(Qt::Key_Alt); - if ((key & Qt::SHIFT) == Qt::SHIFT) - s += macSymbolForQtKey(Qt::Key_Shift); - if ((key & Qt::CTRL) == Qt::CTRL) - s += macSymbolForQtKey(Qt::Key_Control); + // On Mac OS X the order (by default) is Meta, Alt, Shift, Control. + // If the AA_MacDontSwapCtrlAndMeta is enabled, then the order + // is Ctrl, Alt, Shift, Meta. The macSymbolForQtKey does this swap + // for us, which means that we have to adjust our order here. + // The upshot is a lot more infrastructure to keep the number of + // if tests down and the code relatively clean. + static const int ModifierOrder[] = { Qt::META, Qt::ALT, Qt::SHIFT, Qt::CTRL, 0 }; + static const int QtKeyOrder[] = { Qt::Key_Meta, Qt::Key_Alt, Qt::Key_Shift, Qt::Key_Control, 0 }; + static const int DontSwapModifierOrder[] = { Qt::CTRL, Qt::ALT, Qt::SHIFT, Qt::META, 0 }; + static const int DontSwapQtKeyOrder[] = { Qt::Key_Control, Qt::Key_Alt, Qt::Key_Shift, Qt::Key_Meta, 0 }; + const int *modifierOrder; + const int *qtkeyOrder; + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + modifierOrder = DontSwapModifierOrder; + qtkeyOrder = DontSwapQtKeyOrder; + } else { + modifierOrder = ModifierOrder; + qtkeyOrder = QtKeyOrder; + } + + for (int i = 0; modifierOrder[i] != 0; ++i) { + if (key & modifierOrder[i]) + s += qt_macSymbolForQtKey(qtkeyOrder[i]); + } } else #endif { @@ -1138,7 +1204,7 @@ QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat int i=0; #if defined(Q_WS_MAC) if (nativeText) { - QChar ch = macSymbolForQtKey(key); + QChar ch = qt_macSymbolForQtKey(key); if (!ch.isNull()) p = ch; else diff --git a/src/gui/kernel/qkeysequence.h b/src/gui/kernel/qkeysequence.h index 1c4776f..f78e34a 100644 --- a/src/gui/kernel/qkeysequence.h +++ b/src/gui/kernel/qkeysequence.h @@ -136,7 +136,9 @@ public: DeleteEndOfLine, InsertParagraphSeparator, InsertLineSeparator, - SaveAs + SaveAs, + Preferences, + Quit }; QKeySequence(); diff --git a/src/gui/kernel/qlayoutengine.cpp b/src/gui/kernel/qlayoutengine.cpp index fbaa388..3673f08 100644 --- a/src/gui/kernel/qlayoutengine.cpp +++ b/src/gui/kernel/qlayoutengine.cpp @@ -329,7 +329,7 @@ void qGeomCalc(QVector<QLayoutStruct> &chain, int start, int count, qDebug() << "qGeomCalc" << "start" << start << "count" << count << "pos" << pos << "space" << space << "spacer" << spacer; for (i = start; i < start + count; ++i) { - qDebug() << i << ":" << chain[i].minimumSize << chain[i].smartSizeHint() + qDebug() << i << ':' << chain[i].minimumSize << chain[i].smartSizeHint() << chain[i].maximumSize << "stretch" << chain[i].stretch << "empty" << chain[i].empty << "expansive" << chain[i].expansive << "spacing" << chain[i].spacing; diff --git a/src/gui/kernel/qlayoutitem.cpp b/src/gui/kernel/qlayoutitem.cpp index 0fd73b8..c70ab2d 100644 --- a/src/gui/kernel/qlayoutitem.cpp +++ b/src/gui/kernel/qlayoutitem.cpp @@ -54,7 +54,8 @@ QT_BEGIN_NAMESPACE inline static QRect fromLayoutItemRect(QWidgetPrivate *priv, const QRect &rect) { - return priv->fromOrToLayoutItemRect(rect, -1); + return rect.adjusted(priv->leftLayoutItemMargin, priv->topLayoutItemMargin, + -priv->rightLayoutItemMargin, -priv->bottomLayoutItemMargin); } inline static QSize fromLayoutItemSize(QWidgetPrivate *priv, const QSize &size) @@ -64,7 +65,8 @@ inline static QSize fromLayoutItemSize(QWidgetPrivate *priv, const QSize &size) inline static QRect toLayoutItemRect(QWidgetPrivate *priv, const QRect &rect) { - return priv->fromOrToLayoutItemRect(rect, +1); + return rect.adjusted(-priv->leftLayoutItemMargin, -priv->topLayoutItemMargin, + priv->rightLayoutItemMargin, priv->bottomLayoutItemMargin); } inline static QSize toLayoutItemSize(QWidgetPrivate *priv, const QSize &size) 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/qmime_win.cpp b/src/gui/kernel/qmime_win.cpp index cd0aae6..109ce20 100644 --- a/src/gui/kernel/qmime_win.cpp +++ b/src/gui/kernel/qmime_win.cpp @@ -852,7 +852,7 @@ QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pData if (end > start && start > 0) { html = "<!--StartFragment-->" + html.mid(start, end - start); html += "<!--EndFragment-->"; - html.replace("\r", ""); + html.replace('\r', ""); result = QString::fromUtf8(html); } } diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp index 86894b4..3c2fcdd 100644 --- a/src/gui/kernel/qshortcutmap.cpp +++ b/src/gui/kernel/qshortcutmap.cpp @@ -61,8 +61,6 @@ QT_BEGIN_NAMESPACE -extern bool qt_mac_no_native_menubar; // qmenu_mac.cpp - // To enable verbose output uncomment below //#define DEBUG_QSHORTCUTMAP @@ -111,7 +109,7 @@ QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) { dbg.nospace() << "QShortcutEntry(" << se->keyseq << "), id(" << se->id << "), enabled(" << se->enabled << "), autorepeat(" << se->autorepeat - << "), owner(" << se->owner << ")"; + << "), owner(" << se->owner << ')'; return dbg.space(); } #endif // QT_NO_DEBUGSTREAM @@ -660,7 +658,7 @@ bool QShortcutMap::correctWidgetContext(Qt::ShortcutContext context, QWidget *w, { bool visible = w->isVisible(); #ifdef Q_WS_MAC - if (!qt_mac_no_native_menubar && qobject_cast<QMenuBar *>(w)) + if (!qApp->testAttribute(Qt::AA_DontUseNativeMenuBar) && qobject_cast<QMenuBar *>(w)) visible = true; #endif @@ -723,7 +721,7 @@ bool QShortcutMap::correctGraphicsWidgetContext(Qt::ShortcutContext context, QGr { bool visible = w->isVisible(); #ifdef Q_WS_MAC - if (!qt_mac_no_native_menubar && qobject_cast<QMenuBar *>(w)) + if (!qApp->testAttribute(Qt::AA_DontUseNativeMenuBar) && qobject_cast<QMenuBar *>(w)) visible = true; #endif @@ -878,7 +876,7 @@ void QShortcutMap::dispatchEvent(QKeyEvent *e) qDebug().nospace() << "QShortcutMap::dispatchEvent(): Sending QShortcutEvent(\"" << (QString)next->keyseq << "\", " << next->id << ", " - << (bool)(enabledShortcuts>1) << ") to object(" << next->owner << ")"; + << (bool)(enabledShortcuts>1) << ") to object(" << next->owner << ')'; #endif QShortcutEvent se(next->keyseq, next->id, enabledShortcuts>1); QApplication::sendEvent(const_cast<QObject *>(next->owner), &se); diff --git a/src/gui/kernel/qt_x11_p.h b/src/gui/kernel/qt_x11_p.h index 563b7e9..b9ace9d 100644 --- a/src/gui/kernel/qt_x11_p.h +++ b/src/gui/kernel/qt_x11_p.h @@ -652,6 +652,10 @@ struct QX11Data _XEMBED, _XEMBED_INFO, + XWacomStylus, + XWacomCursor, + XWacomEraser, + NPredefinedAtoms, _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms, diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index bbf6758..d6e5cce 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -167,39 +167,48 @@ static inline bool bypassGraphicsProxyWidget(QWidget *p) extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp extern QDesktopWidget *qt_desktopWidget; // qapplication.cpp -QWidgetPrivate::QWidgetPrivate(int version) : - QObjectPrivate(version), extra(0), focus_child(0) - ,layout(0), widgetItem(0) - ,leftmargin(0), topmargin(0), rightmargin(0), bottommargin(0) - ,leftLayoutItemMargin(0), topLayoutItemMargin(0), rightLayoutItemMargin(0) - ,bottomLayoutItemMargin(0) - ,fg_role(QPalette::NoRole) - ,bg_role(QPalette::NoRole) - ,hd(0) - ,dirty(0) - ,needsFlush(0) - ,dirtyOpaqueChildren(1) - ,isOpaque(0) - ,inDirtyList(0) - ,isScrolled(0) - ,isMoved(0) - ,usesDoubleBufferedGLContext(0) -#ifdef Q_WS_WIN - ,noPaintOnScreen(0) -#endif - ,inheritedFontResolveMask(0) - ,inheritedPaletteResolveMask(0) +QWidgetPrivate::QWidgetPrivate(int version) + : QObjectPrivate(version) + , extra(0) + , focus_next(0) + , focus_prev(0) + , focus_child(0) + , layout(0) + , needsFlush(0) + , redirectDev(0) + , widgetItem(0) + , extraPaintEngine(0) + , polished(0) + , inheritedFontResolveMask(0) + , inheritedPaletteResolveMask(0) + , leftmargin(0) + , topmargin(0) + , rightmargin(0) + , bottommargin(0) + , leftLayoutItemMargin(0) + , topLayoutItemMargin(0) + , rightLayoutItemMargin(0) + , bottomLayoutItemMargin(0) + , hd(0) + , size_policy(QSizePolicy::Preferred, QSizePolicy::Preferred) + , fg_role(QPalette::NoRole) + , bg_role(QPalette::NoRole) + , dirtyOpaqueChildren(1) + , isOpaque(0) + , inDirtyList(0) + , isScrolled(0) + , isMoved(0) + , usesDoubleBufferedGLContext(0) #if defined(Q_WS_X11) - ,picture(0) -#endif -#ifdef Q_WS_MAC - ,needWindowChange(0) - ,isGLWidget(0) + , picture(0) +#elif defined(Q_WS_WIN) + , noPaintOnScreen(0) +#elif defined(Q_WS_MAC) + , needWindowChange(0) + , isGLWidget(0) + , window_event(0) + , qd_hd(0) #endif - ,polished(0) - - , size_policy(QSizePolicy::Preferred, QSizePolicy::Preferred) - , redirectDev(0) { if (!qApp) { qFatal("QWidget: Must construct a QApplication before a QPaintDevice"); @@ -1027,7 +1036,7 @@ void QWidgetPrivate::adjustFlags(Qt::WindowFlags &flags, QWidget *w) if (customize) ; // don't modify window flags if the user explicitely set them. else if (type == Qt::Dialog || type == Qt::Sheet) -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowContextHelpButtonHint | Qt::WindowCloseButtonHint; #else flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; @@ -1094,7 +1103,7 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) if (f & Qt::MSWindowsOwnDC) q->setAttribute(Qt::WA_NativeWindow); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE data.window_state_internal = 0; #endif @@ -1345,7 +1354,7 @@ QWidget::~QWidget() d->setDirtyOpaqueRegion(); if (isWindow() && isVisible() && internalWinId()) - hide(); + d->close_helper(QWidgetPrivate::CloseNoEvent); #if defined(Q_WS_WIN) || defined(Q_WS_X11) else if (!internalWinId() && isVisible()) qApp->d_func()->sendSyntheticEnterLeave(this); @@ -1412,36 +1421,26 @@ void QWidgetPrivate::createTLExtra() createExtra(); if (!extra->topextra) { QTLWExtra* x = extra->topextra = new QTLWExtra; + x->icon = 0; + x->iconPixmap = 0; + x->backingStore = 0; x->windowSurface = 0; + x->sharedPainter = 0; + x->incw = x->inch = 0; + x->basew = x->baseh = 0; + x->frameStrut.setCoords(0, 0, 0, 0); + x->normalGeometry = QRect(0,0,-1,-1); + x->savedFlags = 0; x->opacity = 255; x->posFromMove = false; x->sizeAdjusted = false; x->inTopLevelResize = false; x->inRepaint = false; - x->backingStore = 0; - x->icon = 0; - x->iconPixmap = 0; - x->frameStrut.setCoords(0, 0, 0, 0); - x->incw = x->inch = 0; - x->basew = x->baseh = 0; - x->normalGeometry = QRect(0,0,-1,-1); -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_MAC) x->embedded = 0; -#endif -#if defined(Q_WS_X11) - x->parentWinId = 0; - x->spont_unmapped = 0; - x->dnd = 0; -#endif - x->savedFlags = 0; -#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER) - x->qwsManager = 0; -#endif - x->sharedPainter = 0; createTLSysExtra(); #ifdef QWIDGET_EXTRA_DEBUG - static int count = 0; - qDebug() << "tlextra" << ++count; + static int count = 0; + qDebug() << "tlextra" << ++count; #endif } } @@ -1455,27 +1454,28 @@ void QWidgetPrivate::createExtra() { if (!extra) { // if not exists extra = new QWExtra; - extra->minw = extra->minh = 0; - extra->maxw = extra->maxh = QWIDGETSIZE_MAX; + extra->glContext = 0; + extra->topextra = 0; + extra->proxyWidget = 0; +#ifndef QT_NO_CURSOR + extra->curs = 0; +#endif + extra->minw = 0; + extra->minh = 0; + extra->maxw = QWIDGETSIZE_MAX; + extra->maxh = QWIDGETSIZE_MAX; + extra->customDpiX = 0; + extra->customDpiY = 0; extra->explicitMinSize = 0; extra->explicitMaxSize = 0; extra->autoFillBackground = 0; extra->nativeChildrenForced = 0; extra->inRenderWithPainter = 0; extra->hasMask = 0; -#ifndef QT_NO_CURSOR - extra->curs = 0; -#endif - extra->style = 0; - extra->topextra = 0; - extra->proxyWidget = 0; - extra->glContext = 0; - extra->customDpiX = 0; - extra->customDpiY = 0; createSysExtra(); #ifdef QWIDGET_EXTRA_DEBUG - static int count = 0; - qDebug() << "extra" << ++count; + static int count = 0; + qDebug() << "extra" << ++count; #endif } } @@ -1516,45 +1516,6 @@ void QWidgetPrivate::deleteExtra() } /* - Returns true if the background is inherited; otherwise returns - false. - - Mainly used in the paintOnScreen case. -*/ - -bool QWidgetPrivate::isBackgroundInherited() const -{ - Q_Q(const QWidget); - - // windows do not inherit their background - if (q->isWindow() || q->windowType() == Qt::SubWindow) - return false; - - if (q->testAttribute(Qt::WA_NoSystemBackground) || q->testAttribute(Qt::WA_OpaquePaintEvent)) - return false; - - const QPalette &pal = q->palette(); - QPalette::ColorRole bg = q->backgroundRole(); - QBrush brush = pal.brush(bg); - - // non opaque brushes leaves us no choice, we must inherit - if (!q->autoFillBackground() || !brush.isOpaque()) - return true; - - if (brush.style() == Qt::SolidPattern) { - // the background is just a solid color. If there is no - // propagated contents, then we claim as performance - // optimization that it was not inheritet. This is the normal - // case in standard Windows or Motif style. - const QWidget *w = q->parentWidget(); - if (!w->d_func()->isBackgroundInherited()) - return false; - } - - return true; -} - -/* Returns true if there are widgets above this which overlap with \a rect, which is in parent's coordinate system (same as crect). */ @@ -1900,24 +1861,6 @@ void QWidgetPrivate::clipToEffectiveMask(QRegion ®ion) const } } -bool QWidgetPrivate::hasBackground() const -{ - Q_Q(const QWidget); - if (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_PaintOnScreen)) - return true; - if (q->testAttribute(Qt::WA_PaintOnScreen)) - return true; - if (!q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) { - const QPalette &pal = q->palette(); - QPalette::ColorRole bg = q->backgroundRole(); - QBrush bgBrush = pal.brush(bg); - return (bgBrush.style() != Qt::NoBrush && - ((q->isWindow() || q->windowType() == Qt::SubWindow) - || (QPalette::ColorRole(bg_role) != QPalette::NoRole || (pal.resolve() & (1<<bg))))); - } - return false; -} - bool QWidgetPrivate::paintOnScreen() const { #if defined(Q_WS_QWS) @@ -2969,10 +2912,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 @@ -4209,7 +4157,7 @@ const QPalette &QWidget::palette() const if (!isEnabled()) { data->pal.setCurrentColorGroup(QPalette::Disabled); } else if ((!isVisible() || isActiveWindow()) -#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +#if defined(Q_OS_WIN) && !defined(Q_WS_WINCE) && !QApplicationPrivate::isBlockedByModal(const_cast<QWidget *>(this)) #endif ) { @@ -4797,7 +4745,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, } const qreal opacity = painter->opacity(); - if (qFuzzyCompare(opacity + 1, qreal(1.0))) + if (qFuzzyIsNull(opacity)) return; // Fully transparent. Q_D(QWidget); @@ -5288,7 +5236,7 @@ static QString constructWindowTitleFromFilePath(const QString &filePath) #ifndef Q_WS_MAC QString appName = QApplication::applicationName(); if (!appName.isEmpty()) - windowTitle += QLatin1String(" ") + QChar(0x2014) + QLatin1String(" ") + appName; + windowTitle += QLatin1Char(' ') + QChar(0x2014) + QLatin1Char(' ') + appName; #endif return windowTitle; } @@ -5352,7 +5300,7 @@ QString qt_setWindowTitle_helperHelper(const QString &title, const QWidget *widg && widget->style()->styleHint(QStyle::SH_TitleBar_ModifyNotification, 0, widget)) cap.replace(lastIndex, 3, QWidget::tr("*")); else - cap.replace(lastIndex, 3, QLatin1String("")); + cap.remove(lastIndex, 3); } index = cap.indexOf(placeHolder, index); @@ -5907,6 +5855,8 @@ QWidget *QWidget::focusWidget() const /*! Returns the next widget in this widget's focus chain. + + \sa previousInFocusChain */ QWidget *QWidget::nextInFocusChain() const { @@ -5914,6 +5864,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 @@ -6151,14 +6113,6 @@ int QWidgetPrivate::pointToRect(const QPoint &p, const QRect &r) return dx + dy; } -QRect QWidgetPrivate::fromOrToLayoutItemRect(const QRect &rect, int sign) const -{ - QRect r = rect; - r.adjust(-sign * leftLayoutItemMargin, -sign * topLayoutItemMargin, - +sign * rightLayoutItemMargin, +sign * bottomLayoutItemMargin); - return r; -} - /*! \property QWidget::frameSize \brief the size of the widget including any window frame @@ -7332,7 +7286,7 @@ QSize QWidgetPrivate::adjustedSize() const #else // all others QRect screen = QApplication::desktop()->screenGeometry(q->pos()); #endif -#if defined (Q_OS_WINCE) +#if defined (Q_WS_WINCE) s.setWidth(qMin(s.width(), screen.width())); s.setHeight(qMin(s.height(), screen.height())); #else @@ -7640,16 +7594,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; @@ -9805,27 +9753,6 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) QEvent e(QEvent::MouseTrackingChange); QApplication::sendEvent(this, &e); break; } -#if !defined(QT_NO_DIRECT3D) && defined(Q_WS_WIN) - case Qt::WA_MSWindowsUseDirect3D: - if (!qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault)) { - if (on) { - if (!d->extra) - d->createExtra(); - d->extra->had_auto_fill_bg = d->extra->autoFillBackground; - d->extra->had_no_system_bg = testAttribute(Qt::WA_NoSystemBackground); - d->extra->had_paint_on_screen = testAttribute(Qt::WA_PaintOnScreen); - // enforce the opaque widget state D3D needs - d->extra->autoFillBackground = true; - setAttribute(Qt::WA_PaintOnScreen); - setAttribute(Qt::WA_NoSystemBackground); - } else if (d->extra) { - d->extra->autoFillBackground = d->extra->had_auto_fill_bg; - setAttribute(Qt::WA_PaintOnScreen, d->extra->had_paint_on_screen); - setAttribute(Qt::WA_NoSystemBackground, d->extra->had_no_system_bg); - } - } - break; -#endif case Qt::WA_NativeWindow: { QInputContext *ic = 0; if (on && !internalWinId() && testAttribute(Qt::WA_InputMethodEnabled) && hasFocus()) { @@ -9837,7 +9764,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; } @@ -9868,10 +9795,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 @@ -9879,7 +9802,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..6703d26 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -130,7 +130,7 @@ public: int alloc_region_index; // int alloc_region_revision; #endif -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) uint window_state_internal : 4; #endif QRect wrect; @@ -469,7 +469,7 @@ public Q_SLOTS: virtual void setVisible(bool visible); inline void setHidden(bool hidden) { setVisible(!hidden); } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE inline void show() { setVisible(true); } #else void show(); @@ -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 f863428..8243f32 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -390,21 +390,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) @@ -734,6 +728,7 @@ static OSWindowRef qt_mac_create_window(QWidget *, WindowClass wclass, WindowAtt static EventTypeSpec window_events[] = { { kEventClassWindow, kEventWindowClose }, { kEventClassWindow, kEventWindowExpanded }, + { kEventClassWindow, kEventWindowHidden }, { kEventClassWindow, kEventWindowZoomed }, { kEventClassWindow, kEventWindowCollapsed }, { kEventClassWindow, kEventWindowToolbarSwitchMode }, @@ -993,6 +988,19 @@ OSStatus QWidgetPrivate::qt_window_event(EventHandlerCallRef er, EventRef event, } } } + } else if (ekind == kEventWindowHidden) { + // Make sure that we also hide any visible sheets on our window. + // Cocoa does the right thing for us. + const QObjectList children = widget->children(); + const int childCount = children.count(); + for (int i = 0; i < childCount; ++i) { + QObject *obj = children.at(i); + if (obj->isWidgetType()) { + QWidget *widget = static_cast<QWidget *>(obj); + if (qt_mac_is_macsheet(widget) && widget->isVisible()) + widget->hide(); + } + } } else { handled_event = false; } @@ -1595,24 +1603,6 @@ bool QWidgetPrivate::qt_create_root_win() return true; } -bool QWidgetPrivate::qt_recreate_root_win() -{ - if(!qt_root_win) //sanity check - return false; - //store old - OSWindowRef old_root_win = qt_root_win; - //recreate - qt_root_win = 0; - qt_create_root_win(); - //cleanup old window -#ifdef QT_MAC_USE_COCOA - [old_root_win release]; -#else - CFRelease(old_root_win); -#endif - return true; -} - bool QWidgetPrivate::qt_widget_rgn(QWidget *widget, short wcode, RgnHandle rgn, bool force = false) { bool ret = false; @@ -4434,11 +4424,13 @@ void QWidgetPrivate::deleteSysExtra() void QWidgetPrivate::createTLSysExtra() { + extra->topextra->resizer = 0; + extra->topextra->isSetGeometry = 0; + extra->topextra->isMove = 0; + extra->topextra->wattr = 0; extra->topextra->wclass = 0; extra->topextra->group = 0; extra->topextra->windowIcon = 0; - extra->topextra->resizer = 0; - extra->topextra->isSetGeometry = 0; extra->topextra->savedWindowAttributesFromMaximized = 0; } @@ -4513,14 +4505,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); @@ -4533,12 +4517,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/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 2461820..bf4f091 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -99,93 +99,92 @@ class QWidgetItemV2; class QStyle; struct QTLWExtra { + // *************************** Cross-platform variables ***************************** + + // Regular pointers (keep them together to avoid gaps on 64 bits architectures). + QIcon *icon; // widget icon + QPixmap *iconPixmap; + QWidgetBackingStore *backingStore; + QWindowSurface *windowSurface; + QPainter *sharedPainter; + + // Implicit pointers (shared_null). QString caption; // widget caption QString iconText; // widget icon text QString role; // widget role QString filePath; // widget file path - QIcon *icon; // widget icon - QPixmap *iconPixmap; + + // Other variables. short incw, inch; // size increments + short basew, baseh; // base sizes // frame strut, don't use these directly, use QWidgetPrivate::frameStrut() instead. QRect frameStrut; + QRect normalGeometry; // used by showMin/maximized/FullScreen + Qt::WindowFlags savedFlags; // Save widget flags while showing fullscreen + + // *************************** Cross-platform bit fields **************************** uint opacity : 8; uint posFromMove : 1; uint sizeAdjusted : 1; uint inTopLevelResize : 1; uint inRepaint : 1; - QWidgetBackingStore *backingStore; -#if defined(Q_WS_WIN) - ulong savedFlags; // Save window flags while showing fullscreen - uint embedded : 1; // window is embedded in another application -#else - Qt::WindowFlags savedFlags; // Save widget flags while showing fullscreen -#endif - short basew, baseh; // base sizes -#if defined(Q_WS_X11) - WId parentWinId; // parent window Id (valid after reparenting) - uint embedded : 1; // window is embedded in another Qt application + uint embedded : 1; + + // *************************** Platform specific values (bit fields first) ********** +#if defined(Q_WS_X11) // <----------------------------------------------------------- X11 uint spont_unmapped: 1; // window was spontaneously unmapped uint dnd : 1; // DND properties installed uint validWMState : 1; // is WM_STATE valid? uint waitingForMapNotify : 1; // show() has been called, haven't got the MapNotify yet + WId parentWinId; // parent window Id (valid after reparenting) WId userTimeWindow; // window id that contains user-time timestamp when WM supports a _NET_WM_USER_TIME_WINDOW atom QPoint fullScreenOffset; -#endif -#if defined(Q_WS_MAC) +#elif defined(Q_WS_WIN) // <--------------------------------------------------------- WIN + HICON winIconBig; // internal big Windows icon + HICON winIconSmall; // internal small Windows icon +#elif defined(Q_WS_MAC) // <--------------------------------------------------------- MAC + uint resizer : 4; + uint isSetGeometry : 1; + uint isMove : 1; quint32 wattr; quint32 wclass; WindowGroupRef group; IconRef windowIcon; // the current window icon, if set with setWindowIcon_sys. quint32 savedWindowAttributesFromMaximized; // Saved attributes from when the calling updateMaximizeButton_sys() - uint resizer : 4; - uint isSetGeometry : 1; - uint isMove : 1; - uint embedded : 1; -#endif -#if defined(Q_WS_QWS) && !defined (QT_NO_QWS_MANAGER) +#elif defined(Q_WS_QWS) // <--------------------------------------------------------- QWS +#ifndef QT_NO_QWS_MANAGER QWSManager *qwsManager; #endif -#if defined(Q_WS_WIN) - HICON winIconBig; // internal big Windows icon - HICON winIconSmall; // internal small Windows icon #endif - QRect normalGeometry; // used by showMin/maximized/FullScreen - QWindowSurface *windowSurface; - QPainter *sharedPainter; }; struct QWExtra { - qint32 minw, minh; // minimum size - qint32 maxw, maxh; // maximum size - QPointer<QWidget> focus_proxy; -#ifndef QT_NO_CURSOR - QCursor *curs; -#endif + // *************************** Cross-platform variables ***************************** + + // Regular pointers (keep them together to avoid gaps on 64 bits architectures). + void *glContext; // if the widget is hijacked by QGLWindowSurface QTLWExtra *topextra; // only useful for TLWs QGraphicsProxyWidget *proxyWidget; // if the widget is embedded - void *glContext; // if the widget is hijacked by QGLWindowSurface -#if defined(Q_WS_WIN) && !defined(QT_NO_DRAGANDDROP) - QOleDropTarget *dropTarget; // drop target - QList<QPointer<QWidget> > oleDropWidgets; -#endif -#if defined(Q_WS_X11) - WId xDndProxy; // XDND forwarding to embedded windows +#ifndef QT_NO_CURSOR + QCursor *curs; #endif + QPointer<QStyle> style; + QPointer<QWidget> focus_proxy; + + // Implicit pointers (shared_empty/shared_null). QRegion mask; // widget mask + QString styleSheet; + + // Other variables. + qint32 minw; + qint32 minh; // minimum size + qint32 maxw; + qint32 maxh; // maximum size + quint16 customDpiX; + quint16 customDpiY; QSize staticContentsSize; -//bit flags at the end to improve packing -#if defined(Q_WS_WIN) - uint shown_mode : 8; // widget show mode -#ifndef QT_NO_DIRECT3D - uint had_paint_on_screen : 1; - uint had_no_system_bg : 1; - uint had_auto_fill_bg : 1; -#endif -#endif -#if defined(Q_WS_X11) - uint compress_events : 1; -#endif + // *************************** Cross-platform bit fields **************************** uint explicitMinSize : 2; uint explicitMaxSize : 2; uint autoFillBackground : 1; @@ -193,16 +192,22 @@ struct QWExtra { uint inRenderWithPainter : 1; uint hasMask : 1; - QPointer<QStyle> style; - QString styleSheet; - - quint16 customDpiX; - quint16 customDpiY; -#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + // *************************** Platform specific values (bit fields first) ********** +#if defined(Q_WS_WIN) // <----------------------------------------------------------- WIN +#ifndef QT_NO_DRAGANDDROP + QOleDropTarget *dropTarget; // drop target + QList<QPointer<QWidget> > oleDropWidgets; +#endif +#elif defined(Q_WS_X11) // <--------------------------------------------------------- X11 + uint compress_events : 1; + WId xDndProxy; // XDND forwarding to embedded windows +#elif defined(Q_WS_MAC) // <------------------------------------------------------ MAC +#ifdef QT_MAC_USE_COCOA // Cocoa Mask stuff QImage maskBits; CGImageRef imageMask; #endif +#endif }; class Q_GUI_EXPORT QWidgetPrivate : public QObjectPrivate @@ -210,6 +215,24 @@ class Q_GUI_EXPORT QWidgetPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QWidget) public: + // *************************** Cross-platform *************************************** + enum DrawWidgetFlags { + DrawAsRoot = 0x01, + DrawPaintOnScreen = 0x02, + DrawRecursive = 0x04, + DrawInvisible = 0x08, + DontSubtractOpaqueChildren = 0x10, + DontSetCompositionMode = 0x20, + DontDrawOpaqueChildren = 0x40 + }; + + enum CloseMode { + CloseNoEvent, + CloseWithEvent, + CloseWithSpontaneousEvent + }; + + // Functions. explicit QWidgetPrivate(int version = QObjectPrivateVersion); ~QWidgetPrivate(); @@ -219,10 +242,6 @@ public: QPainter *sharedPainter() const; void setSharedPainter(QPainter *painter); QWidgetBackingStore *maybeBackingStore() const; -#ifdef Q_WS_QWS - void setMaxWindowState_helper(); - void setFullScreenSize_helper(); -#endif void init(QWidget *desktopWidget, Qt::WindowFlags f); void create_sys(WId window, bool initializeWindow, bool destroyOldWindow); void createRecursively(); @@ -243,24 +262,6 @@ public: QPalette naturalWidgetPalette(uint inheritedMask) const; void setMask_sys(const QRegion &); -#ifdef Q_WS_WIN - bool shouldShowMaximizeButton(); - void winUpdateIsOpaque(); -#endif - -#ifdef Q_WS_MAC - void macUpdateSizeAttribute(); - void macUpdateHideOnSuspend(); - void macUpdateOpaqueSizeGrip(); - void macUpdateIgnoreMouseEvents(); - void macUpdateMetalAttribute(); - void macUpdateIsOpaque(); - void setEnabled_helper_sys(bool enable); - bool isRealWindow() const; - void adjustWithinMaxAndMinSize(int &w, int &h); - void applyMaxAndMinSizeOnWindow(); -#endif - void raise_sys(); void lower_sys(); void stackUnder_sys(QWidget *); @@ -285,20 +286,9 @@ public: void setStyle_helper(QStyle *newStyle, bool propagate, bool metalHack = false); void inheritStyle(); - bool isBackgroundInherited() const; - void setUpdatesEnabled_helper(bool ); void paintBackground(QPainter *, const QRegion &, const QPoint & = QPoint(), int flags = DrawAsRoot) const; - enum DrawWidgetFlags { - DrawAsRoot = 0x01, - DrawPaintOnScreen = 0x02, - DrawRecursive = 0x04, - DrawInvisible = 0x08, - DontSubtractOpaqueChildren = 0x10, - DontSetCompositionMode = 0x20, - DontDrawOpaqueChildren = 0x40 - }; bool isAboutToShow() const; QRegion prepareToRender(const QRegion ®ion, QWidget::RenderFlags renderFlags); void render_helper(QPainter *painter, const QPoint &targetOffset, const QRegion &sourceRegion, @@ -321,10 +311,6 @@ public: QWindowSurface *createDefaultWindowSurface(); QWindowSurface *createDefaultWindowSurface_sys(); void repaint_sys(const QRegion &rgn); -#ifdef Q_WS_MAC - void update_sys(const QRect &rect); - void update_sys(const QRegion &rgn); -#endif QRect clipRect() const; QRegion clipRegion() const; @@ -335,42 +321,20 @@ public: void updateIsOpaque(); void setOpaque(bool opaque); void updateIsTranslucent(); - bool hasBackground() const; bool paintOnScreen() const; QRegion getOpaqueRegion() const; const QRegion &getOpaqueChildren() const; void setDirtyOpaqueRegion(); - QRegion opaqueChildren; - - enum CloseMode { - CloseNoEvent, - CloseWithEvent, - CloseWithSpontaneousEvent - }; bool close_helper(CloseMode mode); - bool compositeEvent(QEvent *e); void setWindowIcon_helper(); void setWindowIcon_sys(bool forceReset = false); void setWindowOpacity_sys(qreal opacity); - void adjustQuitOnCloseAttribute(); -#if defined(Q_WS_X11) - void setWindowRole(); - void sendStartupMessage(const char *message) const; - void setNetWmWindowTypes(); - void x11UpdateIsOpaque(); -#endif - -#if defined (Q_WS_WIN) - void reparentChildren(); -#endif - void scrollChildren(int dx, int dy); - void moveRect(const QRect &, int dx, int dy); void scrollRect(const QRect &, int dx, int dy); void invalidateBuffer_resizeHelper(const QPoint &oldPos, const QSize &oldSize); @@ -384,7 +348,6 @@ public: void reparentFocusWidgets(QWidget *oldtlw); static int pointToRect(const QPoint &p, const QRect &r); - QRect fromOrToLayoutItemRect(const QRect &rect, int sign) const; void setWinId(WId); void showChildren(bool spontaneous); @@ -394,9 +357,6 @@ public: void scroll_sys(int dx, int dy, const QRect &r); void deactivateWidgetCleanup(); void setGeometry_sys(int, int, int, int, bool); -#ifdef Q_WS_MAC - void setGeometry_sys_helper(int, int, int, int, bool); -#endif void sendPendingMoveAndResizeEvents(bool recursive = false, bool disableUpdates = false); void activateChildLayoutsRecursively(); void show_recursive(); @@ -408,10 +368,6 @@ public: void setEnabled_helper(bool); void registerDropSite(bool); -#if defined(Q_WS_WIN) && !defined(QT_NO_DRAGANDDROP) - QOleDropTarget *registerOleDnd(QWidget *widget); - void unregisterOleDnd(QWidget *widget, QOleDropTarget *target); -#endif static void adjustFlags(Qt::WindowFlags &flags, QWidget *w = 0); void updateFrameStrut(); @@ -421,32 +377,11 @@ public: void setWindowIconText_helper(const QString &cap); void setWindowTitle_sys(const QString &cap); -#ifdef Q_OS_WIN - void grabMouseWhileInWindow(); -#endif - #ifndef QT_NO_CURSOR void setCursor_sys(const QCursor &cursor); void unsetCursor_sys(); #endif -#ifdef Q_WS_MAC - void setWindowModified_sys(bool b); - void updateMaximizeButton_sys(); - void setWindowFilePath_sys(const QString &filePath); - void createWindow_sys(); - void recreateMacWindow(); -#ifndef QT_MAC_USE_COCOA - void initWindowPtr(); - void finishCreateWindow_sys_Carbon(OSWindowRef windowRef); -#else - void finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ windowRef); - void syncCocoaMask(); - void finishCocoaMaskSetup(); -#endif - void determineWindowClass(); - void transferChildren(); -#endif void setWindowTitle_helper(const QString &cap); void setWindowFilePath_helper(const QString &filePath); @@ -462,59 +397,89 @@ public: QInputContext *inputContext() const; -#if defined(Q_WS_QWS) - void moveSurface(QWindowSurface *surface, const QPoint &offset); + void setModal_sys(); - QRegion localRequestedRegion() const; - QRegion localAllocatedRegion() const; + inline void setRedirected(QPaintDevice *replacement, const QPoint &offset) + { + Q_ASSERT(q_func()->testAttribute(Qt::WA_WState_InPaintEvent)); + redirectDev = replacement; + redirectOffset = offset; + } - void blitToScreen(const QRegion &globalrgn); -#ifndef QT_NO_CURSOR - void updateCursor() const; -#endif + inline QPaintDevice *redirected(QPoint *offset) const + { + if (offset) + *offset = redirectDev ? redirectOffset : QPoint(); + return redirectDev; + } - QScreen* getScreen() const; + inline void restoreRedirected() + { redirectDev = 0; } - friend class QWSManager; - friend class QWSManagerPrivate; - friend class QDecoration; -#endif + inline void enforceNativeChildren() + { + if (!extra) + createExtra(); - static int instanceCounter; // Current number of widget instances - static int maxInstances; // Maximum number of widget instances + if (extra->nativeChildrenForced) + return; + extra->nativeChildrenForced = 1; -#ifdef QT_KEYPAD_NAVIGATION - static QPointer<QWidget> editingWidget; -#endif + for (int i = 0; i < children.size(); ++i) { + if (QWidget *child = qobject_cast<QWidget *>(children.at(i))) + child->setAttribute(Qt::WA_NativeWindow); + } + } - QWidgetData data; + inline bool nativeChildrenForced() const + { + return extra ? extra->nativeChildrenForced : false; + } + QSize adjustedSize() const; + +#ifndef Q_WS_QWS // Almost cross-platform :-) + void setWSGeometry(bool dontShow=false, const QRect &oldRect = QRect()); + + inline QPoint mapToWS(const QPoint &p) const + { return p - data.wrect.topLeft(); } + + inline QPoint mapFromWS(const QPoint &p) const + { return p + data.wrect.topLeft(); } + + inline QRect mapToWS(const QRect &r) const + { QRect rr(r); rr.translate(-data.wrect.topLeft()); return rr; } + + inline QRect mapFromWS(const QRect &r) const + { QRect rr(r); rr.translate(data.wrect.topLeft()); return rr; } +#endif + + // Variables. + // Regular pointers (keep them together to avoid gaps on 64 bit architectures). QWExtra *extra; QWidget *focus_next; QWidget *focus_prev; QWidget *focus_child; -#ifndef QT_NO_ACTION - QList<QAction*> actions; -#endif QLayout *layout; + QRegion *needsFlush; + QPaintDevice *redirectDev; QWidgetItemV2 *widgetItem; -#if !defined(QT_NO_IM) - QPointer<QInputContext> ic; -#endif + QPaintEngine *extraPaintEngine; + mutable const QMetaObject *polished; // All widgets are initially added into the uncreatedWidgets set. Once // they receive a window id they are removed and added to the mapper static QWidgetMapper *mapper; static QWidgetSet *uncreatedWidgets; +#if !defined(QT_NO_IM) + QPointer<QInputContext> ic; +#endif +#ifdef QT_KEYPAD_NAVIGATION + static QPointer<QWidget> editingWidget; +#endif - short leftmargin, topmargin, rightmargin, bottommargin; - - signed char leftLayoutItemMargin; - signed char topLayoutItemMargin; - signed char rightLayoutItemMargin; - signed char bottomLayoutItemMargin; - - // ### TODO: reorganize private/extra/topextra to save memory - QPointer<QWidget> compositeChildGrab; + // Implicit pointers (shared_null/shared_empty). + QRegion opaqueChildren; + QRegion dirty; #ifndef QT_NO_TOOLTIP QString toolTip; #endif @@ -524,14 +489,37 @@ public: #ifndef QT_NO_WHATSTHIS QString whatsThis; #endif - QString accessibleName, accessibleDescription; +#ifndef QT_NO_ACCESSIBILITY + QString accessibleName; + QString accessibleDescription; +#endif + + // Other variables. + uint inheritedFontResolveMask; + uint inheritedPaletteResolveMask; + short leftmargin; + short topmargin; + short rightmargin; + short bottommargin; + signed char leftLayoutItemMargin; + signed char topLayoutItemMargin; + signed char rightLayoutItemMargin; + signed char bottomLayoutItemMargin; + static int instanceCounter; // Current number of widget instances + static int maxInstances; // Maximum number of widget instances + Qt::HANDLE hd; + QWidgetData data; + QSizePolicy size_policy; + QLocale locale; + QPoint redirectOffset; +#ifndef QT_NO_ACTION + QList<QAction*> actions; +#endif + // Bit fields. + uint high_attributes[3]; // the low ones are in QWidget::widget_attributes QPalette::ColorRole fg_role : 8; QPalette::ColorRole bg_role : 8; - uint high_attributes[3]; // the low ones are in QWidget::widget_attributes - Qt::HANDLE hd; - QRegion dirty; - QRegion *needsFlush; uint dirtyOpaqueChildren : 1; uint isOpaque : 1; uint inDirtyList : 1; @@ -539,35 +527,33 @@ public: uint isMoved : 1; uint usesDoubleBufferedGLContext : 1; -#ifdef Q_WS_WIN - uint noPaintOnScreen : 1; // see qwidget_win.cpp ::paintEngine() -#endif - - uint inheritedFontResolveMask; - uint inheritedPaletteResolveMask; -#if defined(Q_WS_X11) + // *************************** Platform specific ************************************ +#if defined(Q_WS_X11) // <----------------------------------------------------------- X11 QX11Info xinfo; Qt::HANDLE picture; + static QWidget *mouseGrabber; + static QWidget *keyboardGrabber; + + void setWindowRole(); + void sendStartupMessage(const char *message) const; + void setNetWmWindowTypes(); + void x11UpdateIsOpaque(); + bool isBackgroundInherited() const; +#elif defined(Q_WS_WIN) // <--------------------------------------------------------- WIN + uint noPaintOnScreen : 1; // see qwidget_win.cpp ::paintEngine() + + bool shouldShowMaximizeButton(); + void winUpdateIsOpaque(); + void reparentChildren(); +#ifndef QT_NO_DRAGANDDROP + QOleDropTarget *registerOleDnd(QWidget *widget); + void unregisterOleDnd(QWidget *widget, QOleDropTarget *target); #endif -#if defined(Q_WS_MAC) - enum PaintChildrenOPs { - PC_None = 0x00, - PC_Now = 0x01, - PC_NoPaint = 0x04, - PC_Later = 0x10 - }; - EventHandlerRef window_event; - bool qt_mac_dnd_event(uint, DragRef); - void toggleDrawers(bool); - //mac event functions - static bool qt_create_root_win(); - static void qt_clean_root_win(); - static bool qt_recreate_root_win(); - static bool qt_mac_update_sizer(QWidget *, int up = 0); - static OSStatus qt_window_event(EventHandlerCallRef er, EventRef event, void *); - static OSStatus qt_widget_event(EventHandlerCallRef er, EventRef event, void *); - static bool qt_widget_rgn(QWidget *, short, RgnHandle, bool); - static bool qt_widget_shape(QWidget *, short, HIMutableShapeRef, bool); + void grabMouseWhileInWindow(); +#elif defined(Q_WS_MAC) // <--------------------------------------------------------- MAC + // This is new stuff + uint needWindowChange : 1; + uint isGLWidget : 1; // Each wiget keeps a list of all its child and grandchild OpenGL widgets. // This list is used to update the gl context whenever a parent and a granparent @@ -580,95 +566,70 @@ public: QWidget * widget; QWidget * lastUpdateWidget; }; - QList<GlWidgetInfo> glWidgets; // dirtyOnWidget contains the areas in the widget that needs to be repained, // in the same way as dirtyOnScreen does for the window. Areas are added in // dirtyWidget_sys and cleared in the paint event. In scroll_sys we then use // this information repaint invalid areas when widgets are scrolled. QRegion dirtyOnWidget; + EventHandlerRef window_event; + QList<GlWidgetInfo> glWidgets; //these are here just for code compat (HIViews) Qt::HANDLE qd_hd; - // This is new stuff - uint needWindowChange : 1; - uint isGLWidget : 1; -#endif - -#if defined(Q_WS_X11) || defined (Q_WS_WIN) || defined(Q_WS_MAC) -#ifdef Q_WS_MAC - void setWSGeometry(bool dontShow=false, const QRect &oldRect = QRect()); + void macUpdateSizeAttribute(); + void macUpdateHideOnSuspend(); + void macUpdateOpaqueSizeGrip(); + void macUpdateIgnoreMouseEvents(); + void macUpdateMetalAttribute(); + void macUpdateIsOpaque(); + void setEnabled_helper_sys(bool enable); + bool isRealWindow() const; + void adjustWithinMaxAndMinSize(int &w, int &h); + void applyMaxAndMinSizeOnWindow(); + void update_sys(const QRect &rect); + void update_sys(const QRegion &rgn); + void setGeometry_sys_helper(int, int, int, int, bool); + void setWindowModified_sys(bool b); + void updateMaximizeButton_sys(); + void setWindowFilePath_sys(const QString &filePath); + void createWindow_sys(); + void recreateMacWindow(); +#ifndef QT_MAC_USE_COCOA + void initWindowPtr(); + void finishCreateWindow_sys_Carbon(OSWindowRef windowRef); #else - void setWSGeometry(bool dontShow=false); + void finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ windowRef); + void syncCocoaMask(); + void finishCocoaMaskSetup(); #endif + void determineWindowClass(); + void transferChildren(); + bool qt_mac_dnd_event(uint, DragRef); + void toggleDrawers(bool); + //mac event functions + static bool qt_create_root_win(); + static void qt_clean_root_win(); + static bool qt_mac_update_sizer(QWidget *, int up = 0); + static OSStatus qt_window_event(EventHandlerCallRef er, EventRef event, void *); + static OSStatus qt_widget_event(EventHandlerCallRef er, EventRef event, void *); + static bool qt_widget_rgn(QWidget *, short, RgnHandle, bool); +#elif defined(Q_WS_QWS) // <--------------------------------------------------------- QWS + void setMaxWindowState_helper(); + void setFullScreenSize_helper(); + void moveSurface(QWindowSurface *surface, const QPoint &offset); + QRegion localRequestedRegion() const; + QRegion localAllocatedRegion() const; - inline QPoint mapToWS(const QPoint &p) const - { return p - data.wrect.topLeft(); } - - inline QPoint mapFromWS(const QPoint &p) const - { return p + data.wrect.topLeft(); } - - inline QRect mapToWS(const QRect &r) const - { QRect rr(r); rr.translate(-data.wrect.topLeft()); return rr; } - - inline QRect mapFromWS(const QRect &r) const - { QRect rr(r); rr.translate(data.wrect.topLeft()); return rr; } + friend class QWSManager; + friend class QWSManagerPrivate; + friend class QDecoration; +#ifndef QT_NO_CURSOR + void updateCursor() const; #endif - - QPaintEngine *extraPaintEngine; - - mutable const QMetaObject *polished; - - void setModal_sys(); - QSizePolicy size_policy; - QLocale locale; - -#ifdef Q_WS_X11 - static QWidget *mouseGrabber; - static QWidget *keyboardGrabber; + QScreen* getScreen() const; #endif - QPaintDevice *redirectDev; - QPoint redirectOffset; - - inline void setRedirected(QPaintDevice *replacement, const QPoint &offset) - { - Q_ASSERT(q_func()->testAttribute(Qt::WA_WState_InPaintEvent)); - redirectDev = replacement; - redirectOffset = offset; - } - - inline QPaintDevice *redirected(QPoint *offset) const - { - if (offset) - *offset = redirectDev ? redirectOffset : QPoint(); - return redirectDev; - } - - inline void restoreRedirected() - { redirectDev = 0; } - - inline void enforceNativeChildren() - { - if (!extra) - createExtra(); - - if (extra->nativeChildrenForced) - return; - extra->nativeChildrenForced = 1; - - for (int i = 0; i < children.size(); ++i) { - if (QWidget *child = qobject_cast<QWidget *>(children.at(i))) - child->setAttribute(Qt::WA_NativeWindow); - } - } - - inline bool nativeChildrenForced() const - { - return extra ? extra->nativeChildrenForced : false; - } - - QSize adjustedSize() const; }; inline QWExtra *QWidgetPrivate::extraData() const diff --git a/src/gui/kernel/qwidget_qws.cpp b/src/gui/kernel/qwidget_qws.cpp index 1445f57..94bdb85 100644 --- a/src/gui/kernel/qwidget_qws.cpp +++ b/src/gui/kernel/qwidget_qws.cpp @@ -565,20 +565,6 @@ void QWidget::activateWindow() } } -/* - Should we require that q is a toplevel window ??? - - Used by QWSManager - */ -void QWidgetPrivate::blitToScreen(const QRegion &globalrgn) -{ - Q_Q(QWidget); - QWidget *win = q->window(); - QBrush bgBrush = win->palette().brush(win->backgroundRole()); - bool opaque = bgBrush.style() == Qt::NoBrush || bgBrush.isOpaque(); - QWidget::qwsDisplay()->repaintRegion(win->data->winid, win->windowFlags(), opaque, globalrgn); -} - void QWidgetPrivate::show_sys() { Q_Q(QWidget); @@ -1037,6 +1023,9 @@ void QWidgetPrivate::deleteSysExtra() void QWidgetPrivate::createTLSysExtra() { +#ifndef QT_NO_QWS_MANAGER + extra->topextra->qwsManager = 0; +#endif } void QWidgetPrivate::deleteTLSysExtra() diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index ffbb341..0f341fd 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -56,19 +56,13 @@ #include "private/qbackingstore_p.h" #include "private/qwindowsurface_raster_p.h" -#ifndef QT_NO_DIRECT3D -#include "private/qpaintengine_d3d_p.h" -#include "private/qwindowsurface_d3d_p.h" -#endif - - #include <qdebug.h> #include <private/qapplication_p.h> #include <private/qwininputcontext_p.h> #include <private/qpaintengine_raster_p.h> -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) #include "qguifunctions_wince.h" QT_USE_NAMESPACE extern void qt_wince_maximize(QWidget *widget); //defined in qguifunctions_wince.cpp @@ -257,7 +251,7 @@ extern "C" LRESULT CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM); QWidget member functions *****************************************************************************/ -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow) { Q_Q(QWidget); @@ -546,7 +540,7 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO } } -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE void QWidget::destroy(bool destroyWindow, bool destroySubWindows) @@ -574,7 +568,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) if (destroyWindow && !(windowType() == Qt::Desktop) && internalWinId()) { DestroyWindow(internalWinId()); } -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (destroyWindow && (windowType() == Qt::Desktop) && !GetDesktopWindow()) { DestroyWindow(internalWinId()); } @@ -681,7 +675,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED); } -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE // Show borderless toplevel windows in tasklist & NavBar if (!parent) { QString txt = q->windowTitle().isEmpty()?qAppName():q->windowTitle(); @@ -877,12 +871,12 @@ QCursor *qt_grab_cursor() } // The procedure does nothing, but is required for mousegrabbing to work -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE LRESULT CALLBACK qJournalRecordProc(int nCode, WPARAM wParam, LPARAM lParam) { return CallNextHookEx(journalRec, nCode, wParam, lParam); } -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE /* Works only as long as pointer is inside the application's window, which is good enough for QDockWidget. @@ -905,7 +899,7 @@ void QWidgetPrivate::grabMouseWhileInWindow() } } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE void QWidget::grabMouse() { if (!qt_nograb()) { @@ -982,7 +976,7 @@ void QWidget::activateWindow() SetForegroundWindow(window()->internalWinId()); } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE void QWidget::setWindowState(Qt::WindowStates newstate) { Q_D(QWidget); @@ -1031,13 +1025,13 @@ void QWidget::setWindowState(Qt::WindowStates newstate) if (newstate & Qt::WindowFullScreen) { if (d->topData()->normalGeometry.width() < 0 && !(oldstate & Qt::WindowMaximized)) d->topData()->normalGeometry = geometry(); - d->topData()->savedFlags = GetWindowLongA(internalWinId(), GWL_STYLE); + d->topData()->savedFlags = Qt::WindowFlags(GetWindowLongA(internalWinId(), GWL_STYLE)); #ifndef Q_FLATTEN_EXPOSE UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP; #else UINT style = WS_POPUP; #endif - if (d->topData()->savedFlags & WS_SYSMENU) + if (ulong(d->topData()->savedFlags) & WS_SYSMENU) style |= WS_SYSMENU; if (isVisible()) style |= WS_VISIBLE; @@ -1084,7 +1078,7 @@ void QWidget::setWindowState(Qt::WindowStates newstate) QWindowStateChangeEvent e(oldstate); QApplication::sendEvent(this, &e); } -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE /* @@ -1097,7 +1091,7 @@ void QWidgetPrivate::hide_sys() Q_Q(QWidget); deactivateWidgetCleanup(); Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (!qt_wince_is_mobile() && q->isFullScreen()) { HWND handle = FindWindow(L"HHTaskBar", L""); if (handle) { @@ -1126,7 +1120,7 @@ void QWidgetPrivate::hide_sys() \internal Platform-specific part of QWidget::show(). */ -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE void QWidgetPrivate::show_sys() { Q_Q(QWidget); @@ -1194,7 +1188,7 @@ void QWidgetPrivate::show_sys() invalidateBuffer(q->rect()); } -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE void QWidgetPrivate::setFocus_sys() { @@ -1240,7 +1234,7 @@ void QWidgetPrivate::stackUnder_sys(QWidget* w) (In all comments below: s/X/Windows/g) */ -void QWidgetPrivate::setWSGeometry(bool dontShow) +void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &) { Q_Q(QWidget); Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); @@ -1452,7 +1446,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) show_sys(); } else if (!q->testAttribute(Qt::WA_DontShowOnScreen)) { q->setAttribute(Qt::WA_OutsideWSRange, false); -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE // If the window is hidden and in maximized state or minimized, instead of moving the // window, set the normal position of the window. WINDOWPLACEMENT wndpl; @@ -1571,7 +1565,7 @@ bool QWidgetPrivate::shouldShowMaximizeButton() void QWidgetPrivate::winUpdateIsOpaque() { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE Q_Q(QWidget); if (!q->isWindow() || !q->testAttribute(Qt::WA_TranslucentBackground)) @@ -1592,7 +1586,7 @@ void QWidgetPrivate::winUpdateIsOpaque() void QWidgetPrivate::setConstraints_sys() { -#ifndef Q_OS_WINCE_WM +#ifndef Q_WS_WINCE_WM Q_Q(QWidget); if (q->isWindow() && q->testAttribute(Qt::WA_WState_Created)) { int style = GetWindowLongA(q->internalWinId(), GWL_STYLE); @@ -1710,28 +1704,24 @@ int QWidget::metric(PaintDeviceMetric m) const return val; } -#ifndef Q_OS_WINCE void QWidgetPrivate::createSysExtra() { #ifndef QT_NO_DRAGANDDROP extra->dropTarget = 0; #endif -#ifndef QT_NO_DIRECT3D - extra->had_auto_fill_bg = 0; - extra->had_paint_on_screen = 0; - extra->had_no_system_bg = 0; -#endif } +#ifndef Q_WS_WINCE void QWidgetPrivate::deleteSysExtra() { } -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE void QWidgetPrivate::createTLSysExtra() { - extra->topextra->winIconSmall = 0; + extra->topextra->savedFlags = 0; extra->topextra->winIconBig = 0; + extra->topextra->winIconSmall = 0; } void QWidgetPrivate::deleteTLSysExtra() @@ -1877,7 +1867,7 @@ void QWidgetPrivate::updateFrameStrut() GetWindowLongA(q->internalWinId(), GWL_EXSTYLE)); uint style = QT_WA_INLINE(GetWindowLongW(q->internalWinId(), GWL_STYLE), GetWindowLongA(q->internalWinId(), GWL_STYLE)); -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE if (AdjustWindowRectEx(&rect, style & ~(WS_OVERLAPPED), FALSE, exstyle)) { #else if (AdjustWindowRectEx(&rect, style, FALSE, exstyle)) { @@ -1887,7 +1877,7 @@ void QWidgetPrivate::updateFrameStrut() } } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE void QWidgetPrivate::setWindowOpacity_sys(qreal level) { Q_Q(QWidget); @@ -1922,7 +1912,7 @@ void QWidgetPrivate::setWindowOpacity_sys(qreal level) } (*ptrSetLayeredWindowAttributes)(q->internalWinId(), 0, (int)(level * 255), Q_LWA_ALPHA); } -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE // class QGlobalRasterPaintEngine: public QRasterPaintEngine // { @@ -1931,22 +1921,6 @@ void QWidgetPrivate::setWindowOpacity_sys(qreal level) // }; // Q_GLOBAL_STATIC(QGlobalRasterPaintEngine, globalRasterPaintEngine) -#ifndef QT_NO_DIRECT3D -static void cleanup_d3d_engine(); -Q_GLOBAL_STATIC_WITH_INITIALIZER(QDirect3DPaintEngine, _qt_d3dEngine, - { - qAddPostRoutine(cleanup_d3d_engine); - }) -static void cleanup_d3d_engine() -{ - _qt_d3dEngine()->cleanup(); -} -QDirect3DPaintEngine* qt_d3dEngine() -{ - return _qt_d3dEngine(); -} -#endif - #ifndef QT_NO_DIRECTDRAW static uchar *qt_primary_surface_bits; @@ -2059,19 +2033,6 @@ void qt_win_initialize_directdraw() { } QPaintEngine *QWidget::paintEngine() const { -#ifndef QT_NO_DIRECT3D - if ((qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault) - || testAttribute(Qt::WA_MSWindowsUseDirect3D)) - && qt_d3dEngine()->hasDirect3DSupport()) - { - QDirect3DPaintEngine *engine = qt_d3dEngine(); - if (qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault)) - engine->setFlushOnEnd(false); - else - engine->setFlushOnEnd(true); - return engine; - } -#endif #ifndef QT_NO_DIRECTDRAW QOnScreenRasterPaintEngine *pe = onScreenPaintEngine(); pe->widget = this; @@ -2100,13 +2061,6 @@ QPaintEngine *QWidget::paintEngine() const QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys() { Q_Q(QWidget); -#ifndef QT_NO_DIRECT3D - extern QDirect3DPaintEngine *qt_d3dEngine(); - if (qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault) && (q->windowOpacity() == 1.0f) - && qt_d3dEngine()->hasDirect3DSupport()) { - return new QD3DWindowSurface(q); - } -#endif return new QRasterWindowSurface(q); } @@ -2119,6 +2073,6 @@ void QWidgetPrivate::setModal_sys() QT_END_NAMESPACE -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE # include "qwidget_wince.cpp" #endif diff --git a/src/gui/kernel/qwidget_wince.cpp b/src/gui/kernel/qwidget_wince.cpp index bb9681e..435fd31 100644 --- a/src/gui/kernel/qwidget_wince.cpp +++ b/src/gui/kernel/qwidget_wince.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #include "qguifunctions_wince.h" @@ -211,7 +211,7 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO style |= WS_SYSMENU; if (flags & Qt::WindowContextHelpButtonHint) exsty |= WS_EX_CONTEXTHELP; -#ifndef Q_OS_WINCE_WM +#ifndef Q_WS_WINCE_WM if (flags & Qt::WindowMinimizeButtonHint) style |= WS_MINIMIZEBOX; if (shouldShowMaximizeButton()) @@ -414,7 +414,7 @@ void QWidgetPrivate::show_sys() { int sm = SW_SHOW; bool fakedMaximize = false; if (q->isWindow()) { -#ifndef Q_OS_WINCE_WM +#ifndef Q_WS_WINCE_WM if (q->isMinimized()) { sm = SW_SHOWMINIMIZED; } else if (q->isMaximized()) { @@ -450,7 +450,7 @@ void QWidgetPrivate::show_sys() { if (q->isMaximized() && q->isWindow()) qt_wince_maximize(q); -#ifndef Q_OS_WINCE_WM +#ifndef Q_WS_WINCE_WM if (!qt_wince_is_mobile() && q->isFullScreen()) { HWND handle = FindWindow(L"HHTaskBar", L""); if (handle) { @@ -535,7 +535,7 @@ void QWidget::setWindowState(Qt::WindowStates newstate) if (newstate & Qt::WindowFullScreen) { if (d->topData()->normalGeometry.width() < 0 && !(oldstate & Qt::WindowMaximized)) d->topData()->normalGeometry = geometry(); - d->topData()->savedFlags = GetWindowLongA(internalWinId(), GWL_STYLE); + d->topData()->savedFlags = (Qt::WindowFlags) GetWindowLongA(internalWinId(), GWL_STYLE); UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP; if (isVisible()) style |= WS_VISIBLE; @@ -583,7 +583,7 @@ void QWidget::setWindowState(Qt::WindowStates newstate) } if ((newstate & Qt::WindowMaximized) && !(newstate & Qt::WindowFullScreen)) { QRect r = d->topData()->normalGeometry; -#ifdef Q_OS_WINCE_WM +#ifdef Q_WS_WINCE_WM if (!inherits("QDialog") && !inherits("QMdiArea") && !isVisible()) { d->data.crect.setRect(0, 0, -1, -1); } @@ -598,13 +598,6 @@ void QWidget::setWindowState(Qt::WindowStates newstate) QApplication::sendEvent(this, &e); } - -void QWidgetPrivate::createSysExtra() { -#ifndef QT_NO_DRAGANDDROP - extra->dropTarget = 0; -#endif -} - void QWidgetPrivate::deleteSysExtra() { Q_Q(QWidget); @@ -704,4 +697,4 @@ void QWidget::show() QT_END_NAMESPACE -#endif Q_OS_WINCE +#endif // Q_WS_WINCE diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index 6202b35..7e41ea1 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -893,19 +893,70 @@ void QWidgetPrivate::x11UpdateIsOpaque() int screen = xinfo.screen(); if (topLevel && X11->use_xrender && X11->argbVisuals[screen] && xinfo.depth() != 32) { - // recreate widget - QPoint pos = q->pos(); - bool visible = q->isVisible(); - if (visible) - q->hide(); - q->setParent(q->parentWidget(), q->windowFlags()); - q->move(pos); - if (visible) - q->show(); + + if (q->inherits("QGLWidget")) { + // We send QGLWidgets a ParentChange event which causes them to + // recreate their GL context, which in turn causes them to choose + // their visual again. Now that WA_TranslucentBackground is set, + // QGLContext::chooseVisual will select an ARGB visual. + QEvent e(QEvent::ParentChange); + QApplication::sendEvent(q, &e); + } + else { + // For regular widgets, reparent them with their parent which + // also triggers a recreation of the native window + QPoint pos = q->pos(); + bool visible = q->isVisible(); + if (visible) + q->hide(); + + q->setParent(q->parentWidget(), q->windowFlags()); + q->move(pos); + if (visible) + q->show(); + } } #endif } +/* + Returns true if the background is inherited; otherwise returns + false. + + Mainly used in the paintOnScreen case. +*/ +bool QWidgetPrivate::isBackgroundInherited() const +{ + Q_Q(const QWidget); + + // windows do not inherit their background + if (q->isWindow() || q->windowType() == Qt::SubWindow) + return false; + + if (q->testAttribute(Qt::WA_NoSystemBackground) || q->testAttribute(Qt::WA_OpaquePaintEvent)) + return false; + + const QPalette &pal = q->palette(); + QPalette::ColorRole bg = q->backgroundRole(); + QBrush brush = pal.brush(bg); + + // non opaque brushes leaves us no choice, we must inherit + if (!q->autoFillBackground() || !brush.isOpaque()) + return true; + + if (brush.style() == Qt::SolidPattern) { + // the background is just a solid color. If there is no + // propagated contents, then we claim as performance + // optimization that it was not inheritet. This is the normal + // case in standard Windows or Motif style. + const QWidget *w = q->parentWidget(); + if (!w->d_func()->isBackgroundInherited()) + return false; + } + + return true; +} + void QWidget::destroy(bool destroyWindow, bool destroySubWindows) { Q_D(QWidget); @@ -2152,7 +2203,7 @@ static void do_size_hints(QWidget* widget, QWExtra *x) parentWRect is the geometry of the parent's X rect, measured in parent's coord sys */ -void QWidgetPrivate::setWSGeometry(bool dontShow) +void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &) { Q_Q(QWidget); Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); @@ -2610,8 +2661,8 @@ int QWidget::metric(PaintDeviceMetric m) const void QWidgetPrivate::createSysExtra() { - extra->xDndProxy = 0; extra->compress_events = true; + extra->xDndProxy = 0; } void QWidgetPrivate::deleteSysExtra() @@ -2620,8 +2671,11 @@ void QWidgetPrivate::deleteSysExtra() void QWidgetPrivate::createTLSysExtra() { + extra->topextra->spont_unmapped = 0; + extra->topextra->dnd = 0; extra->topextra->validWMState = 0; extra->topextra->waitingForMapNotify = 0; + extra->topextra->parentWinId = 0; extra->topextra->userTimeWindow = 0; } diff --git a/src/gui/kernel/qwindowdefs_win.h b/src/gui/kernel/qwindowdefs_win.h index 3899c23..a24afd4 100644 --- a/src/gui/kernel/qwindowdefs_win.h +++ b/src/gui/kernel/qwindowdefs_win.h @@ -122,7 +122,7 @@ QT_BEGIN_NAMESPACE Q_CORE_EXPORT HINSTANCE qWinAppInst(); Q_CORE_EXPORT HINSTANCE qWinAppPrevInst(); -Q_GUI_EXPORT int qWinAppCmdShow(); +Q_CORE_EXPORT int qWinAppCmdShow(); Q_GUI_EXPORT HDC qt_win_display_dc(); QT_END_NAMESPACE diff --git a/src/gui/kernel/qx11embed_x11.cpp b/src/gui/kernel/qx11embed_x11.cpp index 6329135..ae93efe 100644 --- a/src/gui/kernel/qx11embed_x11.cpp +++ b/src/gui/kernel/qx11embed_x11.cpp @@ -1297,9 +1297,6 @@ bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event) // focus is set to our focus proxy. We want to intercept all // keypresses. if (o == window() && d->client) { - if (!d->isEmbedded() && d->activeContainer == this) - d->moveInputToProxy(); - if (d->clientIsXEmbed) { sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE); } else { @@ -1307,6 +1304,8 @@ bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event) if (hasFocus()) XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time()); } + if (!d->isEmbedded()) + d->moveInputToProxy(); } break; case QEvent::WindowDeactivate: @@ -1729,10 +1728,10 @@ void QX11EmbedContainerPrivate::acceptClient(WId window) checkGrab(); if (q->hasFocus()) { XSetInputFocus(q->x11Info().display(), client, XRevertToParent, x11Time()); - } else { - if (!isEmbedded()) - moveInputToProxy(); } + } else { + if (!isEmbedded()) + moveInputToProxy(); } emit q->clientIsEmbedded(); @@ -1749,11 +1748,9 @@ void QX11EmbedContainerPrivate::acceptClient(WId window) void QX11EmbedContainerPrivate::moveInputToProxy() { Q_Q(QX11EmbedContainer); - WId focus; - int revert_to; - XGetInputFocus(q->x11Info().display(), &focus, &revert_to); - if (focus != focusProxy->internalWinId()) - XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, x11Time()); + // Following Owen Taylor's advice from the XEmbed specification to + // always use CurrentTime when no explicit user action is involved. + XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, CurrentTime); } /*! \internal diff --git a/src/gui/mac/images/leopard-unified-toolbar-on.png b/src/gui/mac/images/leopard-unified-toolbar-on.png Binary files differnew file mode 100644 index 0000000..6716597 --- /dev/null +++ b/src/gui/mac/images/leopard-unified-toolbar-on.png diff --git a/src/gui/mac/maccursors.qrc b/src/gui/mac/macresources.qrc index d80a63b..9696002 100644 --- a/src/gui/mac/maccursors.qrc +++ b/src/gui/mac/macresources.qrc @@ -6,4 +6,7 @@ <file>images/waitcursor.png</file> <file>images/pluscursor.png</file> </qresource> +<qresource prefix="/trolltech/mac/style"> +<file>images/leopard-unified-toolbar-on.png</file> +</qresource> </RCC> diff --git a/src/gui/math3d/math3d.pri b/src/gui/math3d/math3d.pri new file mode 100644 index 0000000..e4dd53a --- /dev/null +++ b/src/gui/math3d/math3d.pri @@ -0,0 +1,15 @@ +HEADERS += \ + math3d/qgenericmatrix.h \ + math3d/qmatrix4x4.h \ + math3d/qquaternion.h \ + math3d/qvector2d.h \ + math3d/qvector3d.h \ + math3d/qvector4d.h + +SOURCES += \ + math3d/qgenericmatrix.cpp \ + math3d/qmatrix4x4.cpp \ + math3d/qquaternion.cpp \ + math3d/qvector2d.cpp \ + math3d/qvector3d.cpp \ + math3d/qvector4d.cpp diff --git a/src/gui/math3d/qgenericmatrix.cpp b/src/gui/math3d/qgenericmatrix.cpp new file mode 100644 index 0000000..a77ca42 --- /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 intended + 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 intended 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 intended 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..d0b22de --- /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 <QtCore/qmetatype.h> +#include <QtCore/qdebug.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 T(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] = T(m[col][row]); +} + +// Define aliases for the useful variants of QGenericMatrix. +typedef QGenericMatrix<2, 2, qreal, float> QMatrix2x2; +typedef QGenericMatrix<2, 3, qreal, float> QMatrix2x3; +typedef QGenericMatrix<2, 4, qreal, float> QMatrix2x4; +typedef QGenericMatrix<3, 2, qreal, float> QMatrix3x2; +typedef QGenericMatrix<3, 3, qreal, float> QMatrix3x3; +typedef QGenericMatrix<3, 4, qreal, float> QMatrix3x4; +typedef QGenericMatrix<4, 2, qreal, float> QMatrix4x2; +typedef QGenericMatrix<4, 3, qreal, float> 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/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp new file mode 100644 index 0000000..a8dabf3 --- /dev/null +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -0,0 +1,1670 @@ +/**************************************************************************** +** +** 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 <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, float>& 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, float> 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, float>& 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, float> 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 float *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 float& 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 float matrixDet3 + (const float 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 float matrixDet4(const float m[4][4]) +{ + float 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 qreal(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. + + float det = matrixDet4(m); + if (det == 0.0f) { + if (invertible) + *invertible = false; + return QMatrix4x4(); + } + det = 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] = 1.0f / m[0][0]; + inv.data()[4] = 1.0f / m[1][1]; + inv.data()[8] = 1.0f / m[2][2]; + return inv; + } + + float det = matrixDet3(m, 0, 1, 2, 0, 1, 2); + if (det == 0.0f) + return inv; + det = 1.0f / det; + + float *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) +{ + m[0][0] /= divisor; + m[0][1] /= divisor; + m[0][2] /= divisor; + m[0][3] /= divisor; + m[1][0] /= divisor; + m[1][1] /= divisor; + m[1][2] /= divisor; + m[1][3] /= divisor; + m[2][0] /= divisor; + m[2][1] /= divisor; + m[2][2] /= divisor; + m[2][3] /= divisor; + m[3][0] /= divisor; + m[3][1] /= divisor; + m[3][2] /= divisor; + m[3][3] /= divisor; + 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. + m.m[0][0] = matrix.m[0][0] / divisor; + m.m[0][1] = matrix.m[0][1] / divisor; + m.m[0][2] = matrix.m[0][2] / divisor; + m.m[0][3] = matrix.m[0][3] / divisor; + m.m[1][0] = matrix.m[1][0] / divisor; + m.m[1][1] = matrix.m[1][1] / divisor; + m.m[1][2] = matrix.m[1][2] / divisor; + m.m[1][3] = matrix.m[1][3] / divisor; + m.m[2][0] = matrix.m[2][0] / divisor; + m.m[2][1] = matrix.m[2][1] / divisor; + m.m[2][2] = matrix.m[2][2] / divisor; + m.m[2][3] = matrix.m[2][3] / divisor; + m.m[3][0] = matrix.m[3][0] / divisor; + m.m[3][1] = matrix.m[3][1] / divisor; + m.m[3][2] = matrix.m[3][2] / divisor; + m.m[3][3] = matrix.m[3][3] / divisor; + 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) +{ + float vx = vector.xp; + float vy = vector.yp; + float 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) +{ + float vx(x); + float vy(y); + float 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) +{ + if (flagBits == Identity) { + m[0][0] = factor; + m[1][1] = factor; + m[2][2] = factor; + flagBits = Scale; + } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[0][0] *= factor; + m[1][1] *= factor; + m[2][2] *= factor; + } else if (flagBits == Translation) { + m[0][0] = factor; + m[1][1] = factor; + m[2][2] = factor; + flagBits |= Scale; + } else { + m[0][0] *= factor; + m[0][1] *= factor; + m[0][2] *= factor; + m[0][3] *= factor; + m[1][0] *= factor; + m[1][1] *= factor; + m[1][2] *= factor; + m[1][3] *= factor; + m[2][0] *= factor; + m[2][1] *= factor; + m[2][2] *= factor; + m[2][3] *= factor; + 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) +{ + float vx = vector.xp; + float vy = vector.yp; + float 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) +{ + float vx(x); + float vy(y); + float 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 + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#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. + qreal a = angle * M_PI / 180.0f; + qreal c = qCos(a); + qreal s = qSin(a); + qreal ic; + 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) { + qreal len = x * x + y * y + z * z; + if (!qFuzzyIsNull(len - 1.0f) && !qFuzzyIsNull(len)) { + len = qSqrt(len); + x /= len; + y /= len; + z /= len; + } + ic = 1.0f - c; + m.m[0][0] = x * x * ic + c; + m.m[1][0] = x * y * ic - z * s; + m.m[2][0] = x * z * ic + y * s; + m.m[3][0] = 0.0f; + m.m[0][1] = y * x * ic + z * s; + m.m[1][1] = y * y * ic + c; + m.m[2][1] = y * z * ic - x * s; + m.m[3][1] = 0.0f; + m.m[0][2] = x * z * ic - y * s; + m.m[1][2] = y * z * ic + x * s; + m.m[2][2] = z * z * 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); + float xx = quaternion.xp * quaternion.xp; + float xy = quaternion.xp * quaternion.yp; + float xz = quaternion.xp * quaternion.zp; + float xw = quaternion.xp * quaternion.wp; + float yy = quaternion.yp * quaternion.yp; + float yz = quaternion.yp * quaternion.zp; + float yw = quaternion.yp * quaternion.wp; + float zz = quaternion.zp * quaternion.zp; + float 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 + (-(left + right) / width, + -(top + bottom) / invheight, + 0.0f, 1)); + scale(QVector3D + (2.0f / width, + 2.0f / invheight, + -1.0f, 1)); + return *this; + } +#endif + QMatrix4x4 m(1); + m.m[0][0] = 2.0f / width; + m.m[1][0] = 0.0f; + m.m[2][0] = 0.0f; + m.m[3][0] = -(left + right) / width; + m.m[0][1] = 0.0f; + m.m[1][1] = 2.0f / invheight; + m.m[2][1] = 0.0f; + m.m[3][1] = -(top + bottom) / invheight; + m.m[0][2] = 0.0f; + m.m[1][2] = 0.0f; + m.m[2][2] = -2.0f / clip; + m.m[3][2] = -(nearPlane + farPlane) / clip; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = 0.0f; + m.m[3][3] = 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] = 2.0f * nearPlane / width; + m.m[1][0] = 0.0f; + m.m[2][0] = (left + right) / width; + m.m[3][0] = 0.0f; + m.m[0][1] = 0.0f; + m.m[1][1] = 2.0f * nearPlane / invheight; + m.m[2][1] = (top + bottom) / invheight; + m.m[3][1] = 0.0f; + m.m[0][2] = 0.0f; + m.m[1][2] = 0.0f; + m.m[2][2] = -(nearPlane + farPlane) / clip; + m.m[3][2] = -2.0f * nearPlane * farPlane / clip; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = -1.0f; + m.m[3][3] = 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] = cotan / aspect; + m.m[1][0] = 0.0f; + m.m[2][0] = 0.0f; + m.m[3][0] = 0.0f; + m.m[0][1] = 0.0f; + m.m[1][1] = cotan; + m.m[2][1] = 0.0f; + m.m[3][1] = 0.0f; + m.m[0][2] = 0.0f; + m.m[1][2] = 0.0f; + m.m[2][2] = -(nearPlane + farPlane) / clip; + m.m[3][2] = -(2.0f * nearPlane * farPlane) / clip; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = -1.0f; + m.m[3][3] = 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] = qreal(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(qreal(m[0][0]), qreal(m[0][1]), + qreal(m[1][0]), qreal(m[1][1]), + qreal(m[3][0]), qreal(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(qreal(m[0][0]), qreal(m[0][1]), qreal(m[0][3]), + qreal(m[1][0]), qreal(m[1][1]), qreal(m[1][3]), + qreal(m[3][0]), qreal(m[3][1]), qreal(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 float *QMatrix4x4::data() + + Returns a pointer to the raw data of this matrix. This is intended + for use with raw GL functions. + + \sa constData(), inferSpecialType() +*/ + +/*! + \fn const float *QMatrix4x4::data() const + + Returns a constant pointer to the raw data of this matrix. + This is intended for use with raw GL functions. + + \sa constData() +*/ + +/*! + \fn const float *QMatrix4x4::constData() const + + Returns a constant pointer to the raw data of this matrix. + This is intended 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 + float tr = m[0][0] + m[1][1] + m[2][2]; + qreal cosa = qreal(0.5f * (tr - 1.0f)); + angle = acos(cosa) * 180.0f / M_PI; + + // Any axis will work if r is zero (means no rotation) + if (qFuzzyIsNull(angle)) { + 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 + float tmp; + + // r00 is maximum + if ((m[0][0] >= m[2][2]) && (m[0][0] >= m[1][1])) { + axis.xp = 0.5f * qSqrt(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 * qSqrt(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 * qSqrt(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..2b485c1 --- /dev/null +++ b/src/gui/math3d/qmatrix4x4.h @@ -0,0 +1,977 @@ +/**************************************************************************** +** +** 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, float>& matrix); +#endif + QMatrix4x4(const float *values, int cols, int rows); + QMatrix4x4(const QTransform& transform); + QMatrix4x4(const QMatrix& matrix); + + inline qreal operator()(int row, int column) const; + inline float& 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, float> toGenericMatrix() const; +#endif + + inline float *data(); + inline const float *data() const { return m[0]; } + inline const float *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: + float 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, float>& matrix) +{ + const float *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, float> QMatrix4x4::toGenericMatrix() const +{ + QGenericMatrix<N, M, qreal, float> result; + float *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 qreal(m[column][row]); +} + +inline float& 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) +{ + m[0][0] *= factor; + m[0][1] *= factor; + m[0][2] *= factor; + m[0][3] *= factor; + m[1][0] *= factor; + m[1][1] *= factor; + m[1][2] *= factor; + m[1][3] *= factor; + m[2][0] *= factor; + m[2][1] *= factor; + m[2][2] *= factor; + m[2][3] *= factor; + m[3][0] *= factor; + m[3][1] *= factor; + m[3][2] *= factor; + m[3][3] *= factor; + 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) +{ + float 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) +{ + float 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) +{ + float 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) +{ + float 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) +{ + float xin, yin; + float 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) +{ + float xin, yin; + float 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(qreal(x), qreal(y)); + } else { + return QPointF(qreal(x / w), qreal(y / w)); + } +} + +inline QPoint operator*(const QMatrix4x4& matrix, const QPoint& point) +{ + float xin, yin; + float 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) +{ + float xin, yin; + float 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(qreal(x), qreal(y)); + } else { + return QPointF(qreal(x / w), qreal(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); + m.m[0][0] = matrix.m[0][0] * factor; + m.m[0][1] = matrix.m[0][1] * factor; + m.m[0][2] = matrix.m[0][2] * factor; + m.m[0][3] = matrix.m[0][3] * factor; + m.m[1][0] = matrix.m[1][0] * factor; + m.m[1][1] = matrix.m[1][1] * factor; + m.m[1][2] = matrix.m[1][2] * factor; + m.m[1][3] = matrix.m[1][3] * factor; + m.m[2][0] = matrix.m[2][0] * factor; + m.m[2][1] = matrix.m[2][1] * factor; + m.m[2][2] = matrix.m[2][2] * factor; + m.m[2][3] = matrix.m[2][3] * factor; + m.m[3][0] = matrix.m[3][0] * factor; + m.m[3][1] = matrix.m[3][1] * factor; + m.m[3][2] = matrix.m[3][2] * factor; + m.m[3][3] = matrix.m[3][3] * factor; + return m; +} + +inline QMatrix4x4 operator*(const QMatrix4x4& matrix, qreal factor) +{ + QMatrix4x4 m(1); + m.m[0][0] = matrix.m[0][0] * factor; + m.m[0][1] = matrix.m[0][1] * factor; + m.m[0][2] = matrix.m[0][2] * factor; + m.m[0][3] = matrix.m[0][3] * factor; + m.m[1][0] = matrix.m[1][0] * factor; + m.m[1][1] = matrix.m[1][1] * factor; + m.m[1][2] = matrix.m[1][2] * factor; + m.m[1][3] = matrix.m[1][3] * factor; + m.m[2][0] = matrix.m[2][0] * factor; + m.m[2][1] = matrix.m[2][1] * factor; + m.m[2][2] = matrix.m[2][2] * factor; + m.m[2][3] = matrix.m[2][3] * factor; + m.m[3][0] = matrix.m[3][0] * factor; + m.m[3][1] = matrix.m[3][1] * factor; + m.m[3][2] = matrix.m[3][2] * factor; + m.m[3][3] = matrix.m[3][3] * factor; + 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 float *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, float>& matrix) +{ + return QMatrix4x4(matrix.constData(), N, M); +} + +template <int N, int M> +QGenericMatrix<N, M, qreal, float> qGenericMatrixFromMatrix4x4(const QMatrix4x4& matrix) +{ + QGenericMatrix<N, M, qreal, float> result; + const float *m = matrix.constData(); + float *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..96659ea --- /dev/null +++ b/src/gui/math3d/qquaternion.cpp @@ -0,0 +1,584 @@ +/**************************************************************************** +** +** 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 <QtCore/qmath.h> +#include <QtCore/qdebug.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. +*/ + +#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 qSqrt(xp * xp + yp * yp + zp * zp + wp * wp); +} + +/*! + Returns the squared length of the quaternion. + + \sa length() +*/ +qreal QQuaternion::lengthSquared() const +{ + return xp * xp + yp * yp + zp * zp + wp * wp; +} + +/*! + Returns the normalized unit form of this quaternion. + + If this quaternion is null, then a null quaternion is returned. + If the length of the quaternion is very close to 1, then the quaternion + will be returned as-is. Otherwise the normalized form of the + quaternion of length 1 will be returned. + + \sa length(), normalize() +*/ +QQuaternion QQuaternion::normalized() const +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / qSqrt(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 or the length of the quaternion is very close to 1. + + \sa length(), normalized() +*/ +void QQuaternion::normalize() +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + 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 M_PI +#define M_PI 3.14159265358979323846 +#endif + +#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. + qreal a = (angle / 2.0f) * M_PI / 180.0f; + qreal s = qSin(a); + qreal c = qCos(a); + QVector3D ax = axis.normalized(); + 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) +{ + float xp = x; + float yp = y; + float zp = z; + qreal length = qSqrt(xp * xp + yp * yp + zp * zp); + if (!qIsNull(length)) { + xp /= length; + yp /= length; + zp /= length; + } + qreal a = (angle / 2.0f) * M_PI / 180.0f; + qreal s = qSin(a); + qreal c = qCos(a); + 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. + + \sa nlerp() +*/ +QQuaternion QQuaternion::slerp + (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 = q1.xp * q2.xp + q1.yp * q2.yp + q1.zp * q2.zp + 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; +} + +/*! + Interpolates along the shortest linear path between the rotational + positions \a q1 and \a q2. The value \a t should be between 0 and 1, + indicating the distance to travel between \a q1 and \a q2. + The result will be normalized(). + + 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. + + The nlerp() function is typically faster than slerp() and will + give approximate results to spherical interpolation that are + good enough for some applications. + + \sa slerp() +*/ +QQuaternion QQuaternion::nlerp + (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 = q1.xp * q2.xp + q1.yp * q2.yp + q1.zp * q2.zp + q1.wp * q2.wp; + if (dot >= 0.0f) + q2b = q2; + else + q2b = -q2; + + // Perform the linear interpolation. + return (q1 * (1.0f - t) + q2b * t).normalized(); +} + +#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..c05c641 --- /dev/null +++ b/src/gui/math3d/qquaternion.h @@ -0,0 +1,337 @@ +/**************************************************************************** +** +** 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); +#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 slerp + (const QQuaternion& q1, const QQuaternion& q2, qreal t); + static QQuaternion nlerp + (const QQuaternion& q1, const QQuaternion& q2, qreal t); + +private: + float wp, xp, yp, zp; + + friend class QMatrix4x4; + + QQuaternion(float scalar, float xpos, float ypos, float 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(float scalar, float xpos, float ypos, float zpos, int) : 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 qreal(xp); } +inline qreal QQuaternion::y() const { return qreal(yp); } +inline qreal QQuaternion::z() const { return qreal(zp); } +inline qreal QQuaternion::scalar() const { return qreal(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) +{ + xp *= factor; + yp *= factor; + zp *= factor; + wp *= factor; + return *this; +} + +inline const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2) +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q53 + float x = q1.wp * q2.xp + + q1.xp * q2.wp + + q1.yp * q2.zp - + q1.zp * q2.yp; + float y = q1.wp * q2.yp + + q1.yp * q2.wp + + q1.zp * q2.xp - + q1.xp * q2.zp; + float z = q1.wp * q2.zp + + q1.zp * q2.wp + + q1.xp * q2.yp - + q1.yp * q2.xp; + float 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) +{ + xp /= divisor; + yp /= divisor; + zp /= divisor; + wp /= divisor; + 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) +{ + return QQuaternion(quaternion.wp * factor, quaternion.xp * factor, quaternion.yp * factor, quaternion.zp * factor, 1); +} + +inline const QQuaternion operator*(const QQuaternion &quaternion, qreal factor) +{ + return QQuaternion(quaternion.wp * factor, quaternion.xp * factor, quaternion.yp * factor, quaternion.zp * factor, 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) +{ + return QQuaternion(quaternion.wp / divisor, quaternion.xp / divisor, quaternion.yp / divisor, quaternion.zp / divisor, 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..c3aaa42 --- /dev/null +++ b/src/gui/math3d/qvector2d.cpp @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** 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 <QtCore/qdebug.h> +#include <QtCore/qmath.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(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 qSqrt(xp * xp + 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 xp * xp + yp * yp; +} + +/*! + Returns the normalized unit vector form of this vector. + + If this vector is null, then a null vector is returned. If the length + of the vector is very close to 1, then the vector will be returned as-is. + Otherwise the normalized form of the vector of length 1 will be returned. + + \sa length(), normalize() +*/ +QVector2D QVector2D::normalized() const +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / qSqrt(len); + else + return QVector2D(); +} + +/*! + Normalizes the currect vector in place. Nothing happens if this + vector is a null vector or the length of the vector is very close to 1. + + \sa length(), normalized() +*/ +void QVector2D::normalize() +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + 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 v1.xp * v2.xp + 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..b027df4 --- /dev/null +++ b/src/gui/math3d/qvector2d.h @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** 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 <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); + 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: + float xp, yp; + + QVector2D(float xpos, float ypos, int dummy); + + friend class QVector3D; + friend class QVector4D; +}; + +inline QVector2D::QVector2D() : xp(0.0f), yp(0.0f) {} + +inline QVector2D::QVector2D(float xpos, float ypos, int) : xp(xpos), yp(ypos) {} + +inline QVector2D::QVector2D(qreal xpos, qreal 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 qreal(xp); } +inline qreal QVector2D::y() const { return qreal(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) +{ + xp *= factor; + yp *= factor; + return *this; +} + +inline QVector2D &QVector2D::operator*=(const QVector2D &vector) +{ + xp *= vector.xp; + yp *= vector.yp; + return *this; +} + +inline QVector2D &QVector2D::operator/=(qreal divisor) +{ + xp /= divisor; + yp /= divisor; + 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) +{ + return QVector2D(vector.xp * factor, vector.yp * factor, 1); +} + +inline const QVector2D operator*(const QVector2D &vector, qreal factor) +{ + return QVector2D(vector.xp * factor, vector.yp * factor, 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) +{ + return QVector2D(vector.xp / divisor, vector.yp / divisor, 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(qreal(xp), qreal(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..c83cd60 --- /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 <QtCore/qmath.h> +#include <QtCore/qdebug.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(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 null, then a null vector is returned. If the length + of the vector is very close to 1, then the vector will be returned as-is. + Otherwise the normalized form of the vector of length 1 will be returned. + + \sa length(), normalize() +*/ +QVector3D QVector3D::normalized() const +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / qSqrt(len); + else + return QVector3D(); +} + +/*! + Normalizes the currect vector in place. Nothing happens if this + vector is a null vector or the length of the vector is very close to 1. + + \sa length(), normalized() +*/ +void QVector3D::normalize() +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + 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 v1.xp * v2.xp + v1.yp * v2.yp + 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 qSqrt(xp * xp + yp * yp + 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 xp * xp + yp * yp + 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..02873f2 --- /dev/null +++ b/src/gui/math3d/qvector3d.h @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** 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 <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); + 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: + float xp, yp, zp; + + QVector3D(float xpos, float ypos, float zpos, int dummy); + + friend class QVector2D; + friend class QVector4D; + friend class QQuaternion; + friend class QMatrix4x4; +#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(float xpos, float ypos, float zpos, int) : 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 qreal(xp); } +inline qreal QVector3D::y() const { return qreal(yp); } +inline qreal QVector3D::z() const { return qreal(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) +{ + xp *= factor; + yp *= factor; + zp *= factor; + 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) +{ + xp /= divisor; + yp /= divisor; + zp /= divisor; + 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) +{ + return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor, 1); +} + +inline const QVector3D operator*(const QVector3D &vector, qreal factor) +{ + return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor, 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) +{ + return QVector3D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor, 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(qreal(xp), qreal(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..010fa53 --- /dev/null +++ b/src/gui/math3d/qvector4d.cpp @@ -0,0 +1,518 @@ +/**************************************************************************** +** +** 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 <QtCore/qdebug.h> +#include <QtCore/qmath.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(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 qSqrt(xp * xp + yp * yp + zp * zp + 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 xp * xp + yp * yp + zp * zp + wp * wp; +} + +/*! + Returns the normalized unit vector form of this vector. + + If this vector is null, then a null vector is returned. If the length + of the vector is very close to 1, then the vector will be returned as-is. + Otherwise the normalized form of the vector of length 1 will be returned. + + \sa length(), normalize() +*/ +QVector4D QVector4D::normalized() const +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / qSqrt(len); + else + return QVector4D(); +} + +/*! + Normalizes the currect vector in place. Nothing happens if this + vector is a null vector or the length of the vector is very close to 1. + + \sa length(), normalized() +*/ +void QVector4D::normalize() +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + 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 v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp + 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..8e673f3 --- /dev/null +++ b/src/gui/math3d/qvector4d.h @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** 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 <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); + 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: + float xp, yp, zp, wp; + + QVector4D(float xpos, float ypos, float zpos, float wpos, int dummy); + + friend class QVector2D; + friend class QVector3D; + friend class QQuaternion; + friend class QMatrix4x4; +#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(float xpos, float ypos, float zpos, float wpos, int) : 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 qreal(xp); } +inline qreal QVector4D::y() const { return qreal(yp); } +inline qreal QVector4D::z() const { return qreal(zp); } +inline qreal QVector4D::w() const { return qreal(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) +{ + xp *= factor; + yp *= factor; + zp *= factor; + wp *= factor; + 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) +{ + xp /= divisor; + yp /= divisor; + zp /= divisor; + wp /= divisor; + 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) +{ + return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor, 1); +} + +inline const QVector4D operator*(const QVector4D &vector, qreal factor) +{ + return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor, 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) +{ + return QVector4D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor, vector.wp / divisor, 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(qreal(xp), qreal(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/painting.pri b/src/gui/painting/painting.pri index 528559c..34d1779 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -103,12 +103,6 @@ win32 { painting/qprinterinfo_win.cpp \ painting/qregion_win.cpp !win32-borland:!wince*:LIBS += -lmsimg32 - contains(QT_CONFIG, direct3d) { - HEADERS += painting/qpaintengine_d3d_p.h - SOURCES += painting/qpaintengine_d3d.cpp - RESOURCES += painting/qpaintengine_d3d.qrc - LIBS += -ldxguid - } } embedded { @@ -362,8 +356,4 @@ embedded { SOURCES += painting/qwindowsurface_qws.cpp } -win32:contains(QT_CONFIG, direct3d) { - HEADERS += painting/qwindowsurface_d3d_p.h - SOURCES += painting/qwindowsurface_d3d.cpp -} diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp index 8317dd8..7ed521e 100644 --- a/src/gui/painting/qbezier.cpp +++ b/src/gui/painting/qbezier.cpp @@ -127,13 +127,13 @@ static inline void flattenBezierWithoutInflections(QBezier &bez, qreal dy = bez.y2 - bez.y1; qreal normalized = qSqrt(dx * dx + dy * dy); - if (qFuzzyCompare(normalized + 1, 1)) + if (qFuzzyIsNull(normalized)) break; qreal d = qAbs(dx * (bez.y3 - bez.y2) - dy * (bez.x3 - bez.x2)); qreal t = qSqrt(4. / 3. * normalized * flatness / d); - if (t > 1 || qFuzzyCompare(t, (qreal)1.)) + if (t > 1 || qFuzzyIsNull(t - (qreal)1.)) break; bez.parameterSplitLeft(t, &left); p->append(bez.pt1()); @@ -144,19 +144,19 @@ static inline void flattenBezierWithoutInflections(QBezier &bez, static inline int quadraticRoots(qreal a, qreal b, qreal c, qreal *x1, qreal *x2) { - if (qFuzzyCompare(a + 1, 1)) { - if (qFuzzyCompare(b + 1, 1)) + if (qFuzzyIsNull(a)) { + if (qFuzzyIsNull(b)) return 0; *x1 = *x2 = (-c / b); return 1; } else { const qreal det = b * b - 4 * a * c; - if (qFuzzyCompare(det + 1, 1)) { + if (qFuzzyIsNull(det)) { *x1 = *x2 = -b / (2 * a); return 1; } if (det > 0) { - if (qFuzzyCompare(b + 1, 1)) { + if (qFuzzyIsNull(b)) { *x2 = qSqrt(-c / a); *x1 = -(*x2); return 2; @@ -187,7 +187,7 @@ static inline bool findInflections(qreal a, qreal b, qreal c, *t1 = r2; *t2 = r1; } - if (!qFuzzyCompare(a + 1, 1)) + if (!qFuzzyIsNull(a)) *tCups = 0.5 * (-b / a); else *tCups = 2; @@ -243,7 +243,7 @@ void QBezier::addToPolygonMixed(QPolygonF *polygon) const qreal b = 6 * (ay * cx - ax * cy); qreal c = 2 * (by * cx - bx * cy); - if ((qFuzzyCompare(a + 1, 1) && qFuzzyCompare(b + 1, 1)) || + if ((qFuzzyIsNull(a) && qFuzzyIsNull(b)) || (b * b - 4 * a *c) < 0) { QBezier bez(*this); flattenBezierWithoutInflections(bez, polygon); @@ -447,7 +447,7 @@ static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qr qreal r = 1.0 + prev_normal.x() * next_normal.x() + prev_normal.y() * next_normal.y(); - if (qFuzzyCompare(r + 1, 1)) { + if (qFuzzyIsNull(r)) { points_shifted[i] = points[i] + offset * prev_normal; } else { qreal k = offset / r; @@ -477,12 +477,12 @@ static bool addCircle(const QBezier *b, qreal offset, QBezier *o) normals[0] = QPointF(b->y2 - b->y1, b->x1 - b->x2); qreal dist = qSqrt(normals[0].x()*normals[0].x() + normals[0].y()*normals[0].y()); - if (qFuzzyCompare(dist + 1, 1)) + if (qFuzzyIsNull(dist)) return false; normals[0] /= dist; normals[2] = QPointF(b->y4 - b->y3, b->x3 - b->x4); dist = qSqrt(normals[2].x()*normals[2].x() + normals[2].y()*normals[2].y()); - if (qFuzzyCompare(dist + 1, 1)) + if (qFuzzyIsNull(dist)) return false; normals[2] /= dist; @@ -1022,7 +1022,7 @@ int QBezier::stationaryYPoints(qreal &t0, qreal &t1) const QList<qreal> result; - if (qFuzzyCompare(reciprocal + 1, 1)) { + if (qFuzzyIsNull(reciprocal)) { t0 = -b / (2 * a); return 1; } else if (reciprocal > 0) { diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp index 93f11e1..f83ff7b 100644 --- a/src/gui/painting/qblendfunctions.cpp +++ b/src/gui/painting/qblendfunctions.cpp @@ -104,6 +104,37 @@ struct Blend_RGB16_on_RGB16_ConstAlpha { quint32 m_ialpha; }; +struct Blend_ARGB24_on_RGB16_SourceAlpha { + inline void write(quint16 *dst, qargb8565 src) { + const uint alpha = src.alpha(); + if (alpha) { + quint16 s = qrgb565(src).rawValue(); + if (alpha < 255) + s += BYTE_MUL_RGB16(*dst, 255 - alpha); + *dst = s; + } + } +}; + +struct Blend_ARGB24_on_RGB16_SourceAndConstAlpha { + inline Blend_ARGB24_on_RGB16_SourceAndConstAlpha(quint32 alpha) { + m_alpha = (alpha * 255) >> 8; + } + + inline void write(quint16 *dst, qargb8565 src) { + src = src.byte_mul(src.alpha(m_alpha)); + const uint alpha = src.alpha(); + if (alpha) { + quint16 s = qrgb565(src).rawValue(); + if (alpha < 255) + s += BYTE_MUL_RGB16(*dst, 255 - alpha); + *dst = s; + } + } + + quint32 m_alpha; +}; + struct Blend_ARGB32_on_RGB16_SourceAlpha { inline void write(quint16 *dst, quint32 src) { const quint8 alpha = qAlpha(src); @@ -237,6 +268,32 @@ void qt_scale_image_rgb16_on_rgb16(uchar *destPixels, int dbpl, } } +void qt_scale_image_argb24_on_rgb16(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + int const_alpha) +{ +#ifdef QT_DEBUG_DRAW + printf("qt_scale_argb24_on_rgb16: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n", + destPixels, dbpl, srcPixels, sbpl, + targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(), + sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(), + const_alpha); +#endif + if (const_alpha == 256) { + Blend_ARGB24_on_RGB16_SourceAlpha noAlpha; + qt_scale_image_16bit<qargb8565>(destPixels, dbpl, srcPixels, sbpl, + targetRect, sourceRect, clip, noAlpha); + } else { + Blend_ARGB24_on_RGB16_SourceAndConstAlpha constAlpha(const_alpha); + qt_scale_image_16bit<qargb8565>(destPixels, dbpl, srcPixels, sbpl, + targetRect, sourceRect, clip, constAlpha); + } +} + + void qt_scale_image_argb32_on_rgb16(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, const QRectF &targetRect, @@ -513,11 +570,10 @@ static void qt_blend_argb32_on_argb32(uchar *destPixels, int dbpl, for (int y=0; y<h; ++y) { for (int x=0; x<w; ++x) { uint s = src[x]; - if ((s & 0xff000000) == 0xff000000) + if (s >= 0xff000000) dst[x] = s; - else { + else if (s != 0) dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); - } } dst = (quint32 *)(((uchar *) dst) + dbpl); src = (const quint32 *)(((const uchar *) src) + sbpl); @@ -875,7 +931,7 @@ SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats] = 0, // Format_ARGB32, qt_scale_image_argb32_on_rgb16, // Format_ARGB32_Premultiplied, qt_scale_image_rgb16_on_rgb16, // Format_RGB16, - 0, // Format_ARGB8565_Premultiplied, + qt_scale_image_argb24_on_rgb16, // Format_ARGB8565_Premultiplied, 0, // Format_RGB666, 0, // Format_ARGB6666_Premultiplied, 0, // Format_RGB555, diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index c50004e..b1fe3aa 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -1387,7 +1387,7 @@ QColor QColor::toHsv() const const qreal min = Q_MIN_3(r, g, b); const qreal delta = max - min; color.ct.ahsv.value = qRound(max * USHRT_MAX); - if (qFuzzyCompare(delta + 1, 1)) { + if (qFuzzyIsNull(delta)) { // achromatic case, hue is undefined color.ct.ahsv.hue = USHRT_MAX; color.ct.ahsv.saturation = 0; @@ -1441,7 +1441,7 @@ QColor QColor::toCmyk() const // cmy -> cmyk const qreal k = qMin(c, qMin(m, y)); - if (!qFuzzyCompare(k,1)) { + if (!qFuzzyIsNull(k - 1)) { c = (c - k) / (1.0 - k); m = (m - k) / (1.0 - k); y = (y - k) / (1.0 - k); @@ -2022,12 +2022,12 @@ QDebug operator<<(QDebug dbg, const QColor &c) if (!c.isValid()) dbg.nospace() << "QColor(Invalid)"; else if (c.spec() == QColor::Rgb) - dbg.nospace() << "QColor(ARGB " << c.alphaF() << ", " << c.redF() << ", " << c.greenF() << ", " << c.blueF() << ")"; + dbg.nospace() << "QColor(ARGB " << c.alphaF() << ", " << c.redF() << ", " << c.greenF() << ", " << c.blueF() << ')'; else if (c.spec() == QColor::Hsv) - dbg.nospace() << "QColor(AHSV " << c.alphaF() << ", " << c.hueF() << ", " << c.saturationF() << ", " << c.valueF() << ")"; + dbg.nospace() << "QColor(AHSV " << c.alphaF() << ", " << c.hueF() << ", " << c.saturationF() << ", " << c.valueF() << ')'; else if (c.spec() == QColor::Cmyk) dbg.nospace() << "QColor(ACMYK " << c.alphaF() << ", " << c.cyanF() << ", " << c.magentaF() << ", " << c.yellowF() << ", " - << c.blackF()<< ")"; + << c.blackF()<< ')'; return dbg.space(); #else diff --git a/src/gui/painting/qcolor_p.cpp b/src/gui/painting/qcolor_p.cpp index 5bdbee4..fb6d10c 100644 --- a/src/gui/painting/qcolor_p.cpp +++ b/src/gui/painting/qcolor_p.cpp @@ -49,7 +49,7 @@ #include "qrgb.h" #include "qstringlist.h" -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) #include "qguifunctions_wince.h" #endif QT_BEGIN_NAMESPACE diff --git a/src/gui/painting/qcolormap_win.cpp b/src/gui/painting/qcolormap_win.cpp index 7d36582..9ca2521 100644 --- a/src/gui/painting/qcolormap_win.cpp +++ b/src/gui/painting/qcolormap_win.cpp @@ -44,7 +44,7 @@ #include "qvector.h" #include "qt_windows.h" -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) #include "qguifunctions_wince.h" #endif diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 9f2831d..bbe1a76 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 quint32p *src = reinterpret_cast<const quint32p*>(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 }, }; @@ -3219,8 +2925,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); @@ -3266,28 +2971,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) { @@ -3365,45 +3049,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); @@ -3454,13 +3210,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; } @@ -4706,8 +4461,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; } @@ -4769,8 +4523,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, @@ -4785,8 +4538,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, @@ -4801,8 +4553,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, @@ -4817,8 +4568,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, @@ -4833,8 +4583,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, @@ -4849,8 +4598,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, @@ -4865,8 +4613,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, @@ -4881,8 +4628,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, @@ -4897,13 +4643,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); @@ -4957,13 +4701,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; } @@ -5019,8 +4762,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; } @@ -5090,8 +4832,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) @@ -5105,8 +4846,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) @@ -5120,8 +4860,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) @@ -5135,8 +4874,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) @@ -5150,8 +4888,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) @@ -5165,8 +4902,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) @@ -5180,8 +4916,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) @@ -5195,8 +4930,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) @@ -5210,19 +4944,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; } @@ -5401,8 +5133,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; } @@ -5605,8 +5336,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) @@ -5620,8 +5350,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) @@ -5635,8 +5364,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) @@ -5650,8 +5378,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, @@ -5666,8 +5393,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) @@ -5681,8 +5407,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) @@ -5696,8 +5421,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) @@ -5711,8 +5435,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) @@ -5726,18 +5449,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; } @@ -5923,13 +5644,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; } @@ -6051,8 +5771,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; } @@ -6201,8 +5920,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, @@ -6217,8 +5935,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, @@ -6233,8 +5950,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, @@ -6249,8 +5965,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, @@ -6265,8 +5980,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, @@ -6281,8 +5995,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, @@ -6297,8 +6010,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, @@ -6313,8 +6025,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, @@ -6329,18 +6040,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; } @@ -6474,8 +6183,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; } @@ -6625,8 +6333,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, @@ -6641,8 +6348,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, @@ -6657,8 +6363,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, @@ -6673,8 +6378,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, @@ -6689,8 +6393,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, @@ -6705,8 +6408,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, @@ -6721,8 +6423,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, @@ -6737,8 +6438,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, @@ -6753,36 +6453,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)); + blend_src_generic<RegularSpans>(count, spans, userData); } -#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)); \ -} - -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 */ @@ -7149,8 +6823,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); } } @@ -7198,8 +6871,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); } } @@ -7258,7 +6930,13 @@ static void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer, } void qt_build_pow_tables() { - qreal smoothing = 1.7; + qreal smoothing = qreal(1.7); + +#ifdef Q_WS_MAC + // decided by testing a few things on an iMac, should probably get this from the + // system... + smoothing = 2.0; +#endif #ifdef Q_WS_WIN int winSmooth; @@ -7274,15 +6952,15 @@ void qt_build_pow_tables() { } #else for (int i=0; i<256; ++i) { - qt_pow_rgb_gamma[i] = uchar(qRound(pow(i / 255.0, smoothing) * 255)); - qt_pow_rgb_invgamma[i] = uchar(qRound(pow(i / 255.0, 1 / smoothing) * 255)); + qt_pow_rgb_gamma[i] = uchar(qRound(pow(i / qreal(255.0), smoothing) * 255)); + qt_pow_rgb_invgamma[i] = uchar(qRound(pow(i / qreal(255.), 1 / smoothing) * 255)); } #endif #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) const qreal gray_gamma = 2.31; for (int i=0; i<256; ++i) - qt_pow_gamma[i] = uint(qRound(pow(i / 255.0, gray_gamma) * 2047)); + qt_pow_gamma[i] = uint(qRound(pow(i / qreal(255.), gray_gamma) * 2047)); for (int i=0; i<2048; ++i) qt_pow_invgamma[i] = uchar(qRound(pow(i / 2047.0, 1 / gray_gamma) * 255)); #endif @@ -7404,7 +7082,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); } } } @@ -7445,7 +7123,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/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index de97683..019402a 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -110,6 +110,7 @@ struct QSpanData; class QGradient; class QRasterBuffer; class QClipData; +class QRasterPaintEngineState; typedef QT_FT_SpanFunc ProcessSpans; typedef void (*BitmapBlitFunc)(QRasterBuffer *rasterBuffer, @@ -293,7 +294,7 @@ struct QSpanData }; void init(QRasterBuffer *rb, const QRasterPaintEngine *pe); - void setup(const QBrush &brush, int alpha); + void setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode); void setupMatrix(const QTransform &matrix, int bilinear); void initTexture(const QImage *image, int alpha, QTextureData::Type = QTextureData::Plain, const QRect &sourceRect = QRect()); void adjustSpanMethods(); diff --git a/src/gui/painting/qdrawutil.cpp b/src/gui/painting/qdrawutil.cpp index 2beeb0e..230d30b 100644 --- a/src/gui/painting/qdrawutil.cpp +++ b/src/gui/painting/qdrawutil.cpp @@ -1038,4 +1038,301 @@ void qDrawItem(QPainter *p, Qt::GUIStyle gs, #endif +/*! + \struct QMargins + \since 4.6 + + Holds the borders used to split a pixmap into nine segments in order to + draw it, similar to \l{http://www.w3.org/TR/css3-background/} + {CSS3 border-images}. + + \sa qDrawBorderPixmap, Qt::TileRule, QTileRules +*/ + +/*! + \struct QTileRules + \since 4.6 + + Holds the rules used to draw a pixmap or image split into nine segments, + similar to \l{http://www.w3.org/TR/css3-background/}{CSS3 border-images}. + + \sa qDrawBorderPixmap, Qt::TileRule, QMargins +*/ + +/*! + \fn qDrawBorderPixmap(QPainter *painter, const QRect &target, const QMargins &margins, const QPixmap &pixmap) + \since 4.6 + + Draws the given \a pixmap into the given \a target rectangle, using the + given \a painter. The pixmap will be split into nine segments and drawn + according to the \a margins structure. +*/ + +static inline void qVerticalRepeat(QPainter *painter, const QRect &target, const QPixmap &pixmap, const QRect &source, + void (*drawPixmap)(QPainter*, const QRect&, const QPixmap&, const QRect&)) +{ + const int x = target.x(); + const int width = target.width(); + const int height = source.height(); + const int bottom = target.bottom() - height; + int y = target.y(); + for (; y < bottom; y += height) + (*drawPixmap)(painter, QRect(x, y, width, height), pixmap, source); + const QRect remaining(source.x(), source.y(), source.width(), target.bottom() - y + 1); + (*drawPixmap)(painter, QRect(x, y, width, remaining.height()), pixmap, remaining); +} + +static inline void qHorizontalRepeat(QPainter *painter, const QRect &target, const QPixmap &pixmap, const QRect &source, + void (*drawPixmap)(QPainter*, const QRect&, const QPixmap&, const QRect&)) +{ + const int y = target.y(); + const int width = source.width(); + const int height = target.height(); + const int right = target.right() - width; + int x = target.x(); + for (; x < right; x += width) + (*drawPixmap)(painter, QRect(x, y, width, height), pixmap, source); + const QRect remaining(source.x(), source.y(), target.right() - x + 1, source.height()); + (*drawPixmap)(painter, QRect(x, y, remaining.width(), height), pixmap, remaining); +} + +static inline void qVerticalRound(QPainter *painter, const QRect &target, const QPixmap &pixmap, const QRect &source, + void (*drawPixmap)(QPainter*, const QRect&, const QPixmap&, const QRect&)) +{ + // qreal based - slow on non-fpu devices + const qreal x = target.x(); + const qreal width = target.width(); + const qreal verticalFactor = static_cast<qreal>(target.height()) / static_cast<qreal>(source.height()); + const qreal verticalIncrement = static_cast<qreal>(target.height()) / static_cast<int>(verticalFactor + 0.5); + const qreal bottom = target.bottom(); + for (qreal y = static_cast<qreal>(target.y()); y < bottom; y += verticalIncrement) + (*drawPixmap)(painter, QRectF(x, y, width, verticalIncrement).toRect(), pixmap, source); + +} + +static inline void qHorizontalRound(QPainter *painter, const QRect &target, const QPixmap &pixmap, const QRect &source, + void (*drawPixmap)(QPainter*, const QRect&, const QPixmap&, const QRect&)) +{ + // qreal based - slow on non-fpu devices + const qreal y = target.y(); + const qreal height = target.height(); + const qreal horizontalFactor = static_cast<qreal>(target.width()) / static_cast<qreal>(source.width()); + const qreal horizontalIncrement = static_cast<qreal>(target.width()) / static_cast<int>(horizontalFactor + 0.5); + const qreal right = target.right(); + for (qreal x = target.x(); x < right; x += horizontalIncrement) + (*drawPixmap)(painter, QRectF(x, y, horizontalIncrement, height).toRect(), pixmap, source); +} + +static inline void qDrawPixmap(QPainter *painter, const QRect &target, const QPixmap &pixmap, const QRect &source) +{ + painter->drawPixmap(target, pixmap, source); +} + +static inline void qDrawVerticallyRepeatedPixmap(QPainter *painter, const QRect &target, const QPixmap &pixmap, const QRect &source) +{ + qVerticalRepeat(painter, target, pixmap, source, qDrawPixmap); +} + +static inline void qDrawHorizontallyRepeatedPixmap(QPainter *painter, const QRect &target, const QPixmap &pixmap, const QRect &source) +{ + qHorizontalRepeat(painter, target, pixmap, source, qDrawPixmap); +} + +static inline void qDrawVerticallyRoundedPixmap(QPainter *painter, const QRect &target, const QPixmap &pixmap, const QRect &source) +{ + qVerticalRound(painter, target, pixmap, source, qDrawPixmap); +} + +static inline void qDrawHorizontallyRoundedPixmap(QPainter *painter, const QRect &target, const QPixmap &pixmap, const QRect &source) +{ + qHorizontalRound(painter, target, pixmap, source, qDrawPixmap); +} + +/*! + \since 4.6 + + Draws the indicated \a sourceRect rectangle from the given \a pixmap into + the given \a targetRect rectangle, using the given \a painter. The pixmap + will be split into nine segments according to the given \a targetMargins + and \a sourceMargins structures. Finally, the pixmap will be drawn + according to the given \a rules. + + This function is used to draw a scaled pixmap, similar to + \l{http://www.w3.org/TR/css3-background/}{CSS3 border-images} + + \sa Qt::TileRule, QTileRules, QMargins +*/ + +void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, const QPixmap &pixmap, + const QRect &sourceRect, const QMargins &sourceMargins, const QTileRules &rules) +{ + // source center + const int sourceTop = sourceRect.top(); + const int sourceLeft = sourceRect.left(); + const int sourceCenterTop = sourceTop + sourceMargins.top; + const int sourceCenterLeft = sourceLeft + sourceMargins.left; + const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom + 1; + const int sourceCenterRight = sourceRect.right() - sourceMargins.right + 1; + const int sourceCenterWidth = sourceCenterRight - sourceMargins.left; + const int sourceCenterHeight = sourceCenterBottom - sourceMargins.top; + // target center + const int targetTop = targetRect.top(); + const int targetLeft = targetRect.left(); + const int targetCenterTop = targetTop + targetMargins.top; + const int targetCenterLeft = targetLeft + targetMargins.left; + const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom + 1; + const int targetCenterRight = targetRect.right() - targetMargins.right + 1; + const int targetCenterWidth = targetCenterRight - targetCenterLeft; + const int targetCenterHeight = targetCenterBottom - targetCenterTop; + + // corners + if (targetMargins.top > 0 && targetMargins.left > 0 && sourceMargins.top > 0 && sourceMargins.left > 0) { // top left + const QRect targetTopLeftRect(targetLeft, targetTop, targetMargins.left, targetMargins.top); + const QRect sourceTopLeftRect(sourceLeft, sourceTop, sourceMargins.left, sourceMargins.top); + qDrawPixmap(painter, targetTopLeftRect, pixmap, sourceTopLeftRect); + } + if (targetMargins.top > 0 && targetMargins.right > 0 && sourceMargins.top > 0 && sourceMargins.right > 0) { // top right + const QRect targetTopRightRect(targetCenterRight, targetTop, targetMargins.right, targetMargins.top); + const QRect sourceTopRightRect(sourceCenterRight, sourceTop, sourceMargins.right, sourceMargins.top); + qDrawPixmap(painter, targetTopRightRect, pixmap, sourceTopRightRect); + } + if (targetMargins.bottom > 0 && targetMargins.left > 0 && sourceMargins.bottom > 0 && sourceMargins.left > 0) { // bottom left + const QRect targetBottomLeftRect(targetLeft, targetCenterBottom, targetMargins.left, targetMargins.bottom); + const QRect sourceBottomLeftRect(sourceLeft, sourceCenterBottom, sourceMargins.left, sourceMargins.bottom); + qDrawPixmap(painter, targetBottomLeftRect, pixmap, sourceBottomLeftRect); + } + if (targetMargins.bottom > 0 && targetMargins.right > 0 && sourceMargins.bottom > 0 && sourceMargins.right > 0) { // bottom right + const QRect targetBottomRightRect(targetCenterRight, targetCenterBottom, targetMargins.right, targetMargins.bottom); + const QRect sourceBottomRightRect(sourceCenterRight, sourceCenterBottom, sourceMargins.right, sourceMargins.bottom); + qDrawPixmap(painter, targetBottomRightRect, pixmap, sourceBottomRightRect); + } + + // horizontal edges + switch (rules.horizontal) { + case Qt::Stretch: + if (targetMargins.top > 0 && sourceMargins.top > 0) { // top + const QRect targetTopRect(targetCenterLeft, targetTop, targetCenterWidth, targetMargins.top); + const QRect sourceTopRect(sourceCenterLeft, sourceTop, sourceCenterWidth, sourceMargins.top); + qDrawPixmap(painter, targetTopRect, pixmap, sourceTopRect); + } + if (targetMargins.bottom > 0 && sourceMargins.bottom > 0) { // bottom + const QRect targetBottomRect(targetCenterLeft, targetCenterBottom, targetCenterWidth, targetMargins.bottom); + const QRect sourceBottomRect(sourceCenterLeft, sourceCenterBottom, sourceCenterWidth, sourceMargins.bottom); + qDrawPixmap(painter, targetBottomRect, pixmap, sourceBottomRect); + } + break; + case Qt::Repeat: + if (targetMargins.top > 0 && sourceMargins.top > 0) { // top + const QRect targetTopRect(targetCenterLeft, targetTop, targetCenterWidth, targetMargins.top); + const QRect sourceTopRect(sourceCenterLeft, sourceTop, sourceCenterWidth, sourceMargins.top); + qDrawHorizontallyRepeatedPixmap(painter, targetTopRect, pixmap, sourceTopRect); + } + if (targetMargins.bottom > 0 && sourceMargins.bottom > 0) { // bottom + const QRect targetBottomRect(targetCenterLeft, targetCenterBottom, targetCenterWidth, targetMargins.bottom); + const QRect sourceBottomRect(sourceCenterLeft, sourceCenterBottom, sourceCenterWidth, sourceMargins.bottom); + qDrawHorizontallyRepeatedPixmap(painter, targetBottomRect, pixmap, sourceBottomRect); + } + break; + case Qt::Round: + if (targetMargins.top > 0 && sourceMargins.top > 0) { // top + const QRect targetTopRect(targetCenterLeft, targetTop, targetCenterWidth, targetMargins.top); + const QRect sourceTopRect(sourceCenterLeft, sourceTop, sourceCenterWidth, sourceMargins.top); + qDrawHorizontallyRoundedPixmap(painter, targetTopRect, pixmap, sourceTopRect); + } + if (targetMargins.bottom > 0 && sourceMargins.bottom > 0) { // bottom + const QRect targetBottomRect(targetCenterLeft, targetCenterBottom, targetCenterWidth, targetMargins.bottom); + const QRect sourceBottomRect(sourceCenterLeft, sourceCenterBottom, sourceCenterWidth, sourceMargins.bottom); + qDrawHorizontallyRoundedPixmap(painter, targetBottomRect, pixmap, sourceBottomRect); + } + break; + } + + // vertical edges + switch (rules.vertical) { + case Qt::Stretch: + if (targetMargins.left > 0 && sourceMargins.left > 0) { // left + const QRect targetLeftRect(targetLeft, targetCenterTop, targetMargins.left, targetCenterHeight); + const QRect sourceLeftRect(sourceLeft, sourceCenterTop, sourceMargins.left, sourceCenterHeight); + qDrawPixmap(painter, targetLeftRect, pixmap, sourceLeftRect); + } + if (targetMargins.right > 0 && sourceMargins.right > 0) { // right + const QRect targetRightRect(targetCenterRight, targetCenterTop, targetMargins.right, targetCenterHeight); + const QRect sourceRightRect(sourceCenterRight, sourceCenterTop, sourceMargins.right, sourceCenterHeight); + qDrawPixmap(painter, targetRightRect, pixmap, sourceRightRect); + } + break; + case Qt::Repeat: + if (targetMargins.left > 0 && sourceMargins.left > 0) { // left + const QRect targetLeftRect(targetLeft, targetCenterTop, targetMargins.left, targetCenterHeight); + const QRect sourceLeftRect(sourceLeft, sourceCenterTop, sourceMargins.left, sourceCenterHeight); + qDrawVerticallyRepeatedPixmap(painter, targetLeftRect, pixmap, sourceLeftRect); + } + if (targetMargins.right > 0 && sourceMargins.right > 0) { // right + const QRect targetRightRect(targetCenterRight, targetCenterTop, targetMargins.right, targetCenterHeight); + const QRect sourceRightRect(sourceCenterRight, sourceCenterTop, sourceMargins.right, sourceCenterHeight); + qDrawVerticallyRepeatedPixmap(painter, targetRightRect, pixmap, sourceRightRect); + } + break; + case Qt::Round: + if (targetMargins.left > 0 && sourceMargins.left > 0) { // left + const QRect targetLeftRect(targetLeft, targetCenterTop, targetMargins.left, targetCenterHeight); + const QRect sourceLeftRect(sourceLeft, sourceCenterTop, sourceMargins.left, sourceCenterHeight); + qDrawVerticallyRoundedPixmap(painter, targetLeftRect, pixmap, sourceLeftRect); + } + if (targetMargins.right > 0 && sourceMargins.right > 0) { // right + const QRect targetRightRect(targetCenterRight, targetCenterTop, targetMargins.right, targetCenterHeight); + const QRect sourceRightRect(sourceCenterRight, sourceCenterTop, sourceMargins.right, sourceCenterHeight); + qDrawVerticallyRoundedPixmap(painter, targetRightRect, pixmap, sourceRightRect); + } + break; + } + + // center + if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { + const QRect targetCenterRect(targetCenterLeft, targetCenterTop, targetCenterWidth, targetCenterHeight); + const QRect sourceCenterRect(sourceCenterLeft, sourceCenterTop, sourceCenterWidth, sourceCenterHeight); + switch (rules.horizontal) { + case Qt::Stretch: + switch (rules.vertical) { + case Qt::Stretch: // stretch stretch + qDrawPixmap(painter, targetCenterRect, pixmap, sourceCenterRect); + break; + case Qt::Repeat: // stretch repeat + qVerticalRepeat(painter, targetCenterRect, pixmap, sourceCenterRect, qDrawPixmap); + break; + case Qt::Round: // stretch round + qVerticalRound(painter, targetCenterRect, pixmap, sourceCenterRect, qDrawPixmap); + break; + } + break; + case Qt::Repeat: + switch (rules.vertical) { + case Qt::Stretch: // repeat stretch + qHorizontalRepeat(painter, targetCenterRect, pixmap, sourceCenterRect, qDrawPixmap); + break; + case Qt::Repeat: // repeat repeat + qVerticalRepeat(painter, targetCenterRect, pixmap, sourceCenterRect, qDrawHorizontallyRepeatedPixmap); + break; + case Qt::Round: // repeat round + qVerticalRound(painter, targetCenterRect, pixmap, sourceCenterRect, qDrawHorizontallyRepeatedPixmap); + break; + } + break; + case Qt::Round: + switch (rules.vertical) { + case Qt::Stretch: // round stretch + qHorizontalRound(painter, targetCenterRect, pixmap, sourceCenterRect, qDrawPixmap); + break; + case Qt::Repeat: // round repeat + qHorizontalRound(painter, targetCenterRect, pixmap, sourceCenterRect, qDrawVerticallyRepeatedPixmap); + break; + case Qt::Round: // round round + qHorizontalRound(painter, targetCenterRect, pixmap, sourceCenterRect, qDrawVerticallyRoundedPixmap); + break; + } + break; + } + } +} + QT_END_NAMESPACE diff --git a/src/gui/painting/qdrawutil.h b/src/gui/painting/qdrawutil.h index 14901f3..38d9ec0 100644 --- a/src/gui/painting/qdrawutil.h +++ b/src/gui/painting/qdrawutil.h @@ -44,6 +44,7 @@ #include <QtCore/qnamespace.h> #include <QtCore/qstring.h> // char*->QString conversion +#include <QtGui/qpixmap.h> QT_BEGIN_HEADER @@ -60,7 +61,6 @@ class QPoint; class QColor; class QBrush; class QRect; -class QPixmap; // // Standard shade drawing @@ -133,6 +133,42 @@ Q_GUI_EXPORT QT3_SUPPORT void qDrawArrow(QPainter *p, Qt::ArrowType type, Qt::GU const QPalette &pal, bool enabled); #endif +struct Q_GUI_EXPORT QMargins +{ + inline QMargins(int margin = 0) + : top(margin), + left(margin), + bottom(margin), + right(margin) {} + inline QMargins(int topMargin, int leftMargin, int bottomMargin, int rightMargin) + : top(topMargin), + left(leftMargin), + bottom(bottomMargin), + right(rightMargin) {} + int top; + int left; + int bottom; + int right; +}; + +struct Q_GUI_EXPORT QTileRules +{ + inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule = Qt::Stretch) + : horizontal(horizontalRule), vertical(verticalRule) {} + inline QTileRules(Qt::TileRule rule = Qt::Stretch) + : horizontal(rule), vertical(rule) {} + Qt::TileRule horizontal; + Qt::TileRule vertical; +}; + +Q_GUI_EXPORT void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, const QPixmap &pixmap, + const QRect &sourceRect, const QMargins &sourceMargins, const QTileRules &rules = QTileRules()); + +Q_GUI_EXPORT inline void qDrawBorderPixmap(QPainter *painter, const QRect &target, const QMargins &margins, const QPixmap &pixmap) +{ + qDrawBorderPixmap(painter, target, margins, pixmap, pixmap.rect(), margins); +} + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/gui/painting/qgraphicssystemfactory_p.h b/src/gui/painting/qgraphicssystemfactory_p.h index 9e95324..523b908 100644 --- a/src/gui/painting/qgraphicssystemfactory_p.h +++ b/src/gui/painting/qgraphicssystemfactory_p.h @@ -63,7 +63,7 @@ QT_MODULE(Gui) class QGraphicsSystem; -class Q_GUI_EXPORT QGraphicsSystemFactory +class QGraphicsSystemFactory { public: static QStringList keys(); diff --git a/src/gui/painting/qgraphicssystemplugin_p.h b/src/gui/painting/qgraphicssystemplugin_p.h index 2e70333..ea7aa2d 100644 --- a/src/gui/painting/qgraphicssystemplugin_p.h +++ b/src/gui/painting/qgraphicssystemplugin_p.h @@ -64,7 +64,7 @@ QT_MODULE(Gui) class QGraphicsSystem; -struct Q_GUI_EXPORT QGraphicsSystemFactoryInterface : public QFactoryInterface +struct QGraphicsSystemFactoryInterface : public QFactoryInterface { virtual QGraphicsSystem *create(const QString &key) = 0; }; diff --git a/src/gui/painting/qmatrix.cpp b/src/gui/painting/qmatrix.cpp index 4439d52..030415d 100644 --- a/src/gui/painting/qmatrix.cpp +++ b/src/gui/painting/qmatrix.cpp @@ -208,9 +208,13 @@ QT_BEGIN_NAMESPACE */ QMatrix::QMatrix() + : _m11(1.) + , _m12(0.) + , _m21(0.) + , _m22(1.) + , _dx(0.) + , _dy(0.) { - _m11 = _m22 = 1.0; - _m12 = _m21 = _dx = _dy = 0.0; } /*! @@ -220,12 +224,14 @@ QMatrix::QMatrix() \sa setMatrix() */ -QMatrix::QMatrix(qreal m11, qreal m12, qreal m21, qreal m22, - qreal dx, qreal dy) +QMatrix::QMatrix(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy) + : _m11(m11) + , _m12(m12) + , _m21(m21) + , _m22(m22) + , _dx(dx) + , _dy(dy) { - _m11 = m11; _m12 = m12; - _m21 = m21; _m22 = m22; - _dx = dx; _dy = dy; } @@ -233,8 +239,13 @@ QMatrix::QMatrix(qreal m11, qreal m12, qreal m21, qreal m22, Constructs a matrix that is a copy of the given \a matrix. */ QMatrix::QMatrix(const QMatrix &matrix) + : _m11(matrix._m11) + , _m12(matrix._m12) + , _m21(matrix._m21) + , _m22(matrix._m22) + , _dx(matrix._dx) + , _dy(matrix._dy) { - *this = matrix; } /*! @@ -249,12 +260,14 @@ QMatrix::QMatrix(const QMatrix &matrix) \sa QMatrix() */ -void QMatrix::setMatrix(qreal m11, qreal m12, qreal m21, qreal m22, - qreal dx, qreal dy) +void QMatrix::setMatrix(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy) { - _m11 = m11; _m12 = m12; - _m21 = m21; _m22 = m22; - _dx = dx; _dy = dy; + _m11 = m11; + _m12 = m12; + _m21 = m21; + _m22 = m22; + _dx = dx; + _dy = dy; } @@ -968,18 +981,17 @@ QMatrix QMatrix::inverted(bool *invertible) const if (determinant == 0.0) { if (invertible) *invertible = false; // singular matrix - QMatrix defaultMatrix; - return defaultMatrix; + return QMatrix(true); } else { // invertible matrix if (invertible) *invertible = true; qreal dinv = 1.0/determinant; - QMatrix imatrix((_m22*dinv), (-_m12*dinv), - (-_m21*dinv), (_m11*dinv), - ((_m21*_dy - _m22*_dx)*dinv), - ((_m12*_dx - _m11*_dy)*dinv)); - return imatrix; + return QMatrix((_m22*dinv), (-_m12*dinv), + (-_m21*dinv), (_m11*dinv), + ((_m21*_dy - _m22*_dx)*dinv), + ((_m12*_dx - _m11*_dy)*dinv), + true); } } @@ -1054,9 +1066,14 @@ QMatrix &QMatrix::operator *=(const QMatrix &m) QMatrix QMatrix::operator *(const QMatrix &m) const { - QMatrix result = *this; - result *= m; - return result; + qreal tm11 = _m11*m._m11 + _m12*m._m21; + qreal tm12 = _m11*m._m12 + _m12*m._m22; + qreal tm21 = _m21*m._m11 + _m22*m._m21; + qreal tm22 = _m21*m._m12 + _m22*m._m22; + + qreal tdx = _dx*m._m11 + _dy*m._m21 + m._dx; + qreal tdy = _dx*m._m12 + _dy*m._m22 + m._dy; + return QMatrix(tm11, tm12, tm21, tm22, tdx, tdy, true); } /*! @@ -1161,7 +1178,7 @@ QDebug operator<<(QDebug dbg, const QMatrix &m) << " 22=" << m.m22() << " dx=" << m.dx() << " dy=" << m.dy() - << ")"; + << ')'; return dbg.space(); } #endif diff --git a/src/gui/painting/qmatrix.h b/src/gui/painting/qmatrix.h index bf53c32..1e5fbb4 100644 --- a/src/gui/painting/qmatrix.h +++ b/src/gui/painting/qmatrix.h @@ -99,7 +99,7 @@ public: QMatrix &shear(qreal sh, qreal sv); QMatrix &rotate(qreal a); - bool isInvertible() const { return !qFuzzyCompare(_m11*_m22 - _m12*_m21 + 1, 1); } + bool isInvertible() const { return !qFuzzyIsNull(_m11*_m22 - _m12*_m21); } qreal det() const { return _m11*_m22 - _m12*_m21; } QMatrix inverted(bool *invertible = 0) const; @@ -121,6 +121,20 @@ public: #endif private: + inline QMatrix(bool) + : _m11(1.) + , _m12(0.) + , _m21(0.) + , _m22(1.) + , _dx(0.) + , _dy(0.) {} + inline QMatrix(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy, bool) + : _m11(m11) + , _m12(m12) + , _m21(m21) + , _m22(m22) + , _dx(dx) + , _dy(dy) {} friend class QTransform; qreal _m11, _m12; qreal _m21, _m22; @@ -147,8 +161,8 @@ Q_GUI_EXPORT QPainterPath operator *(const QPainterPath &p, const QMatrix &m); inline bool QMatrix::isIdentity() const { - return qFuzzyCompare(_m11, 1) && qFuzzyCompare(_m22, 1) && qFuzzyCompare(_m12 + 1, 1) - && qFuzzyCompare(_m21 + 1, 1) && qFuzzyCompare(_dx + 1, 1) && qFuzzyCompare(_dy + 1, 1); + return qFuzzyIsNull(_m11 - 1) && qFuzzyIsNull(_m22 - 1) && qFuzzyIsNull(_m12) + && qFuzzyIsNull(_m21) && qFuzzyIsNull(_dx) && qFuzzyIsNull(_dy); } /***************************************************************************** diff --git a/src/gui/painting/qpaintengine_d3d.cpp b/src/gui/painting/qpaintengine_d3d.cpp deleted file mode 100644 index bb81623..0000000 --- a/src/gui/painting/qpaintengine_d3d.cpp +++ /dev/null @@ -1,4576 +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 <qdebug.h> -#include "qpaintengine_d3d_p.h" - -#include "private/qdrawhelper_p.h" -#include "private/qfont_p.h" -#include "private/qfontengine_p.h" -#include "private/qpaintengine_p.h" -#include "private/qtessellator_p.h" -#include <private/qbezier_p.h> -#include <private/qpainter_p.h> -#include <private/qpixmap_raster_p.h> -#include <private/qpolygonclipper_p.h> -#include <qbuffer.h> -#include <qcache.h> -#include <qdir.h> -#include <qfileinfo.h> -#include <qlibrary.h> -#include <qlibraryinfo.h> -#include <qmath.h> -#include <qpaintdevice.h> -#include <qpixmapcache.h> - -#include <qwidget.h> -#include <d3d9.h> -#include <d3dx9.h> - -#include <mmintrin.h> -#include <xmmintrin.h> - -QT_BEGIN_NAMESPACE - -#ifndef M_PI - #define M_PI 3.14159265358979323846 -#endif - -#define QD3D_MASK_MARGIN 1 -#define QD3D_BATCH_SIZE 256 - -// for the ClearType detection stuff.. -#ifndef SPI_GETFONTSMOOTHINGTYPE -#define SPI_GETFONTSMOOTHINGTYPE 0x200A -#endif - -#ifndef FE_FONTSMOOTHINGCLEARTYPE -#define FE_FONTSMOOTHINGCLEARTYPE 0x0002 -#endif - -//#include <performance.h> -#define PM_INIT -#define PM_MEASURE(A) -#define PM_DISPLAY - -//debugging -//#define QT_DEBUG_VERTEXBUFFER_ACCESS -//#define QT_DEBUG_D3D -//#define QT_DEBUG_D3D_CALLS - -#define QD3D_SET_MARK(output) \ - D3DPERF_SetMarker(0, QString(output).utf16()); - -#define QT_VERTEX_RESET_LIMIT 24576 -#define QT_VERTEX_BUF_SIZE 32768 -#define QD3DFVF_CSVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX2 | D3DFVF_TEXCOORDSIZE4(0) | D3DFVF_TEXCOORDSIZE4(1)) - -// this is a different usage of the effect framework than intended, -// but it's convenient for us to use (See effect file) -#define PASS_STENCIL_ODDEVEN 0 -#define PASS_STENCIL_WINDING 1 -#define PASS_STENCIL_DRAW 2 -#define PASS_STENCIL_DRAW_DIRECT 3 -#define PASS_STENCIL_CLIP 4 -#define PASS_STENCIL_NOSTENCILCHECK 5 -#define PASS_STENCIL_NOSTENCILCHECK_DIRECT 6 -#define PASS_TEXT 7 -#define PASS_CLEARTYPE_TEXT 8 -#define PASS_ALIASED_LINES 9 -#define PASS_ALIASED_LINES_DIRECT 10 - -#define PASS_AA_CREATEMASK 0 -#define PASS_AA_DRAW 1 -#define PASS_AA_DRAW_DIRECT 2 - -#define D3D_STAGE_COUNT 2 -#define D3D_RENDER_STATES 210 -#define D3D_TEXTURE_STATES 33 -#define D3D_SAMPLE_STATES 14 - - -typedef HRESULT (APIENTRY *PFND3DXCREATEBUFFER)(DWORD, LPD3DXBUFFER *); -typedef HRESULT (APIENTRY *PFND3DXCREATEEFFECT)(LPDIRECT3DDEVICE9, LPCVOID, UINT, CONST D3DXMACRO *, - LPD3DXINCLUDE, DWORD, LPD3DXEFFECTPOOL, - LPD3DXEFFECT *, LPD3DXBUFFER *); -typedef D3DXMATRIX *(APIENTRY *PFND3DXMATRIXORTHOOFFCENTERLH)(D3DMATRIX *, FLOAT, FLOAT, - FLOAT, FLOAT, FLOAT, FLOAT); -typedef IDirect3D9 *(APIENTRY *PFNDIRECT3DCREATE9)(uint); - -static PFNDIRECT3DCREATE9 pDirect3DCreate9 = 0; -static PFND3DXCREATEBUFFER pD3DXCreateBuffer = 0; -static PFND3DXCREATEEFFECT pD3DXCreateEffect = 0; -static PFND3DXMATRIXORTHOOFFCENTERLH pD3DXMatrixOrthoOffCenterLH = 0; - - -class QD3DSurfaceManager : public QObject { - Q_OBJECT - -public: - enum QD3DSurfaceManagerStatus { - NoStatus = 0, - NeedsResetting = 0x01, - MaxSizeChanged = 0x02 - }; - - QD3DSurfaceManager(); - ~QD3DSurfaceManager(); - - void init(LPDIRECT3D9 object); - - void setPaintDevice(QPaintDevice *pd); - - int status() const; - void reset(); - - LPDIRECT3DSURFACE9 renderTarget(); - - LPDIRECT3DSURFACE9 surface(QPaintDevice *pd); - LPDIRECT3DSWAPCHAIN9 swapChain(QPaintDevice *pd); - void releasePaintDevice(QPaintDevice *pd); - - LPDIRECT3DDEVICE9 device(); - void cleanup(); - - QSize maxSize() const; - -private: - struct D3DSwapChain { - QSize size; - LPDIRECT3DSWAPCHAIN9 swapchain; - LPDIRECT3DSURFACE9 surface; - }; - - void updateMaxSize(); - void initPresentParameters(D3DPRESENT_PARAMETERS *params); - D3DSwapChain *createSwapChain(QWidget *w); - - QSize m_max_size; - int m_status; - QMap<QPaintDevice *, D3DSwapChain *> m_swapchains; - - LPDIRECT3DDEVICE9 m_device; - QPaintDevice *m_pd; - HWND m_dummy; - D3DSwapChain *m_current; - -private Q_SLOTS: - void cleanupPaintDevice(QObject *); -}; - -struct vertex { - D3DVECTOR pos; - DWORD color; - FLOAT s0, t0, r0, q0; - FLOAT s1, t1, r1, q1; -}; - -struct QD3DMaskPosition { - int x, y, channel; -}; - - -struct QD3DBatchItem { - enum QD3DBatchInfo { - BI_WINDING = 0x0001, - BI_AA = 0x0002, - BI_BRECT = 0x0004, - BI_MASKFULL = 0x0008, - BI_TEXT = 0x0010, - BI_MASK = 0x0020, - BI_CLIP = 0x0040, - BI_SCISSOR = 0x0080, - - BI_PIXMAP = 0x0100, - BI_IMAGE = 0x0200, - BI_COMPLEXBRUSH = 0x0400, - - BI_CLEARCLIP = 0x0800, // clip nothing (filling the clip mask with 0) - BI_TRANSFORM = 0x1000, - BI_MASKSCISSOR = 0x2000, - BI_FASTLINE = 0x4000, - BI_COSMETICPEN = 0x8000 - }; - - int m_info; - - int m_count; - int m_offset; - - QD3DMaskPosition m_maskpos; - qreal m_xoffset; - qreal m_yoffset; - qreal m_opacity; - - QPixmap m_pixmap; - QRectF m_brect; - QBrush m_brush; - - IDirect3DTexture9 *m_texture; - - qreal m_width; - qreal m_distance; - - QTransform m_matrix; - QPainter::CompositionMode m_cmode; - - QVector<int> m_pointstops; -}; - -struct QD3DBatch { - int m_item_index; - QD3DBatchItem items[QD3D_BATCH_SIZE]; -}; - -class QD3DStateManager; -class QD3DFontCache; -class QD3DDrawHelper; -class QD3DGradientCache; - -class QDirect3DPaintEnginePrivate : public QPaintEnginePrivate -{ - Q_DECLARE_PUBLIC(QDirect3DPaintEngine) - -public: - enum RenderTechnique { - RT_NoTechnique, - RT_Antialiased, - RT_Aliased, - }; - - QDirect3DPaintEnginePrivate() - : m_d3d_object(0) - , m_d3d_device(0) - , m_txop(QTransform::TxNone) - , m_effect(0) - , m_flush_on_end(0) - { init(); } - - ~QDirect3DPaintEnginePrivate(); - - bool init(); - void initDevice(); - - inline QD3DBatchItem *nextBatchItem(); - - QPolygonF brushCoordinates(const QRectF &r, bool stroke, qreal *fp) const; - void fillAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform); - void fillAntialiasedPath(const QPainterPath &path, const QRectF &brect, - const QTransform &txform, bool stroke); - void fillPath(const QPainterPath &path, QRectF brect); - - void strokePath(const QPainterPath &path, QRectF brect, bool simple = false); - QPainterPath strokePathFastPen(const QPainterPath &path); - void strokeAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform); - - void flushBatch(); - int flushAntialiased(int offset); - void flushAliased(QD3DBatchItem *item, int offset); - void flushText(QD3DBatchItem *item, int offset); - void flushLines(QD3DBatchItem *item, int offset); - - void updateTransform(const QTransform &matrix); - void updatePen(const QPen &pen); - void updateBrush(const QBrush &pen); - void updateClipRegion(const QRegion &clipregion, Qt::ClipOperation op = Qt::ReplaceClip); - void updateClipPath(const QPainterPath &clipregion, Qt::ClipOperation op = Qt::ReplaceClip); - void updateFont(const QFont &font); - - void setRenderTechnique(RenderTechnique technique); - - QPointF transformPoint(const QPointF &p, qreal *w) const; - - bool prepareBatch(QD3DBatchItem *item, int offset); - void prepareItem(QD3DBatchItem *item); - void cleanupItem(QD3DBatchItem *item); - void setCompositionMode(QPainter::CompositionMode mode); - - void verifyTexture(const QPixmap &pixmap); - - bool isFastRect(const QRectF &rect); - - void releaseDC(); - - void cleanup(); - bool testCaps(); - - QPixmap getPattern(Qt::BrushStyle style) const; - - // clipping - QPainterPath m_sysclip_path; - QPainterPath m_clip_path; - QRegion m_sysclip_region; - QRegion m_clip_region; - - qreal m_opacity; - D3DCOLOR m_opacity_color; - - int m_current_state; - - ID3DXEffect* m_effect; - - RenderTechnique m_current_technique; - - QTransform m_matrix; - qreal m_inv_scale; - - QPen m_pen; - Qt::BrushStyle m_pen_brush_style; - QTransform m_inv_pen_matrix; - D3DCOLOR m_pen_color; - qreal m_pen_width; - - QBrush m_brush; - Qt::BrushStyle m_brush_style; - QTransform m_inv_brush_matrix; - D3DCOLOR m_brush_color; - QTransform m_brush_origin; - - uint m_clipping_enabled : 1; - uint m_has_complex_clipping : 1; - uint m_cleartype_text: 1; - uint m_has_pen : 1; - uint m_has_cosmetic_pen : 1; - uint m_has_brush : 1; - uint m_has_fast_pen : 1; - uint m_has_aa_fast_pen : 1; - uint m_flush_on_end : 1; - uint m_supports_d3d : 1; - - QTransform::TransformationType m_txop; - - QPainter::CompositionMode m_cmode; - - QD3DSurfaceManager m_surface_manager; - QSize m_surface_size; - - LPDIRECT3D9 m_d3d_object; - LPDIRECT3DDEVICE9 m_d3d_device; - IDirect3DSurface9 *m_current_surface; - bool m_in_scene; - - QD3DGradientCache *m_gradient_cache; - QD3DDrawHelper *m_draw_helper; - QD3DBatch m_batch; - QD3DStateManager *m_statemanager; - - HDC m_dc; - IDirect3DSurface9 *m_dcsurface; - - QMap<Qt::BrushStyle, QPixmap> m_patterns; -}; - - -class QD3DStateManager : public ID3DXEffectStateManager { -public: - QD3DStateManager(LPDIRECT3DDEVICE9 pDevice, ID3DXEffect *effect); - void reset(); - - inline void startStateBlock(); - inline void endStateBlock(); - - inline void setCosmeticPen(bool enabled); - inline void setBrushMode(int mode); - inline void setTexture(LPDIRECT3DBASETEXTURE9 pTexture); - inline void setTexture(LPDIRECT3DBASETEXTURE9 pTexture, QGradient::Spread spread); - inline void setTransformation(const QTransform *matrix = 0); - inline void setProjection(const D3DXMATRIX *pMatrix); - inline void setMaskChannel(int channel); - inline void setMaskOffset(qreal x, qreal y); - inline void setFocalDistance(const qreal &fd); - - inline void beginPass(int pass); - inline void endPass(); - - STDMETHOD(QueryInterface)(REFIID iid, LPVOID *ppv); - STDMETHOD_(ULONG, AddRef)(); - STDMETHOD_(ULONG, Release)(); - - STDMETHOD(SetTransform)(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix); - STDMETHOD(SetMaterial)(CONST D3DMATERIAL9 *pMaterial); - STDMETHOD(SetLight)(DWORD Index, CONST D3DLIGHT9 *pLight); - STDMETHOD(LightEnable)(DWORD Index, BOOL Enable); - STDMETHOD(SetRenderState)(D3DRENDERSTATETYPE State, DWORD Value); - STDMETHOD(SetTexture)(DWORD Stage, LPDIRECT3DBASETEXTURE9 pTexture); - STDMETHOD(SetTextureStageState)(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value); - STDMETHOD(SetSamplerState)(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value); - STDMETHOD(SetNPatchMode)(FLOAT NumSegments); - STDMETHOD(SetFVF)(DWORD FVF); - STDMETHOD(SetVertexShader)(LPDIRECT3DVERTEXSHADER9 pShader); - STDMETHOD(SetVertexShaderConstantF)(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount); - STDMETHOD(SetVertexShaderConstantI)(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount); - STDMETHOD(SetVertexShaderConstantB)(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount); - STDMETHOD(SetPixelShader)(LPDIRECT3DPIXELSHADER9 pShader); - STDMETHOD(SetPixelShaderConstantF)(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount); - STDMETHOD(SetPixelShaderConstantI)(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount); - STDMETHOD(SetPixelShaderConstantB)(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount); -private: - LPDIRECT3DVERTEXSHADER9 m_vertexshader; - LPDIRECT3DPIXELSHADER9 m_pixelshader; - - LPDIRECT3DBASETEXTURE9 m_textures[D3D_STAGE_COUNT]; - DWORD m_texturestates[D3D_STAGE_COUNT][D3D_TEXTURE_STATES]; - DWORD m_samplerstates[D3D_STAGE_COUNT][D3D_SAMPLE_STATES]; - DWORD m_renderstate[D3D_RENDER_STATES]; - - qreal m_radgradfd; - - bool m_cosmetic_pen; - int m_pass; - int m_maskchannel; - int m_brushmode; - LPDIRECT3DBASETEXTURE9 m_texture; - D3DXMATRIX m_projection; - - D3DXMATRIX m_d3dIdentityMatrix; - bool m_isIdentity; - QTransform m_transformation; - - LPDIRECT3DDEVICE9 m_pDevice; - ID3DXEffect *m_effect; - - LONG m_refs; - bool m_changed; - qreal m_xoffset, m_yoffset; - static int m_mask_channels[4][4]; -}; - -// -// font cache stuff -// - -struct QD3DGlyphCoord { - // stores the offset and size of a glyph texture - qreal x; - qreal y; - qreal width; - qreal height; - qreal log_width; - qreal log_height; - QFixed x_offset; - QFixed y_offset; -}; - -struct QD3DFontTexture { - int x_offset; // current glyph offset within the texture - int y_offset; - int width; - int height; - IDirect3DTexture9 *texture; -}; - -typedef QHash<glyph_t, QD3DGlyphCoord*> QD3DGlyphHash; -typedef QHash<QFontEngine*, QD3DGlyphHash*> QD3DFontGlyphHash; -typedef QHash<quint64, QD3DFontTexture*> QD3DFontTexHash; - -class QD3DGlyphCache : public QObject -{ - Q_OBJECT -public: - QD3DGlyphCache() - : QObject(0) - , current_cache(0) {} - ~QD3DGlyphCache(); - QD3DGlyphCoord *lookup(QFontEngine *, glyph_t); - void cacheGlyphs(QDirect3DPaintEngine *, const QTextItemInt &, const QVarLengthArray<glyph_t> &, - bool); - void cleanCache(); - inline QD3DFontTexture *fontTexture(QFontEngine *engine) { - return font_textures.constFind(reinterpret_cast<quint64>(engine)).value(); - } - -public slots: - void fontEngineDestroyed(QObject *); - -private: - QImage clearTypeGlyph(QFontEngine *, glyph_t glyph); - QD3DGlyphHash *current_cache; - QD3DFontTexHash font_textures; - QD3DFontGlyphHash font_cache; -}; - -QD3DGlyphCache::~QD3DGlyphCache() -{ -} - -QD3DGlyphCoord *QD3DGlyphCache::lookup(QFontEngine *, glyph_t g) -{ - Q_ASSERT(current_cache != 0); - QD3DGlyphHash::const_iterator it = current_cache->constFind(g); - if (it == current_cache->constEnd()) - return 0; - return it.value(); -} - -void QD3DGlyphCache::cleanCache() -{ - QList<quint64> keys = font_textures.keys(); - for (int i=0; i<keys.size(); ++i) - font_textures.value(keys.at(i))->texture->Release(); - - qDeleteAll(font_textures); - qDeleteAll(font_cache); - font_textures.clear(); - font_cache.clear(); - current_cache = 0; -} - -void QD3DGlyphCache::fontEngineDestroyed(QObject *object) -{ -// qDebug() << "=> font engine destroyed: " << object; - QFontEngine *engine = static_cast<QFontEngine *>(object); - - QD3DFontGlyphHash::iterator cache_it = font_cache.find(engine); - if (cache_it != font_cache.end()) { - QD3DGlyphHash *cache = font_cache.take(engine); - delete cache; - } - - quint64 font_key = reinterpret_cast<quint64>(engine); - QD3DFontTexture *tex = font_textures.take(font_key); - if (tex) { - tex->texture->Release(); - delete tex; - } -} - -QImage QD3DGlyphCache::clearTypeGlyph(QFontEngine *engine, glyph_t glyph) -{ - glyph_metrics_t gm = engine->boundingBox(glyph); - int glyph_x = qFloor(gm.x.toReal()); - int glyph_y = qFloor(gm.y.toReal()); - int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x + 2; - int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y + 2; - - if (glyph_width + glyph_x <= 0 || glyph_height <= 0) - return QImage(); - QImage im(glyph_width + glyph_x, glyph_height, QImage::Format_ARGB32_Premultiplied); - im.fill(0xff000000); // solid black - QPainter p(&im); - - p.setPen(Qt::white); - p.setBrush(Qt::NoBrush); - - QTextItemInt ti; - ti.ascent = engine->ascent(); - ti.descent = engine->descent(); - ti.width = glyph_width; - ti.fontEngine = engine; - - QGlyphLayoutArray<1> glyphLayout; - ti.glyphs = glyphLayout; - ti.glyphs.glyphs[0] = glyph; - ti.glyphs.advances_x[0] = glyph_width; - p.drawTextItem(QPointF(-glyph_x, -glyph_y), ti); - p.end(); - return im; -} - -#if 0 -static void dump_font_texture(QD3DFontTexture *tex) -{ - QColor color(Qt::red); - D3DLOCKED_RECT rect; - if (FAILED(tex->texture->LockRect(0, &rect, 0, 0))) { - qDebug() << "debug: unable to lock texture rect."; - return; - } - -// cleartype version -// uint *tex_data = (uint *) rect.pBits; -// QImage im(tex->width, tex->height, QImage::Format_ARGB32); -// for (int y=0; y<tex->height; ++y) { -// for (int x=0; x<tex->width; ++x) { -// im.setPixel(x, y, ((*(tex_data+x+y*tex->width)))); -// } -// } - uchar *tex_data = (uchar *) rect.pBits; - QImage im(rect.Pitch, tex->height, QImage::Format_ARGB32); - for (int y=0; y<tex->height; ++y) { - for (int x=0; x<rect.Pitch; ++x) { - uchar val = ((*(tex_data+x+y*rect.Pitch))); - im.setPixel(x, y, 0xff000000 | (val << 16) | (val << 8) | val); - } - } - tex->texture->UnlockRect(0); - static int i= 0; - im.save(QString("tx%1.png").arg(i++)); -} -#endif - -void QD3DGlyphCache::cacheGlyphs(QDirect3DPaintEngine *engine, const QTextItemInt &ti, - const QVarLengthArray<glyph_t> &glyphs, bool clearType) -{ - IDirect3DDevice9 *device = engine->d_func()->m_d3d_device; - QD3DFontGlyphHash::const_iterator cache_it = font_cache.constFind(ti.fontEngine); - QD3DGlyphHash *cache = 0; - if (cache_it == font_cache.constEnd()) { - cache = new QD3DGlyphHash; - font_cache.insert(ti.fontEngine, cache); - connect(ti.fontEngine, SIGNAL(destroyed(QObject *)), SLOT(fontEngineDestroyed(QObject *))); - } else { - cache = cache_it.value(); - } - - current_cache = cache; - - D3DFORMAT tex_format = clearType ? D3DFMT_A8R8G8B8 : D3DFMT_A8; - quint64 font_key = reinterpret_cast<quint64>(ti.fontEngine); - QD3DFontTexHash::const_iterator it = font_textures.constFind(font_key); - QD3DFontTexture *font_tex = 0; - if (it == font_textures.constEnd()) { - // alloc a new texture, put it into the cache - int tex_height = qCeil(ti.ascent.toReal() + ti.descent.toReal()) + 5; - int tex_width = tex_height * 30; // ### - IDirect3DTexture9 *tex; - if (FAILED(device->CreateTexture(tex_width, tex_height, 1, 0, - tex_format, D3DPOOL_MANAGED, &tex, NULL))) - { - qWarning("QD3DGlyphCache::cacheGlyphs(): can't allocate font texture (%dx%d).", - tex_width, tex_height); - return; - } else { -// qDebug() << "=> new font texture: " << QSize(tex_width,tex_height); - font_tex = new QD3DFontTexture; - font_tex->texture = tex; - font_tex->x_offset = 0; - font_tex->y_offset = 0; - font_tex->width = tex_width; - font_tex->height = tex_height; - font_textures.insert(font_key, font_tex); - } - } else { - font_tex = it.value(); - // make it current render target.. - } - - // cache each glyph - for (int i=0; i<glyphs.size(); ++i) { - QD3DGlyphHash::const_iterator it = cache->constFind(glyphs[i]); - if (it == cache->constEnd()) { - glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyphs[i]); - int glyph_width = qCeil(metrics.width.toReal()) + 5; - int glyph_height = qCeil(ti.ascent.toReal() + ti.descent.toReal()) + 5; - if (font_tex->x_offset + glyph_width > font_tex->width) { - // no room on the current line, start new glyph strip - int strip_height = glyph_height; - font_tex->x_offset = 0; - font_tex->y_offset += strip_height; - if (font_tex->y_offset >= font_tex->height) { - // if no room in the current texture - realloc a larger texture - int old_tex_height = font_tex->height; - font_tex->height += strip_height; - - IDirect3DTexture9 *new_tex; - if (FAILED(device->CreateTexture(font_tex->width, font_tex->height, 1, 0, - tex_format, D3DPOOL_MANAGED, &new_tex, NULL))) - { - qWarning("QD3DGlyphCache(): can't re-allocate font texture."); - return; - } else { -// qDebug() << " -> new glyph strip added:" << QSize(font_tex->width,font_tex->height); - - D3DLOCKED_RECT new_rect, old_rect; - if (FAILED(font_tex->texture->LockRect(0, &old_rect, 0, D3DLOCK_READONLY))) { - qDebug() << "QD3DGlyphCache: unable to lock texture rect."; - return; - } - if (FAILED(new_tex->LockRect(0, &new_rect, 0, 0))) { - qDebug() << "QD3DGlyphCache: unable to lock texture rect."; - return; - } - memcpy(new_rect.pBits, old_rect.pBits, new_rect.Pitch * old_tex_height); - font_tex->texture->UnlockRect(0); - new_tex->UnlockRect(0); - engine->d_func()->flushBatch(); - font_tex->texture->Release(); - font_tex->texture = new_tex; - } - - // update the texture coords and the y offset for the existing glyphs in - // the cache, because of the texture size change - QD3DGlyphHash::iterator it = cache->begin(); - while (it != cache->end()) { - it.value()->height = (it.value()->height * old_tex_height) / font_tex->height; - it.value()->y = (it.value()->y * old_tex_height) / font_tex->height; - ++it; - } - } - } - QD3DGlyphCoord *d3d_glyph = new QD3DGlyphCoord; - d3d_glyph->x = qreal(font_tex->x_offset) / font_tex->width; - d3d_glyph->y = qreal(font_tex->y_offset) / font_tex->height; - d3d_glyph->width = qreal(glyph_width) / font_tex->width; - d3d_glyph->height = qreal(glyph_height) / font_tex->height; - d3d_glyph->log_width = d3d_glyph->width * font_tex->width; - d3d_glyph->log_height = d3d_glyph->height * font_tex->height; - d3d_glyph->x_offset = -metrics.x; - d3d_glyph->y_offset = metrics.y; - - QImage glyph_im; - if (clearType) - glyph_im = clearTypeGlyph(ti.fontEngine, glyphs[i]); - else - glyph_im = ti.fontEngine->alphaMapForGlyph(glyphs[i]).convertToFormat(QImage::Format_Indexed8); - - // write glyph to texture - D3DLOCKED_RECT rect; - RECT glyph_rect = { font_tex->x_offset, font_tex->y_offset, - font_tex->x_offset + glyph_im.width(), - font_tex->y_offset + glyph_im.height() }; - -// qDebug() << " > new glyph char added:" << QSize(glyph_im.width(), glyph_im.height()); - if (FAILED(font_tex->texture->LockRect(0, &rect, &glyph_rect, 0))) { - qDebug() << "QD3DGlyphCache: unable to lock texture rect."; - return; - } - - // ### unify these loops - if (clearType) { - int ppl = rect.Pitch / 4; - uint *tex_data = (uint *) rect.pBits; - for (int y=0; y<glyph_im.height(); ++y) { - uint *s = (uint *) glyph_im.scanLine(y); - for (int x=0; x<glyph_im.width(); ++x) { - tex_data[ppl*y + x] = *s; - ++s; - } - } - } else { - int ppl = rect.Pitch; - uchar *tex_data = (uchar *) rect.pBits; - for (int y=0; y<glyph_im.height(); ++y) { - uchar *s = (uchar *) glyph_im.scanLine(y); - for (int x=0; x<glyph_im.width(); ++x) { - tex_data[ppl*y + x] = *s; - ++s; - } - } - } - font_tex->texture->UnlockRect(0); - - // debug -// dump_font_texture(font_tex); - - if (font_tex->x_offset + glyph_width > font_tex->width) { - font_tex->x_offset = 0; - font_tex->y_offset += glyph_height; - } else { - font_tex->x_offset += glyph_width; - } - - cache->insert(glyphs[i], d3d_glyph); - } - } -} - -Q_GLOBAL_STATIC(QD3DGlyphCache, qd3d_glyph_cache) - -// -// end font caching stuff -// - - -// -// D3D image cache stuff -// - -// ### keep the GL stuff in mind.. -typedef void (*_qt_image_cleanup_hook_64)(qint64); -extern Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64; - -static void qd3d_image_cleanup(qint64 key); - -class QD3DImage -{ -public: - QD3DImage(IDirect3DDevice9 *device, const QImage &image); - ~QD3DImage(); - - IDirect3DTexture9 *texture; -}; - -static QList<IDirect3DTexture9 *> qd3d_release_list; - -QD3DImage::QD3DImage(IDirect3DDevice9 *device, const QImage &image) -{ - texture = 0; - Q_ASSERT(device); - QImage im = image.convertToFormat(QImage::Format_ARGB32); - if (FAILED(device->CreateTexture(im.width(), im.height(), 1, 0, - D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture, 0))) { - qWarning("QD3DImage(): unable to create Direct3D texture."); - return; - } -// qDebug(" -> created image texture: %p - 0x%08x%08x",texture,uint (image.cacheKey() >> 32),uint (image.cacheKey() & 0xffffffff)); - D3DLOCKED_RECT rect; - if (FAILED(texture->LockRect(0, &rect, 0, 0))) { - qDebug() << "QD3DImage: unable to lock texture rect."; - return; - } - DWORD *dst = (DWORD *) rect.pBits; - DWORD *src = (DWORD *) im.scanLine(0); - - Q_ASSERT((rect.Pitch/4) == (im.bytesPerLine()/4)); - memcpy(dst, src, rect.Pitch*im.height()); - texture->UnlockRect(0); -} - -QD3DImage::~QD3DImage() -{ - if (texture) - qd3d_release_list.append(texture); -} - -static int qd3d_cache_limit = 64*1024; // cache ~64 MB worth of textures -typedef QCache<quint64, QD3DImage> QD3DImageCache; - -class QD3DImageManager -{ -public: - QD3DImageManager() { - // ### GL does the same! - qt_image_cleanup_hook_64 = qd3d_image_cleanup; - cache.setMaxCost(qd3d_cache_limit); - } - ~QD3DImageManager() { -// qDebug() << "unhooking d3d image cache"; - qt_image_cleanup_hook_64 = 0; - cache.clear(); - } - - IDirect3DTexture9 *lookup(IDirect3DDevice9 *device, const QImage &image); - void remove(quint64 key); - -private: - QD3DImageCache cache; -}; - -IDirect3DTexture9 *QD3DImageManager::lookup(IDirect3DDevice9 *device, const QImage &image) -{ - QD3DImage *tex_image = 0; - - tex_image = cache.object(image.cacheKey()); - if (!tex_image) { - // to avoid cache thrashing we remove images from the cache - // that have the same serial no as the cached image, since - // that image is most likely destoyed already, and we got a - // stale cache entry - uint serial = (uint) (image.cacheKey() >> 32); - QList<quint64> keys = cache.keys(); - for (int i=0; i<keys.size(); ++i) { - if ((uint)(keys.at(i) >> 32) == serial) { - cache.remove(keys.at(i)); - break; - } - } -// qDebug(" => cached: %d, adding cache image: 0x%08x%08x",cache.size(), uint (image.cacheKey() >> 32),uint (image.cacheKey() & 0xffffffff)); - // add cache entry - int cost = image.width()*image.height()*4/1024; - if (cache.totalCost() + cost > cache.maxCost()) { - // no room for new entries? kick out half the cached images - int old_max_cost = cache.maxCost(); - cache.setMaxCost(old_max_cost/2); - cache.setMaxCost(old_max_cost); - } - tex_image = new QD3DImage(device, image); - cache.insert(image.cacheKey(), tex_image, cost); -// qDebug() << "==> total cache cost: " << cache.totalCost() << cost; - } - - return tex_image->texture; -} - -void QD3DImageManager::remove(quint64 key) -{ -// QList<quint64> keys = cache.keys(); -// if (keys.contains(key)) -// qDebug() << "entery removed from cache"; - cache.remove(key); -} - -Q_GLOBAL_STATIC(QD3DImageManager, qd3d_image_cache) - -static void qd3d_image_cleanup(qint64 key) -{ -// qDebug() << "qd3d_image_cleanup:"; -// qDebug(" => key: 0x%08x%08x", (uint) (key >> 32), (uint)(key & 0xffffffff)); - qd3d_image_cache()->remove(key); -} - -// -// end D3D image cache stuff -// - -class QD3DDrawHelper : public QTessellator -{ -public: - QD3DDrawHelper(QDirect3DPaintEnginePrivate *pe); - ~QD3DDrawHelper(); - - bool needsFlushing() const; - QD3DMaskPosition allocateMaskPosition(const QRectF &brect, bool *breakbatch); - - void setClipPath(const QPainterPath &path, QD3DBatchItem **item); - - void queueAntialiasedMask(const QPolygonF &poly, QD3DBatchItem **item, const QRectF &brect); - QRectF queueAliasedMask(const QPainterPath &path, QD3DBatchItem **item, D3DCOLOR color); - - void queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color, const QPolygonF &trect); - void queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color); - - void queueTextGlyph(const QRectF &rect, const qreal *tex_coords, QD3DBatchItem *item, - D3DCOLOR color); - - void queueAntialiasedLines(const QPainterPath &path, QD3DBatchItem **item, const QRectF &brect); - void queueAliasedLines(const QLineF *lines, int lineCount, QD3DBatchItem **item); - - int drawAntialiasedMask(int offset, int maxoffset); - void drawAliasedMask(int offset); - void drawAntialiasedBoundingRect(QD3DBatchItem *item); - void drawAliasedBoundingRect(QD3DBatchItem *item); - void drawTextItem(QD3DBatchItem *item); - void drawAliasedLines(QD3DBatchItem *item); - - void setMaskSize(QSize size); - - void beforeReset(); - void afterReset(); - - IDirect3DSurface9 *freeMaskSurface(); - - inline void lockVertexBuffer(); - inline void unlockVertexBuffer(); - - inline int index() { return m_index; } - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - enum VertexBufferAccess { - CLEAR = 0x00, - READ = 0x01, - WRITE = 0x02 - }; - int accesscontrol[QT_VERTEX_BUF_SIZE]; -#endif - -private: - void addTrap(const Trapezoid &trap); - void tessellate(const QPolygonF &poly); - inline void lineToStencil(qreal x, qreal y); - inline void curveToStencil(const QPointF &cp1, const QPointF &cp2, const QPointF &ep); - QRectF pathToVertexArrays(const QPainterPath &path); - void resetMask(); - - QDirect3DPaintEnginePrivate *m_pe; - - qreal m_xoffset, m_yoffset; - int m_startindex; - int m_index; - int m_height, m_width; - LPDIRECT3DVERTEXBUFFER9 m_d3dvbuff; - vertex *m_vbuff; - QD3DBatchItem *m_item; - QRectF m_boundingRect; - - qreal max_x; - qreal max_y; - qreal min_x; - qreal min_y; - qreal firstx; - qreal firsty; - - QPointF tess_lastpoint; - int tess_index; - - bool m_locked; - IDirect3DTexture9 *m_mask; - IDirect3DSurface9 *m_maskSurface; - IDirect3DSurface9 *m_depthStencilSurface; - - D3DCOLOR m_color; - bool m_clearmask; - bool m_isLine; - bool m_firstPoint; - - QD3DMaskPosition m_mask_position; - int m_mask_offsetX2; - int m_mask_offsetY2; -}; - -QD3DStateManager::QD3DStateManager(LPDIRECT3DDEVICE9 pDevice, ID3DXEffect *effect) - : m_pDevice(pDevice), m_effect(effect), m_refs(0) -{ - if (FAILED(D3DXMatrixIdentity(&m_d3dIdentityMatrix))) { - qWarning("QDirect3DPaintEngine: D3DXMatrixIdentity failed"); - } - reset(); -} - -void QD3DStateManager::reset() -{ - m_radgradfd = -1; - - m_cosmetic_pen = false; - m_pass = -1; - m_maskchannel = -1; - m_brushmode = -1; - m_texture = 0; - m_xoffset = INT_MAX; - m_yoffset = INT_MAX; - - m_vertexshader = 0; - m_pixelshader = 0; - - m_isIdentity = true; - m_transformation = QTransform(); - m_effect->SetMatrix("g_mTransformation", &m_d3dIdentityMatrix); - - ZeroMemory(&m_projection, sizeof(D3DMATRIX)); - ZeroMemory(m_textures, sizeof(LPDIRECT3DBASETEXTURE9) * D3D_STAGE_COUNT); - FillMemory(m_samplerstates, sizeof(DWORD) * D3D_SAMPLE_STATES * D3D_STAGE_COUNT, 0xFFFFFFFE); - FillMemory(m_texturestates, sizeof(DWORD) * D3D_TEXTURE_STATES * D3D_STAGE_COUNT, 0xFFFFFFFE); - FillMemory(m_renderstate, sizeof(DWORD) * D3D_RENDER_STATES, 0xFFFFFFFE); -} - -inline void QD3DStateManager::beginPass(int pass) -{ - if (pass != m_pass) { - if (m_pass != -1) - m_effect->EndPass(); - m_effect->BeginPass(pass); - m_pass = pass; - } -} - -inline void QD3DStateManager::endPass() -{ - if (m_pass != -1) { - m_pass = -1; - m_effect->EndPass(); - } -} - -inline void QD3DStateManager::startStateBlock() { - m_changed = false; -} - -inline void QD3DStateManager::setCosmeticPen(bool enabled) -{ - if (enabled != m_cosmetic_pen) { - m_effect->SetBool("g_mCosmeticPen", enabled); - m_cosmetic_pen = enabled; - m_changed = true; - } -} - -inline void QD3DStateManager::setBrushMode(int mode) -{ - if (mode != m_brushmode) { - m_effect->SetInt("g_mBrushMode", mode); - m_brushmode = mode; - m_changed = true; - } -} - -inline void QD3DStateManager::setTexture(LPDIRECT3DBASETEXTURE9 pTexture) -{ - SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER); - SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); - - if (pTexture != m_texture) { - m_texture = pTexture; - m_effect->SetTexture("g_mTexture", pTexture); - m_changed = true; - } -} - -inline void QD3DStateManager::setTexture(LPDIRECT3DBASETEXTURE9 pTexture, QGradient::Spread spread) -{ - switch(spread) { - case QGradient::RepeatSpread: - SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); - SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); - break; - case QGradient::ReflectSpread: - SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR); - SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR); - break; - default: - SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - break; - }; - - if (pTexture != m_texture) { - m_texture = pTexture; - m_effect->SetTexture("g_mTexture", pTexture); - m_changed = true; - } -} - -inline void QD3DStateManager::setTransformation(const QTransform *matrix) -{ - if (matrix) { - if (*matrix != m_transformation) { - D3DXMATRIX dxmatrix(matrix->m11(), matrix->m12(), 0, matrix->m13(), - matrix->m21(), matrix->m22(), 0, matrix->m23(), - 0, 0, 1, 0, - matrix->dx(), matrix->dy(), 0, 1); - m_effect->SetMatrix("g_mTransformation", &dxmatrix); - m_transformation = *matrix; - m_changed = true; - m_isIdentity = false; - } - } else if (!m_isIdentity) { - m_effect->SetMatrix("g_mTransformation", &m_d3dIdentityMatrix); - m_transformation = QTransform(); - m_changed = true; - m_isIdentity = true; - } -} - -inline void QD3DStateManager::setProjection(const D3DXMATRIX *pMatrix) -{ - if (*pMatrix != m_projection) { - m_effect->SetMatrix("g_mViewProjection", pMatrix); - m_projection = *pMatrix; - m_changed = true; - } -} - -inline void QD3DStateManager::setFocalDistance(const qreal &fd) -{ - if (fd != m_radgradfd) { - m_effect->SetFloat("g_mFocalDist", fd); - m_changed = true; - m_radgradfd = fd; - } -} - -inline void QD3DStateManager::setMaskOffset(qreal x, qreal y) -{ - if (x != m_xoffset || y != m_yoffset) { - float offset[2] = {x, y}; - m_effect->SetFloatArray("g_mMaskOffset", offset, 2); - m_xoffset = x; - m_yoffset = y; - m_changed = true; - } -} - -inline void QD3DStateManager::setMaskChannel(int channel) -{ - if (m_maskchannel != channel) { - m_effect->SetIntArray("g_mChannel", m_mask_channels[channel], 4); - m_maskchannel = channel; - m_changed = true; - } -} - -inline void QD3DStateManager::endStateBlock() -{ - if (m_changed) { - m_effect->CommitChanges(); - m_changed = false; - } -} - -STDMETHODIMP QD3DStateManager::QueryInterface(REFIID iid, LPVOID *ppv) -{ - if(iid == IID_IUnknown || iid == IID_ID3DXEffectStateManager) - { - *ppv = this; - ++m_refs; - return NOERROR; - } - *ppv = NULL; - return ResultFromScode(E_NOINTERFACE); -} - -STDMETHODIMP_(ULONG) QD3DStateManager::AddRef(void) -{ - return (ULONG)InterlockedIncrement( &m_refs ); -} - - -STDMETHODIMP_(ULONG) QD3DStateManager::Release(void) -{ - if( 0L == InterlockedDecrement( &m_refs ) ) { - delete this; - return 0L; - } - - return m_refs; -} -STDMETHODIMP QD3DStateManager::SetTransform(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix) -{ - return m_pDevice->SetTransform(State, pMatrix); -} - -STDMETHODIMP QD3DStateManager::SetMaterial(CONST D3DMATERIAL9 *pMaterial) -{ - return m_pDevice->SetMaterial(pMaterial); -} - -STDMETHODIMP QD3DStateManager::SetLight(DWORD Index, CONST D3DLIGHT9 *pLight) -{ - return m_pDevice->SetLight(Index, pLight); -} - -STDMETHODIMP QD3DStateManager::LightEnable(DWORD Index, BOOL Enable) -{ - return m_pDevice->LightEnable(Index, Enable); -} - -STDMETHODIMP QD3DStateManager::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) -{ - if (State < D3D_RENDER_STATES) { - if (m_renderstate[State] == Value) - return S_OK; - m_renderstate[State] = Value; - } - return m_pDevice->SetRenderState(State, Value); -} - -STDMETHODIMP QD3DStateManager::SetTexture(DWORD Stage, LPDIRECT3DBASETEXTURE9 pTexture) -{ - if (Stage < D3D_STAGE_COUNT) { - if (m_textures[Stage] == pTexture) - return S_OK; - m_textures[Stage] = pTexture; - } - return m_pDevice->SetTexture(Stage, pTexture); -} - -STDMETHODIMP QD3DStateManager::SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value) -{ - if (Stage < D3D_STAGE_COUNT && Type < D3D_TEXTURE_STATES) { - if (m_texturestates[Stage][Type] == Value) - return S_OK; - m_texturestates[Stage][Type] = Value; - } - return m_pDevice->SetTextureStageState(Stage, Type, Value); -} - -STDMETHODIMP QD3DStateManager::SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value) -{ - if (Sampler < D3D_STAGE_COUNT && Type < D3D_SAMPLE_STATES) { - if (m_samplerstates[Sampler][Type] == Value) - return S_OK; - m_samplerstates[Sampler][Type] = Value; - } - return m_pDevice->SetSamplerState(Sampler, Type, Value); -} - -STDMETHODIMP QD3DStateManager::SetNPatchMode(FLOAT NumSegments) -{ - return m_pDevice->SetNPatchMode(NumSegments); -} - -STDMETHODIMP QD3DStateManager::SetFVF(DWORD FVF) -{ - return m_pDevice->SetFVF(FVF); -} - -STDMETHODIMP QD3DStateManager::SetVertexShader(LPDIRECT3DVERTEXSHADER9 pShader) -{ - if (m_vertexshader == pShader) - return S_OK; - m_vertexshader = pShader; - return m_pDevice->SetVertexShader(pShader); -} - -STDMETHODIMP QD3DStateManager::SetVertexShaderConstantF(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount) -{ - return m_pDevice->SetVertexShaderConstantF(RegisterIndex, pConstantData, RegisterCount); -} - -STDMETHODIMP QD3DStateManager::SetVertexShaderConstantI(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount) -{ - return m_pDevice->SetVertexShaderConstantI(RegisterIndex, pConstantData, RegisterCount); -} - -STDMETHODIMP QD3DStateManager::SetVertexShaderConstantB(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount) -{ - return m_pDevice->SetVertexShaderConstantB(RegisterIndex, pConstantData, RegisterCount); -} - -STDMETHODIMP QD3DStateManager::SetPixelShader(LPDIRECT3DPIXELSHADER9 pShader) -{ - if (m_pixelshader == pShader) - return S_OK; - m_pixelshader = pShader; - return m_pDevice->SetPixelShader(pShader); -} - -STDMETHODIMP QD3DStateManager::SetPixelShaderConstantF(UINT RegisterIndex, CONST FLOAT *pConstantData, UINT RegisterCount) -{ - return m_pDevice->SetPixelShaderConstantF(RegisterIndex, pConstantData, RegisterCount); -} - -STDMETHODIMP QD3DStateManager::SetPixelShaderConstantI(UINT RegisterIndex, CONST INT *pConstantData, UINT RegisterCount) -{ - return m_pDevice->SetPixelShaderConstantI(RegisterIndex, pConstantData, RegisterCount); -} - -STDMETHODIMP QD3DStateManager::SetPixelShaderConstantB(UINT RegisterIndex, CONST BOOL *pConstantData, UINT RegisterCount) -{ - return m_pDevice->SetPixelShaderConstantB(RegisterIndex, pConstantData, RegisterCount); -} - -#define QD3D_GRADIENT_CACHE_SIZE 60 -#define QD3D_GRADIENT_PALETTE_SIZE 1024 - -class QD3DGradientCache -{ - struct CacheInfo - { - inline CacheInfo(QGradientStops s, qreal op) : - stops(s), opacity(op) {} - - IDirect3DTexture9 *texture; - QGradientStops stops; - qreal opacity; - }; - - typedef QMultiHash<quint64, CacheInfo> QD3DGradientColorTableHash; - -public: - QD3DGradientCache(LPDIRECT3DDEVICE9 device); - ~QD3DGradientCache(); - - inline IDirect3DTexture9 *getBuffer(const QGradientStops &stops, qreal opacity); - -protected: - inline void generateGradientColorTable(const QGradientStops& s, - uint *colorTable, - int size, qreal opacity) const; - IDirect3DTexture9 *addCacheElement(quint64 hash_val, const QGradientStops &stops, qreal opacity); - void cleanCache(); - - QD3DGradientColorTableHash cache; - LPDIRECT3DDEVICE9 m_device; -}; - -QD3DGradientCache::QD3DGradientCache(LPDIRECT3DDEVICE9 device) - : m_device(device) -{ - -} - -QD3DGradientCache::~QD3DGradientCache() -{ - cleanCache(); -} - -inline IDirect3DTexture9 *QD3DGradientCache::getBuffer(const QGradientStops &stops, qreal opacity) -{ - quint64 hash_val = 0; - - for (int i = 0; i < stops.size() && i <= 2; i++) - hash_val += stops[i].second.rgba(); - - QD3DGradientColorTableHash::const_iterator it = cache.constFind(hash_val); - - if (it == cache.constEnd()) - return addCacheElement(hash_val, stops, opacity); - else { - do { - const CacheInfo &cache_info = it.value(); - if (cache_info.stops == stops && cache_info.opacity == opacity) { - return cache_info.texture; - } - ++it; - } while (it != cache.constEnd() && it.key() == hash_val); - // an exact match for these stops and opacity was not found, create new cache - return addCacheElement(hash_val, stops, opacity); - } -} - -void QD3DGradientCache::generateGradientColorTable(const QGradientStops& s, uint *colorTable, int size, qreal opacity) const -{ - int pos = 0; - qreal fpos = 0.0; - qreal incr = 1.0 / qreal(size); - QVector<uint> colors(s.size()); - - for (int i = 0; i < s.size(); ++i) - colors[i] = s[i].second.rgba(); - - uint alpha = qRound(opacity * 255); - while (fpos < s.first().first) { - colorTable[pos] = ARGB_COMBINE_ALPHA(colors[0], alpha); - pos++; - fpos += incr; - } - - for (int i = 0; i < s.size() - 1; ++i) { - qreal delta = 1/(s[i+1].first - s[i].first); - while (fpos < s[i+1].first && pos < size) { - int dist = int(256 * ((fpos - s[i].first) * delta)); - int idist = 256 - dist; - uint current_color = ARGB_COMBINE_ALPHA(colors[i], alpha); - uint next_color = ARGB_COMBINE_ALPHA(colors[i+1], alpha); -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist); -#else - uint c = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist); - colorTable[pos] = ( (c << 24) & 0xff000000) - | ((c >> 24) & 0x000000ff) - | ((c << 8) & 0x00ff0000) - | ((c >> 8) & 0x0000ff00); -#endif // Q_BYTE_ORDER - ++pos; - fpos += incr; - } - } - for (;pos < size; ++pos) - colorTable[pos] = colors[s.size() - 1]; -} - -IDirect3DTexture9 *QD3DGradientCache::addCacheElement(quint64 hash_val, const QGradientStops &stops, qreal opacity) -{ - if (cache.size() == QD3D_GRADIENT_CACHE_SIZE) { - int elem_to_remove = qrand() % QD3D_GRADIENT_CACHE_SIZE; - uint key = cache.keys()[elem_to_remove]; - - // need to call release on each removed cache entry: - QD3DGradientColorTableHash::const_iterator it = cache.constFind(key); - do { - it.value().texture->Release(); - } while (++it != cache.constEnd() && it.key() == key); - - cache.remove(key); // may remove more than 1, but OK - } - - CacheInfo cache_entry(stops, opacity); - uint buffer[QD3D_GRADIENT_PALETTE_SIZE]; - generateGradientColorTable(stops, buffer, QD3D_GRADIENT_PALETTE_SIZE, opacity); - - if (FAILED(m_device->CreateTexture(QD3D_GRADIENT_PALETTE_SIZE, 1, 1, 0, - D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &cache_entry.texture, 0))) { - qWarning("QD3DGradientCache::addCacheElement(): unable to create Direct3D texture."); - return 0; - } - - D3DLOCKED_RECT rect; - if (FAILED(cache_entry.texture->LockRect(0, &rect, 0, 0))) { - qDebug() << "QD3DGradientCache::addCacheElement(): unable to lock texture rect."; - return 0; - } - memcpy(rect.pBits, buffer, rect.Pitch); - cache_entry.texture->UnlockRect(0); - - return cache.insert(hash_val, cache_entry).value().texture; -} - -void QD3DGradientCache::cleanCache() -{ - QD3DGradientColorTableHash::const_iterator it = cache.constBegin(); - for (; it != cache.constEnd(); ++it) { - const CacheInfo &cache_info = it.value(); - cache_info.texture->Release(); - } - cache.clear(); -} - -QD3DSurfaceManager::QD3DSurfaceManager() : - m_status(NoStatus), m_dummy(0), m_device(0), m_pd(0), m_current(0) -{ - -} - -QD3DSurfaceManager::~QD3DSurfaceManager() -{ -} - -void QD3DSurfaceManager::setPaintDevice(QPaintDevice *pd) -{ - m_status = NoStatus; - m_pd = pd; - m_current = 0; - - if (m_device->TestCooperativeLevel() != D3D_OK) { - m_status = NeedsResetting; - return; - } - - m_current = m_swapchains.value(pd, 0); - QWidget *w = static_cast<QWidget*>(pd); - - if (m_current) { - if (m_current->size != w->size()) { - m_swapchains.remove(pd); - m_current->surface->Release(); - m_current->swapchain->Release(); - delete m_current; - m_current = 0; - } - } - - if (!m_current) { - m_current = createSwapChain(w); - updateMaxSize(); - } -} - -int QD3DSurfaceManager::status() const -{ - return m_status; -} - -void QD3DSurfaceManager::reset() -{ - QList<QPaintDevice *> pds = m_swapchains.keys(); - - QMap<QPaintDevice *, D3DSwapChain *>::const_iterator i = m_swapchains.constBegin(); - while (i != m_swapchains.constEnd()) { - i.value()->surface->Release(); - i.value()->swapchain->Release(); - ++i; - } - qDeleteAll(m_swapchains.values()); - m_swapchains.clear(); - - D3DPRESENT_PARAMETERS params; - initPresentParameters(¶ms); - params.hDeviceWindow = m_dummy; - - HRESULT res = m_device->Reset(¶ms); - if (FAILED(res)) { - switch (res) { - case D3DERR_DEVICELOST: - qWarning("QDirect3DPaintEngine: Reset failed (D3DERR_DEVICELOST)"); - break; - case D3DERR_DRIVERINTERNALERROR: - qWarning("QDirect3DPaintEngine: Reset failed (D3DERR_DRIVERINTERNALERROR)"); - break; - case D3DERR_OUTOFVIDEOMEMORY: - qWarning("QDirect3DPaintEngine: Reset failed (D3DERR_OUTOFVIDEOMEMORY)"); - break; - default: - qWarning("QDirect3DPaintEngine: Reset failed"); - }; - } - - for (int i=0; i<pds.count(); ++i) { - QWidget *w = static_cast<QWidget*>(pds.at(i)); - createSwapChain(w); - } - - // reset the mask as well - m_status = MaxSizeChanged; - - setPaintDevice(m_pd); - updateMaxSize(); -} - -LPDIRECT3DSURFACE9 QD3DSurfaceManager::renderTarget() -{ - return m_current ? m_current->surface : 0; -} - -LPDIRECT3DSURFACE9 QD3DSurfaceManager::surface(QPaintDevice *pd) -{ - D3DSwapChain *swapchain = m_swapchains.value(pd, 0); - return swapchain ? swapchain->surface : 0; -} - -LPDIRECT3DSWAPCHAIN9 QD3DSurfaceManager::swapChain(QPaintDevice *pd) -{ - D3DSwapChain *swapchain = m_swapchains.value(pd, 0); - return swapchain ? swapchain->swapchain : 0; -} - -void QD3DSurfaceManager::releasePaintDevice(QPaintDevice *pd) -{ - D3DSwapChain *swapchain = m_swapchains.take(pd); - - if (swapchain) { - swapchain->surface->Release(); - swapchain->swapchain->Release(); - delete swapchain; - if (swapchain == m_current) - m_current = 0; - } -} - -LPDIRECT3DDEVICE9 QD3DSurfaceManager::device() -{ - return m_device; -} - -void QD3DSurfaceManager::cleanup() -{ - QPixmapCache::clear(); - qd3d_glyph_cache()->cleanCache(); - - // release doomed textures - for (int k=0; k<qd3d_release_list.size(); ++k) - qd3d_release_list.at(k)->Release(); - qd3d_release_list.clear(); - - QMap<QPaintDevice *, D3DSwapChain *>::const_iterator i = m_swapchains.constBegin(); - while (i != m_swapchains.constEnd()) { - i.value()->surface->Release(); - i.value()->swapchain->Release(); - ++i; - } - qDeleteAll(m_swapchains.values()); - - if (m_device) - m_device->Release(); - - DestroyWindow(m_dummy); - QString cname(QLatin1String("qt_d3d_dummy")); - QT_WA({ - UnregisterClass((TCHAR*)cname.utf16(), (HINSTANCE)qWinAppInst()); - } , { - UnregisterClassA(cname.toLatin1(), (HINSTANCE)qWinAppInst()); - }); -} - -QSize QD3DSurfaceManager::maxSize() const -{ - return m_max_size; -} - -extern "C" { - LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); -}; - -void QD3DSurfaceManager::init(LPDIRECT3D9 object) -{ - QString cname(QLatin1String("qt_d3d_dummy")); - uint style = CS_DBLCLKS | CS_SAVEBITS; - ATOM atom; - QT_WA({ - WNDCLASS wc; - wc.style = style; - wc.lpfnWndProc = (WNDPROC)QtWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = (HINSTANCE)qWinAppInst(); - wc.hIcon = 0; - wc.hCursor = 0; - wc.hbrBackground = 0; - wc.lpszMenuName = 0; - wc.lpszClassName = (TCHAR*)cname.utf16(); - atom = RegisterClass(&wc); - } , { - WNDCLASSA wc; - wc.style = style; - wc.lpfnWndProc = (WNDPROC)QtWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = (HINSTANCE)qWinAppInst(); - wc.hIcon = 0; - wc.hCursor = 0; - wc.hbrBackground = 0; - wc.lpszMenuName = 0; - QByteArray tempArray = cname.toLatin1(); - wc.lpszClassName = tempArray; - atom = RegisterClassA(&wc); - }); - - QT_WA({ - const TCHAR *className = (TCHAR*)cname.utf16(); - m_dummy = CreateWindow(className, className, 0, - 0, 0, 1, 1, - 0, 0, qWinAppInst(), 0); - } , { - m_dummy = CreateWindowA(cname.toLatin1(), cname.toLatin1(), 0, - 0, 0, 1, 1, - 0, 0, qWinAppInst(), 0); - }); - - D3DPRESENT_PARAMETERS params; - initPresentParameters(¶ms); - params.hDeviceWindow = m_dummy; - - HRESULT res = object->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 0, - D3DCREATE_PUREDEVICE|D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_NOWINDOWCHANGES|D3DCREATE_FPU_PRESERVE, - ¶ms, &m_device); - - if (FAILED(res) || m_device == 0) - qWarning("QDirect3DPaintEngine: failed to create Direct3D device (error=0x%x).", res); -} - -void QD3DSurfaceManager::updateMaxSize() -{ - int w = 0, h = 0; - QMap<QPaintDevice *, D3DSwapChain *>::const_iterator i = m_swapchains.constBegin(); - while (i != m_swapchains.constEnd()) { - - int nw = i.key()->width(); - if (nw > w) - w = nw; - - int nh = i.key()->height(); - if (nh > h) - h = nh; - - ++i; - } - - QSize newsize = QSize(w, h); - if (newsize != m_max_size) { - m_status |= MaxSizeChanged; - m_max_size = newsize; - } -} - -void QD3DSurfaceManager::initPresentParameters(D3DPRESENT_PARAMETERS *params) -{ - ZeroMemory(params, sizeof(D3DPRESENT_PARAMETERS)); - params->Windowed = true; - params->SwapEffect = D3DSWAPEFFECT_COPY; - params->BackBufferFormat = D3DFMT_UNKNOWN; - params->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - params->Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; -} - -QD3DSurfaceManager::D3DSwapChain *QD3DSurfaceManager::createSwapChain(QWidget *w) -{ - D3DPRESENT_PARAMETERS params; - initPresentParameters(¶ms); - params.hDeviceWindow = w->winId(); - D3DSwapChain *swapchain = new D3DSwapChain(); - swapchain->size = w->size(); - if (FAILED(m_device->CreateAdditionalSwapChain(¶ms, &swapchain->swapchain))) - qWarning("QDirect3DPaintEngine: CreateAdditionalSwapChain failed"); - if (FAILED(swapchain->swapchain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &swapchain->surface))) - qWarning("QDirect3DPaintEngine: GetBackBuffer failed"); - m_swapchains.insert(w, swapchain); - connect(w, SIGNAL(destroyed(QObject *)), SLOT(cleanupPaintDevice(QObject *))); - - // init with background color - QColor bg = w->palette().color(QPalette::Background); - m_device->ColorFill(swapchain->surface, 0, D3DCOLOR_ARGB(bg.alpha(), bg.red(),bg.green(),bg.blue())); - - return swapchain; -} - -void QD3DSurfaceManager::cleanupPaintDevice(QObject *object) -{ - QWidget *w = static_cast<QWidget *>(object); - releasePaintDevice(w); -} - -int QD3DStateManager::m_mask_channels[4][4] = - {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}; - -QD3DDrawHelper::QD3DDrawHelper(QDirect3DPaintEnginePrivate *pe) - : m_pe(pe), m_d3dvbuff(0), m_maskSurface(0), m_depthStencilSurface(0), - m_locked(false), m_mask(0), m_startindex(0), m_index(0), m_vbuff(0), m_clearmask(true), - m_isLine(false), m_firstPoint(true) -{ - resetMask(); -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - memset(accesscontrol, 0, QT_VERTEX_BUF_SIZE * sizeof(VertexBufferAccess)); -#endif - - // create vertex buffer - afterReset(); -} - -QD3DDrawHelper::~QD3DDrawHelper() -{ - if (m_maskSurface) - m_maskSurface->Release(); - - if (m_mask) - m_mask->Release(); - - if (m_depthStencilSurface) - m_depthStencilSurface->Release(); - - if (m_d3dvbuff) - m_d3dvbuff->Release(); -} - -inline void QD3DDrawHelper::lockVertexBuffer() -{ - if (!m_locked) { - DWORD lockflags = D3DLOCK_NOOVERWRITE; - if (m_startindex >= QT_VERTEX_RESET_LIMIT) { - m_startindex = 0; - m_index = 0; - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - for (int i=0; i<QT_VERTEX_BUF_SIZE; ++i) { - if (accesscontrol[i] != (WRITE|READ) && accesscontrol[i] != CLEAR) - qDebug() << "Vertex Buffer: Access Error"; - } - memset(accesscontrol, 0, QT_VERTEX_BUF_SIZE * sizeof(VertexBufferAccess)); -#endif - - lockflags = D3DLOCK_DISCARD; - } - - if (FAILED(m_d3dvbuff->Lock(0, 0, (void**)&m_vbuff, lockflags))) { - qWarning() << "QDirect3DPaintEngine: unable to lock vertex buffer."; - } - m_locked = true; - } -} - -inline void QD3DDrawHelper::unlockVertexBuffer() -{ - if (m_locked) { - if (FAILED(m_d3dvbuff->Unlock())) { - qWarning() << "QDirect3DPaintEngine: unable to unlock vertex buffer."; - } - m_locked = false; - } -} - -void QD3DDrawHelper::setClipPath(const QPainterPath &path, QD3DBatchItem **item) -{ - lockVertexBuffer(); - - m_item = *item; - m_item->m_maskpos.x = m_item->m_maskpos.y = 0; - m_item->m_maskpos.channel = 3; - m_item->m_info |= QD3DBatchItem::BI_CLIP; - - bool winding = (path.fillRule() == Qt::WindingFill); - if (winding) - m_item->m_info |= QD3DBatchItem::BI_WINDING; - - if (!path.isEmpty()) { - m_item->m_info |= QD3DBatchItem::BI_MASK; - m_item->m_info &= ~QD3DBatchItem::BI_AA; - m_color = 0; - QRectF brect = pathToVertexArrays(path); - queueRect(brect, m_item, 0); - } - - *item = m_item; -} - - - -void QD3DDrawHelper::queueAntialiasedMask(const QPolygonF &poly, QD3DBatchItem **item, const QRectF &brect) -{ - lockVertexBuffer(); - - m_item = *item; - m_item->m_info |= QD3DBatchItem::BI_MASK; - setWinding(m_item->m_info & QD3DBatchItem::BI_WINDING); - - int xoffset = m_item->m_maskpos.x; - int yoffset = m_item->m_maskpos.y; - - int x = brect.left(); - int y = brect.top(); - - m_item->m_xoffset = (xoffset - x) + 1; - m_item->m_yoffset = (yoffset - y) + 1; - - m_boundingRect = brect; - tessellate(poly); - - *item = m_item; -} - -QRectF QD3DDrawHelper::queueAliasedMask(const QPainterPath &path, QD3DBatchItem **item, D3DCOLOR color) -{ - lockVertexBuffer(); - - m_color = color; - m_item = *item; - m_item->m_info |= QD3DBatchItem::BI_MASK; - - bool winding = (path.fillRule() == Qt::WindingFill); - if (winding) - m_item->m_info |= QD3DBatchItem::BI_WINDING; - - QRectF result = pathToVertexArrays(path); - *item = m_item; - return result; -} - -// used for drawing aliased transformed rects directly -// don't use for antialiased or masked drawing -void QD3DDrawHelper::queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color, const QPolygonF &trect) -{ - lockVertexBuffer(); - - qreal zval = (item->m_info & QD3DBatchItem::BI_CLIP) ? 0.0f : 0.5f; - item->m_info |= QD3DBatchItem::BI_BRECT; - - // if the item does not have a mask, the offset is different - if (!(item->m_info & QD3DBatchItem::BI_MASK)) { - item->m_offset = m_index; - item->m_count = (item->m_info & QD3DBatchItem::BI_AA) ? 0 : -2; - } - - qreal x1 = rect.left(); - qreal y1 = rect.top(); - qreal x2 = rect.right(); - qreal y2 = rect.bottom(); - - QPointF tc = trect.at(0); - vertex v1 = { {x1, y1, zval} , color, - tc.x(), tc.y(), 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f }; - - tc = trect.at(1); - vertex v2 = { {x2, y1, zval} , color, - tc.x(), tc.y(), 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f}; - - tc = trect.at(2); - vertex v3 = { {x2, y2, zval} , color, - tc.x(), tc.y(), 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f};; - - tc = trect.at(3); - vertex v4 = { {x1, y2, zval} , color, - tc.x(), tc.y(), 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f}; - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - for (int i=m_index; i<(m_index + 4); ++i) { - if ((m_index + 4) > QT_VERTEX_BUF_SIZE) - qDebug() << "Vertex Buffer: Buffer overflow"; - if (accesscontrol[i] != CLEAR) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[i] |= WRITE; - } -#endif - - m_vbuff[m_index++] = v1; - m_vbuff[m_index++] = v2; - m_vbuff[m_index++] = v3; - m_vbuff[m_index++] = v4; - - m_startindex = m_index; -} - - -QD3DMaskPosition QD3DDrawHelper::allocateMaskPosition(const QRectF &brect, bool *breakbatch) -{ - int w = brect.width(); - int h = brect.height(); - - w += 3; - h += 3; - - if (w > m_width) - w = m_width; - if (h > m_height) - h = m_height; - - *breakbatch = false; - - if ((m_height - m_mask_offsetY2) >= h && (m_width - m_mask_position.x) >= w) { - m_mask_position.y = m_mask_offsetY2; - } else if ((m_width - m_mask_offsetX2) >= w) { - m_mask_position.y = QD3D_MASK_MARGIN; - m_mask_position.x = m_mask_offsetX2; - } else if (m_mask_position.channel < 3) { - ++m_mask_position.channel; - m_mask_position.x = m_mask_position.y = QD3D_MASK_MARGIN; - m_mask_offsetX2 = m_mask_offsetY2 = QD3D_MASK_MARGIN; - } else { - resetMask(); - *breakbatch = true; - } - - int newoffset = m_mask_position.x + w; - if (m_mask_offsetX2 < newoffset) - m_mask_offsetX2 = newoffset; - m_mask_offsetY2 = (m_mask_position.y + h); - - return m_mask_position; - -} - -void QD3DDrawHelper::queueRect(const QRectF &rect, QD3DBatchItem *item, D3DCOLOR color) -{ - lockVertexBuffer(); - - QRectF brect; - item->m_info |= QD3DBatchItem::BI_BRECT; - qreal zval = (item->m_info & QD3DBatchItem::BI_CLIP) ? 0.0f : 0.5f; - - if (item->m_info & QD3DBatchItem::BI_AA) { - int xoffset = item->m_maskpos.x; - int yoffset = item->m_maskpos.y; - - int x = rect.left(); - int y = rect.top(); - - brect = QRectF(x, y, rect.width() + 1, rect.height() + 1); - - item->m_xoffset = (xoffset - x) + 1; - item->m_yoffset = (yoffset - y) + 1; - - // if the item does not have a mask, the offset is different - if (!(item->m_info & QD3DBatchItem::BI_MASK)) { - item->m_offset = m_index; - item->m_count = 0; - } - } else { - brect = rect; - - if (!(item->m_info & QD3DBatchItem::BI_MASK)) { - item->m_offset = m_index; - item->m_count = -2; - } - } - - qreal left = brect.left(); - qreal right = brect.right(); - qreal top = brect.top(); - qreal bottom = brect.bottom(); - - vertex v1 = { {left, bottom, zval}, color, - 0.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 0.f}; - vertex v2 = { {left, top, zval}, color, - 0.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 0.f}; - vertex v3 = { {right, top, zval}, color, - 0.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 0.f}; - vertex v4 = { {right, bottom, zval}, color, - 0.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 0.f}; - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - for (int i=m_index; i<(m_index + 4); ++i) { - if ((m_index + 4) > QT_VERTEX_BUF_SIZE) - qDebug() << "Vertex Buffer: Buffer overflow"; - if (accesscontrol[i] != CLEAR) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[i] |= WRITE; - } -#endif - - m_vbuff[m_index++] = v1; - m_vbuff[m_index++] = v2; - m_vbuff[m_index++] = v3; - m_vbuff[m_index++] = v4; - - m_startindex = m_index; -} - -void QD3DDrawHelper::queueAntialiasedLines(const QPainterPath &path, QD3DBatchItem **item, const QRectF &brect) -{ - lockVertexBuffer(); - - m_item = *item; - m_item->m_info |= QD3DBatchItem::BI_MASK; - setWinding(m_item->m_info & QD3DBatchItem::BI_WINDING); - - int xoffset = m_item->m_maskpos.x; - int yoffset = m_item->m_maskpos.y; - int x = brect.left(); - int y = brect.top(); - - m_item->m_xoffset = (xoffset - x) + 1; - m_item->m_yoffset = (yoffset - y) + 1; - - m_boundingRect = brect; - - m_xoffset = (x - xoffset) + 0.5f; - m_yoffset = (y - yoffset) + 0.5f; - - QPointF last; - for (int i = 0; i < path.elementCount(); ++i) { - QPainterPath::Element element = path.elementAt(i); - - //Q_ASSERT(!element.isCurveTo()); - - if (element.isLineTo()) - QTessellator::tessellateRect(last, element, m_item->m_width); - - last = element; - } - - m_item->m_offset = m_startindex; - m_item->m_count = ( m_index - m_startindex ) / 3; - m_startindex = m_index; - - *item = m_item; -} - -void QD3DDrawHelper::queueAliasedLines(const QLineF *lines, int lineCount, QD3DBatchItem **item) -{ - lockVertexBuffer(); - - m_item = *item; - m_item->m_info |= QD3DBatchItem::BI_FASTLINE; - - for (int i=0; i<lineCount; ++i) { - const QLineF line = lines[i]; - qreal p1x = line.p1().x(); - qreal p1y = line.p1().y(); - qreal p2x = line.p2().x(); - qreal p2y = line.p2().y(); - - vertex v1 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color, - -1.f, -1.f, p2x, p2y, - 0.f, 0.f, 0.f, 0.f }; - vertex v2 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color, - 1.f, -1.f, p2x, p2y, - 0.f, 0.f, 0.f, 0.f }; - vertex v3 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color, - 1.f, 1.f, p2x, p2y, - 0.f, 0.f, 0.f, 0.f }; - vertex v4 = { {p1x, p1y, m_pe->m_pen_width} , m_pe->m_pen_color, - -1.f, 1.f, p2x, p2y, - 0.f, 0.f, 0.f, 0.f }; - - m_vbuff[m_index++] = v1; - m_vbuff[m_index++] = v2; - m_vbuff[m_index++] = v4; - m_vbuff[m_index++] = v4; - m_vbuff[m_index++] = v2; - m_vbuff[m_index++] = v3; - - if (m_index >= (QT_VERTEX_BUF_SIZE - 16)) { - m_item->m_offset = m_startindex; - m_item->m_count = ( m_index - m_startindex ) / 2; - m_startindex = m_index; - - QD3DBatchItem itemcopy = *m_item; - m_item = m_pe->nextBatchItem(); - *m_item = itemcopy; - - lockVertexBuffer(); - } - } - - m_item->m_offset = m_startindex; - m_item->m_count = ( m_index - m_startindex ) - 2; - m_startindex = m_index; - - *item = m_item; -} - -void QD3DDrawHelper::queueTextGlyph(const QRectF &rect, const qreal *tex_coords, - QD3DBatchItem *item, D3DCOLOR color) -{ - lockVertexBuffer(); - - qreal x1 = rect.left(); - qreal y1 = rect.top(); - qreal x2 = rect.right(); - qreal y2 = rect.bottom(); - - vertex v1 = { {x1, y1, 0.5f}, color, - tex_coords[0], tex_coords[1], 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f}; - vertex v2 = { {x2, y1, 0.5f}, color, - tex_coords[2], tex_coords[1], 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f}; - vertex v3 = { {x2, y2, 0.5f}, color, - tex_coords[2], tex_coords[3], 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f}; - vertex v4 = { {x1, y1, 0.5f}, color, - tex_coords[0], tex_coords[1], 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f}; - vertex v5 = { {x2, y2, 0.5f}, color, - tex_coords[2], tex_coords[3], 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f}; - vertex v6 = { {x1, y2, 0.5f}, color, - tex_coords[0], tex_coords[3], 0.f, 0.f, - 0.f , 0.f , 0.f, 0.f}; - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - for (int i=m_index; i<(m_index + 6); ++i) { - if ((m_index + 6) > QT_VERTEX_BUF_SIZE) - qDebug() << "Vertex Buffer: Buffer overflow"; - if (accesscontrol[i] != CLEAR) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[i] |= WRITE; - } -#endif - - m_vbuff[m_index++] = v1; - m_vbuff[m_index++] = v2; - m_vbuff[m_index++] = v3; - m_vbuff[m_index++] = v4; - m_vbuff[m_index++] = v5; - m_vbuff[m_index++] = v6; - - m_startindex = m_index; - ++item->m_count; -} - -bool QD3DDrawHelper::needsFlushing() const -{ - return (m_pe->m_batch.m_item_index >= QD3D_BATCH_SIZE || m_startindex >= QT_VERTEX_RESET_LIMIT); -} - -void QD3DDrawHelper::setMaskSize(QSize size) -{ - m_width = size.width(); - m_height = size.height(); - - if (m_maskSurface) - m_maskSurface->Release(); - - if (m_mask) - m_mask->Release(); - - if (FAILED(m_pe->m_d3d_device->CreateTexture(m_width, m_height, 1, D3DUSAGE_RENDERTARGET, - D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_mask, NULL))) { - qWarning() << "QDirect3DPaintEngine: CreateTexture() failed."; - } - - if (m_depthStencilSurface) - m_depthStencilSurface->Release(); - - if (FAILED(m_pe->m_d3d_device->CreateDepthStencilSurface(m_width, m_height, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, - TRUE, &m_depthStencilSurface, NULL))) { - qWarning() << "QDirect3DPaintEngine: CreateDepthStencilSurface() failed."; - } - - m_pe->m_d3d_device->SetDepthStencilSurface(m_depthStencilSurface); - m_pe->m_d3d_device->Clear(0, 0, D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, 0, 0.0f, 0); - - if (FAILED(m_mask->GetSurfaceLevel(0, &m_maskSurface))) { - qWarning() << "QDirect3DPaintEngine: GetSurfaceLevel() failed."; - } - - m_pe->m_d3d_device->ColorFill(m_maskSurface, 0, D3DCOLOR_ARGB(0,0,0,0)); - D3DXMATRIX projMatrix; - pD3DXMatrixOrthoOffCenterLH(&projMatrix, 0, m_width, m_height, 0, 0, 1); - m_pe->m_effect->SetMatrix("g_mMaskProjection", &projMatrix); - m_pe->m_effect->SetTexture("g_mAAMask", m_mask); -} - -void QD3DDrawHelper::beforeReset() -{ - resetMask(); - m_clearmask = true; - - if (m_maskSurface) { - m_maskSurface->Release(); - m_maskSurface = 0; - } - - if (m_mask) { - m_mask->Release(); - m_mask = 0; - } - - if (m_depthStencilSurface) { - m_depthStencilSurface->Release(); - m_depthStencilSurface = 0; - } - - if (m_d3dvbuff) - m_d3dvbuff->Release(); -} - -void QD3DDrawHelper::afterReset() -{ - if (FAILED(m_pe->m_d3d_device->CreateVertexBuffer(QT_VERTEX_BUF_SIZE*sizeof(vertex), D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, - QD3DFVF_CSVERTEX, - D3DPOOL_DEFAULT, &m_d3dvbuff, NULL))) { - qWarning() << "QDirect3DPaintEngine: failed to create vertex buffer."; - } - - m_pe->m_d3d_device->SetStreamSource(0, m_d3dvbuff, 0, sizeof(vertex)); - m_pe->m_d3d_device->SetFVF(QD3DFVF_CSVERTEX); - - m_startindex = 0; - m_index = 0; -} - -IDirect3DSurface9 *QD3DDrawHelper::freeMaskSurface() -{ - // we need to make sure the mask is cleared when it's used for something else - resetMask(); - m_clearmask = true; - - return m_maskSurface; -} - -int QD3DDrawHelper::drawAntialiasedMask(int offset, int maxoffset) -{ - int newoffset = offset; - QD3DBatchItem *item = &(m_pe->m_batch.items[offset]); - - // set mask as render target - if (FAILED(m_pe->m_d3d_device->SetRenderTarget(0, m_maskSurface))) - qWarning() << "QDirect3DPaintEngine: SetRenderTarget failed!"; - - if (m_clearmask) { - m_pe->m_d3d_device->Clear(0, 0, D3DCLEAR_TARGET,D3DCOLOR_ARGB(0,0,0,0), 0, 0); - m_clearmask = false; - } - - // fill the mask - m_pe->m_statemanager->beginPass(PASS_AA_CREATEMASK); - for (; newoffset<maxoffset; ++newoffset) { - item = &(m_pe->m_batch.items[newoffset]); - if (!(item->m_info & QD3DBatchItem::BI_AA) || !(item->m_info & QD3DBatchItem::BI_MASK)) { - break; - } else if (item->m_info & QD3DBatchItem::BI_MASKFULL) { - item->m_info &= ~QD3DBatchItem::BI_MASKFULL; - m_clearmask = true; - break; - } - - m_pe->m_statemanager->startStateBlock(); - if (item->m_info & QD3DBatchItem::BI_MASKSCISSOR) { - RECT rect; - QRectF srect = item->m_brect.adjusted(item->m_xoffset, item->m_yoffset, - item->m_xoffset, item->m_yoffset); - rect.left = qMax(qRound(srect.left()), 0); - rect.top = qMax(qRound(srect.top()), 0); - rect.bottom = qMin(m_height, qRound(srect.bottom())); - rect.right = qMin(m_width, qRound(srect.right())); - m_pe->m_d3d_device->SetScissorRect(&rect); - m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); - } - m_pe->m_statemanager->setMaskChannel(item->m_maskpos.channel); - m_pe->m_statemanager->endStateBlock(); - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - int vbstart = item->m_offset; - for (int i=vbstart; i<(vbstart + (item->m_count * 3)); ++i) { - if (accesscontrol[i] != WRITE) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[i] |= READ; - } -#endif - - m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, item->m_offset, item->m_count); - - if (item->m_info & QD3DBatchItem::BI_MASKSCISSOR) { - m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); - } - } - m_pe->m_statemanager->endPass(); - - return newoffset; -} - -void QD3DDrawHelper::drawAliasedMask(int offset) -{ - QD3DBatchItem *item = &(m_pe->m_batch.items[offset]); - if (item->m_info & QD3DBatchItem::BI_MASK) { - m_pe->m_statemanager->beginPass( (item->m_info & QD3DBatchItem::BI_WINDING) ? PASS_STENCIL_WINDING : PASS_STENCIL_ODDEVEN ); - int prev_stop = 0; - for (int i=0; i<item->m_pointstops.count(); ++i) { - int stop = item->m_pointstops.at(i); - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - int vbstart = (item->m_offset + prev_stop); - for (int j=vbstart; j<(vbstart+(stop - prev_stop)); ++j) { - if (accesscontrol[j] != WRITE) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[j] |= READ; - } -#endif - m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLEFAN, item->m_offset + prev_stop, (stop - prev_stop) - 2); - prev_stop = stop; - } - m_pe->m_statemanager->endPass(); - } -} - -void QD3DDrawHelper::drawTextItem(QD3DBatchItem *item) -{ -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - int vbstart = item->m_offset; - for (int j=vbstart; j<(vbstart + ((item->m_count * 2) * 3)); ++j) { - if (accesscontrol[j] != WRITE) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[j] |= READ; - } -#endif - m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, item->m_offset, item->m_count*2); -} - -void QD3DDrawHelper::drawAliasedLines(QD3DBatchItem *item) -{ - m_pe->m_statemanager->setCosmeticPen(item->m_info & QD3DBatchItem::BI_COSMETICPEN); - if (item->m_info & QD3DBatchItem::BI_TRANSFORM) { - m_pe->m_statemanager->setTransformation(&item->m_matrix); - } else { - m_pe->m_statemanager->setTransformation(); - } - int pass = (item->m_info & QD3DBatchItem::BI_MASK) - ? PASS_ALIASED_LINES - : PASS_ALIASED_LINES_DIRECT; - m_pe->m_statemanager->beginPass(pass); - m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, item->m_offset, (item->m_count + 2) / 3); - m_pe->m_statemanager->endPass(); -} - -void QD3DDrawHelper::drawAntialiasedBoundingRect(QD3DBatchItem *item) -{ - if (item->m_info & QD3DBatchItem::BI_SCISSOR) { - RECT rect; - rect.left = qMax(qRound(item->m_brect.left()), 0); - rect.top = qMax(qRound(item->m_brect.top()), 0); - rect.bottom = qMin(m_height, qRound(item->m_brect.bottom())); - rect.right = qMin(m_width, qRound(item->m_brect.right())); - m_pe->m_d3d_device->SetScissorRect(&rect); - m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); - } - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - int vbstart = item->m_offset + (item->m_count * 3); - for (int j=vbstart; j<(vbstart + 4); ++j) { - if (accesscontrol[j] != WRITE) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[j] |= READ; - } -#endif - - m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLEFAN, item->m_offset + (item->m_count * 3), 2); - - if (item->m_info & QD3DBatchItem::BI_SCISSOR) { - m_pe->m_statemanager->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); - } -} - -void QD3DDrawHelper::drawAliasedBoundingRect(QD3DBatchItem *item) -{ -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - int vbstart = (item->m_offset + item->m_count + 2); - for (int j=vbstart; j<(vbstart + 4); ++j) { - if (accesscontrol[j] != WRITE) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[j] |= READ; - } -#endif - - m_pe->m_d3d_device->DrawPrimitive(D3DPT_TRIANGLEFAN, item->m_offset + item->m_count + 2, 2); -} - -void QD3DDrawHelper::addTrap(const Trapezoid &trap) -{ - qreal topLeftY = Q27Dot5ToDouble(trap.topLeft->y) - m_yoffset; - qreal topLeftX = Q27Dot5ToDouble(trap.topLeft->x) - m_xoffset; - qreal topRightY = Q27Dot5ToDouble(trap.topRight->y) - m_yoffset; - qreal topRightX = Q27Dot5ToDouble(trap.topRight->x) - m_xoffset; - qreal top = Q27Dot5ToDouble(trap.top) - m_yoffset; - qreal bottom = Q27Dot5ToDouble(trap.bottom) - m_yoffset; - - Q27Dot5 _h = trap.topLeft->y - trap.bottomLeft->y; - Q27Dot5 _w = trap.topLeft->x - trap.bottomLeft->x; - qreal _leftA = (qreal)_w/_h; - qreal _leftB = topLeftX - _leftA * topLeftY; - - _h = trap.topRight->y - trap.bottomRight->y; - _w = trap.topRight->x - trap.bottomRight->x; - qreal _rightA = (qreal)_w/_h; - qreal _rightB = topRightX - _rightA * topRightY; - - qreal invLeftA = qFuzzyCompare(_leftA + 1, 1) ? 0.0 : 1.0 / _leftA; - qreal invRightA = qFuzzyCompare(_rightA + 1, 1) ? 0.0 : 1.0 / _rightA; - - vertex v1 = { {1.f, top - 1.f, 0.5f}, 0.f, - top, bottom, invLeftA, -invRightA, - _leftA, _leftB, _rightA, _rightB}; - vertex v2 = { {0.f, top - 1.f, 0.5f}, 0.f, - top, bottom, invLeftA, -invRightA, - _leftA, _leftB, _rightA, _rightB}; - vertex v3 = { {0.f, bottom + 1.f, 0.5f}, 0.f, - top, bottom, invLeftA, -invRightA, - _leftA, _leftB, _rightA, _rightB}; - - vertex v4 = { {1.f, top - 1.f, 0.5f}, 0.f, - top, bottom, invLeftA, -invRightA, - _leftA, _leftB, _rightA, _rightB}; - vertex v5 = { {0.f, bottom + 1.f, 0.5f}, 0.f, - top, bottom, invLeftA, -invRightA, - _leftA, _leftB, _rightA, _rightB}; - vertex v6 = { {1.f, bottom + 1.f, 0.5f}, 0.f, - top, bottom, invLeftA, -invRightA, - _leftA, _leftB, _rightA, _rightB}; - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - for (int i=m_index; i<(m_index + 6); ++i) { - if ((m_index + 6) > QT_VERTEX_BUF_SIZE) - qDebug() << "Vertex Buffer: Buffer overflow"; - if (accesscontrol[i] != CLEAR) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[i] |= WRITE; - } -#endif - - m_vbuff[m_index++] = v1; - m_vbuff[m_index++] = v2; - m_vbuff[m_index++] = v3; - m_vbuff[m_index++] = v4; - m_vbuff[m_index++] = v5; - m_vbuff[m_index++] = v6; - - // check if buffer is full - if (m_index >= (QT_VERTEX_BUF_SIZE - 16)) { - m_item->m_offset = m_startindex; - m_item->m_count = ( m_index - m_startindex ) / 3; - m_startindex = m_index; - - QD3DBatchItem itemcopy = *m_item; - m_item = m_pe->nextBatchItem(); - *m_item = itemcopy; - m_item->m_info &= ~QD3DBatchItem::BI_MASKFULL; - - lockVertexBuffer(); - } -} - -void QD3DDrawHelper::tessellate(const QPolygonF &poly) { - int xoffset = m_item->m_maskpos.x; - int yoffset = m_item->m_maskpos.y; - - int x = m_boundingRect.left(); - int y = m_boundingRect.top(); - m_xoffset = (x - xoffset) + 0.5f; - m_yoffset = (y - yoffset) + 0.5f; - - QTessellator::tessellate(poly.data(), poly.count()); - - m_item->m_offset = m_startindex; - m_item->m_count = ( m_index - m_startindex ) / 3; - m_startindex = m_index; -} - -inline void QD3DDrawHelper::lineToStencil(qreal x, qreal y) -{ - QPointF lastPt = tess_lastpoint; - tess_lastpoint = QPointF(x, y); - - if (m_isLine && m_firstPoint) - return; - - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - if (m_index > QT_VERTEX_BUF_SIZE) - qDebug() << "Vertex Buffer: Buffer overflow"; - if (accesscontrol[m_index] != CLEAR) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[m_index] |= WRITE; -#endif - - vertex v; - if (m_isLine) { - vertex v1 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color, - -1.f, -1.f, x, y, - 0.f, 0.f, 0.f, 0.f}; - vertex v2 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color, - 1.f, -1.f, x, y, - 0.f, 0.f, 0.f, 0.f}; - vertex v3 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color, - 1.f, 1.f, x, y, - 0.f, 0.f, 0.f, 0.f}; - vertex v4 = { {lastPt.x(), lastPt.y(), m_pe->m_pen_width }, m_color, - -1.f, 1.f, x, y, - 0.f, 0.f, 0.f, 0.f}; - m_vbuff[m_index++] = v1; - m_vbuff[m_index++] = v2; - m_vbuff[m_index++] = v4; - m_vbuff[m_index++] = v4; - m_vbuff[m_index++] = v2; - m_vbuff[m_index++] = v3; - } else { - vertex v1 = { {x, y, 0.5f}, m_color, - 0.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 0.f}; - m_vbuff[m_index++] = v1; - v = v1; - } - ++tess_index; - - // check if buffer is full - if (m_index >= (QT_VERTEX_BUF_SIZE - 16)) { - int firstindex = m_startindex; - if (!m_item->m_pointstops.isEmpty()) - firstindex = m_item->m_pointstops.last(); - - vertex first = m_vbuff[firstindex]; - - // finish current polygon - m_item->m_pointstops.append(tess_index); - m_item->m_offset = m_startindex; - m_startindex = m_index; - - // copy item - QD3DBatchItem itemcopy = *m_item; - m_item = m_pe->nextBatchItem(); - *m_item = itemcopy; - - // start new polygon - lockVertexBuffer(); - m_item->m_pointstops.clear(); - if (!m_isLine) { - tess_index = 2; - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - if (accesscontrol[m_index] != CLEAR) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[m_index] |= WRITE; -#endif - - m_vbuff[m_index++] = first; - -#ifdef QT_DEBUG_VERTEXBUFFER_ACCESS - if (accesscontrol[m_index] != CLEAR) - qDebug() << "Vertex Buffer: Access Error"; - accesscontrol[m_index] |= WRITE; -#endif - - m_vbuff[m_index++] = v; - } else { - tess_index = 0; - } - } - - if (x > max_x) - max_x = x; - else if (x < min_x) - min_x = x; - if (y > max_y) - max_y = y; - else if (y < min_y) - min_y = y; -} - -inline void QD3DDrawHelper::curveToStencil(const QPointF &cp1, const QPointF &cp2, - const QPointF &ep) -{ - qreal inverseScale = 0.5f; - qreal inverseScaleHalf = inverseScale / 2; - - QBezier beziers[32]; - beziers[0] = QBezier::fromPoints(tess_lastpoint, cp1, cp2, ep); - QBezier *b = beziers; - while (b >= beziers) { - // check if we can pop the top bezier curve from the stack - qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1); - qreal d; - if (l > inverseScale) { - d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - (b->y4 - b->y1)*(b->x1 - b->x2) ) - + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - (b->y4 - b->y1)*(b->x1 - b->x3) ); - d /= l; - } else { - d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) + - qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3); - } - if (d < inverseScaleHalf || b == beziers + 31) { - // good enough, we pop it off and add the endpoint - lineToStencil(b->x4, b->y4); - --b; - } else { - // split, second half of the polygon goes lower into the stack - b->split(b+1, b); - ++b; - } - } -} - -QRectF QD3DDrawHelper::pathToVertexArrays(const QPainterPath &path) -{ - m_isLine = (m_item->m_info & QD3DBatchItem::BI_FASTLINE); - const QPainterPath::Element &first = path.elementAt(0); - firstx = first.x; - firsty = first.y; - min_x = max_x = firstx; - min_y = max_y = firsty; - - m_firstPoint = true; - tess_index = 0; - m_item->m_pointstops.clear(); - lineToStencil(firstx, firsty); - m_firstPoint = false; - - for (int i=1; i<path.elementCount(); ++i) { - const QPainterPath::Element &e = path.elementAt(i); - switch (e.type) { - case QPainterPath::MoveToElement: - m_item->m_pointstops.append(tess_index); - m_firstPoint = true; - lineToStencil(e.x, e.y); - m_firstPoint = false; - break; - case QPainterPath::LineToElement: - lineToStencil(e.x, e.y); - break; - case QPainterPath::CurveToElement: - curveToStencil(e, path.elementAt(i+1), path.elementAt(i+2)); - i+=2; - break; - default: - break; - } - } - - if (!m_isLine) - lineToStencil(firstx, firsty); - - m_item->m_pointstops.append(tess_index); - - m_item->m_offset = m_startindex; - m_item->m_count = ( m_index - m_startindex ) - 2; - m_startindex = m_index; - - QRectF result; - result.setLeft(min_x); - result.setRight(max_x); - result.setTop(min_y); - result.setBottom(max_y); - - if (m_isLine) - result.adjust(0,0,1,1); - - return result; -} - -void QD3DDrawHelper::resetMask() -{ - m_mask_position.x = m_mask_position.y = QD3D_MASK_MARGIN; - m_mask_position.channel = 0; - m_mask_offsetX2 = m_mask_offsetY2 = QD3D_MASK_MARGIN; -} - - -static inline QPainterPath strokeForPath(const QPainterPath &path, const QPen &cpen) { - QPainterPathStroker stroker; - if (cpen.style() == Qt::CustomDashLine) - stroker.setDashPattern(cpen.dashPattern()); - else - stroker.setDashPattern(cpen.style()); - - stroker.setCapStyle(cpen.capStyle()); - stroker.setJoinStyle(cpen.joinStyle()); - stroker.setMiterLimit(cpen.miterLimit()); - stroker.setWidth(cpen.widthF()); - - QPainterPath stroke = stroker.createStroke(path); - stroke.setFillRule(Qt::WindingFill); - return stroke; -} - - -QDirect3DPaintEnginePrivate::~QDirect3DPaintEnginePrivate() -{ - -} - -void QDirect3DPaintEnginePrivate::updateClipPath(const QPainterPath &path, Qt::ClipOperation op) -{ - //#### remove me - QRegion r(path.toFillPolygon().toPolygon(), path.fillRule()); - updateClipRegion(r, op); - -/* if (m_draw_helper->needsFlushing()) - flushBatch(); - - if (op == Qt::IntersectClip && !has_clipping) - op = Qt::ReplaceClip; - - // switch to paths - if (!m_has_complex_clipping) { - m_clip_path = QPainterPath(); - m_clip_path.addRegion(m_clip_region); - m_clip_region = QRegion(); - m_sysclip_path = QPainterPath(); - m_sysclip_path.addRegion(m_sysclip_region); - m_sysclip_region = QRegion(); - m_has_complex_clipping = true; - } - - QPainterPath cpath = m_matrix.map(path); - - QD3DBatchItem *item = &m_batch.items[m_batch.m_item_index++]; - item->m_info = QD3DBatchItem::BI_COMPLEXCLIP; - - switch (op) { - case Qt::UniteClip: - has_clipping = true; - m_clip_path = m_clip_path.united(cpath); - break; - case Qt::ReplaceClip: - has_clipping = true; - m_clip_path = cpath; - break; - case Qt::NoClip: - m_has_complex_clipping = false; - has_clipping = false; - item->m_info |= QD3DBatchItem::BI_CLEARCLIP; - break; - default: // intersect clip - has_clipping = true; - m_clip_path = m_clip_path.intersected(cpath); - break; - } - - if (!m_sysclip_path.isEmpty()) { - item->m_info &= ~QD3DBatchItem::BI_CLEARCLIP; - if (has_clipping) - m_clip_path = m_clip_path.intersected(m_sysclip_path); - else - m_clip_path = m_sysclip_path; - } - - // update the aliased clipping mask - m_draw_helper->setClipPath(m_clip_path, item); - - // update the antialiased clipping mask - if (m_draw_helper->needsFlushing()) - flushBatch(); - - QD3DBatchItem *aaitem = &m_batch.items[m_batch.m_item_index++]; - aaitem->m_info = item->m_info|QD3DBatchItem::BI_AA; - m_draw_helper->setClipPath(m_clip_path, aaitem); */ -} - -extern QPainterPath qt_regionToPath(const QRegion ®ion); - -void QDirect3DPaintEnginePrivate::updateClipRegion(const QRegion &clipregion, Qt::ClipOperation op) -{ - if (m_draw_helper->needsFlushing()) - flushBatch(); - if (m_has_complex_clipping) { - QPainterPath path = qt_regionToPath(clipregion); - updateClipPath(path, op); - return; - } - - if (op == Qt::IntersectClip && m_clip_region.isEmpty()) - op = Qt::ReplaceClip; - - QRegion cregion = m_matrix.map(clipregion); - - QD3DBatchItem *item = nextBatchItem(); - item->m_info &= ~QD3DBatchItem::BI_AA; - - switch (op) { - case Qt::UniteClip: - m_clip_region = m_clip_region.united(cregion); - break; - case Qt::ReplaceClip: - m_clip_region = cregion; - break; - case Qt::NoClip: - m_clip_region = QRegion(); - item->m_info |= QD3DBatchItem::BI_CLEARCLIP; - break; - default: // intersect clip - m_clip_region = m_clip_region.intersected(cregion); - break; - } - - QRegion crgn = m_clip_region; - if (!m_sysclip_region.isEmpty()) { - item->m_info &= ~QD3DBatchItem::BI_CLEARCLIP; - if (!crgn.isEmpty()) - crgn = crgn.intersected(m_sysclip_region); - else - crgn = m_sysclip_region; - } - - QPainterPath path = qt_regionToPath(crgn); - m_draw_helper->setClipPath(path, &item); -} - -void QDirect3DPaintEnginePrivate::updateFont(const QFont &) -{ -} - -void QDirect3DPaintEnginePrivate::setRenderTechnique(RenderTechnique technique) -{ - if (m_current_technique != technique) { - if (m_current_technique != RT_NoTechnique) - m_effect->End(); - - if (technique == RT_Aliased) { - m_effect->SetTechnique("Aliased"); - m_effect->Begin(0,D3DXFX_DONOTSAVESTATE); - } else if (technique == RT_Antialiased) { - m_effect->SetTechnique("Antialiased"); - m_effect->Begin(0,D3DXFX_DONOTSAVESTATE); - } - } - - m_current_technique = technique; -} - -/*QPolygonF QDirect3DPaintEnginePrivate::transformedRect(const QRectF &brect) const -{ - QPolygonF poly(brect); - return m_matrix.map(poly); -} - -QPolygonF QDirect3DPaintEnginePrivate::calcTextureCoords(const QPolygonF &trect) const -{ - QPolygonF result(4); - QRectF brect = trect.boundingRect(); - qreal angle = atan(trect.at(0).x() - -} - -QPolygonF QDirect3DPaintEnginePrivate::offsetTextureCoords(const QRectF &brect, const QPolygonF &trect) const -{ - -}*/ - -inline QD3DBatchItem *QDirect3DPaintEnginePrivate::nextBatchItem() -{ - if (m_draw_helper->needsFlushing()) - flushBatch(); - - QD3DBatchItem *item = &m_batch.items[m_batch.m_item_index++]; - item->m_info = m_current_state; - item->m_cmode = m_cmode; - return item; -} - -qreal calculateAngle(qreal dx, qreal dy) -{ - qreal angle; - - if (qFuzzyCompare(dx + 1, 1)) { - angle = (dy < 0) ? -M_PI/2 : M_PI/2; - } else { - angle = atanf(dy/dx); - if (dx < 0) - angle += M_PI; - } - - return angle; -} - -QPolygonF QDirect3DPaintEnginePrivate::brushCoordinates(const QRectF &r, bool stroke, qreal *fd) const -{ - QBrush brush; - QTransform matrix; - Qt::BrushStyle style; - - if (stroke) { - brush = m_pen.brush(); - matrix = m_inv_pen_matrix; - style = m_pen_brush_style; - } else { - brush = m_brush; - matrix = m_inv_brush_matrix; - style = m_brush_style; - } - - QPolygonF bpoly; - switch(style) { - case Qt::TexturePattern: { - QTransform totxcoords; - QRectF adj_brect = r.adjusted(-0.5f, -0.5f, -0.5f, -0.5f); - totxcoords.scale(1.0f/brush.texture().width(), - 1.0f/brush.texture().height()); - bpoly = matrix.map(QPolygonF(adj_brect)); - bpoly = totxcoords.map(bpoly); - break; } - case Qt::LinearGradientPattern: { - const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient()); - QPointF start = g->start(); - QPointF stop = g->finalStop(); - qreal dx = stop.x() - start.x(); - qreal dy = stop.y() - start.y(); - qreal length = sqrt(dx * dx + dy * dy); - qreal angle = calculateAngle(dx, dy); - QTransform totxcoords; - QRectF adj_brect = r.adjusted(-0.5f, -0.5f, -0.5f, -0.5f); - totxcoords.scale(1.0f/length, 1.0f/length); - totxcoords.rotateRadians(-angle); - totxcoords.translate(-start.x(), -start.y()); - bpoly = matrix.map(QPolygonF(adj_brect)); - bpoly = totxcoords.map(bpoly); - break; } - case Qt::ConicalGradientPattern: { - const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient()); - QPointF center = g->center(); - qreal angle = g->angle(); - QTransform totxcoords; - totxcoords.rotate(angle); - totxcoords.translate(-center.x(), -center.y()); - bpoly = matrix.map(QPolygonF(r)); - bpoly = totxcoords.map(bpoly); - break; } - case Qt::RadialGradientPattern: { - const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient()); - QPointF center = g->center(); - QPointF focalpoint = g->focalPoint(); - qreal dx = focalpoint.x() - center.x(); - qreal dy = focalpoint.y() - center.y(); - qreal radius = g->radius(); - *fd = sqrt(dx * dx + dy * dy) / radius; - qreal angle = calculateAngle(dx, dy); - QTransform totxcoords; - totxcoords.scale(1.0f/radius, 1.0f/radius); - totxcoords.rotateRadians(-angle); - totxcoords.translate(-center.x(), -center.y()); - bpoly = matrix.map(QPolygonF(r)); - bpoly = totxcoords.map(bpoly); - break; } - default: { - QTransform totxcoords; - QRectF adj_brect = r.adjusted(-0.5f, -0.5f, -0.5f, -0.5f); - QPixmap pat = getPattern(style); - totxcoords.scale(1.0f/pat.width(), - 1.0f/pat.height()); - bpoly = matrix.map(QPolygonF(adj_brect)); - bpoly = totxcoords.map(bpoly); } - }; - - return bpoly; -} - -void QDirect3DPaintEnginePrivate::strokeAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform) -{ - D3DCOLOR solid_color; - QD3DBatchItem *item = nextBatchItem(); - - if (!txform.isIdentity()) - path = txform.map(path); - - QRectF trect; - QPolygonF txcoord; - - solid_color = m_pen_color; - bool has_complex_brush = false; - if (m_pen_brush_style != Qt::SolidPattern) { - has_complex_brush = true; - item->m_brush = m_pen.brush(); - item->m_info |= QD3DBatchItem::BI_COMPLEXBRUSH; - item->m_opacity = m_opacity; - } - - if (m_has_fast_pen) { - item->m_info |= QD3DBatchItem::BI_FASTLINE; - if (m_pen_brush_style == Qt::SolidPattern) { - m_draw_helper->queueAliasedMask(path, &item, solid_color); - item->m_info &= ~QD3DBatchItem::BI_MASK; // bypass stencil buffer - return; - } - } - - QRectF txrect = m_draw_helper->queueAliasedMask(path, &item, 0); - - if (has_complex_brush) { - trect = brect; - txcoord = brushCoordinates(brect, true, &item->m_distance); - item->m_info |= QD3DBatchItem::BI_TRANSFORM; - item->m_matrix = m_matrix; - } else { - trect = txrect; - static const QPolygonF empty_poly(4); - txcoord = empty_poly; - } - - m_draw_helper->queueRect(trect, item, solid_color, txcoord); -} - -void QDirect3DPaintEnginePrivate::fillAliasedPath(QPainterPath path, const QRectF &brect, const QTransform &txform) -{ - D3DCOLOR solid_color; - QD3DBatchItem *item = nextBatchItem(); - - if (!txform.isIdentity()) - path = txform.map(path); - - QRectF trect; - QPolygonF txcoord; - - solid_color = m_brush_color; - bool has_complex_brush = false; - if (m_brush_style != Qt::SolidPattern) { - has_complex_brush = true; - item->m_brush = m_brush; - item->m_info |= QD3DBatchItem::BI_COMPLEXBRUSH; - item->m_opacity = m_opacity; - } - - QRectF txrect = m_draw_helper->queueAliasedMask(path, &item, 0); - - if (has_complex_brush) { - trect = brect; - txcoord = brushCoordinates(brect, false, &item->m_distance); - item->m_info |= QD3DBatchItem::BI_TRANSFORM; - item->m_matrix = m_matrix; - } else { - trect = txrect; - static const QPolygonF empty_poly(4); - txcoord = empty_poly; - } - - m_draw_helper->queueRect(trect, item, solid_color, txcoord); -} - -void QDirect3DPaintEnginePrivate::fillAntialiasedPath(const QPainterPath &path, const QRectF &brect, - const QTransform &txform, bool stroke) -{ - D3DCOLOR solid_color; - bool winding = (path.fillRule() == Qt::WindingFill); - QPolygonF poly; - QRectF txrect; - QPainterPath tpath; - - if (m_has_aa_fast_pen && stroke) { - tpath = txform.map(path); - txrect = tpath.controlPointRect(); - txrect.adjust(-(m_pen_width/2),-(m_pen_width/2), m_pen_width, m_pen_width); - } else { - poly = path.toFillPolygon(txform); - txrect = poly.boundingRect(); - } - - // brect = approx. bounding rect before transformation - // txrect = exact bounding rect after transformation - // trect = the rectangle to be drawn - // txcoord = the texture coordinates - // adj_txrect = adjusted rect to include aliased outline - - bool use_scissor = false; - if (txrect.left() < 0) { - txrect.adjust(-txrect.left(),0,0,0); - use_scissor = true; - } - if (txrect.top() < 0) { - txrect.adjust(0,-txrect.top(),0,0); - use_scissor = true; - } - - if (!txrect.isValid()) - return; - - QD3DBatchItem *item = nextBatchItem(); - - QRectF adj_txrect = txrect.adjusted(-1,-1,1,1); - QRectF trect; - QPolygonF txcoord; - - bool has_complex_brush = false; - if (stroke) { - solid_color = m_pen_color; - if (m_pen_brush_style != Qt::SolidPattern) { - has_complex_brush = true; - item->m_brush = m_pen.brush(); - } - item->m_width = m_pen_width; - } else { - solid_color = m_brush_color; - if (m_brush_style != Qt::SolidPattern) { - has_complex_brush = true; - item->m_brush = m_brush; - } - } - - qreal focaldist = 0; - if (has_complex_brush) { - trect = brect; - txcoord = brushCoordinates(brect, stroke, &focaldist); - } else { - trect = adj_txrect; - static const QPolygonF empty_poly(4); - txcoord = empty_poly; - } - - bool maskfull; - item->m_maskpos = m_draw_helper->allocateMaskPosition(txrect, &maskfull); - if (maskfull) - item->m_info |= QD3DBatchItem::BI_MASKFULL; - item->m_distance = focaldist; - - if (winding) - item->m_info |= QD3DBatchItem::BI_WINDING; - - if (has_complex_brush) { - item->m_info |= QD3DBatchItem::BI_SCISSOR|QD3DBatchItem::BI_COMPLEXBRUSH| - QD3DBatchItem::BI_TRANSFORM; - item->m_brect = adj_txrect; - item->m_matrix = m_matrix; - item->m_opacity = m_opacity; - } - if (use_scissor) { - item->m_info |= QD3DBatchItem::BI_MASKSCISSOR; - item->m_brect = adj_txrect; - } - - if (m_has_aa_fast_pen && stroke) { - m_draw_helper->queueAntialiasedLines(tpath, &item, txrect); - } else { - m_draw_helper->queueAntialiasedMask(poly, &item, txrect); - } - - m_draw_helper->queueRect(trect, item, solid_color, txcoord); -} - -QPainterPath QDirect3DPaintEnginePrivate::strokePathFastPen(const QPainterPath &path) -{ - QPainterPath result; - QBezier beziers[32]; - for (int i=0; i<path.elementCount(); ++i) { - const QPainterPath::Element &e = path.elementAt(i); - switch (e.type) { - case QPainterPath::MoveToElement: - result.moveTo(e.x, e.y); - break; - case QPainterPath::LineToElement: - result.lineTo(e.x, e.y); - break; - - case QPainterPath::CurveToElement: - { - QPointF sp = path.elementAt(i-1); - QPointF cp2 = path.elementAt(i+1); - QPointF ep = path.elementAt(i+2); - i+=2; - - qreal inverseScaleHalf = m_inv_scale / 2; - beziers[0] = QBezier::fromPoints(sp, e, cp2, ep); - QBezier *b = beziers; - while (b >= beziers) { - // check if we can pop the top bezier curve from the stack - qreal l = qAbs(b->x4 - b->x1) + qAbs(b->y4 - b->y1); - qreal d; - if (l > m_inv_scale) { - d = qAbs( (b->x4 - b->x1)*(b->y1 - b->y2) - - (b->y4 - b->y1)*(b->x1 - b->x2) ) - + qAbs( (b->x4 - b->x1)*(b->y1 - b->y3) - - (b->y4 - b->y1)*(b->x1 - b->x3) ); - d /= l; - } else { - d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) + - qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3); - } - if (d < inverseScaleHalf || b == beziers + 31) { - // good enough, we pop it off and add the endpoint - result.lineTo(b->x4, b->y4); - --b; - } else { - // split, second half of the polygon goes lower into the stack - b->split(b+1, b); - ++b; - } - } - } // case CurveToElement - default: - break; - } // end of switch - } - return result; -} - -void QDirect3DPaintEnginePrivate::strokePath(const QPainterPath &path, QRectF brect, bool simple) -{ - QTransform txform; - QPainterPath tpath; - - if (m_has_fast_pen || m_has_aa_fast_pen) { - if (!simple) - tpath = strokePathFastPen(path); - else - tpath = path; //already only lines - } else { - tpath = strokeForPath(path, m_pen); - } - - if (tpath.isEmpty()) - return; - - //brect is null if the path is not transformed - if (brect.isNull()) - txform = m_matrix; - - if (!brect.isNull()) { - // brect is set when the path is transformed already, - // this is the case when we have a cosmetic pen. - brect.adjust(-(m_pen_width/2),-(m_pen_width/2), m_pen_width, m_pen_width); - } - - if (brect.isNull()) - brect = tpath.controlPointRect(); - brect.adjust(-m_inv_scale,-m_inv_scale,m_inv_scale,m_inv_scale); //adjust for antialiasing - - if (m_current_state & QD3DBatchItem::BI_AA) { - fillAntialiasedPath(tpath, brect, txform, true); - } else { - strokeAliasedPath(tpath, brect, txform); - } -} - -void QDirect3DPaintEnginePrivate::fillPath(const QPainterPath &path, QRectF brect) -{ - QTransform txform; - - //brect is null if the path is not transformed - if (brect.isNull()) - txform = m_matrix; - - if (brect.isNull()) - brect = path.controlPointRect(); - brect.adjust(-m_inv_scale,-m_inv_scale,m_inv_scale,m_inv_scale); //adjust for antialiasing - - if (m_current_state & QD3DBatchItem::BI_AA) { - fillAntialiasedPath(path, brect, txform, false); - } else { - fillAliasedPath(path, brect, txform); - } -} - - -bool QDirect3DPaintEnginePrivate::init() -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEnginePrivate::init()"; -#endif - - m_draw_helper = 0; - m_gradient_cache = 0; - m_dc = 0; - m_dcsurface = 0; - - m_supports_d3d = false; - m_current_state = 0; - m_in_scene = false; - m_has_fast_pen = false; - m_has_aa_fast_pen = false; - m_has_pen = false; - m_has_brush = false; - m_pen_color = 0; - m_brush_color = 0; - m_current_surface = 0; - m_batch.m_item_index = 0; - m_current_technique = RT_NoTechnique; - - if (!pDirect3DCreate9) { - QLibrary d3d_lib(QLatin1String("d3d9.dll")); - pDirect3DCreate9 = (PFNDIRECT3DCREATE9) d3d_lib.resolve("Direct3DCreate9"); - if (!pDirect3DCreate9) { - qWarning("QDirect3DPaintEngine: failed to resolve symbols from d3d9.dll.\n" - "Make sure you have the DirectX run-time installed."); - return false; - } - } - - if (!pD3DXCreateBuffer || !pD3DXCreateEffect || !pD3DXMatrixOrthoOffCenterLH) { - QLibrary d3dx_lib(QLatin1String("d3dx9_32.dll")); - pD3DXCreateBuffer = (PFND3DXCREATEBUFFER) d3dx_lib.resolve("D3DXCreateBuffer"); - pD3DXCreateEffect = (PFND3DXCREATEEFFECT) d3dx_lib.resolve("D3DXCreateEffect"); - pD3DXMatrixOrthoOffCenterLH = (PFND3DXMATRIXORTHOOFFCENTERLH) - d3dx_lib.resolve("D3DXMatrixOrthoOffCenterLH"); - if (!(pD3DXCreateBuffer && pD3DXCreateEffect && pD3DXMatrixOrthoOffCenterLH)) { - qWarning("QDirect3DPaintEngine: failed to resolve symbols from d3dx9_32.dll.\n" - "Make sure you have the DirectX run-time installed."); - return false; - } - } - - if (!m_d3d_object) { - m_d3d_object = pDirect3DCreate9(D3D_SDK_VERSION); - if (!m_d3d_object) { - qWarning("QDirect3DPaintEngine: failed to create Direct3D object.\n" - "Direct3D support in Qt will be disabled."); - return false; - } - } - - m_supports_d3d = testCaps(); - if (!m_supports_d3d) - return false; - - m_surface_manager.init(m_d3d_object); - m_d3d_device = m_surface_manager.device(); - - if (!m_d3d_device) - return false; - - /* load shaders */ - QFile file(QLatin1String(":/qpaintengine_d3d.fx")); - QByteArray fxFile; - if (file.open(QFile::ReadOnly)) - fxFile = file.readAll(); - - if (fxFile.size() > 0) { - LPD3DXBUFFER compout; - pD3DXCreateBuffer(4096, &compout); - DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE|D3DXFX_DONOTSAVESTATE|D3DXSHADER_OPTIMIZATION_LEVEL3; - if(FAILED(pD3DXCreateEffect(m_d3d_device, fxFile.constData(), fxFile.size(), - NULL, NULL, dwShaderFlags, NULL, &m_effect, &compout))) { - qWarning("QDirect3DPaintEngine: failed to compile effect file"); - if (compout) - qWarning((char *)compout->GetBufferPointer()); - m_supports_d3d = false; - return false; - } - if (m_effect) { - m_statemanager = new QD3DStateManager(m_d3d_device, m_effect); - m_effect->SetStateManager(m_statemanager); - m_draw_helper = new QD3DDrawHelper(this); - initDevice(); - m_gradient_cache = new QD3DGradientCache(m_d3d_device); - } - } else { - return false; - } - - return true; -} - -QPixmap QDirect3DPaintEnginePrivate::getPattern(Qt::BrushStyle style) const -{ - if (!m_patterns.contains(style)) { - QImage img(16,16,QImage::Format_ARGB32); - img.fill(0); - QPainter p(&img); - p.setBrush(QBrush(Qt::white, style)); - p.setPen(Qt::NoPen); - p.drawRect(0,0,16,16); - p.end(); - QPixmap pattern(QPixmap::fromImage(img)); - QDirect3DPaintEnginePrivate *ct = const_cast<QDirect3DPaintEnginePrivate *>(this); - ct->verifyTexture(pattern); - ct->m_patterns.insert(style, pattern); - } - - return m_patterns.value(style); -} - -bool QDirect3DPaintEnginePrivate::testCaps() -{ - D3DCAPS9 caps; - if (FAILED(m_d3d_object->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps))) - return false; - - if ((caps.PresentationIntervals & D3DPRESENT_INTERVAL_IMMEDIATE) - && (caps.DevCaps & D3DDEVCAPS_PUREDEVICE) - && (caps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST) - && (caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED)) - return true; -#if 0 - qDebug() << "Direct3D caps:"; - qDebug() << "D3DPRESENT_INTERVAL_IMMEDIATE:" << ((caps.PresentationIntervals & D3DPRESENT_INTERVAL_IMMEDIATE) != 0); - qDebug() << "D3DDEVCAPS_PUREDEVICE:" << ((caps.DevCaps & D3DDEVCAPS_PUREDEVICE) != 0); - qDebug() << "D3DPRASTERCAPS_SCISSORTEST:" << ((caps.RasterCaps & D3DPRASTERCAPS_SCISSORTEST) != 0); - qDebug() << "D3DSTENCILCAPS_TWOSIDED:" << ((caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED) != 0); -#endif - return false; -} - -void QDirect3DPaintEnginePrivate::initDevice() -{ - m_statemanager->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - m_statemanager->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); - m_statemanager->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); - m_statemanager->SetRenderState(D3DRS_LIGHTING, FALSE); - m_statemanager->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - m_statemanager->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - m_statemanager->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTALPHA); -} - -void QDirect3DPaintEnginePrivate::updatePen(const QPen &pen) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::updatePen"; -#endif - m_pen = pen; - m_has_cosmetic_pen = false; - m_has_pen = (m_pen.style() != Qt::NoPen); - if (m_has_pen) { - m_pen_brush_style = m_pen.brush().style(); - - if (m_pen_brush_style >= Qt::SolidPattern && m_pen_brush_style <= Qt::DiagCrossPattern) { - int a, r, g, b; - m_pen.color().getRgb(&r, &g, &b, &a); - m_pen_color = D3DCOLOR_ARGB((int)(a * m_opacity),r,g,b); - } else { - m_pen_color = m_opacity_color; - } - - m_has_cosmetic_pen = m_pen.isCosmetic(); - - if (m_pen_brush_style != Qt::NoBrush && - m_pen_brush_style != Qt::SolidPattern) { - bool ok; - m_inv_pen_matrix = m_pen.brush().transform().inverted(&ok); - if (!ok) - qWarning() << "QDirect3DPaintEngine: No inverse matix for pen brush matrix."; - } - - m_pen_width = m_pen.widthF(); - if (m_pen_width == 0.0f) - m_pen_width = 1.0f; - } -} - -void QDirect3DPaintEnginePrivate::updateBrush(const QBrush &brush) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::updateBrush"; -#endif - m_brush = brush; - m_brush_style = m_brush.style(); - m_has_brush = (m_brush_style != Qt::NoBrush); - if (m_has_brush) { - if (m_brush_style >= Qt::SolidPattern && m_brush_style <= Qt::DiagCrossPattern) { - int a, r, g, b; - m_brush.color().getRgb(&r, &g, &b, &a); - m_brush_color = D3DCOLOR_ARGB((int)(a * m_opacity),r,g,b); - } else { - m_brush_color = m_opacity_color; - } - - if (m_brush_style != Qt::SolidPattern) { - bool ok; - m_inv_brush_matrix = (m_brush.transform() * m_brush_origin).inverted(&ok); - if (!ok) - qWarning() << "QDirect3DPaintEngine: No inverse matix for brush matrix."; - - // make sure the texture is loaded as a texture - if (m_brush_style == Qt::TexturePattern) - verifyTexture(m_brush.texture()); - - - } - } -} - -void QDirect3DPaintEnginePrivate::updateTransform(const QTransform &matrix) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::updateTransform"; -#endif - m_matrix = matrix; - m_inv_scale = qMax(1 / qMax( qMax(qAbs(m_matrix.m11()), qAbs(m_matrix.m22())), - qMax(qAbs(m_matrix.m12()), qAbs(m_matrix.m21())) ), 0.0001); - m_txop = matrix.type(); -} - -int QDirect3DPaintEnginePrivate::flushAntialiased(int offset) -{ - // fills the mask (returns number of items added to the mask) - int newoffset = m_draw_helper->drawAntialiasedMask(offset, m_batch.m_item_index); - - // set the render target to the current output surface - if (FAILED(m_d3d_device->SetRenderTarget(0, m_current_surface))) - qWarning() << "QDirect3DPaintEngine: SetRenderTarget failed!"; - - // draw the bounding boxes (using the mask generated by drawAntialiasedMask) - for (int i=offset; i<newoffset; ++i) { - QD3DBatchItem *item = &(m_batch.items[i]); - int pass = (item->m_info & QD3DBatchItem::BI_COMPLEXBRUSH) ? PASS_AA_DRAW : PASS_AA_DRAW_DIRECT; - m_statemanager->beginPass(pass); - prepareItem(item); - if (item->m_info & QD3DBatchItem::BI_BRECT) - m_draw_helper->drawAntialiasedBoundingRect(item); - cleanupItem(item); - } - - m_statemanager->endPass(); - - return newoffset; -} - -bool QDirect3DPaintEnginePrivate::prepareBatch(QD3DBatchItem *item, int offset) -{ - if (item->m_info & QD3DBatchItem::BI_CLIP) { - setRenderTechnique(RT_Aliased); - if (item->m_info & QD3DBatchItem::BI_CLEARCLIP) { - m_d3d_device->Clear(0, 0, D3DCLEAR_ZBUFFER, 0, 0.0f, 0); - return true; - } - - m_draw_helper->drawAliasedMask(offset); - m_d3d_device->Clear(0, 0, D3DCLEAR_ZBUFFER, 0, 1.0f, 0); - if (item->m_info & QD3DBatchItem::BI_BRECT) { - m_statemanager->beginPass(PASS_STENCIL_CLIP); - m_draw_helper->drawAliasedBoundingRect(item); - m_statemanager->endPass(); - } - - return true; - } - - if (item->m_info & QD3DBatchItem::BI_AA) { - setRenderTechnique(RT_Antialiased); - } else { - setRenderTechnique(RT_Aliased); - } - - return false; -} - -void QDirect3DPaintEnginePrivate::prepareItem(QD3DBatchItem *item) { - // pixmap - int brushmode = 0; - m_statemanager->startStateBlock(); - if ((item->m_info & QD3DBatchItem::BI_PIXMAP) || (item->m_info & QD3DBatchItem::BI_IMAGE)) { - QRasterPixmapData *data = static_cast<QRasterPixmapData*>(item->m_pixmap.data); - IDirect3DTexture9 *tex = (item->m_info & QD3DBatchItem::BI_PIXMAP) ? - data->texture : item->m_texture; - m_statemanager->setTexture(tex); - brushmode = 5; - } - - if (item->m_info & QD3DBatchItem::BI_AA) { - m_statemanager->setMaskChannel(item->m_maskpos.channel); - m_statemanager->setMaskOffset(item->m_xoffset, item->m_yoffset); - } - - if (item->m_info & QD3DBatchItem::BI_COMPLEXBRUSH) { - const QBrush brush = item->m_brush; - switch (brush.style()) { - case Qt::TexturePattern: { - QRasterPixmapData *data = static_cast<QRasterPixmapData*>(brush.texture().data); - m_statemanager->setTexture(data->texture, QGradient::RepeatSpread); - brushmode = 1; - break; - } - case Qt::LinearGradientPattern: - m_statemanager->setTexture(m_gradient_cache-> - getBuffer(brush.gradient()->stops(), item->m_opacity), - brush.gradient()->spread()); - brushmode = 2; - break; - case Qt::ConicalGradientPattern: - m_statemanager->setTexture(m_gradient_cache-> - getBuffer(brush.gradient()->stops(), item->m_opacity), - brush.gradient()->spread()); - brushmode = 3; - break; - case Qt::RadialGradientPattern: - m_statemanager->setTexture(m_gradient_cache-> - getBuffer(brush.gradient()->stops(), item->m_opacity), - brush.gradient()->spread()); - m_statemanager->setFocalDistance(item->m_distance); - brushmode = 4; - break; - default: { - QRasterPixmapData *data = static_cast<QRasterPixmapData*>(getPattern(brush.style()).data); - m_statemanager->setTexture(data->texture, QGradient::RepeatSpread); - brushmode = 5; - } - }; - } - - if (item->m_info & QD3DBatchItem::BI_TRANSFORM) { - m_statemanager->setTransformation(&item->m_matrix); - } else { - m_statemanager->setTransformation(); - } - - m_statemanager->setBrushMode(brushmode); - setCompositionMode(item->m_cmode); - m_statemanager->endStateBlock(); -} - - -void QDirect3DPaintEnginePrivate::releaseDC() -{ - if (m_dc) { - m_dcsurface->ReleaseDC(m_dc); - m_dcsurface = 0; - m_dc = 0; - } -} - - -void QDirect3DPaintEnginePrivate::cleanupItem(QD3DBatchItem *item) -{ - if (item->m_info & QD3DBatchItem::BI_PIXMAP) - item->m_pixmap = QPixmap(); - item->m_brush = QBrush(); -} - -void QDirect3DPaintEnginePrivate::verifyTexture(const QPixmap &pm) -{ - QRasterPixmapData *pmData = static_cast<QRasterPixmapData*>(pm.data); - if (!pmData->texture) { - QImage im = pmData->image; - // bitmaps are drawn with the current pen color - if (im.depth() == 1) { - QVector<QRgb> colors(2); - colors[0] = 0; - colors[1] = m_pen.color().rgba(); - im.setColorTable(colors); - } - im = im.convertToFormat(QImage::Format_ARGB32); - if (FAILED(m_d3d_device->CreateTexture(im.width(), im.height(), 1, 0, - D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &pmData->texture, 0))) - { - qWarning("QDirect3DPaintEngine: unable to create Direct3D texture from pixmap."); - return; - } - D3DLOCKED_RECT rect; - if (FAILED(pmData->texture->LockRect(0, &rect, 0, 0))) { - qDebug() << "QDirect3DPaintEngine: unable to lock texture rect."; - return; - } - DWORD *dst = (DWORD *) rect.pBits; - DWORD *src = (DWORD *) im.scanLine(0); - - Q_ASSERT((rect.Pitch/4) == (im.bytesPerLine()/4)); - memcpy(dst, src, rect.Pitch*im.height()); - pmData->texture->UnlockRect(0); - } -} - -bool QDirect3DPaintEnginePrivate::isFastRect(const QRectF &rect) -{ - if (m_matrix.type() < QTransform::TxRotate) { - QRectF r = m_matrix.mapRect(rect); - return r.topLeft().toPoint() == r.topLeft() - && r.bottomRight().toPoint() == r.bottomRight(); - } - - return false; -} - -void QDirect3DPaintEnginePrivate::setCompositionMode(QPainter::CompositionMode mode) -{ - switch(mode) { - case QPainter::CompositionMode_SourceOver: - default: - m_statemanager->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - m_statemanager->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - }; -} - -void QDirect3DPaintEnginePrivate::cleanup() -{ - // clean batch - for(int i=0; i<QD3D_BATCH_SIZE; ++i) { - m_batch.items[i].m_brush = QBrush(); - m_batch.items[i].m_pixmap = QPixmap(); - } - - m_surface_manager.cleanup(); - m_patterns.clear(); - - delete m_gradient_cache; - delete m_draw_helper; - - if (m_effect) - m_effect->Release(); - - if (m_d3d_object) - m_d3d_object->Release(); - - m_effect = 0; - m_d3d_object = 0; - m_gradient_cache = 0; - m_draw_helper = 0; -} - -void QDirect3DPaintEnginePrivate::flushAliased(QD3DBatchItem *item, int offset) -{ - m_draw_helper->drawAliasedMask(offset); - - if (item->m_info & QD3DBatchItem::BI_BRECT) { - int pass = (item->m_info & QD3DBatchItem::BI_MASK) ? PASS_STENCIL_DRAW_DIRECT : PASS_STENCIL_NOSTENCILCHECK_DIRECT; - if (item->m_info & (QD3DBatchItem::BI_COMPLEXBRUSH|QD3DBatchItem::BI_IMAGE|QD3DBatchItem::BI_PIXMAP) ) - pass = (item->m_info & QD3DBatchItem::BI_MASK) ? PASS_STENCIL_DRAW : PASS_STENCIL_NOSTENCILCHECK; - m_statemanager->beginPass(pass); - prepareItem(item); - m_draw_helper->drawAliasedBoundingRect(item); - cleanupItem(item); - m_statemanager->endPass(); - } -} - -void QDirect3DPaintEnginePrivate::flushText(QD3DBatchItem *item, int) -{ - prepareItem(item); - m_statemanager->setTexture(item->m_texture); - m_statemanager->setBrushMode(1); -// m_statemanager->SetRenderState(D3DRS_BLENDFACTOR, item->m_brush.color().rgba()); - m_statemanager->beginPass(m_cleartype_text ? PASS_CLEARTYPE_TEXT : PASS_TEXT); - m_draw_helper->drawTextItem(item); - m_statemanager->endPass(); - cleanupItem(item); -} - -void QDirect3DPaintEnginePrivate::flushLines(QD3DBatchItem *item, int) -{ - m_draw_helper->drawAliasedLines(item); - - if (item->m_info & QD3DBatchItem::BI_BRECT) { - int pass = (item->m_info & QD3DBatchItem::BI_COMPLEXBRUSH) ? PASS_STENCIL_DRAW : PASS_STENCIL_DRAW_DIRECT; - m_statemanager->beginPass(pass); - prepareItem(item); - m_draw_helper->drawAliasedBoundingRect(item); - cleanupItem(item); - m_statemanager->endPass(); - } -} - -void QDirect3DPaintEnginePrivate::flushBatch() -{ -// static int dbgcounter = 0; -// ++dbgcounter; -// qDebug() << " -> flush" << dbgcounter; - - int offset = 0; - m_draw_helper->unlockVertexBuffer(); - releaseDC(); - - // iterate over all items in the batch - while (offset != m_batch.m_item_index) { - QD3DBatchItem *item = &(m_batch.items[offset]); - - if (prepareBatch(item, offset)) { - ++offset; - continue; - } - - if (item->m_info & QD3DBatchItem::BI_FASTLINE) { - flushLines(item, offset++); - } else if (item->m_info & QD3DBatchItem::BI_AA) { - offset = flushAntialiased(offset); - } else if (item->m_info & QD3DBatchItem::BI_TEXT) { - flushText(item, offset++); - } else { - flushAliased(item, offset++); - } - } - - // reset batch - m_batch.m_item_index = 0; - - // release doomed textures - for (int i=0; i<qd3d_release_list.size(); ++i) - qd3d_release_list.at(i)->Release(); - qd3d_release_list.clear(); -} - -QDirect3DPaintEngine::QDirect3DPaintEngine() - : QPaintEngine(*(new QDirect3DPaintEnginePrivate), - PaintEngineFeatures(AllFeatures & ~ObjectBoundingModeGradients)) -{ } - -QDirect3DPaintEngine::~QDirect3DPaintEngine() -{ -} - -bool QDirect3DPaintEngine::begin(QPaintDevice *device) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::begin"; -#endif - Q_D(QDirect3DPaintEngine); - setActive(true); - - QSize old_size = d->m_surface_size; - d->m_surface_size = QRect(0, 0, device->width(), device->height()).size(); - - d->m_current_state = 0; - d->m_inv_scale = 1; - d->m_opacity = 1.0f; - d->m_opacity_color = D3DCOLOR_ARGB(255,255,255,255); - d->m_matrix = QTransform(); - d->m_brush_origin = QTransform(); - d->m_txop = QTransform::TxNone; - d->m_cmode = QPainter::CompositionMode_SourceOver; - - Q_ASSERT(device && device->devType() == QInternal::Widget); - if (d->m_d3d_device == 0) { - qWarning() << "QDirect3DPaintEngine: No Device!"; - return false; - } - - d->m_cleartype_text = false; -// QT_WA({ -// UINT result; -// BOOL ok; -// ok = SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0); -// if (ok) -// d->m_cleartype_text = (result == FE_FONTSMOOTHINGCLEARTYPE); -// }, { -// UINT result; -// BOOL ok; -// ok = SystemParametersInfoA(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0); -// if (ok) -// d->m_cleartype_text = (result == FE_FONTSMOOTHINGCLEARTYPE); -// }); - - d->m_surface_manager.setPaintDevice(device); - int status = d->m_surface_manager.status(); - if (status & QD3DSurfaceManager::NeedsResetting) { - d->m_effect->OnLostDevice(); - d->m_draw_helper->beforeReset(); - d->m_statemanager->reset(); - d->m_surface_manager.reset(); - d->m_draw_helper->afterReset(); - d->m_effect->OnResetDevice(); - d->initDevice(); - } - - LPDIRECT3DSURFACE9 newsurface = d->m_surface_manager.renderTarget(); - if (d->m_current_surface != newsurface) { - d->m_current_surface = newsurface; - if (FAILED(d->m_d3d_device->SetRenderTarget(0, newsurface))) - qWarning() << "QDirect3DPaintEngine: SetRenderTarget failed!"; - } - - status = d->m_surface_manager.status(); - if (status & QD3DSurfaceManager::MaxSizeChanged) { - QSize maxsize = d->m_surface_manager.maxSize(); - d->m_draw_helper->setMaskSize(maxsize); - int masksize[2] = {maxsize.width(), maxsize.height()}; - d->m_effect->SetIntArray("g_mMaskSize", masksize, 2); - } - - if (old_size != d->m_surface_size) { - D3DXMATRIX projMatrix; - pD3DXMatrixOrthoOffCenterLH(&projMatrix, 0, d->m_surface_size.width(), d->m_surface_size.height(), 0, 0.0f, 1.0f); - d->m_statemanager->setProjection(&projMatrix); - } - - if (!d->m_in_scene) { - if (FAILED(d->m_d3d_device->BeginScene())) { - qWarning() << "QDirect3DPaintEngine: BeginScene() failed."; - return false; - } - QWidget *widget = static_cast<QWidget *>(device); - if (widget->autoFillBackground() == true) { - QColor color = widget->palette().brush(widget->backgroundRole()).color(); - RECT rect = {0, 0, widget->width(), widget->height()}; - d->m_d3d_device->ColorFill(d->m_current_surface, &rect, - D3DCOLOR_ARGB(color.alpha(), color.red(), color.green(), color.blue())); - } - d->m_in_scene = true; - } - - // set system clip - d->m_clipping_enabled = false; - d->m_has_complex_clipping = false; - - d->m_sysclip_region = systemClip(); - QVector<QRect> rects = d->m_sysclip_region.rects(); - if (rects.count() == 1 && rects.at(0).size() == d->m_surface_size) - d->m_sysclip_region = QRegion(); - - d->updateClipRegion(QRegion(), Qt::NoClip); - - return true; -} - -void QDirect3DPaintEngine::drawEllipse(const QRectF &rect) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawEllipse (float)"; -#endif - QPaintEngine::drawEllipse(rect); -} - -void QDirect3DPaintEngine::drawEllipse(const QRect &rect) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawEllipse"; -#endif - QPaintEngine::drawEllipse(rect); -} - -void QDirect3DPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, - Qt::ImageConversionFlags) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawImage"; -#endif - - Q_D(QDirect3DPaintEngine); - int width = image.width(); - int height = image.height(); - - // transform rectangle - QPolygonF txrect(QRectF(sr.left() / width, sr.top() / height, - sr.width() / width, sr.height() / height)); - - QD3DBatchItem *item = d->nextBatchItem(); - item->m_info = QD3DBatchItem::BI_IMAGE | QD3DBatchItem::BI_TRANSFORM; - item->m_texture = qd3d_image_cache()->lookup(d->m_d3d_device, image); - item->m_matrix = d->m_matrix; - d->m_draw_helper->queueRect(r.adjusted(-0.5f,-0.5f,-0.5f,-0.5f), item, d->m_opacity_color, txrect); -} - -void QDirect3DPaintEngine::drawLines(const QLineF *lines, int lineCount) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawLines (float)"; -#endif - Q_D(QDirect3DPaintEngine); - - if (!d->m_has_pen) - return; - - if (d->m_has_fast_pen && (d->m_pen_brush_style == Qt::SolidPattern)) { - QD3DBatchItem *item = d->nextBatchItem(); - if (d->m_pen.isCosmetic()) - item->m_info |= QD3DBatchItem::BI_COSMETICPEN; - item->m_info |= QD3DBatchItem::BI_TRANSFORM; - item->m_matrix = d->m_matrix; - d->m_draw_helper->queueAliasedLines(lines, lineCount, &item); - } else { - QRectF brect; - QPainterPath path; - - // creates a path with the lines - path.moveTo(lines[0].x1(), lines[0].y1()); - qreal lastx = lines[0].x2(); - qreal lasty = lines[0].y2(); - path.lineTo(lastx, lasty); - - for (int i=1; i<lineCount; ++i) { - qreal x = lines[i].x1(); - qreal y = lines[i].y1(); - if (lastx != x || lasty != y) { - path.moveTo(x, y); - } - path.lineTo(lines[i].x2(), lines[i].y2()); - } - - if (d->m_has_cosmetic_pen) { - brect = path.controlPointRect(); - path = d->m_matrix.map(path); - } - - d->strokePath(path, brect, true); - } -} - -void QDirect3DPaintEngine::drawLines(const QLine *lines, int lineCount) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawLines"; -#endif - QPaintEngine::drawLines(lines, lineCount); -} - -void QDirect3DPaintEngine::drawPath(const QPainterPath &path) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawPath"; -#endif - Q_D(QDirect3DPaintEngine); - - if (path.isEmpty()) - return; - - QRectF brect; - QPainterPath tpath; - - if (d->m_has_cosmetic_pen) { - brect = path.controlPointRect(); - tpath = d->m_matrix.map(path); - } else { - tpath = path; - } - - if (d->m_has_brush) - d->fillPath(tpath, brect); - - if (d->m_has_pen) - d->strokePath(tpath, brect); -} - - -QPointF QDirect3DPaintEnginePrivate::transformPoint(const QPointF &p, qreal *w) const -{ - (*w) = 1.0f; - qreal fx = p.x(); - qreal fy = p.y(); - qreal nx = m_matrix.m11()*fx + m_matrix.m21()*fy + m_matrix.m31(); - qreal ny = m_matrix.m12()*fx + m_matrix.m22()*fy + m_matrix.m32(); - if (!m_matrix.isAffine()) { - *w = m_matrix.m13()*fx + m_matrix.m23()*fy + m_matrix.m33(); - //*w = 1/(*w); - nx = nx/(*w); - ny = ny/(*w); - } - return QPointF(nx, ny); -} - -void QDirect3DPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawPixmap"; -#endif - Q_D(QDirect3DPaintEngine); - - if (d->m_draw_helper->needsFlushing()) - d->flushBatch(); - - int width = pm.width(); - int height = pm.height(); - - // transform rectangle - QPolygonF txrect(QRectF(sr.left() / width, sr.top() / height, - sr.width() / width, sr.height() / height)); - - QD3DBatchItem *item = d->nextBatchItem(); - item->m_info = QD3DBatchItem::BI_PIXMAP|QD3DBatchItem::BI_TRANSFORM; - - item->m_pixmap = pm; - d->verifyTexture(item->m_pixmap); - - item->m_matrix = d->m_matrix; - d->m_draw_helper->queueRect(r.adjusted(-0.5f,-0.5f,-0.5f,-0.5f), item, d->m_opacity_color, txrect); -} - -void QDirect3DPaintEngine::drawPoints(const QPointF *points, int pointCount) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawPoints (float)"; -#endif - QPaintEngine::drawPoints(points, pointCount); -} - -void QDirect3DPaintEngine::drawPoints(const QPoint *points, int pointCount) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawPoints"; -#endif - QPaintEngine::drawPoints(points, pointCount); -} - -void QDirect3DPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawPolygon"; -#endif - Q_D(QDirect3DPaintEngine); - - if (d->m_has_brush && mode != PolylineMode) { - QPainterPath path; - path.setFillRule(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill); - path.moveTo(points[0]); - for (int i=1; i<pointCount; ++i) - path.lineTo(points[i]); - if (path.isEmpty()) - return; - d->fillPath(path, QRectF()); - } - - if (d->m_has_pen) { - QPainterPath path(points[0]); - for (int i = 1; i < pointCount; ++i) - path.lineTo(points[i]); - if (mode != PolylineMode) - path.lineTo(points[0]); - - if (path.isEmpty()) - return; - QRectF brect; - if (d->m_has_cosmetic_pen) { - brect = path.controlPointRect(); - path = d->m_matrix.map(path); - } - - d->strokePath(path, brect); - } -} - -void QDirect3DPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawPolygon"; -#endif - QPaintEngine::drawPolygon(points, pointCount, mode); -} - -void QDirect3DPaintEngine::drawRects(const QRectF *rects, int rectCount) -{ - Q_D(QDirect3DPaintEngine); -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawRects (float)"; -#endif - for (int i=0; i<rectCount; ++i) { - if ((d->m_brush_style == Qt::SolidPattern) && - (!(d->m_current_state & QD3DBatchItem::BI_AA) || d->isFastRect(rects[i]))) { - QD3DBatchItem *item = d->nextBatchItem(); - item->m_info |= QD3DBatchItem::BI_TRANSFORM; - item->m_info &= ~QD3DBatchItem::BI_AA; - item->m_matrix = d->m_matrix; - const QRectF rect = rects[i]; - d->m_draw_helper->queueRect(rect, item, d->m_brush_color); - - if (d->m_has_pen) { - if (d->m_has_fast_pen && (d->m_pen_brush_style == Qt::SolidPattern)) { - QLineF lines[4]; - qreal x1 = rect.x(); - qreal y1 = rect.y(); - qreal x2 = rect.width() + x1; - qreal y2 = rect.height() + y1; - lines[0] = QLineF(x1, y1, x2, y1); - lines[1] = QLineF(x2, y1, x2, y2); - lines[2] = QLineF(x2, y2, x1, y2); - lines[3] = QLineF(x1, y2, x1, y1); - QD3DBatchItem *item = d->nextBatchItem(); - if (d->m_pen.isCosmetic()) - item->m_info |= QD3DBatchItem::BI_COSMETICPEN; - item->m_info |= QD3DBatchItem::BI_TRANSFORM; - item->m_matrix = d->m_matrix; - d->m_draw_helper->queueAliasedLines(lines, 4, &item); - } else { - QPainterPath path; - QRectF brect; - - path.addRect(rects[i]); - if (d->m_has_cosmetic_pen) { - brect = path.controlPointRect(); - path = d->m_matrix.map(path); - } - - d->strokePath(path, brect, true); - } - } - } else { - QPainterPath path; - QRectF brect; - - path.addRect(rects[i]); - if (d->m_has_cosmetic_pen) { - brect = path.controlPointRect(); - path = d->m_matrix.map(path); - } - - if (d->m_has_brush) - d->fillPath(path, brect); - - if (d->m_has_pen) - d->strokePath(path, brect, true); - } - } -} - -void QDirect3DPaintEngine::drawRects(const QRect *rects, int rectCount) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawRects"; -#endif - QPaintEngine::drawRects(rects, rectCount); -} - - -void QDirect3DPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) -{ - Q_D(QDirect3DPaintEngine); - -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawTextItem"; -#endif -// if (d->m_matrix.isScaling() || (d->m_pen_brush_style >= Qt::LinearGradientPattern -// && d->m_pen_brush_style <= Qt::ConicalGradientPattern)) { -// QPaintEngine::drawTextItem(p, textItem); -// return; -// } - - const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); - QVarLengthArray<QFixedPoint> positions; - QVarLengthArray<glyph_t> glyphs; - QTransform matrix; - matrix.translate(p.x(), p.y()); - ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); - - qd3d_glyph_cache()->cacheGlyphs(this, ti, glyphs, d->m_cleartype_text); - QD3DFontTexture *font_tex = qd3d_glyph_cache()->fontTexture(ti.fontEngine); - - QD3DBatchItem *item = d->nextBatchItem(); - d->m_draw_helper->lockVertexBuffer(); - - item->m_info = QD3DBatchItem::BI_TEXT - | (d->m_current_state & ~QD3DBatchItem::BI_AA) | QD3DBatchItem::BI_TRANSFORM; - item->m_texture = font_tex->texture; - item->m_offset = d->m_draw_helper->index(); - item->m_matrix = d->m_matrix; - item->m_count = 0; - item->m_brush = d->m_pen.brush(); - - for (int i=0; i< glyphs.size(); ++i) { - QD3DGlyphCoord *g = qd3d_glyph_cache()->lookup(ti.fontEngine, glyphs[i]); - - // we don't cache glyphs with no width/height - if (!g) - continue; - - // texture coords - qreal tex_coords[] = { g->x, g->y, g->x + g->width, g->y + g->height }; - QPointF logical_pos(qRound((positions[i].x - g->x_offset).toReal()) - 0.5f, - qRound((positions[i].y + g->y_offset).toReal()) - 0.5f); - - QRectF glyph_rect(logical_pos, QSizeF(g->log_width, g->log_height)); - d->m_draw_helper->queueTextGlyph(glyph_rect, tex_coords, item, d->m_pen_color); - } -} - -void QDirect3DPaintEngine::drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::drawTiledPixmap"; -#endif - QPaintEngine::drawTiledPixmap(rect, pixmap, p); -} - -bool QDirect3DPaintEngine::end() -{ - Q_D(QDirect3DPaintEngine); - - d->flushBatch(); - - if (d->m_flush_on_end) { - QPaintDevice *pdev = paintDevice(); - LPDIRECT3DSWAPCHAIN9 swapchain = swapChain(pdev); - - - QWidget *w = 0; - if (pdev->devType() == QInternal::Widget) { - w = static_cast<QWidget *>(pdev); - } - - if (w && swapchain) { - QRect br = w->rect(); - QRect wbr = br;//.translated(-w->pos()); - - RECT destrect; - destrect.left = wbr.x(); - destrect.top = wbr.y(); - destrect.right = destrect.left + wbr.width(); - destrect.bottom = destrect.top + wbr.height(); - - RECT srcrect; - srcrect.left = br.x();// + w->x(); - srcrect.top = br.y();// + w->y(); - srcrect.right = wbr.width() + srcrect.left; - srcrect.bottom = wbr.height() + srcrect.top; - int devwidth = w->width(); - int devheight = w->height(); - - if (devwidth <= srcrect.right) { - int diff = srcrect.right - devwidth; - srcrect.right -= diff; - destrect.right -= diff; - if (srcrect.right <= srcrect.left) - return false; - } - if (devheight <= srcrect.bottom) { - int diff = srcrect.bottom - devheight; - srcrect.bottom -= diff; - destrect.bottom -= diff; - if (srcrect.bottom <= srcrect.top) - return false; - } - - if (FAILED(swapchain->Present(&srcrect, &destrect, w->winId(), 0, 0))) - qWarning("QDirect3DPaintEngine: failed to present back buffer."); - } - } - - - return true; -} - -void QDirect3DPaintEngine::updateState(const QPaintEngineState &state) -{ -#ifdef QT_DEBUG_D3D_CALLS - qDebug() << "QDirect3DPaintEngine::updateState"; -#endif - Q_D(QDirect3DPaintEngine); - - bool update_fast_pen = false; - DirtyFlags flags = state.state(); - - if (flags & DirtyOpacity) { - d->m_opacity = state.opacity(); - if (d->m_opacity > 1.0f) - d->m_opacity = 1.0f; - if (d->m_opacity < 0.f) - d->m_opacity = 0.f; - uint c = (d->m_opacity * 255); - d->m_opacity_color = D3DCOLOR_ARGB(c,c,c,c); - flags |= (DirtyPen | DirtyBrush); - } - - if (flags & DirtyCompositionMode) { - d->m_cmode = state.compositionMode(); - } - - if (flags & DirtyTransform) { - d->updateTransform(state.transform()); - update_fast_pen = true; - } - - if (flags & DirtyHints) { - if (state.renderHints() & QPainter::Antialiasing) - d->m_current_state |= QD3DBatchItem::BI_AA; - else - d->m_current_state &= ~QD3DBatchItem::BI_AA; - update_fast_pen = true; - } - - if (flags & DirtyFont) { - d->updateFont(state.font()); - } - - if (state.state() & DirtyClipEnabled) { - if (state.isClipEnabled() && !d->m_clipping_enabled) { - d->m_clipping_enabled = true; - if (d->m_has_complex_clipping) - d->updateClipPath(painter()->clipPath(), Qt::ReplaceClip); - else - d->updateClipRegion(painter()->clipRegion(), Qt::ReplaceClip); - } else if (!state.isClipEnabled() && d->m_clipping_enabled) { - d->m_clipping_enabled = false; - if (d->m_has_complex_clipping) - d->updateClipPath(QPainterPath(), Qt::NoClip); - else - d->updateClipRegion(QRegion(), Qt::NoClip); - } - } - - if (flags & DirtyClipRegion) { - d->updateClipRegion(state.clipRegion(), state.clipOperation()); - } - - if (flags & DirtyClipPath) { - d->updateClipPath(state.clipPath(), state.clipOperation()); - } - - if (flags & DirtyBrushOrigin) { - d->m_brush_origin = QTransform(); - d->m_brush_origin.translate(-state.brushOrigin().x(), - -state.brushOrigin().y()); - flags |= DirtyBrush; - } - - if (flags & DirtyPen) { - d->updatePen(state.pen()); - update_fast_pen = true; - } - - if (flags & DirtyBrush) - d->updateBrush(state.brush()); - - if (update_fast_pen && d->m_has_pen) { - if (d->m_current_state & QD3DBatchItem::BI_AA) { - d->m_has_fast_pen = false; - d->m_has_aa_fast_pen = ((d->m_txop <= QTransform::TxTranslate) || d->m_has_cosmetic_pen) - && (d->m_pen_width <= 1.0f) - && (d->m_pen.style() == Qt::SolidLine); - } else { - d->m_has_aa_fast_pen = false; - d->m_has_fast_pen = ((d->m_txop <= QTransform::TxTranslate) || d->m_has_cosmetic_pen) - && (d->m_pen.style() == Qt::SolidLine) - && (d->m_pen.capStyle() == Qt::SquareCap); - } - } -} - -void QDirect3DPaintEngine::cleanup() -{ - Q_D(QDirect3DPaintEngine); - d->cleanup(); -} - -void QDirect3DPaintEngine::scroll(QPaintDevice *pd, const RECT &srcrect, const RECT &destrect) -{ - Q_D(QDirect3DPaintEngine); - LPDIRECT3DSURFACE9 srcsurf = d->m_surface_manager.surface(pd); - LPDIRECT3DSURFACE9 masksurf = d->m_draw_helper->freeMaskSurface(); - if (FAILED(d->m_d3d_device->StretchRect(srcsurf, &srcrect, masksurf, &srcrect, D3DTEXF_NONE))) - qWarning("QDirect3DPaintEngine: StretchRect failed."); - if (FAILED(d->m_d3d_device->StretchRect(masksurf, &srcrect, srcsurf, &destrect, D3DTEXF_NONE))) - qWarning("QDirect3DPaintEngine: StretchRect failed."); -} - -LPDIRECT3DSWAPCHAIN9 QDirect3DPaintEngine::swapChain(QPaintDevice *pd) -{ - Q_D(QDirect3DPaintEngine); - - if (d->m_in_scene) { - if (d->m_d3d_device == 0) { - qWarning("QDirect3DPaintEngine: No device!"); - return false; - } - - d->setRenderTechnique(QDirect3DPaintEnginePrivate::RT_NoTechnique); - if (FAILED(d->m_d3d_device->EndScene())) - qWarning("QDirect3DPaintEngine: failed to end scene."); - - d->m_in_scene = false; - } - - return d->m_surface_manager.swapChain(pd); -} - -void QDirect3DPaintEngine::releaseSwapChain(QPaintDevice *pd) -{ - Q_D(QDirect3DPaintEngine); - d->m_surface_manager.releasePaintDevice(pd); -} - -HDC QDirect3DPaintEngine::getDC() const -{ - QDirect3DPaintEnginePrivate *d = const_cast<QDirect3DPaintEnginePrivate *>(d_func()); - - if (!d->m_dc && d->m_current_surface) { - d->m_dcsurface = d->m_current_surface; - if (FAILED(d->m_current_surface->GetDC(&d->m_dc))) - qWarning() << "QDirect3DPaintEngine::getDC() failed!"; - } - - return d->m_dc; -} - -void QDirect3DPaintEngine::setFlushOnEnd(bool flushOnEnd) -{ - Q_D(QDirect3DPaintEngine); - - d->m_flush_on_end = flushOnEnd; -} - -bool QDirect3DPaintEngine::hasDirect3DSupport() -{ - Q_D(QDirect3DPaintEngine); - return d->m_supports_d3d; -} - -QT_END_NAMESPACE - -#include "qpaintengine_d3d.moc" diff --git a/src/gui/painting/qpaintengine_d3d.fx b/src/gui/painting/qpaintengine_d3d.fx deleted file mode 100644 index 1148b2a..0000000 --- a/src/gui/painting/qpaintengine_d3d.fx +++ /dev/null @@ -1,608 +0,0 @@ -bool g_mCosmeticPen; -int4 g_mChannel; -float2 g_mMaskOffset; -int2 g_mMaskSize; -float4x4 g_mMaskProjection; -float4x4 g_mViewProjection; -float4x4 g_mTransformation; -texture g_mAAMask; -texture g_mTexture; -int g_mBrushMode; -float g_mFocalDist; - -#define M_PI 3.14159265358979323846 - -sampler PixmapSampler = sampler_state -{ - texture = <g_mTexture>; - MIPFILTER = NONE; - MINFILTER = LINEAR; - MAGFILTER = LINEAR; -}; - -sampler TextSampler = sampler_state -{ - texture = <g_mTexture>; - MIPFILTER = NONE; - MINFILTER = POINT; - MAGFILTER = POINT; -}; - -sampler AAMaskSampler = sampler_state -{ - texture = <g_mAAMask>; - AddressU = WRAP; - AddressV = WRAP; - AddressW = WRAP; - MIPFILTER = NONE; - MINFILTER = POINT; - MAGFILTER = POINT; -}; - -struct VS_FULL -{ - float4 Position : POSITION; - float4 Diffuse : COLOR0; - float4 TexCoords0 : TEXCOORD0; - float4 TexCoords1 : TEXCOORD1; -}; - -VS_FULL TrapezoidVS( float4 Position : POSITION, - float4 Diffuse : COLOR0, - float4 TexCoords0 : TEXCOORD0, - float4 TexCoords1 : TEXCOORD1) -{ - VS_FULL Output; - - float a = (TexCoords1.x * Position.x) + (TexCoords1.z * (1.0 - Position.x) ); // left or right a - float b = (TexCoords1.y * Position.x) + (TexCoords1.w * (1.0 - Position.x) ); // left or right b - float d = 1.0 - (Position.x * 2); - - Position.x = (a * Position.y + b) + ( sqrt( abs(a * a) ) * d ); - //Position.x += step(abs(a), 0) * d; - Position.x += (0.5 * d); - - Output.Position = mul(Position, g_mMaskProjection); - Output.Diffuse = Diffuse; - Output.TexCoords0 = TexCoords0; - Output.TexCoords1 = TexCoords1; - - return Output; -} - -struct PS_OUTPUT -{ - float4 Color : COLOR0; -}; - -PS_OUTPUT TrapezoidPS(VS_FULL In, float2 pixelPos : VPOS) -{ - PS_OUTPUT Out; - - float top = max(pixelPos.y - 0.5, In.TexCoords0.x); - float bottom = min(pixelPos.y + 0.5, In.TexCoords0.y); - - float area = bottom - top; - - float left = pixelPos.x - 0.5; - float right = pixelPos.x + 0.5; - - // use line equations to compute intersections of left/right edges with top/bottom of truncated pixel - // vecX: x = (left, top), y = (left, bottom), z = (right, top), w = (right, bottom) - float4 vecX = In.TexCoords1.xxzz * float2(top, bottom).xyxy + In.TexCoords1.yyww; - - float2 invA = In.TexCoords0.zw; - - // transform right line to left to be able to use same calculations for both - vecX.zw = 2 * pixelPos.x - vecX.zw; - - float2 topX = float2(vecX.x, vecX.z); - float2 bottomX = float2(vecX.y, vecX.w); - - // transform lines such that top intersection is to the right of bottom intersection - float2 topXTemp = max(topX, bottomX); - float2 bottomXTemp = min(topX, bottomX); - - // make sure line slope reflects mirrored lines - invA = lerp(invA, -invA, step(topX, bottomX)); - - float2 vecLeftRight = float2(left, right); - - // compute the intersections of the lines with the left and right edges of the pixel - // intersectY: x = (left_line, left), y = (left_line, right), z = (right_line, left), w = (right_line, right) - float4 intersectY = top + (vecLeftRight.xyxy - topXTemp.xxyy) * invA.xxyy; - - float2 temp = lerp(area - 0.5 * (right - bottomXTemp) * (bottom - intersectY.yw), // left < bottom < right < top - (0.5 * (topXTemp + bottomXTemp) - left) * area, // left < bottom < top < right - step(topXTemp, right)); - - float2 excluded = 0.5 * (intersectY.xz - top) * (topXTemp - left); // bottom < left < top < right - - excluded = lerp(0.5 * (intersectY.yw + intersectY.xz) - top, // bottom < left < right < top - excluded, step(topXTemp, right)); - - excluded = lerp(temp, // left < bottom < right (see calculation of temp) - excluded, step(bottomXTemp, left)); - - excluded = lerp(float2(area, area), // right < bottom < top - excluded, step(bottomXTemp, right)); - - excluded *= step(left, topXTemp); - - float result = (area - excluded.x - excluded.y) * step(top, bottom); - Out.Color.r = result * g_mChannel[0]; - Out.Color.g = result * g_mChannel[1]; - Out.Color.b = result * g_mChannel[2]; - Out.Color.a = result * g_mChannel[3]; - - return Out; -} - -VS_FULL ViewProjectionVS( float4 Position : POSITION, - float4 Diffuse : COLOR0, - float4 TexCoords0 : TEXCOORD0, - float4 TexCoords1 : TEXCOORD1) -{ - VS_FULL Output; - - Output.Position = mul(Position, g_mTransformation); - Output.Position = mul(Output.Position, g_mViewProjection); - Output.Diffuse = Diffuse; - Output.TexCoords0 = TexCoords0; - Output.TexCoords1 = TexCoords1; - - return Output; -} - -PS_OUTPUT DirectMaskPS(VS_FULL In, float2 pixelPos : VPOS) -{ - PS_OUTPUT Out; - Out.Color = In.Diffuse; - - float2 maskcoords = ( (pixelPos + g_mMaskOffset) - 0.5 ) / g_mMaskSize; - float2 clipcoords = (pixelPos - 0.5) / g_mMaskSize; - - float4 c = tex2D(AAMaskSampler, maskcoords.xy) * Out.Color.a; - Out.Color.a = c.r * g_mChannel[0]; - Out.Color.a += c.g * g_mChannel[1]; - Out.Color.a += c.b * g_mChannel[2]; - Out.Color.a += c.a * g_mChannel[3]; - - return Out; -} - -PS_OUTPUT MaskPS(VS_FULL In, float2 pixelPos : VPOS) -{ - PS_OUTPUT Out; - - if (g_mBrushMode == 1) { - float x = In.TexCoords0.x; - float y = In.TexCoords0.y; - x = x - int(x); - y = y - int(y); - Out.Color = tex2D(PixmapSampler, float2(x, y)); - Out.Color.a = Out.Color.a * In.Diffuse.a; - } else if (g_mBrushMode == 2) { - Out.Color = tex1D(PixmapSampler, In.TexCoords0.x); - } else if (g_mBrushMode == 3) { - float t = atan2(In.TexCoords0.y, -In.TexCoords0.x) / (2 * M_PI); - Out.Color = tex1D(PixmapSampler, t + 0.5); - } else if (g_mBrushMode == 4) { - float2 tc = float2(In.TexCoords0.x, abs(In.TexCoords0.y)); - float a = (tc.x - g_mFocalDist) / tc.y; - float b = g_mFocalDist; - - float A = 1 + (a * a); - float B = 2.0 * a * b; - float C = (b * b) - 1; - - float y = (-B + sqrt(B*B - 4.0*A*C)) / (2.0*A); - Out.Color = tex1D(PixmapSampler, (tc.y / y) ); - } else if (g_mBrushMode == 5) { - Out.Color = tex2D(PixmapSampler, In.TexCoords0.xy); - Out.Color = Out.Color * In.Diffuse; - } else { - Out.Color = In.Diffuse; - } - - float2 maskcoords = ( (pixelPos + g_mMaskOffset) - 0.5 ) / g_mMaskSize; - - float4 c = tex2D(AAMaskSampler, maskcoords.xy) * Out.Color.a; - Out.Color.a = c.r * g_mChannel[0]; - Out.Color.a += c.g * g_mChannel[1]; - Out.Color.a += c.b * g_mChannel[2]; - Out.Color.a += c.a * g_mChannel[3]; - - return Out; -} - -struct VS_NORMAL -{ - float4 Position : POSITION; - float4 Diffuse : COLOR0; - float4 TexCoords : TEXCOORD0; -}; - -VS_NORMAL MaskProjectionVS(VS_NORMAL In) -{ - VS_NORMAL Output; - - Output.Position = mul(In.Position, g_mMaskProjection); - Output.Diffuse = In.Diffuse; - Output.TexCoords = In.TexCoords; - - return Output; -} - -float4 DirectSimplePS(float4 Color : COLOR0) : COLOR0 -{ - return Color; -} - -float4 SimplePS(float4 Color : COLOR0, float4 TexCoords : TEXCOORD0) : COLOR0 -{ - if (g_mBrushMode == 1) { - float opacity = Color.a; - float x = TexCoords.x; - float y = TexCoords.y; - x = x - int(x); - y = y - int(y); - Color = tex2D(PixmapSampler, float2(x, y)); - Color.a = Color.a * opacity; - } else if (g_mBrushMode == 2) { - Color = tex1D(PixmapSampler, TexCoords.x); - } else if (g_mBrushMode == 3) { - float t = atan2(TexCoords.y, -TexCoords.x) / (2 * M_PI); - Color = tex1D(PixmapSampler, t + 0.5); - } else if (g_mBrushMode == 4) { - float2 tc = float2(TexCoords.x, abs(TexCoords.y)); - float a = (tc.x - g_mFocalDist) / tc.y; - float b = g_mFocalDist; - - float A = 1 + (a * a); - float B = 2.0 * a * b; - float C = (b * b) - 1; - - float y = (-B + sqrt(B*B - 4.0*A*C)) / (2.0*A); - Color = tex1D(PixmapSampler, (tc.y / y) ); - } else if (g_mBrushMode == 5) { - Color = tex2D(PixmapSampler, TexCoords.xy) * Color; - } - - return Color; -} - -float4 TextPS(float4 Color : COLOR0, float4 TexCoords : TEXCOORD0) : COLOR0 -{ - Color.a *= tex2D(TextSampler, TexCoords.xy).a; - return Color; -} - -float4 ClearTypePS(float4 Color : COLOR0, float4 TexCoords : TEXCOORD0) : COLOR0 -{ -// if (g_mUsePixmap) { -// float4 MaskColor = tex2D(PixmapSampler, TexCoords.xy); -// Color = float4(1.0, 0.0, 0.0, 1.0); -// Color.a = (1 - MaskColor.a) + MaskColor.a * Color.a; -// Color.r = (1.0 - MaskColor.r) + (MaskColor.r * Color.r); -// Color.g = (1.0 - MaskColor.g) + (MaskColor.g * Color.g); -// Color.b = (1.0 - MaskColor.b) + (MaskColor.b * Color.b); -// Color = MaskColor; - return tex2D(PixmapSampler, TexCoords.xy); -} - -VS_NORMAL NoTxAliasedVS(VS_NORMAL In) -{ - VS_NORMAL Output; - - Output.Position = mul(In.Position, g_mViewProjection); - Output.Diffuse = In.Diffuse; - Output.TexCoords = In.TexCoords; - - return Output; -} - -VS_NORMAL AliasedVS(VS_NORMAL In) -{ - VS_NORMAL Output; - - Output.Position = mul(In.Position, g_mTransformation); - Output.Position = mul(Output.Position, g_mViewProjection); - Output.Diffuse = In.Diffuse; - Output.TexCoords = In.TexCoords; - - return Output; -} - -VS_NORMAL AliasedLinesVS(VS_NORMAL In) -{ - VS_NORMAL Output; - - float4 start = float4(In.Position.x, In.Position.y, 0.5, In.Position.w); - float4 end = float4(In.TexCoords.z, In.TexCoords.w, 0.5, In.Position.w); - if (g_mCosmeticPen) { - start = mul(start, g_mTransformation); - end = mul(end, g_mTransformation); - } - - float2 line_vec = end - start; - float2 vec = normalize(line_vec); - float2 norm = float2(-vec.y, vec.x); - - float pen_width = In.Position.z; - norm = norm * pen_width * 0.5; - vec = vec * pen_width * 0.5; - - Output.Position.w = In.Position.w; - Output.Position.x = start.x + (vec.x * In.TexCoords.x); - Output.Position.x = Output.Position.x + (norm.x * In.TexCoords.y); - Output.Position.x = Output.Position.x + (line_vec.x * step(0, In.TexCoords.x)); - Output.Position.y = start.y + (vec.y * In.TexCoords.x); - Output.Position.y = Output.Position.y + (norm.y * In.TexCoords.y); - Output.Position.y = Output.Position.y + (line_vec.y * step(0, In.TexCoords.x)); - Output.Position.z = 0.5; - - if (!g_mCosmeticPen) { - Output.Position = mul(Output.Position, g_mTransformation); - } - Output.Position = mul(Output.Position, g_mViewProjection); - - Output.Diffuse = In.Diffuse; - Output.TexCoords = In.TexCoords; - - return Output; -} - - -technique Antialiased -{ - pass PASS_AA_CREATEMASK - { - StencilEnable = False; - ZWriteEnable = False; - ColorWriteEnable = 0x0f; - ZEnable = False; - - SrcBlend = One; - DestBlend = One; - - VertexShader = compile vs_3_0 TrapezoidVS(); - PixelShader = compile ps_3_0 TrapezoidPS(); - } - - pass PASS_AA_DRAW - { - StencilEnable = False; - ZFunc = Greater; - ZWriteEnable = False; - ZEnable = True; - ColorWriteEnable = 0x0f; - - VertexShader = compile vs_3_0 ViewProjectionVS(); - PixelShader = compile ps_3_0 MaskPS(); - } - - pass PASS_AA_DRAW_DIRECT - { - StencilEnable = False; - ZFunc = Greater; - ZEnable = True; - ZWriteEnable = False; - ColorWriteEnable = 0x0f; - - VertexShader = compile vs_3_0 ViewProjectionVS(); - PixelShader = compile ps_3_0 DirectMaskPS(); - } -} - -technique Aliased -{ - pass PASS_STENCIL_ODDEVEN - { - TwoSidedStencilMode = False; - StencilEnable = True; - StencilPass = Invert; - StencilFunc = Always; - ColorWriteEnable = 0; - - ZEnable = False; - ZWriteEnable = False; - - VertexShader = compile vs_1_1 NoTxAliasedVS(); - PixelShader = compile ps_2_0 DirectSimplePS(); - } - - pass PASS_STENCIL_WINDING - { - TwoSidedStencilMode = True; - StencilEnable = True; - StencilRef = 0; - StencilMask = 0xFFFFFFFF; - - CCW_StencilPass = Incr; - CCW_StencilFunc = Always; - - StencilPass = Decr; - StencilFunc = Always; - - ColorWriteEnable = 0; - - ZEnable = False; - ZWriteEnable = False; - - VertexShader = compile vs_1_1 NoTxAliasedVS(); - PixelShader = compile ps_2_0 DirectSimplePS(); - } - - pass PASS_STENCIL_DRAW - { - TwoSidedStencilMode = False; - StencilEnable = True; - StencilFunc = NotEqual; - StencilMask = 0xFFFFFFFF; - StencilRef = 0; - StencilPass = Zero; - StencilFail = Zero; - StencilZFail = Zero; - - ColorWriteEnable = 0x0f; - ZEnable = True; - ZWriteEnable = False; - ZFunc = Greater; - - VertexShader = compile vs_1_1 AliasedVS(); - PixelShader = compile ps_2_0 SimplePS(); - } - - pass PASS_STENCIL_DRAW_DIRECT - { - TwoSidedStencilMode = False; - StencilEnable = True; - StencilFunc = NotEqual; - StencilMask = 0xFFFFFFFF; - StencilRef = 0; - StencilPass = Zero; - StencilFail = Zero; - StencilZFail = Zero; - - ColorWriteEnable = 0x0f; - ZEnable = True; - ZWriteEnable = False; - ZFunc = Greater; - - VertexShader = compile vs_1_1 AliasedVS(); - PixelShader = compile ps_2_0 DirectSimplePS(); - } - - pass PASS_STENCIL_CLIP - { - TwoSidedStencilMode = False; - StencilEnable = True; - StencilFunc = NotEqual; - StencilMask = 0xFFFFFFFF; - StencilRef = 0; - StencilPass = Zero; - StencilFail = Zero; - StencilZFail = Zero; - - ColorWriteEnable = 0; - ZEnable = True; - ZWriteEnable = True; - ZFunc = Always; - - VertexShader = compile vs_1_1 NoTxAliasedVS(); - PixelShader = compile ps_2_0 DirectSimplePS(); - } - - pass PASS_STENCIL_NOSTENCILCHECK - { - StencilEnable = False; - - ZEnable = True; - ZWriteEnable = False; - ZFunc = Greater; - - ColorWriteEnable = 0x0f; - - SrcBlend = SrcAlpha; - DestBlend = InvSrcAlpha; - - VertexShader = compile vs_1_1 AliasedVS(); - PixelShader = compile ps_2_0 SimplePS(); - } - - pass PASS_STENCIL_NOSTENCILCHECK_DIRECT - { - StencilEnable = False; - - ZEnable = True; - ZWriteEnable = False; - ZFunc = Greater; - - ColorWriteEnable = 0x0f; - - SrcBlend = SrcAlpha; - DestBlend = InvSrcAlpha; - - VertexShader = compile vs_1_1 AliasedVS(); - PixelShader = compile ps_2_0 DirectSimplePS(); - } - - pass PASS_TEXT - { - StencilEnable = False; - - ZEnable = True; - ZWriteEnable = False; - ZFunc = Greater; - - ColorWriteEnable = 0x0f; - - SrcBlend = SrcAlpha; - DestBlend = InvSrcAlpha; - - VertexShader = compile vs_1_1 AliasedVS(); - PixelShader = compile ps_2_0 TextPS(); - } - - pass PASS_CLEARTYPE_TEXT - { - StencilEnable = False; - - ZEnable = True; - ZWriteEnable = False; - ZFunc = Greater; - - ColorWriteEnable = 0x0f; - -// SrcBlend = SrcAlpha; -// DestBlend = InvSrcAlpha; - -// SrcBlend = DestColor; -// DestBlend = Zero; - SrcBlend = BlendFactor; - DestBlend = InvSrcColor; - -// SrcBlend = Zero; -// DestBlend = SrcColor; - -// SrcBlend = One; -// DestBlend = Zero; - - VertexShader = compile vs_3_0 AliasedVS(); - PixelShader = compile ps_3_0 ClearTypePS(); - } - - pass PASS_ALIASED_LINES - { - TwoSidedStencilMode = False; - StencilEnable = True; - StencilPass = Invert; - StencilFunc = Always; - ColorWriteEnable = 0; - - ZEnable = False; - ZWriteEnable = False; - - VertexShader = compile vs_1_1 AliasedLinesVS(); - PixelShader = compile ps_2_0 DirectSimplePS(); - } - - pass PASS_ALIASED_LINES_DIRECT - { - StencilEnable = False; - - ZEnable = True; - ZWriteEnable = False; - ZFunc = Greater; - - ColorWriteEnable = 0x0f; - - SrcBlend = SrcAlpha; - DestBlend = InvSrcAlpha; - - VertexShader = compile vs_1_1 AliasedLinesVS(); - PixelShader = compile ps_2_0 DirectSimplePS(); - } -} - diff --git a/src/gui/painting/qpaintengine_d3d.qrc b/src/gui/painting/qpaintengine_d3d.qrc deleted file mode 100644 index c106f2b..0000000 --- a/src/gui/painting/qpaintengine_d3d.qrc +++ /dev/null @@ -1,5 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>qpaintengine_d3d.fx</file> -</qresource> -</RCC> diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 847904b..93e0db0 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -139,7 +139,6 @@ extern bool qt_cleartype_enabled; * Span functions */ static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData); -static void qt_span_fill_clipRegion(int count, const QSpan *spans, void *userData); static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData); static void qt_span_clip(int count, const QSpan *spans, void *userData); static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result); @@ -363,7 +362,8 @@ void QRasterPaintEngine::init() d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to); d->dashStroker = 0; - d->baseClip = 0; + d->baseClip = new QClipData(d->device->height()); + d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height())); d->image_filler.init(d->rasterBuffer, this); d->image_filler.type = QSpanData::Texture; @@ -447,6 +447,8 @@ QRasterPaintEngine::~QRasterPaintEngine() delete d->outlineMapper; delete d->rasterizer; delete d->dashStroker; + + delete d->baseClip; } /*! @@ -483,12 +485,12 @@ bool QRasterPaintEngine::begin(QPaintDevice *device) d->rasterizer->setClipRect(d->deviceRect); s->penData.init(d->rasterBuffer, this); - s->penData.setup(s->pen.brush(), s->intOpacity); + s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode); s->stroker = &d->basicStroker; d->basicStroker.setClipRect(d->deviceRect); s->brushData.init(d->rasterBuffer, this); - s->brushData.setup(s->brush, s->intOpacity); + s->brushData.setup(s->brush, s->intOpacity, s->composition_mode); d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver; @@ -509,16 +511,20 @@ bool QRasterPaintEngine::begin(QPaintDevice *device) if (d->mono_surface) d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono; -#ifdef Q_WS_WIN - else if (qt_cleartype_enabled) { +#if defined(Q_WS_WIN) + else if (qt_cleartype_enabled) +#elif defined (Q_WS_MAC) + else if (true) +#else + else if (false) +#endif + { QImage::Format format = static_cast<QImage *>(d->device)->format(); if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32) d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask; else d->glyphCacheType = QFontEngineGlyphCache::Raster_A8; - } -#endif - else + } else d->glyphCacheType = QFontEngineGlyphCache::Raster_A8; setActive(true); @@ -530,19 +536,14 @@ bool QRasterPaintEngine::begin(QPaintDevice *device) */ bool QRasterPaintEngine::end() { - Q_D(QRasterPaintEngine); #ifdef QT_DEBUG_DRAW + Q_D(QRasterPaintEngine); qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect; if (d->baseClip) { dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), d->baseClip); } #endif - if (d->baseClip) { - delete d->baseClip; - d->baseClip = 0; - } - return true; } @@ -618,22 +619,22 @@ void QRasterPaintEngine::updateMatrix(const QTransform &matrix) d->isPlain45DegreeRotation = false; if (txop >= QTransform::TxRotate) { d->isPlain45DegreeRotation = - (qFuzzyCompare(matrix.m11() + 1, qreal(1)) - && qFuzzyCompare(matrix.m12(), qreal(1)) - && qFuzzyCompare(matrix.m21(), qreal(-1)) - && qFuzzyCompare(matrix.m22() + 1, qreal(1)) + (qFuzzyIsNull(matrix.m11()) + && qFuzzyIsNull(matrix.m12() - qreal(1)) + && qFuzzyIsNull(matrix.m21() + qreal(1)) + && qFuzzyIsNull(matrix.m22()) ) || - (qFuzzyCompare(matrix.m11(), qreal(-1)) - && qFuzzyCompare(matrix.m12() + 1, qreal(1)) - && qFuzzyCompare(matrix.m21() + 1, qreal(1)) - && qFuzzyCompare(matrix.m22(), qreal(-1)) + (qFuzzyIsNull(matrix.m11() + qreal(1)) + && qFuzzyIsNull(matrix.m12()) + && qFuzzyIsNull(matrix.m21()) + && qFuzzyIsNull(matrix.m22() + qreal(1)) ) || - (qFuzzyCompare(matrix.m11() + 1, qreal(1)) - && qFuzzyCompare(matrix.m12(), qreal(-1)) - && qFuzzyCompare(matrix.m21(), qreal(1)) - && qFuzzyCompare(matrix.m22() + 1, qreal(1)) + (qFuzzyIsNull(matrix.m11()) + && qFuzzyIsNull(matrix.m12() + qreal(1)) + && qFuzzyIsNull(matrix.m21() - qreal(1)) + && qFuzzyIsNull(matrix.m22()) ) ; } @@ -769,7 +770,7 @@ void QRasterPaintEngine::updatePen(const QPen &pen) s->strokeFlags = 0; s->penData.clip = d->clip(); - s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity); + s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode); if (s->strokeFlags & QRasterPaintEngine::DirtyTransform || pen.brush().transform().type() >= QTransform::TxNone) { @@ -869,7 +870,7 @@ void QRasterPaintEngine::updateBrush(const QBrush &brush) QRasterPaintEngineState *s = state(); // must set clip prior to setup, as setup uses it... s->brushData.clip = d->clip(); - s->brushData.setup(brush, s->intOpacity); + s->brushData.setup(brush, s->intOpacity, s->composition_mode); if (s->fillFlags & DirtyTransform || brush.transform().type() >= QTransform::TxNone) d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix()); @@ -1024,6 +1025,10 @@ void QRasterPaintEnginePrivate::drawImage(const QPointF &pt, { if (alpha == 0 || !clip.isValid()) return; + + if (alpha ==0) + return; + Q_ASSERT(img.depth() >= 8); int srcBPL = img.bytesPerLine(); @@ -1092,11 +1097,10 @@ void QRasterPaintEnginePrivate::systemStateChanged() if (!systemClip.isEmpty()) { QRegion clippedDeviceRgn = systemClip & clipRect; deviceRect = clippedDeviceRgn.boundingRect(); - delete baseClip; - baseClip = new QClipData(device->height()); baseClip->setClipRegion(clippedDeviceRgn); } else { deviceRect = clipRect; + baseClip->setClipRect(deviceRect); } #ifdef QT_DEBUG_DRAW qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip; @@ -1168,6 +1172,25 @@ static void checkClipRatios(QRasterPaintEnginePrivate *d) } #endif +static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s) +{ + if (s->flags.has_clip_ownership) + delete s->clip; + s->clip = 0; + s->flags.has_clip_ownership = false; +} + +static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s) +{ + s->fillFlags |= QPaintEngine::DirtyClipPath; + s->strokeFlags |= QPaintEngine::DirtyClipPath; + s->pixmapFlags |= QPaintEngine::DirtyClipPath; + + d->solid_color_filler.clip = d->clip(); + d->solid_color_filler.adjustSpanMethods(); +} + + /*! \internal */ @@ -1179,12 +1202,12 @@ void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) if (path.elements()) { for (int i=0; i<path.elementCount(); ++i) { qDebug() << " - " << path.elements()[i] - << "(" << path.points()[i*2] << ", " << path.points()[i*2+1] << ")"; + << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')'; } } else { for (int i=0; i<path.elementCount(); ++i) { qDebug() << " ---- " - << "(" << path.points()[i*2] << ", " << path.points()[i*2+1] << ")"; + << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')'; } } #endif @@ -1216,10 +1239,7 @@ void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) } if (op == Qt::NoClip) { - if (s->flags.has_clip_ownership) - delete s->clip; - s->clip = 0; - s->flags.has_clip_ownership = false; + qrasterpaintengine_state_setNoClip(s); } else { QClipData *base = d->baseClip; @@ -1261,17 +1281,7 @@ void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) s->clip = newClip; s->flags.has_clip_ownership = true; } - - s->fillFlags |= DirtyClipPath; - s->strokeFlags |= DirtyClipPath; - s->pixmapFlags |= DirtyClipPath; - - d->solid_color_filler.clip = d->clip(); - d->solid_color_filler.adjustSpanMethods(); - -#ifdef QT_CLIPPING_RATIOS - checkClipRatios(d); -#endif + qrasterpaintengine_dirty_clip(d, s); } @@ -1289,10 +1299,7 @@ void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) QRasterPaintEngineState *s = state(); if (op == Qt::NoClip) { - if (s->flags.has_clip_ownership) - delete s->clip; - s->clip = d->baseClip; - s->flags.has_clip_ownership = false; + qrasterpaintengine_state_setNoClip(s); } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) { QPaintEngineEx::clip(rect, op); @@ -1336,37 +1343,63 @@ void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) return; } } - - s->brushData.clip = d->clip(); - s->penData.clip = d->clip(); - - s->fillFlags |= DirtyClipPath; - s->strokeFlags |= DirtyClipPath; - s->pixmapFlags |= DirtyClipPath; - - d->solid_color_filler.clip = d->clip(); - d->solid_color_filler.adjustSpanMethods(); - - -#ifdef QT_CLIPPING_RATIOS - checkClipRatios(d); -#endif + qrasterpaintengine_dirty_clip(d, s); } + /*! \internal */ void QRasterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op) { - QPaintEngineEx::clip(region, op); -} +#ifdef QT_DEBUG_DRAW + qDebug() << "QRasterPaintEngine::clip(): " << region << op; +#endif -/*! - \internal -*/ -void QRasterPaintEngine::clip(const QPainterPath &path, Qt::ClipOperation op) -{ - QPaintEngineEx::clip(path, op); + Q_D(QRasterPaintEngine); + + if (region.numRects() == 1) { + clip(region.boundingRect(), op); + return; + } + + QRasterPaintEngineState *s = state(); + const QClipData *clip = d->clip(); + const QClipData *baseClip = d->baseClip; + + if (op == Qt::NoClip) { + qrasterpaintengine_state_setNoClip(s); + } else if (s->matrix.type() > QTransform::TxScale + || op == Qt::UniteClip + || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip) + || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) { + QPaintEngineEx::clip(region, op); + } else { + const QClipData *curClip; + QClipData *newClip; + + if (op == Qt::IntersectClip) + curClip = clip; + else + curClip = baseClip; + + if (s->flags.has_clip_ownership) { + newClip = s->clip; + Q_ASSERT(newClip); + } else { + newClip = new QClipData(d->rasterBuffer->height()); + s->clip = newClip; + s->flags.has_clip_ownership = true; + } + + QRegion r = s->matrix.map(region); + if (curClip->hasRectClip) + newClip->setClipRegion(r & curClip->clipRect); + else if (curClip->hasRegionClip) + newClip->setClipRegion(r & clip->clipRegion); + + qrasterpaintengine_dirty_clip(d, s); + } } /*! @@ -1408,7 +1441,7 @@ static void fillRect_normalized(const QRect &r, QSpanData *data, { int x1, x2, y1, y2; - bool rectClipped = false; + bool rectClipped = true; if (data->clip) { x1 = qMax(r.x(), data->clip->xmin); @@ -1738,12 +1771,17 @@ void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen) static inline QRect toNormalizedFillRect(const QRectF &rect) { - const int x1 = qRound(rect.x() + aliasedCoordinateDelta); - const int y1 = qRound(rect.y() + aliasedCoordinateDelta); - const int x2 = qRound(rect.right() + aliasedCoordinateDelta); - const int y2 = qRound(rect.bottom() + aliasedCoordinateDelta); + int x1 = int(rect.x() + aliasedCoordinateDelta); + int y1 = int(rect.y() + aliasedCoordinateDelta); + int x2 = int(rect.right() + aliasedCoordinateDelta); + int y2 = int(rect.bottom() + aliasedCoordinateDelta); - return QRect(x1, y1, x2 - x1, y2 - y1).normalized(); + if (x2 < x1) + qSwap(x1, x2); + if (y2 < y1) + qSwap(y1, y2); + + return QRect(x1, y1, x2 - x1, y2 - y1); } /*! @@ -1903,9 +1941,12 @@ void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color) QRasterPaintEngineState *s = state(); d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity)); + if ((d->solid_color_filler.solid.color & 0xff000000) == 0 + && s->composition_mode == QPainter::CompositionMode_SourceOver) { + return; + } d->solid_color_filler.clip = d->clip(); d->solid_color_filler.adjustSpanMethods(); - fillRect(r, &d->solid_color_filler); } @@ -3072,7 +3113,7 @@ bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const if (cl->clipRect == deviceRect) return true; - if (cl->hasRegionClip) { + if (cl->hasRectClip) { // inline contains() for performance (we know the rects are normalized) const QRect &r1 = cl->clipRect; return (r.left() >= r1.left() && r.right() <= r1.right() @@ -3201,7 +3242,7 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte // ### cases we should delegate painting to the font engine // ### directly... -#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE) +#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) QFontEngine::Type fontEngineType = ti.fontEngine->type(); // qDebug() << "type" << fontEngineType << s->matrix.type(); if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate) @@ -4303,6 +4344,7 @@ void QClipData::initialize() m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight); m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan)); + allocated = clipSpanHeight; if (hasRectClip) { int y = 0; @@ -4347,6 +4389,7 @@ void QClipData::initialize() int y = 0; int firstInBand = 0; + count = 0; while (firstInBand < numRects) { const int currMinY = rects.at(firstInBand).y(); const int currMaxY = currMinY + rects.at(firstInBand).height(); @@ -4407,20 +4450,39 @@ void QClipData::fixup() ymax = m_spans[count-1].y + 1; xmin = INT_MAX; xmax = 0; + + bool isRect = true; + int left = m_spans[0].x; + int right = m_spans[0].x + m_spans[0].len; + for (int i = 0; i < count; ++i) { -// qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage; if (m_spans[i].y != y) { + if (m_spans[i].y != y + 1 && y != -1) { + isRect = false; + } y = m_spans[i].y; m_clipLines[y].spans = m_spans+i; m_clipLines[y].count = 0; // qDebug() << " new line: y=" << y; } ++m_clipLines[y].count; + int sl = (int) m_spans[i].x; + int sr = sl + m_spans[i].len; + xmin = qMin(xmin, (int)m_spans[i].x); xmax = qMax(xmax, (int)m_spans[i].x + m_spans[i].len); + + if (sl != left || sr != right) + isRect = false; } ++xmax; -// qDebug("xmin=%d,xmax=%d,ymin=%d,ymax=%d", xmin, xmax, ymin, ymax); +// qDebug("xmin=%d,xmax=%d,ymin=%d,ymax=%d %s", xmin, xmax, ymin, ymax, isRect ? "rectangular" : ""); + + if (isRect) { + hasRectClip = true; + clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin); + } + } /* @@ -4433,6 +4495,7 @@ void QClipData::setClipRect(const QRect &rect) // qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect; hasRectClip = true; + hasRegionClip = false; clipRect = rect; xmin = rect.x(); @@ -4441,7 +4504,7 @@ void QClipData::setClipRect(const QRect &rect) ymax = qMin(rect.y() + rect.height(), clipSpanHeight); if (m_spans) { - delete m_spans; + free(m_spans); m_spans = 0; } @@ -4459,6 +4522,7 @@ void QClipData::setClipRegion(const QRegion ®ion) } hasRegionClip = true; + hasRectClip = false; clipRegion = region; { // set bounding rect @@ -4470,7 +4534,7 @@ void QClipData::setClipRegion(const QRegion ®ion) } if (m_spans) { - delete m_spans; + free(m_spans); m_spans = 0; } @@ -4718,29 +4782,6 @@ static void qt_span_fill_clipRect(int count, const QSpan *spans, fillData->unclipped_blend(count, spans, fillData); } -static void qt_span_fill_clipRegion(int count, const QSpan *spans, - void *userData) -{ - QSpanData *fillData = reinterpret_cast<QSpanData *>(userData); - Q_ASSERT(fillData->blend && fillData->unclipped_blend); - - Q_ASSERT(fillData->clip); - Q_ASSERT(!fillData->clip->clipRegion.isEmpty()); - - const int NSPANS = 256; - QSpan cspans[NSPANS]; - int currentClip = 0; - while (currentClip < count) { - const int unclipped = qt_intersect_spans(const_cast<QSpan*>(spans), - count, ¤tClip, - &cspans[0], NSPANS, - fillData->clip->clipRegion); - if (unclipped > 0) - fillData->unclipped_blend(unclipped, cspans, fillData); - } - -} - static void qt_span_clip(int count, const QSpan *spans, void *userData) { ClipData *clipData = reinterpret_cast<ClipData *>(userData); @@ -5013,7 +5054,7 @@ void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe) extern QImage qt_imageForBrush(int brushStyle, bool invert); -void QSpanData::setup(const QBrush &brush, int alpha) +void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode) { Qt::BrushStyle brushStyle = qbrush_style(brush); switch (brushStyle) { @@ -5021,6 +5062,10 @@ void QSpanData::setup(const QBrush &brush, int alpha) type = Solid; QColor c = qbrush_color(brush); solid.color = PREMUL(ARGB_COMBINE_ALPHA(c.rgba(), alpha)); + if ((solid.color & 0xff000000) == 0 + && compositionMode == QPainter::CompositionMode_SourceOver) { + type = None; + } break; } @@ -5161,8 +5206,6 @@ void QSpanData::adjustSpanMethods() blend = unclipped_blend; } else if (clip->hasRectClip) { blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect; - } else if (clip->hasRegionClip) { - blend = clip->clipRegion.isEmpty() ? 0 : qt_span_fill_clipRegion; } else { blend = qt_span_fill_clipped; } @@ -6143,7 +6186,7 @@ void dumpClip(int width, int height, QClipData *clip) int y1 = 0; for (int i = 0; i < clip->count; ++i) { - QSpan *span = clip->spans + i; + QSpan *span = clip->spans() + i; for (int j = 0; j < span->len; ++j) clipImg.setPixel(span->x + j, span->y, 0xffffff00); x0 = qMin(x0, int(span->x)); diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h index 26a2b3f..1f3f006 100644 --- a/src/gui/painting/qpaintengine_raster_p.h +++ b/src/gui/painting/qpaintengine_raster_p.h @@ -202,7 +202,6 @@ public: void clip(const QVectorPath &path, Qt::ClipOperation op); void clip(const QRect &rect, Qt::ClipOperation op); void clip(const QRegion ®ion, Qt::ClipOperation op); - void clip(const QPainterPath &path, Qt::ClipOperation op); enum ClipType { RectClip, diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index 8eaad60..28f9220 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -105,7 +105,7 @@ QDebug Q_GUI_EXPORT &operator<<(QDebug &s, const QVectorPath &path) vectorPathBounds.x2 - vectorPathBounds.x1, vectorPathBounds.y2 - vectorPathBounds.y1); s << "QVectorPath(size:" << path.elementCount() << " hints:" << hex << path.hints() - << rf << ")"; + << rf << ')'; return s; } #endif @@ -778,7 +778,7 @@ void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, con { QBrush brush(state()->pen.color(), pixmap); QTransform xform; - xform.translate(-s.x(), -s.y()); + xform.translate(r.x() - s.x(), r.y() - s.y()); brush.setTransform(xform); qreal pts[] = { r.x(), r.y(), diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index cc48d24..4744f14 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -6082,22 +6082,22 @@ void QPainter::drawTextItem(const QPointF &p, const QTextItem &_ti) const QTransform &m = d->state->matrix; if (d->state->matrix.type() < QTransform::TxShear) { bool isPlain90DegreeRotation = - (qFuzzyCompare(m.m11() + 1, qreal(1)) - && qFuzzyCompare(m.m12(), qreal(1)) - && qFuzzyCompare(m.m21(), qreal(-1)) - && qFuzzyCompare(m.m22() + 1, qreal(1)) + (qFuzzyIsNull(m.m11()) + && qFuzzyIsNull(m.m12() - qreal(1)) + && qFuzzyIsNull(m.m21() + qreal(1)) + && qFuzzyIsNull(m.m22()) ) || - (qFuzzyCompare(m.m11(), qreal(-1)) - && qFuzzyCompare(m.m12() + 1, qreal(1)) - && qFuzzyCompare(m.m21() + 1, qreal(1)) - && qFuzzyCompare(m.m22(), qreal(-1)) + (qFuzzyIsNull(m.m11() + qreal(1)) + && qFuzzyIsNull(m.m12()) + && qFuzzyIsNull(m.m21()) + && qFuzzyIsNull(m.m22() + qreal(1)) ) || - (qFuzzyCompare(m.m11() + 1, qreal(1)) - && qFuzzyCompare(m.m12(), qreal(-1)) - && qFuzzyCompare(m.m21(), qreal(1)) - && qFuzzyCompare(m.m22() + 1, qreal(1)) + (qFuzzyIsNull(m.m11()) + && qFuzzyIsNull(m.m12() + qreal(1)) + && qFuzzyIsNull(m.m21() - qreal(1)) + && qFuzzyIsNull(m.m22()) ) ; aa = !isPlain90DegreeRotation; diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 1b2c4e3..bd91dfc 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -1299,10 +1299,10 @@ static QRectF qt_painterpath_bezier_extrema(const QBezier &b) qreal bx = QT_BEZIER_B(b, x); qreal cx = QT_BEZIER_C(b, x); // specialcase quadratic curves to avoid div by zero - if (qFuzzyCompare(ax + 1, 1)) { + if (qFuzzyIsNull(ax)) { // linear curves are covered by initialization. - if (!qFuzzyCompare(bx + 1, 1)) { + if (!qFuzzyIsNull(bx)) { qreal t = -cx / bx; QT_BEZIER_CHECK_T(b, t); } @@ -1329,10 +1329,10 @@ static QRectF qt_painterpath_bezier_extrema(const QBezier &b) qreal cy = QT_BEZIER_C(b, y); // specialcase quadratic curves to avoid div by zero - if (qFuzzyCompare(ay + 1, 1)) { + if (qFuzzyIsNull(ay)) { // linear curves are covered by initialization. - if (!qFuzzyCompare(by + 1, 1)) { + if (!qFuzzyIsNull(by)) { qreal t = -cy / by; QT_BEZIER_CHECK_T(b, t); } @@ -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 @@ -2867,7 +2923,7 @@ qreal QPainterPath::angleAtPercent(qreal t) const return QLineF(0, 0, m1, m2).angle(); } -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) #pragma warning( disable : 4056 4756 ) #endif @@ -3292,7 +3348,7 @@ QDebug operator<<(QDebug s, const QPainterPath &p) s.nospace() << "QPainterPath: Element count=" << p.elementCount() << endl; const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"}; for (int i=0; i<p.elementCount(); ++i) { - s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ")" << endl; + s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << endl; } return s; 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/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h index 29c48df..6fb439d 100644 --- a/src/gui/painting/qpainterpath_p.h +++ b/src/gui/painting/qpainterpath_p.h @@ -124,7 +124,7 @@ private: Q_DISABLE_COPY(QVectorPathConverter) }; -class Q_GUI_EXPORT QPainterPathData : public QPainterPathPrivate +class QPainterPathData : public QPainterPathPrivate { public: QPainterPathData() : diff --git a/src/gui/painting/qpathclipper.cpp b/src/gui/painting/qpathclipper.cpp index 297cdd3..9ef3eb7 100644 --- a/src/gui/painting/qpathclipper.cpp +++ b/src/gui/painting/qpathclipper.cpp @@ -138,11 +138,11 @@ bool QIntersectionFinder::linesIntersect(const QLineF &a, const QLineF &b) const const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x(); - if (qFuzzyCompare(par + 1, 1)) { + if (qFuzzyIsNull(par)) { const QPointF normal(-pDelta.y(), pDelta.x()); // coinciding? - if (qFuzzyCompare(dot(normal, q1 - p1) + 1, 1)) { + if (qFuzzyIsNull(dot(normal, q1 - p1))) { const qreal dp = dot(pDelta, pDelta); const qreal tq1 = dot(pDelta, q1 - p1); @@ -202,13 +202,13 @@ void QIntersectionFinder::intersectBeziers(const QBezier &one, const QBezier &tw qreal alpha_q = t.at(i).second; QPointF pt; - if (qFuzzyCompare(alpha_p + 1, 1)) { + if (qFuzzyIsNull(alpha_p)) { pt = one.pt1(); - } else if (qFuzzyCompare(alpha_p, 1)) { + } else if (qFuzzyIsNull(alpha_p - 1)) { pt = one.pt4(); - } else if (qFuzzyCompare(alpha_q + 1, 1)) { + } else if (qFuzzyIsNull(alpha_q)) { pt = two.pt1(); - } else if (qFuzzyCompare(alpha_q, 1)) { + } else if (qFuzzyIsNull(alpha_q - 1)) { pt = two.pt4(); } else { pt = one.pointAt(alpha_p); @@ -250,11 +250,11 @@ void QIntersectionFinder::intersectLines(const QLineF &a, const QLineF &b, QData const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x(); - if (qFuzzyCompare(par + 1, 1)) { + if (qFuzzyIsNull(par)) { const QPointF normal(-pDelta.y(), pDelta.x()); // coinciding? - if (qFuzzyCompare(dot(normal, q1 - p1) + 1, 1)) { + if (qFuzzyIsNull(dot(normal, q1 - p1))) { const qreal invDp = 1 / dot(pDelta, pDelta); const qreal tq1 = dot(pDelta, q1 - p1) * invDp; @@ -315,11 +315,11 @@ void QIntersectionFinder::intersectLines(const QLineF &a, const QLineF &b, QData if (tp<0 || tp>1 || tq<0 || tq>1) return; - const bool p_zero = qFuzzyCompare(tp + 1, 1); - const bool p_one = qFuzzyCompare(tp, 1); + const bool p_zero = qFuzzyIsNull(tp); + const bool p_one = qFuzzyIsNull(tp - 1); - const bool q_zero = qFuzzyCompare(tq + 1, 1); - const bool q_one = qFuzzyCompare(tq, 1); + const bool q_zero = qFuzzyIsNull(tq); + const bool q_one = qFuzzyIsNull(tq - 1); if ((q_zero || q_one) && (p_zero || p_one)) return; @@ -922,7 +922,7 @@ qreal QWingedEdge::delta(int vertex, int a, int b) const qreal result = b_angle - a_angle; - if (qFuzzyCompare(result + 1, 1) || qFuzzyCompare(result, 128)) + if (qFuzzyIsNull(result) || qFuzzyCompare(result, 128)) return 0; if (result < 0) @@ -951,7 +951,7 @@ static inline QPointF tangentAt(const QWingedEdge &list, int vi, int ei) if (ep->bezier) { normal = ep->bezier->derivedAt(t); - if (qFuzzyCompare(normal.x() + 1, 1) && qFuzzyCompare(normal.y() + 1, 1)) + if (qFuzzyIsNull(normal.x()) && qFuzzyIsNull(normal.y())) normal = ep->bezier->secondDerivedAt(t); } else { const QPointF a = *list.vertex(ep->first); @@ -1080,7 +1080,7 @@ QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(int vi, int ei) const qDebug() << "Delta to edge" << status.edge << d2 << ", angles: " << op->angle << op->invAngle; #endif - if (!(qFuzzyCompare(d2 + 1, 1) && isLeftOf(*this, vi, status.edge, ei)) + if (!(qFuzzyIsNull(d2) && isLeftOf(*this, vi, status.edge, ei)) && (d2 < d || (qFuzzyCompare(d2, d) && isLeftOf(*this, vi, status.edge, position)))) { position = status.edge; d = d2; @@ -1232,10 +1232,10 @@ int QWingedEdge::addEdge(int fi, int si, const QBezier *bezier, qreal t0, qreal QPointF aTangent = bezier->derivedAt(t0); QPointF bTangent = -bezier->derivedAt(t1); - if (qFuzzyCompare(aTangent.x() + 1, 1) && qFuzzyCompare(aTangent.y() + 1, 1)) + if (qFuzzyIsNull(aTangent.x()) && qFuzzyIsNull(aTangent.y())) aTangent = bezier->secondDerivedAt(t0); - if (qFuzzyCompare(bTangent.x() + 1, 1) && qFuzzyCompare(bTangent.y() + 1, 1)) + if (qFuzzyIsNull(bTangent.x()) && qFuzzyIsNull(bTangent.y())) bTangent = bezier->secondDerivedAt(t1); ep->angle = computeAngle(aTangent); @@ -1400,7 +1400,7 @@ static void addLineTo(QPainterPath &path, const QPointF &point) const QPointF p(-d1.y(), d1.x()); - if (qFuzzyCompare(dot(p, d2) + 1, 1)) { + if (qFuzzyIsNull(dot(p, d2))) { path.setElementPositionAt(elementCount - 1, point.x(), point.y()); return; } diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 55006f6..0b2db8c 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -346,7 +346,7 @@ QByteArray QPdf::generateDashes(const QPen &pen) { QByteArray result; ByteStream s(&result); - s << "["; + s << '['; QVector<qreal> dasharray = pen.dashPattern(); qreal w = pen.widthF(); @@ -357,7 +357,7 @@ QByteArray QPdf::generateDashes(const QPen &pen) if (dw < 0.0001) dw = 0.0001; s << dw; } - s << "]"; + s << ']'; //qDebug() << "dasharray: pen has" << dasharray; //qDebug() << " => " << result; return result; @@ -915,17 +915,17 @@ const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize) QByteArray QPdf::stripSpecialCharacters(const QByteArray &string) { QByteArray s = string; - s.replace(" ", ""); - s.replace("(", ""); - s.replace(")", ""); - s.replace("<", ""); - s.replace(">", ""); - s.replace("[", ""); - s.replace("]", ""); - s.replace("{", ""); - s.replace("}", ""); - s.replace("/", ""); - s.replace("%", ""); + s.replace(' ', ""); + s.replace('(', ""); + s.replace(')', ""); + s.replace('<', ""); + s.replace('>', ""); + s.replace('[', ""); + s.replace(']', ""); + s.replace('{', ""); + s.replace('}', ""); + s.replace('/', ""); + s.replace('%', ""); return s; } diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp index 1d68520..ab60861 100644 --- a/src/gui/painting/qpen.cpp +++ b/src/gui/painting/qpen.cpp @@ -978,7 +978,7 @@ QDebug operator<<(QDebug dbg, const QPen &p) dbg.nospace() << "QPen(" << p.width() << ',' << p.brush() << ',' << int(p.style()) << ',' << int(p.capStyle()) << ',' << int(p.joinStyle()) << ',' << p.dashPattern() - << "," << p.dashOffset() + << ',' << p.dashOffset() << ',' << p.miterLimit() << ')'; return dbg.space(); #else 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/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index 2e063b7..e504558 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -423,7 +423,7 @@ int QPdfEnginePrivate::addConstantAlphaObject(int brushAlpha, int penAlpha) object = addXrefEntry(-1); QByteArray alphaDef; QPdf::ByteStream s(&alphaDef); - s << "<<\n/ca " << (brushAlpha/qreal(255.)) << "\n"; + s << "<<\n/ca " << (brushAlpha/qreal(255.)) << '\n'; s << "/CA " << (penAlpha/qreal(255.)) << "\n>>"; xprintf("%s\nendobj\n", alphaDef.constData()); alphaCache.insert(QPair<uint, uint>(brushAlpha, penAlpha), object); @@ -1010,7 +1010,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) s << (char)('A' + (tag % 26)); tag /= 26; } - s << "+" << properties.postscriptName << "\n" + s << '+' << properties.postscriptName << "\n" "/Flags " << 4 << "\n" "/FontBBox [" << properties.boundingBox.x()*scale diff --git a/src/gui/painting/qprintengine_ps.cpp b/src/gui/painting/qprintengine_ps.cpp index 97ec640..29f364e 100644 --- a/src/gui/painting/qprintengine_ps.cpp +++ b/src/gui/painting/qprintengine_ps.cpp @@ -167,7 +167,7 @@ static QByteArray wrapDSC(const QByteArray &str) } wrapped += "\n%%+" + tmp; } - return wrapped + "\n"; + return wrapped + '\n'; } // ----------------------------- Internal class declarations ----------------------------- @@ -422,7 +422,7 @@ void QPSPrintEnginePrivate::drawImageHelper(qreal x, qreal y, qreal w, qreal h, << size << " string readstring\n"; ps_r7(*currentPage, out, out.size()); *currentPage << " pop def\n"; - *currentPage << width << ' ' << height << "[" << scaleX << " 0 0 " << scaleY << " 0 0]sl " + *currentPage << width << ' ' << height << '[' << scaleX << " 0 0 " << scaleY << " 0 0]sl " << bits << (!mask.isNull() ? "mask " : "false ") << x << ' ' << y << " di\n"; } @@ -529,7 +529,7 @@ void QPSPrintEnginePrivate::emitHeader(bool finished) else s << "\n%%BoundingBox: 0 0 " << w << h; } - s << "\n" << wrapDSC("%%Creator: " + creator.toUtf8()); + s << '\n' << wrapDSC("%%Creator: " + creator.toUtf8()); if (!title.isEmpty()) s << wrapDSC("%%Title: " + title.toUtf8()); #ifndef QT_NO_DATESTRING @@ -549,7 +549,7 @@ void QPSPrintEnginePrivate::emitHeader(bool finished) "% Prolog copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).\n" "% You may copy this prolog in any way that is directly related to this document.\n" "% For other use of this prolog, see your licensing agreement for Qt.\n" - << ps_header << "\n"; + << ps_header << '\n'; s << "/pageinit {\n"; @@ -560,12 +560,12 @@ void QPSPrintEnginePrivate::emitHeader(bool finished) s << mtop*scale << mleft*scale << "translate\n"; } if (orientation == QPrinter::Portrait) { - s << "% " << printer->widthMM() << "*" << printer->heightMM() + s << "% " << printer->widthMM() << '*' << printer->heightMM() << "mm (portrait)\n0 " << height*scale - << "translate " << scale << "-" << scale << "scale } def\n"; + << "translate " << scale << '-' << scale << "scale } def\n"; } else { - s << "% " << printer->heightMM() << "*" << printer->widthMM() - << " mm (landscape)\n 90 rotate " << scale << "-" << scale << "scale } def\n"; + s << "% " << printer->heightMM() << '*' << printer->widthMM() + << " mm (landscape)\n 90 rotate " << scale << '-' << scale << "scale } def\n"; } s << "%%EndProlog\n"; @@ -619,8 +619,8 @@ void QPSPrintEnginePrivate::flushPage(bool last) QPdf::ByteStream e(&trailer); buffer << "%%Page: " << pageCount << pageCount << "\n" - << "%%BeginPageSetup\n" - << "QI\n"; + "%%BeginPageSetup\n" + "QI\n"; if (hugeDocument) { for (QHash<QFontEngine::FaceId, QFontSubset *>::const_iterator it = fonts.constBegin(); it != fonts.constEnd(); ++it) { @@ -776,8 +776,8 @@ bool QPSPrintEngine::end() d->flushPage(true); QByteArray trailer; QPdf::ByteStream s(&trailer); - s << "%%Trailer\n"; - s << "%%Pages: " << d->pageCount - 1 << "\n" << + s << "%%Trailer\n" + "%%Pages: " << d->pageCount - 1 << '\n' << wrapDSC("%%DocumentFonts: " + d->fontsUsed); s << "%%EOF\n"; d->outDevice->write(trailer); diff --git a/src/gui/painting/qprinter.cpp b/src/gui/painting/qprinter.cpp index ed72077..ba208fd 100644 --- a/src/gui/painting/qprinter.cpp +++ b/src/gui/painting/qprinter.cpp @@ -834,11 +834,15 @@ void QPrinter::setPrinterName(const QString &name) #endif QList<QPrinterInfo> prnList = QPrinterInfo::availablePrinters(); - d->validPrinter = false; - for (int i = 0; i < prnList.size(); ++i) { - if (prnList[i].printerName() == name) { - d->validPrinter = true; - break; + if (name.isEmpty()) { + d->validPrinter = d->outputFormat == QPrinter::PdfFormat || d->outputFormat == QPrinter::PostScriptFormat; + } else { + d->validPrinter = false; + for (int i = 0; i < prnList.size(); ++i) { + if (prnList[i].printerName() == name) { + d->validPrinter = true; + break; + } } } diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp index c88af7c..c4cd77a 100644 --- a/src/gui/painting/qregion.cpp +++ b/src/gui/painting/qregion.cpp @@ -450,9 +450,9 @@ QDebug operator<<(QDebug s, const QRegion &r) { QVector<QRect> rects = r.rects(); s.nospace() << "QRegion(size=" << rects.size() << "), " - << "bounds = " << r.boundingRect() << "\n"; + << "bounds = " << r.boundingRect() << '\n'; for (int i=0; i<rects.size(); ++i) - s << "- " << i << rects.at(i) << "\n"; + s << "- " << i << rects.at(i) << '\n'; return s; } #endif @@ -1601,7 +1601,7 @@ void QRegionPrivate::selfTest() const for (int i = 0; i < numRects; ++i) { const QRect r = rects.at(i); if ((r.width() * r.height()) > innerArea) - qDebug() << "selfTest(): innerRect" << innerRect << "<" << r; + qDebug() << "selfTest(): innerRect" << innerRect << '<' << r; } QRect r = rects.first(); diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index b894c62..5fffc72 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -763,7 +763,7 @@ template <class Iterator> bool qt_stroke_side(Iterator *it, qreal qt_t_for_arc_angle(qreal angle) { - if (qFuzzyCompare(angle + 1, qreal(1))) + if (qFuzzyIsNull(angle)) return 0; if (qFuzzyCompare(angle, qreal(90))) @@ -904,13 +904,13 @@ QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLengt } // avoid empty start segment - if (qFuzzyCompare(startT, qreal(1))) { + if (qFuzzyIsNull(startT - qreal(1))) { startT = 0; startSegment += delta; } // avoid empty end segment - if (qFuzzyCompare(endT + 1, qreal(1))) { + if (qFuzzyIsNull(endT)) { endT = 1; endSegment -= delta; } @@ -918,8 +918,8 @@ QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLengt startT = qt_t_for_arc_angle(startT * 90); endT = qt_t_for_arc_angle(endT * 90); - const bool splitAtStart = !qFuzzyCompare(startT + 1, qreal(1)); - const bool splitAtEnd = !qFuzzyCompare(endT, qreal(1)); + const bool splitAtStart = !qFuzzyIsNull(startT); + const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1)); const int end = endSegment + delta; @@ -1018,7 +1018,7 @@ void QDashStroker::processCurrentSubpath() sumLength += dashes[i]; } - if (qFuzzyCompare(sumLength + 1, qreal(1))) + if (qFuzzyIsNull(sumLength)) return; Q_ASSERT(dashCount > 0); diff --git a/src/gui/painting/qstroker_p.h b/src/gui/painting/qstroker_p.h index 72141aa..ca1f270 100644 --- a/src/gui/painting/qstroker_p.h +++ b/src/gui/painting/qstroker_p.h @@ -179,7 +179,7 @@ private: }; -class Q_GUI_EXPORT QStroker : public QStrokerOps +class QStroker : public QStrokerOps { public: diff --git a/src/gui/painting/qtessellator.cpp b/src/gui/painting/qtessellator.cpp index e02f02d..b743940 100644 --- a/src/gui/painting/qtessellator.cpp +++ b/src/gui/painting/qtessellator.cpp @@ -1081,7 +1081,7 @@ void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2) yi = y; } QDEBUG() << " between edges " << e1->edge << "and" << e2->edge << "at point (" - << Q27Dot5ToDouble(yi) << ")"; + << Q27Dot5ToDouble(yi) << ')'; Intersection i1; i1.y = yi; @@ -1174,7 +1174,7 @@ void QTessellatorPrivate::addIntersections() for (int i = 0; i < scanline.size; ++i) { Edge *e = scanline.edges[i]; QDEBUG() << " " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right - << ")"; + << ')'; } #endif @@ -1240,8 +1240,8 @@ QRectF QTessellator::tessellate(const QPointF *points, int nPoints) QDEBUG() << " " << i << ": " << "point=" << d->vertices.position(d->vertices.sorted[i]) << "flags=" << d->vertices.sorted[i]->flags - << "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << "/" - << Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ")"; + << "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << '/' + << Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ')'; } #endif @@ -1271,9 +1271,9 @@ QRectF QTessellator::tessellate(const QPointF *points, int nPoints) for (int i = 0; i < d->scanline.size; ++i) { QDEBUG() << " " << d->scanline.edges[i]->edge << "p0= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->x) - << "/" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y) + << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y) << ") p1= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->x) - << "/" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ")" + << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ")" << "x=" << Q27Dot5ToDouble(d->scanline.edges[i]->positionAt(d->y)) << "isLeftOfNext=" << ((i < d->scanline.size - 1) @@ -1436,7 +1436,7 @@ void QTessellator::tessellateRect(const QPointF &a_, const QPointF &b_, qreal wi QPointF perp(pb.y() - pa.y(), pa.x() - pb.x()); qreal length = qSqrt(perp.x() * perp.x() + perp.y() * perp.y()); - if (qFuzzyCompare(length + 1, static_cast<qreal>(1))) + if (qFuzzyIsNull(length)) return; // need the half of the width diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index cec2d16..c00012a 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -238,11 +238,11 @@ QT_BEGIN_NAMESPACE \sa reset() */ QTransform::QTransform() - : m_13(0), m_23(0), m_33(1) + : affine(true) + , m_13(0), m_23(0), m_33(1) , m_type(TxNone) , m_dirty(TxNone) { - } /*! @@ -256,12 +256,11 @@ QTransform::QTransform() QTransform::QTransform(qreal h11, qreal h12, qreal h13, qreal h21, qreal h22, qreal h23, qreal h31, qreal h32, qreal h33) - : affine(h11, h12, h21, h22, h31, h32), - m_13(h13), m_23(h23), m_33(h33) + : affine(h11, h12, h21, h22, h31, h32, true) + , m_13(h13), m_23(h23), m_33(h33) , m_type(TxNone) , m_dirty(TxProject) { - } /*! @@ -273,12 +272,11 @@ QTransform::QTransform(qreal h11, qreal h12, qreal h13, */ QTransform::QTransform(qreal h11, qreal h12, qreal h21, qreal h22, qreal dx, qreal dy) - : affine(h11, h12, h21, h22, dx, dy), - m_13(0), m_23(0), m_33(1) + : affine(h11, h12, h21, h22, dx, dy, true) + , m_13(0), m_23(0), m_33(1) , m_type(TxNone) , m_dirty(TxShear) { - } /*! @@ -289,12 +287,11 @@ QTransform::QTransform(qreal h11, qreal h12, qreal h21, and 1 respectively. */ QTransform::QTransform(const QMatrix &mtx) - : affine(mtx), + : affine(mtx._m11, mtx._m12, mtx._m21, mtx._m22, mtx._dx, mtx._dy, true), m_13(0), m_23(0), m_33(1) , m_type(TxNone) , m_dirty(TxShear) { - } /*! @@ -317,7 +314,7 @@ QTransform QTransform::adjoint() const return QTransform(h11, h12, h13, h21, h22, h23, - h31, h32, h33); + h31, h32, h33, true); } /*! @@ -327,7 +324,7 @@ QTransform QTransform::transposed() const { QTransform t(affine._m11, affine._m21, affine._dx, affine._m12, affine._m22, affine._dy, - m_13, m_23, m_33); + m_13, m_23, m_33, true); t.m_type = m_type; t.m_dirty = m_dirty; return t; @@ -345,11 +342,10 @@ QTransform QTransform::transposed() const */ QTransform QTransform::inverted(bool *invertible) const { - QTransform invert; + QTransform invert(true); bool inv = true; - qreal det; - switch(type()) { + switch(inline_type()) { case TxNone: break; case TxTranslate: @@ -357,11 +353,11 @@ QTransform QTransform::inverted(bool *invertible) const invert.affine._dy = -affine._dy; break; case TxScale: - inv = !qFuzzyCompare(affine._m11 + 1, 1); - inv &= !qFuzzyCompare(affine._m22 + 1, 1); + inv = !qFuzzyIsNull(affine._m11); + inv &= !qFuzzyIsNull(affine._m22); if (inv) { - invert.affine._m11 = 1 / affine._m11; - invert.affine._m22 = 1 / affine._m22; + invert.affine._m11 = 1. / affine._m11; + invert.affine._m22 = 1. / affine._m22; invert.affine._dx = -affine._dx * invert.affine._m11; invert.affine._dy = -affine._dy * invert.affine._m22; } @@ -372,8 +368,8 @@ QTransform QTransform::inverted(bool *invertible) const break; default: // general case - det = determinant(); - inv = !qFuzzyCompare(det + 1, 1); + qreal det = determinant(); + inv = !qFuzzyIsNull(det); if (inv) invert = adjoint() / det; break; @@ -397,12 +393,12 @@ QTransform QTransform::inverted(bool *invertible) const \sa setMatrix() */ -QTransform & QTransform::translate(qreal dx, qreal dy) +QTransform &QTransform::translate(qreal dx, qreal dy) { if (dx == 0 && dy == 0) return *this; - switch(type()) { + switch(inline_type()) { case TxNone: affine._dx = dx; affine._dy = dy; @@ -437,7 +433,7 @@ QTransform & QTransform::translate(qreal dx, qreal dy) */ QTransform QTransform::fromTranslate(qreal dx, qreal dy) { - QTransform transform(1, 0, 0, 1, dx, dy); + QTransform transform(1, 0, 0, 0, 1, 0, dx, dy, 1, true); if (dx == 0 && dy == 0) transform.m_dirty = TxNone; else @@ -456,7 +452,7 @@ QTransform & QTransform::scale(qreal sx, qreal sy) if (sx == 1 && sy == 1) return *this; - switch(type()) { + switch(inline_type()) { case TxNone: case TxTranslate: affine._m11 = sx; @@ -489,8 +485,8 @@ QTransform & QTransform::scale(qreal sx, qreal sy) */ QTransform QTransform::fromScale(qreal sx, qreal sy) { - QTransform transform(sx, 0, 0, sy, 0, 0); - if (sx == 1 && sy == 1) + QTransform transform(sx, 0, 0, 0, sy, 0, 0, 0, 1, true); + if (sx == 1. && sy == 1.) transform.m_dirty = TxNone; else transform.m_dirty = TxScale; @@ -505,7 +501,7 @@ QTransform QTransform::fromScale(qreal sx, qreal sy) */ QTransform & QTransform::shear(qreal sh, qreal sv) { - switch(type()) { + switch(inline_type()) { case TxNone: case TxTranslate: affine._m12 = sv; @@ -574,7 +570,7 @@ QTransform & QTransform::rotate(qreal a, Qt::Axis axis) } if (axis == Qt::ZAxis) { - switch(type()) { + switch(inline_type()) { case TxNone: case TxTranslate: affine._m11 = cosa; @@ -646,7 +642,7 @@ QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis) qreal cosa = qCos(a); if (axis == Qt::ZAxis) { - switch(type()) { + switch(inline_type()) { case TxNone: case TxTranslate: affine._m11 = cosa; @@ -730,11 +726,11 @@ bool QTransform::operator!=(const QTransform &o) const */ QTransform & QTransform::operator*=(const QTransform &o) { - const TransformationType otherType = o.type(); + const TransformationType otherType = o.inline_type(); if (otherType == TxNone) return *this; - const TransformationType thisType = type(); + const TransformationType thisType = inline_type(); if (thisType == TxNone) return operator=(o); @@ -812,9 +808,77 @@ QTransform & QTransform::operator*=(const QTransform &o) */ QTransform QTransform::operator*(const QTransform &m) const { - QTransform result = *this; - result *= m; - return result; + const TransformationType otherType = m.inline_type(); + if (otherType == TxNone) + return *this; + + const TransformationType thisType = inline_type(); + if (thisType == TxNone) + return m; + + QTransform t(true); + TransformationType type = qMax(thisType, otherType); + switch(type) { + case TxNone: + break; + case TxTranslate: + t.affine._dx = affine._dx + m.affine._dx; + t.affine._dy += affine._dy + m.affine._dy; + break; + case TxScale: + { + qreal m11 = affine._m11*m.affine._m11; + qreal m22 = affine._m22*m.affine._m22; + + qreal m31 = affine._dx*m.affine._m11 + m.affine._dx; + qreal m32 = affine._dy*m.affine._m22 + m.affine._dy; + + t.affine._m11 = m11; + t.affine._m22 = m22; + t.affine._dx = m31; t.affine._dy = m32; + break; + } + case TxRotate: + case TxShear: + { + qreal m11 = affine._m11*m.affine._m11 + affine._m12*m.affine._m21; + qreal m12 = affine._m11*m.affine._m12 + affine._m12*m.affine._m22; + + qreal m21 = affine._m21*m.affine._m11 + affine._m22*m.affine._m21; + qreal m22 = affine._m21*m.affine._m12 + affine._m22*m.affine._m22; + + qreal m31 = affine._dx*m.affine._m11 + affine._dy*m.affine._m21 + m.affine._dx; + qreal m32 = affine._dx*m.affine._m12 + affine._dy*m.affine._m22 + m.affine._dy; + + t.affine._m11 = m11; t.affine._m12 = m12; + t.affine._m21 = m21; t.affine._m22 = m22; + t.affine._dx = m31; t.affine._dy = m32; + break; + } + case TxProject: + { + qreal m11 = affine._m11*m.affine._m11 + affine._m12*m.affine._m21 + m_13*m.affine._dx; + qreal m12 = affine._m11*m.affine._m12 + affine._m12*m.affine._m22 + m_13*m.affine._dy; + qreal m13 = affine._m11*m.m_13 + affine._m12*m.m_23 + m_13*m.m_33; + + qreal m21 = affine._m21*m.affine._m11 + affine._m22*m.affine._m21 + m_23*m.affine._dx; + qreal m22 = affine._m21*m.affine._m12 + affine._m22*m.affine._m22 + m_23*m.affine._dy; + qreal m23 = affine._m21*m.m_13 + affine._m22*m.m_23 + m_23*m.m_33; + + qreal m31 = affine._dx*m.affine._m11 + affine._dy*m.affine._m21 + m_33*m.affine._dx; + qreal m32 = affine._dx*m.affine._m12 + affine._dy*m.affine._m22 + m_33*m.affine._dy; + qreal m33 = affine._dx*m.m_13 + affine._dy*m.m_23 + m_33*m.m_33; + + t.affine._m11 = m11; t.affine._m12 = m12; t.m_13 = m13; + t.affine._m21 = m21; t.affine._m22 = m22; t.m_23 = m23; + t.affine._dx = m31; t.affine._dy = m32; t.m_33 = m33; + } + } + + t.m_dirty = type; + t.m_type = type; + + return t; } /*! @@ -956,7 +1020,7 @@ QDebug operator<<(QDebug dbg, const QTransform &m) << " 31=" << m.m31() << " 32=" << m.m32() << " 33=" << m.m33() - << ")"; + << ')'; return dbg.space(); } #endif @@ -976,7 +1040,7 @@ QPoint QTransform::map(const QPoint &p) const qreal x = 0, y = 0; - TransformationType t = type(); + TransformationType t = inline_type(); switch(t) { case TxNone: x = fx; @@ -1027,7 +1091,7 @@ QPointF QTransform::map(const QPointF &p) const qreal x = 0, y = 0; - TransformationType t = type(); + TransformationType t = inline_type(); switch(t) { case TxNone: x = fx; @@ -1098,7 +1162,7 @@ QLine QTransform::map(const QLine &l) const qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0; - TransformationType t = type(); + TransformationType t = inline_type(); switch(t) { case TxNone: x1 = fx1; @@ -1157,7 +1221,7 @@ QLineF QTransform::map(const QLineF &l) const qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0; - TransformationType t = type(); + TransformationType t = inline_type(); switch(t) { case TxNone: x1 = fx1; @@ -1245,7 +1309,10 @@ static QPolygonF mapProjective(const QTransform &transform, const QPolygonF &pol */ QPolygonF QTransform::map(const QPolygonF &a) const { - TransformationType t = type(); + TransformationType t = inline_type(); + if (t <= TxTranslate) + return a.translated(affine._dx, affine._dy); + if (t >= QTransform::TxProject) return mapProjective(*this, a); @@ -1272,7 +1339,10 @@ QPolygonF QTransform::map(const QPolygonF &a) const */ QPolygon QTransform::map(const QPolygon &a) const { - TransformationType t = type(); + TransformationType t = inline_type(); + if (t <= TxTranslate) + return a.translated(qRound(affine._dx), qRound(affine._dy)); + if (t >= QTransform::TxProject) return mapProjective(*this, QPolygonF(a)).toPolygon(); @@ -1314,7 +1384,7 @@ extern QPainterPath qt_regionToPath(const QRegion ®ion); */ QRegion QTransform::map(const QRegion &r) const { - TransformationType t = type(); + TransformationType t = inline_type(); if (t == TxNone) return r; @@ -1341,7 +1411,7 @@ struct QHomogeneousCoordinate QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_) : x(x_), y(y_), w(w_) {} const QPointF toPoint() const { - qreal iw = 1 / w; + qreal iw = 1. / w; return QPointF(x * iw, y * iw); } }; @@ -1481,7 +1551,7 @@ static QPainterPath mapProjective(const QTransform &transform, const QPainterPat */ QPainterPath QTransform::map(const QPainterPath &path) const { - TransformationType t = type(); + TransformationType t = inline_type(); if (t == TxNone || path.isEmpty()) return path; @@ -1489,15 +1559,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]; @@ -1530,7 +1596,7 @@ QPainterPath QTransform::map(const QPainterPath &path) const */ QPolygon QTransform::mapToPolygon(const QRect &rect) const { - TransformationType t = type(); + TransformationType t = inline_type(); QPolygon a(4); qreal x[4] = { 0, 0, 0, 0 }, y[4] = { 0, 0, 0, 0 }; @@ -1704,7 +1770,10 @@ void QTransform::setMatrix(qreal m11, qreal m12, qreal m13, QRect QTransform::mapRect(const QRect &rect) const { - TransformationType t = type(); + TransformationType t = inline_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); @@ -1771,7 +1840,10 @@ QRect QTransform::mapRect(const QRect &rect) const */ QRectF QTransform::mapRect(const QRectF &rect) const { - TransformationType t = type(); + TransformationType t = inline_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; @@ -1842,7 +1914,7 @@ QRectF QTransform::mapRect(const QRectF &rect) const */ void QTransform::map(qreal x, qreal y, qreal *tx, qreal *ty) const { - TransformationType t = type(); + TransformationType t = inline_type(); MAP(x, y, *tx, *ty); } @@ -1856,7 +1928,7 @@ void QTransform::map(qreal x, qreal y, qreal *tx, qreal *ty) const */ void QTransform::map(int x, int y, int *tx, int *ty) const { - TransformationType t = type(); + TransformationType t = inline_type(); qreal fx = 0, fy = 0; MAP(x, y, fx, fy); *tx = qRound(fx); @@ -1885,25 +1957,41 @@ const QMatrix &QTransform::toAffine() const */ QTransform::TransformationType QTransform::type() const { - if (m_dirty >= m_type) { - if (m_dirty > TxShear && (!qFuzzyCompare(m_13 + 1, 1) || !qFuzzyCompare(m_23 + 1, 1) || !qFuzzyCompare(m_33, 1))) + if(m_dirty == TxNone || m_dirty < m_type) + return static_cast<TransformationType>(m_type); + + switch (static_cast<TransformationType>(m_dirty)) { + case TxProject: + if (!qFuzzyIsNull(m_13) || !qFuzzyIsNull(m_23) || !qFuzzyIsNull(m_33 - 1)) { m_type = TxProject; - else if (m_dirty > TxScale && (!qFuzzyCompare(affine._m12 + 1, 1) || !qFuzzyCompare(affine._m21 + 1, 1))) { + break; + } + case TxShear: + case TxRotate: + if (!qFuzzyIsNull(affine._m12) || !qFuzzyIsNull(affine._m21)) { const qreal dot = affine._m11 * affine._m12 + affine._m21 * affine._m22; - if (qFuzzyCompare(dot + 1, 1)) + if (qFuzzyIsNull(dot)) m_type = TxRotate; else m_type = TxShear; - } else if (m_dirty > TxTranslate && (!qFuzzyCompare(affine._m11, 1) || !qFuzzyCompare(affine._m22, 1))) + break; + } + case TxScale: + if (!qFuzzyIsNull(affine._m11 - 1) || !qFuzzyIsNull(affine._m22 - 1)) { m_type = TxScale; - else if (m_dirty > TxNone && (!qFuzzyCompare(affine._dx + 1, 1) || !qFuzzyCompare(affine._dy + 1, 1))) + break; + } + case TxTranslate: + if (!qFuzzyIsNull(affine._dx) || !qFuzzyIsNull(affine._dy)) { m_type = TxTranslate; - else - m_type = TxNone; - - m_dirty = TxNone; + break; + } + case TxNone: + m_type = TxNone; + break; } + m_dirty = TxNone; return static_cast<TransformationType>(m_type); } diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h index c76409b..aac7c31 100644 --- a/src/gui/painting/qtransform.h +++ b/src/gui/painting/qtransform.h @@ -159,6 +159,19 @@ public: static QTransform fromScale(qreal dx, qreal dy); private: + inline QTransform(qreal h11, qreal h12, qreal h13, + qreal h21, qreal h22, qreal h23, + qreal h31, qreal h32, qreal h33, bool) + : affine(h11, h12, h21, h22, h31, h32, true) + , m_13(h13), m_23(h23), m_33(h33) + , m_type(TxNone) + , m_dirty(TxProject) {} + inline QTransform(bool) + : affine(true) + , m_13(0), m_23(0), m_33(1) + , m_type(TxNone) + , m_dirty(TxNone) {} + inline TransformationType inline_type() const; QMatrix affine; qreal m_13; qreal m_23; @@ -173,18 +186,25 @@ private: Q_DECLARE_TYPEINFO(QTransform, Q_MOVABLE_TYPE); /******* inlines *****/ +inline QTransform::TransformationType QTransform::inline_type() const +{ + if (m_dirty == TxNone) + return static_cast<TransformationType>(m_type); + return type(); +} + inline bool QTransform::isAffine() const { - return type() < TxProject; + return inline_type() < TxProject; } inline bool QTransform::isIdentity() const { - return type() == TxNone; + return inline_type() == TxNone; } inline bool QTransform::isInvertible() const { - return !qFuzzyCompare(determinant() + 1, 1); + return !qFuzzyIsNull(determinant()); } inline bool QTransform::isScaling() const @@ -193,12 +213,12 @@ inline bool QTransform::isScaling() const } inline bool QTransform::isRotating() const { - return type() >= TxRotate; + return inline_type() >= TxRotate; } inline bool QTransform::isTranslating() const { - return type() >= TxTranslate; + return inline_type() >= TxTranslate; } inline qreal QTransform::determinant() const diff --git a/src/gui/painting/qwindowsurface_d3d.cpp b/src/gui/painting/qwindowsurface_d3d.cpp deleted file mode 100644 index 2b7f633..0000000 --- a/src/gui/painting/qwindowsurface_d3d.cpp +++ /dev/null @@ -1,169 +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$ -** -****************************************************************************/ - -//#define D3D_DEBUG_BACKBUFFER - -#include <QtGui/QPaintDevice> -#include <QtGui/QWidget> -#include "qdebug.h" - -#include "qpaintengine_d3d_p.h" -#include "qwindowsurface_d3d_p.h" -#include "private/qwidget_p.h" -#include "private/qbackingstore_p.h" - -#include <d3d9.h> - -QT_BEGIN_NAMESPACE - -extern QDirect3DPaintEngine *qt_d3dEngine(); - -struct QD3DWindowSurfacePrivate -{ - QSize m_lastSize; - QWidget *m_widget; -}; - -QD3DWindowSurface::QD3DWindowSurface(QWidget *window) - : QWindowSurface(window), d_ptr(new QD3DWindowSurfacePrivate) -{ - Q_ASSERT(window->isTopLevel()); - d_ptr->m_widget = window; -} - - -QD3DWindowSurface::~QD3DWindowSurface() -{ - delete d_ptr; -} - -QPaintDevice *QD3DWindowSurface::paintDevice() -{ - return d_ptr->m_widget; -} - - -void QD3DWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset) -{ - QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft(); - - QDirect3DPaintEngine *engine = qt_d3dEngine(); - LPDIRECT3DSWAPCHAIN9 swapchain = engine->swapChain(d_ptr->m_widget); - - if (swapchain) { - QRect br = rgn.boundingRect(); - QRect wbr = br.translated(-wOffset); - - RECT destrect; - destrect.left = wbr.x(); - destrect.top = wbr.y(); - destrect.right = destrect.left + wbr.width(); - destrect.bottom = destrect.top + wbr.height(); - - RECT srcrect; - srcrect.left = br.x() + offset.x(); - srcrect.top = br.y() + offset.y(); - srcrect.right = wbr.width() + srcrect.left; - srcrect.bottom = wbr.height() + srcrect.top; - int devwidth = d_ptr->m_lastSize.width(); - int devheight = d_ptr->m_lastSize.height(); - - if (devwidth <= srcrect.right) { - int diff = srcrect.right - devwidth; - srcrect.right -= diff; - destrect.right -= diff; - if (srcrect.right <= srcrect.left) - return; - } - if (devheight <= srcrect.bottom) { - int diff = srcrect.bottom - devheight; - srcrect.bottom -= diff; - destrect.bottom -= diff; - if (srcrect.bottom <= srcrect.top) - return; - } - - if (FAILED(swapchain->Present(&srcrect, &destrect, widget->winId(), 0, 0))) - qWarning("QDirect3DPaintEngine: failed to present back buffer."); - -#ifdef D3D_DEBUG_BACKBUFFER - qDebug() << widget << srcrect.left << srcrect.top << wbr.width() << wbr.height() << "Dest: " << destrect.left << destrect.top; - IDirect3DSurface9 *surface; - swapchain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &surface); - QString filename("C:\\test.bmp"); - D3DXSaveSurfaceToFile(filename.utf16(), D3DXIFF_BMP, surface, 0, 0); - surface->Release(); -#endif - } -} - -void QD3DWindowSurface::setGeometry(const QRect &rect) -{ - if (rect.isEmpty()) - qt_d3dEngine()->releaseSwapChain(d_ptr->m_widget); - - d_ptr->m_lastSize = rect.size(); - QWindowSurface::setGeometry(rect); -} - - -bool QD3DWindowSurface::scroll(const QRegion &area, int dx, int dy) -{ - QDirect3DPaintEngine *engine = qt_d3dEngine(); - QRect rect = area.boundingRect(); - - RECT destrect; - destrect.left = rect.x()+dx; - destrect.top = rect.y()+dy; - destrect.right = rect.width() + destrect.left; - destrect.bottom = rect.height() + destrect.top; - - RECT srcrect; - srcrect.left = rect.x(); - srcrect.top = rect.y(); - srcrect.right = rect.width() + srcrect.left; - srcrect.bottom = rect.height() + srcrect.top; - - engine->scroll(d_ptr->m_widget, srcrect, destrect); - return true; -} - -QT_END_NAMESPACE diff --git a/src/gui/painting/qwindowsurface_raster.cpp b/src/gui/painting/qwindowsurface_raster.cpp index 110ba2f..3e7b015 100644 --- a/src/gui/painting/qwindowsurface_raster.cpp +++ b/src/gui/painting/qwindowsurface_raster.cpp @@ -82,7 +82,7 @@ public: uint translucentBackground : 1; #endif #endif -#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE) +#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) uint canUseLayeredWindow : 1; #endif uint inSetGeometry : 1; @@ -98,7 +98,7 @@ QRasterWindowSurface::QRasterWindowSurface(QWidget *window) && window->x11Info().depth() == 32; #endif #endif -#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE) +#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) d_ptr->canUseLayeredWindow = ptrUpdateLayeredWindowIndirect && (qt_widget_private(window)->data.window_flags & Qt::FramelessWindowHint); #endif @@ -127,9 +127,9 @@ QPaintDevice *QRasterWindowSurface::paintDevice() void QRasterWindowSurface::beginPaint(const QRegion &rgn) { -#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_OS_WINCE)) +#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_WS_WINCE)) if (!qt_widget_private(window())->isOpaque) { -#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE) +#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) if (d_ptr->image->image.format() != QImage::Format_ARGB32_Premultiplied && d_ptr->canUseLayeredWindow) prepareBuffer(QImage::Format_ARGB32_Premultiplied, window()); @@ -159,7 +159,7 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi #ifdef Q_WS_WIN QRect br = rgn.boundingRect(); -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE if (!qt_widget_private(window())->isOpaque && d->canUseLayeredWindow) { QRect r = window()->frameGeometry(); QPoint frameOffset = qt_widget_private(window())->frameStrut().topLeft(); @@ -304,7 +304,7 @@ void QRasterWindowSurface::setGeometry(const QRect &rect) Q_D(QRasterWindowSurface); d->inSetGeometry = true; if (d->image == 0 || d->image->width() < rect.width() || d->image->height() < rect.height()) { -#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_OS_WINCE)) +#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_WS_WINCE)) #ifndef Q_WS_WIN if (d_ptr->translucentBackground) #else diff --git a/src/gui/statemachine/qbasickeyeventtransition.cpp b/src/gui/statemachine/qbasickeyeventtransition.cpp new file mode 100644 index 0000000..f7f1eb6 --- /dev/null +++ b/src/gui/statemachine/qbasickeyeventtransition.cpp @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** 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 "qbasickeyeventtransition_p.h" +#include <QtGui/qevent.h> +#include <qdebug.h> +#include <private/qabstracttransition_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QBasicKeyEventTransition + \since 4.6 + \ingroup statemachine + + \brief The QBasicKeyEventTransition class provides a transition for Qt key events. +*/ + +class QBasicKeyEventTransitionPrivate : public QAbstractTransitionPrivate +{ + Q_DECLARE_PUBLIC(QBasicKeyEventTransition) +public: + QBasicKeyEventTransitionPrivate(); + + static QBasicKeyEventTransitionPrivate *get(QBasicKeyEventTransition *q); + + QEvent::Type eventType; + int key; + Qt::KeyboardModifiers modifiersMask; +}; + +QBasicKeyEventTransitionPrivate::QBasicKeyEventTransitionPrivate() +{ + eventType = QEvent::None; + key = 0; + modifiersMask = Qt::NoModifier; +} + +QBasicKeyEventTransitionPrivate *QBasicKeyEventTransitionPrivate::get(QBasicKeyEventTransition *q) +{ + return q->d_func(); +} + +/*! + Constructs a new key event transition with the given \a sourceState. +*/ +QBasicKeyEventTransition::QBasicKeyEventTransition(QState *sourceState) + : QAbstractTransition(*new QBasicKeyEventTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new event transition for events of the given \a type for the + given \a key, with the given \a sourceState. +*/ +QBasicKeyEventTransition::QBasicKeyEventTransition(QEvent::Type type, int key, + QState *sourceState) + : QAbstractTransition(*new QBasicKeyEventTransitionPrivate, sourceState) +{ + Q_D(QBasicKeyEventTransition); + d->eventType = type; + d->key = key; +} + +/*! + Constructs a new event transition for events of the given \a type for the + given \a key, with the given \a modifiersMask and \a sourceState. +*/ +QBasicKeyEventTransition::QBasicKeyEventTransition(QEvent::Type type, int key, + Qt::KeyboardModifiers modifiersMask, + QState *sourceState) + : QAbstractTransition(*new QBasicKeyEventTransitionPrivate, sourceState) +{ + Q_D(QBasicKeyEventTransition); + d->eventType = type; + d->key = key; + d->modifiersMask = modifiersMask; +} + +/*! + Destroys this event transition. +*/ +QBasicKeyEventTransition::~QBasicKeyEventTransition() +{ +} + +/*! + Returns the event type that this key event transition is associated with. +*/ +QEvent::Type QBasicKeyEventTransition::eventType() const +{ + Q_D(const QBasicKeyEventTransition); + return d->eventType; +} + +/*! + Sets the event \a type that this key event transition is associated with. +*/ +void QBasicKeyEventTransition::setEventType(QEvent::Type type) +{ + Q_D(QBasicKeyEventTransition); + d->eventType = type; +} + +/*! + Returns the key that this key event transition checks for. +*/ +int QBasicKeyEventTransition::key() const +{ + Q_D(const QBasicKeyEventTransition); + return d->key; +} + +/*! + Sets the key that this key event transition will check for. +*/ +void QBasicKeyEventTransition::setKey(int key) +{ + Q_D(QBasicKeyEventTransition); + d->key = key; +} + +/*! + Returns the keyboard modifiers mask that this key event transition checks + for. +*/ +Qt::KeyboardModifiers QBasicKeyEventTransition::modifiersMask() const +{ + Q_D(const QBasicKeyEventTransition); + return d->modifiersMask; +} + +/*! + Sets the keyboard modifiers mask that this key event transition will check + for. +*/ +void QBasicKeyEventTransition::setModifiersMask(Qt::KeyboardModifiers modifiersMask) +{ + Q_D(QBasicKeyEventTransition); + d->modifiersMask = modifiersMask; +} + +/*! + \reimp +*/ +bool QBasicKeyEventTransition::eventTest(QEvent *event) +{ + Q_D(const QBasicKeyEventTransition); + if (event->type() == d->eventType) { + QKeyEvent *ke = static_cast<QKeyEvent*>(event); + return (ke->key() == d->key) + && ((ke->modifiers() & d->modifiersMask) == d->modifiersMask); + } + return false; +} + +/*! + \reimp +*/ +void QBasicKeyEventTransition::onTransition(QEvent *) +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qbasickeyeventtransition_p.h b/src/gui/statemachine/qbasickeyeventtransition_p.h new file mode 100644 index 0000000..39fa6ad --- /dev/null +++ b/src/gui/statemachine/qbasickeyeventtransition_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QBASICKEYEVENTTRANSITION_P_H +#define QBASICKEYEVENTTRANSITION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qabstracttransition.h> +#include <QtGui/qevent.h> + +QT_BEGIN_NAMESPACE + +class QBasicKeyEventTransitionPrivate; +class Q_AUTOTEST_EXPORT QBasicKeyEventTransition : public QAbstractTransition +{ + Q_OBJECT +public: + QBasicKeyEventTransition(QState *sourceState = 0); + QBasicKeyEventTransition(QEvent::Type type, int key, QState *sourceState = 0); + QBasicKeyEventTransition(QEvent::Type type, int key, + Qt::KeyboardModifiers modifiersMask, + QState *sourceState = 0); + ~QBasicKeyEventTransition(); + + QEvent::Type eventType() const; + void setEventType(QEvent::Type type); + + int key() const; + void setKey(int key); + + Qt::KeyboardModifiers modifiersMask() const; + void setModifiersMask(Qt::KeyboardModifiers modifiers); + +protected: + bool eventTest(QEvent *event); + void onTransition(QEvent *); + +private: + Q_DISABLE_COPY(QBasicKeyEventTransition) + Q_DECLARE_PRIVATE(QBasicKeyEventTransition) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/statemachine/qbasicmouseeventtransition.cpp b/src/gui/statemachine/qbasicmouseeventtransition.cpp new file mode 100644 index 0000000..20dd792 --- /dev/null +++ b/src/gui/statemachine/qbasicmouseeventtransition.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** 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 "qbasicmouseeventtransition_p.h" +#include <QtGui/qevent.h> +#include <QtGui/qpainterpath.h> +#include <qdebug.h> +#include <private/qabstracttransition_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QBasicMouseEventTransition + \since 4.6 + \ingroup statemachine + + \brief The QBasicMouseEventTransition class provides a transition for Qt mouse events. +*/ + +class QBasicMouseEventTransitionPrivate : public QAbstractTransitionPrivate +{ + Q_DECLARE_PUBLIC(QBasicMouseEventTransition) +public: + QBasicMouseEventTransitionPrivate(); + + static QBasicMouseEventTransitionPrivate *get(QBasicMouseEventTransition *q); + + QEvent::Type eventType; + Qt::MouseButton button; + Qt::KeyboardModifiers modifiersMask; + QPainterPath path; +}; + +QBasicMouseEventTransitionPrivate::QBasicMouseEventTransitionPrivate() +{ + eventType = QEvent::None; + button = Qt::NoButton; +} + +QBasicMouseEventTransitionPrivate *QBasicMouseEventTransitionPrivate::get(QBasicMouseEventTransition *q) +{ + return q->d_func(); +} + +/*! + Constructs a new mouse event transition with the given \a sourceState. +*/ +QBasicMouseEventTransition::QBasicMouseEventTransition(QState *sourceState) + : QAbstractTransition(*new QBasicMouseEventTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new mouse event transition for events of the given \a type. +*/ +QBasicMouseEventTransition::QBasicMouseEventTransition(QEvent::Type type, + Qt::MouseButton button, + QState *sourceState) + : QAbstractTransition(*new QBasicMouseEventTransitionPrivate, sourceState) +{ + Q_D(QBasicMouseEventTransition); + d->eventType = type; + d->button = button; +} + +/*! + Destroys this mouse event transition. +*/ +QBasicMouseEventTransition::~QBasicMouseEventTransition() +{ +} + +/*! + Returns the event type that this mouse event transition is associated with. +*/ +QEvent::Type QBasicMouseEventTransition::eventType() const +{ + Q_D(const QBasicMouseEventTransition); + return d->eventType; +} + +/*! + Sets the event \a type that this mouse event transition is associated with. +*/ +void QBasicMouseEventTransition::setEventType(QEvent::Type type) +{ + Q_D(QBasicMouseEventTransition); + d->eventType = type; +} + +/*! + Returns the button that this mouse event transition checks for. +*/ +Qt::MouseButton QBasicMouseEventTransition::button() const +{ + Q_D(const QBasicMouseEventTransition); + return d->button; +} + +/*! + Sets the button that this mouse event transition will check for. +*/ +void QBasicMouseEventTransition::setButton(Qt::MouseButton button) +{ + Q_D(QBasicMouseEventTransition); + d->button = button; +} + +/*! + Returns the keyboard modifiers mask that this mouse event transition checks + for. +*/ +Qt::KeyboardModifiers QBasicMouseEventTransition::modifiersMask() const +{ + Q_D(const QBasicMouseEventTransition); + return d->modifiersMask; +} + +/*! + Sets the keyboard modifiers mask that this mouse event transition will check + for. +*/ +void QBasicMouseEventTransition::setModifiersMask(Qt::KeyboardModifiers modifiersMask) +{ + Q_D(QBasicMouseEventTransition); + d->modifiersMask = modifiersMask; +} + +/*! + Returns the path for this mouse event transition. +*/ +QPainterPath QBasicMouseEventTransition::path() const +{ + Q_D(const QBasicMouseEventTransition); + return d->path; +} + +/*! + Sets the path for this mouse event transition. +*/ +void QBasicMouseEventTransition::setPath(const QPainterPath &path) +{ + Q_D(QBasicMouseEventTransition); + d->path = path; +} + +/*! + \reimp +*/ +bool QBasicMouseEventTransition::eventTest(QEvent *event) +{ + Q_D(const QBasicMouseEventTransition); + if (event->type() == d->eventType) { + QMouseEvent *me = static_cast<QMouseEvent*>(event); + return (me->button() == d->button) + && ((me->modifiers() & d->modifiersMask) == d->modifiersMask) + && (d->path.isEmpty() || d->path.contains(me->pos())); + } + return false; +} + +/*! + \reimp +*/ +void QBasicMouseEventTransition::onTransition(QEvent *) +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qbasicmouseeventtransition_p.h b/src/gui/statemachine/qbasicmouseeventtransition_p.h new file mode 100644 index 0000000..6c0afe4 --- /dev/null +++ b/src/gui/statemachine/qbasicmouseeventtransition_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QBASICMOUSEEVENTTRANSITION_P_H +#define QBASICMOUSEEVENTTRANSITION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qabstracttransition.h> +#include <QtGui/qevent.h> + +QT_BEGIN_NAMESPACE + +class QPainterPath; + +class QBasicMouseEventTransitionPrivate; +class Q_AUTOTEST_EXPORT QBasicMouseEventTransition : public QAbstractTransition +{ + Q_OBJECT +public: + QBasicMouseEventTransition(QState *sourceState = 0); + QBasicMouseEventTransition(QEvent::Type type, Qt::MouseButton button, + QState *sourceState = 0); + ~QBasicMouseEventTransition(); + + QEvent::Type eventType() const; + void setEventType(QEvent::Type type); + + Qt::MouseButton button() const; + void setButton(Qt::MouseButton button); + + Qt::KeyboardModifiers modifiersMask() const; + void setModifiersMask(Qt::KeyboardModifiers modifiers); + + QPainterPath path() const; + void setPath(const QPainterPath &path); + +protected: + bool eventTest(QEvent *event); + void onTransition(QEvent *); + +private: + Q_DISABLE_COPY(QBasicMouseEventTransition) + Q_DECLARE_PRIVATE(QBasicMouseEventTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/statemachine/qguistatemachine.cpp b/src/gui/statemachine/qguistatemachine.cpp new file mode 100644 index 0000000..612e43e --- /dev/null +++ b/src/gui/statemachine/qguistatemachine.cpp @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** 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 <QtCore/qstatemachine.h> +#include <private/qstatemachine_p.h> +#include <QtGui/qevent.h> +#include <QtGui/qgraphicssceneevent.h> + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler(); + +static QEvent *cloneEvent(QEvent *e) +{ + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + return new QMouseEvent(*static_cast<QMouseEvent*>(e)); + case QEvent::KeyPress: + case QEvent::KeyRelease: + return new QKeyEvent(*static_cast<QKeyEvent*>(e)); + case QEvent::FocusIn: + case QEvent::FocusOut: + return new QFocusEvent(*static_cast<QFocusEvent*>(e)); + case QEvent::Enter: + return new QEvent(*e); + case QEvent::Leave: + return new QEvent(*e); + break; + case QEvent::Paint: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Move: + return new QMoveEvent(*static_cast<QMoveEvent*>(e)); + case QEvent::Resize: + return new QResizeEvent(*static_cast<QResizeEvent*>(e)); + case QEvent::Create: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Destroy: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Show: + return new QShowEvent(*static_cast<QShowEvent*>(e)); + case QEvent::Hide: + return new QHideEvent(*static_cast<QHideEvent*>(e)); + case QEvent::Close: + return new QCloseEvent(*static_cast<QCloseEvent*>(e)); + case QEvent::Quit: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ParentChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ParentAboutToChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ThreadChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::WindowActivate: + case QEvent::WindowDeactivate: + return new QEvent(*e); + + case QEvent::ShowToParent: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::HideToParent: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Wheel: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WindowTitleChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WindowIconChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationWindowIconChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationFontChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationLayoutDirectionChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationPaletteChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::PaletteChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Clipboard: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Speech: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::MetaCall: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::SockAct: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WinEventAct: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::DeferredDelete: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::DragEnter: + return new QDragEnterEvent(*static_cast<QDragEnterEvent*>(e)); + case QEvent::DragMove: + return new QDragMoveEvent(*static_cast<QDragMoveEvent*>(e)); + case QEvent::DragLeave: + return new QDragLeaveEvent(*static_cast<QDragLeaveEvent*>(e)); + case QEvent::Drop: + return new QDropEvent(*static_cast<QDragMoveEvent*>(e)); + case QEvent::DragResponse: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ChildAdded: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ChildPolished: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#ifdef QT3_SUPPORT + case QEvent::ChildInsertedRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ChildInserted: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LayoutHint: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#endif + case QEvent::ChildRemoved: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ShowWindowRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::PolishRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Polish: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LayoutRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::UpdateRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::UpdateLater: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::EmbeddingControl: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ActivateControl: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::DeactivateControl: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ContextMenu: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::InputMethod: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::AccessibilityPrepare: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::TabletMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LocaleChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LanguageChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LayoutDirectionChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::Style: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::TabletPress: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::TabletRelease: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::OkRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::HelpRequest: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::IconDrag: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::FontChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::EnabledChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ActivationChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::StyleChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::IconTextChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ModifiedChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::MouseTrackingChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::WindowBlocked: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WindowUnblocked: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WindowStateChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ToolTip: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::WhatsThis: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::StatusTip: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ActionChanged: + case QEvent::ActionAdded: + case QEvent::ActionRemoved: + return new QActionEvent(*static_cast<QActionEvent*>(e)); + + case QEvent::FileOpen: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::Shortcut: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ShortcutOverride: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + +#ifdef QT3_SUPPORT + case QEvent::Accel: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::AccelAvailable: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#endif + + case QEvent::WhatsThisClicked: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ToolBarChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ApplicationActivate: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ApplicationDeactivate: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::QueryWhatsThis: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::EnterWhatsThisMode: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LeaveWhatsThisMode: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ZOrderChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::HoverEnter: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::HoverLeave: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::HoverMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::AccessibilityHelp: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::AccessibilityDescription: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + +#ifdef QT_KEYPAD_NAVIGATION + case QEvent::EnterEditFocus: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::LeaveEditFocus: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#endif + case QEvent::AcceptDropsChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::MenubarUpdated: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ZeroTimerEvent: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseRelease: + case QEvent::GraphicsSceneMouseDoubleClick: { + QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent*>(e); + QGraphicsSceneMouseEvent *me2 = new QGraphicsSceneMouseEvent(me->type()); + me2->setWidget(me->widget()); + me2->setPos(me->pos()); + me2->setScenePos(me->scenePos()); + me2->setScreenPos(me->screenPos()); +// ### for all buttons + me2->setButtonDownPos(Qt::LeftButton, me->buttonDownPos(Qt::LeftButton)); + me2->setButtonDownPos(Qt::RightButton, me->buttonDownPos(Qt::RightButton)); + me2->setButtonDownScreenPos(Qt::LeftButton, me->buttonDownScreenPos(Qt::LeftButton)); + me2->setButtonDownScreenPos(Qt::RightButton, me->buttonDownScreenPos(Qt::RightButton)); + me2->setLastPos(me->lastPos()); + me2->setLastScenePos(me->lastScenePos()); + me2->setLastScreenPos(me->lastScreenPos()); + me2->setButtons(me->buttons()); + me2->setButton(me->button()); + me2->setModifiers(me->modifiers()); + return me2; + } + + case QEvent::GraphicsSceneContextMenu: { + QGraphicsSceneContextMenuEvent *me = static_cast<QGraphicsSceneContextMenuEvent*>(e); + QGraphicsSceneContextMenuEvent *me2 = new QGraphicsSceneContextMenuEvent(me->type()); + me2->setWidget(me->widget()); + me2->setPos(me->pos()); + me2->setScenePos(me->scenePos()); + me2->setScreenPos(me->screenPos()); + me2->setModifiers(me->modifiers()); + me2->setReason(me->reason()); + return me2; + } + + case QEvent::GraphicsSceneHoverEnter: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneHoverMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneHoverLeave: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneHelp: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneDragEnter: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneDragMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneDragLeave: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneDrop: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneWheel: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::KeyboardLayoutChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::DynamicPropertyChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::TabletEnterProximity: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::TabletLeaveProximity: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::NonClientAreaMouseMove: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::NonClientAreaMouseButtonPress: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::NonClientAreaMouseButtonRelease: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::NonClientAreaMouseButtonDblClick: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::MacSizeChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::ContentsRectChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::MacGLWindowChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::FutureCallOut: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::GraphicsSceneResize: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::GraphicsSceneMove: { + QGraphicsSceneMoveEvent *me = static_cast<QGraphicsSceneMoveEvent*>(e); + QGraphicsSceneMoveEvent *me2 = new QGraphicsSceneMoveEvent(); + me2->setWidget(me->widget()); + me2->setNewPos(me->newPos()); + me2->setOldPos(me->oldPos()); + return me2; + } + + case QEvent::CursorChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + case QEvent::ToolTipChange: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::NetworkReplyUpdated: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + + case QEvent::GrabMouse: + case QEvent::UngrabMouse: + case QEvent::GrabKeyboard: + case QEvent::UngrabKeyboard: + return new QEvent(*e); + +#ifdef QT_MAC_USE_COCOA + case QEvent::CocoaRequestModal: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; +#endif + case QEvent::User: + case QEvent::MaxUser: + Q_ASSERT_X(false, "cloneEvent()", "not implemented"); + break; + default: + ; + } + return qcoreStateMachineHandler()->cloneEvent(e); +} + +const QStateMachinePrivate::Handler qt_gui_statemachine_handler = { + cloneEvent +}; + +static const QStateMachinePrivate::Handler *qt_guistatemachine_last_handler = 0; +int qRegisterGuiStateMachine() +{ + qt_guistatemachine_last_handler = QStateMachinePrivate::handler; + QStateMachinePrivate::handler = &qt_gui_statemachine_handler; + return 1; +} +Q_CONSTRUCTOR_FUNCTION(qRegisterGuiStateMachine) + +int qUnregisterGuiStateMachine() +{ + QStateMachinePrivate::handler = qt_guistatemachine_last_handler; + return 1; +} +Q_DESTRUCTOR_FUNCTION(qUnregisterGuiStateMachine) + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qkeyeventtransition.cpp b/src/gui/statemachine/qkeyeventtransition.cpp new file mode 100644 index 0000000..f803711 --- /dev/null +++ b/src/gui/statemachine/qkeyeventtransition.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** 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 "qkeyeventtransition.h" +#include "qbasickeyeventtransition_p.h" +#include <QtCore/qwrappedevent.h> +#include <private/qeventtransition_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QKeyEventTransition + + \brief The QKeyEventTransition class provides a transition for key events. + + \since 4.6 + \ingroup statemachine + + QKeyEventTransition is part of \l{The State Machine Framework}. + + \sa QState::addTransition() +*/ + +/*! + \property QKeyEventTransition::key + + \brief the key that this key event transition is associated with +*/ + +/*! + \property QKeyEventTransition::modifiersMask + + \brief the keyboard modifiers mask that this key event transition checks for +*/ + +class QKeyEventTransitionPrivate : public QEventTransitionPrivate +{ + Q_DECLARE_PUBLIC(QKeyEventTransition) +public: + QKeyEventTransitionPrivate() {} + + QBasicKeyEventTransition *transition; +}; + +/*! + Constructs a new key event transition with the given \a sourceState. +*/ +QKeyEventTransition::QKeyEventTransition(QState *sourceState) + : QEventTransition(*new QKeyEventTransitionPrivate, sourceState) +{ + Q_D(QKeyEventTransition); + d->transition = new QBasicKeyEventTransition(); +} + +/*! + Constructs a new key event transition for events of the given \a type for + the given \a object, with the given \a key and \a sourceState. +*/ +QKeyEventTransition::QKeyEventTransition(QObject *object, QEvent::Type type, + int key, QState *sourceState) + : QEventTransition(*new QKeyEventTransitionPrivate, object, type, sourceState) +{ + Q_D(QKeyEventTransition); + d->transition = new QBasicKeyEventTransition(type, key); +} + +/*! + Constructs a new key event transition for events of the given \a type for + the given \a object, with the given \a key, \a targets and \a sourceState. +*/ +QKeyEventTransition::QKeyEventTransition(QObject *object, QEvent::Type type, + int key, const QList<QAbstractState*> &targets, + QState *sourceState) + : QEventTransition(*new QKeyEventTransitionPrivate, object, type, targets, sourceState) +{ + Q_D(QKeyEventTransition); + d->transition = new QBasicKeyEventTransition(type, key); +} + +/*! + Destroys this key event transition. +*/ +QKeyEventTransition::~QKeyEventTransition() +{ + Q_D(QKeyEventTransition); + delete d->transition; +} + +/*! + Returns the key that this key event transition checks for. +*/ +int QKeyEventTransition::key() const +{ + Q_D(const QKeyEventTransition); + return d->transition->key(); +} + +/*! + Sets the key that this key event transition will check for. +*/ +void QKeyEventTransition::setKey(int key) +{ + Q_D(QKeyEventTransition); + d->transition->setKey(key); +} + +/*! + Returns the keyboard modifiers mask that this key event transition checks + for. +*/ +Qt::KeyboardModifiers QKeyEventTransition::modifiersMask() const +{ + Q_D(const QKeyEventTransition); + return d->transition->modifiersMask(); +} + +/*! + Sets the keyboard \a modifiers mask that this key event transition will + check for. +*/ +void QKeyEventTransition::setModifiersMask(Qt::KeyboardModifiers modifiersMask) +{ + Q_D(QKeyEventTransition); + d->transition->setModifiersMask(modifiersMask); +} + +/*! + \reimp +*/ +bool QKeyEventTransition::eventTest(QEvent *event) +{ + Q_D(const QKeyEventTransition); + if (!QEventTransition::eventTest(event)) + return false; + QWrappedEvent *we = static_cast<QWrappedEvent*>(event); + d->transition->setEventType(we->event()->type()); + return QAbstractTransitionPrivate::get(d->transition)->callEventTest(we->event()); +} + +/*! + \reimp +*/ +void QKeyEventTransition::onTransition(QEvent *event) +{ + QEventTransition::onTransition(event); +} + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qkeyeventtransition.h b/src/gui/statemachine/qkeyeventtransition.h new file mode 100644 index 0000000..3c8295f --- /dev/null +++ b/src/gui/statemachine/qkeyeventtransition.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QKEYEVENTTRANSITION_H +#define QKEYEVENTTRANSITION_H + +#include <QtCore/qeventtransition.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QKeyEventTransitionPrivate; +class Q_GUI_EXPORT QKeyEventTransition : public QEventTransition +{ + Q_OBJECT + Q_PROPERTY(int key READ key WRITE setKey) + Q_PROPERTY(Qt::KeyboardModifiers modifiersMask READ modifiersMask WRITE setModifiersMask) +public: + QKeyEventTransition(QState *sourceState = 0); + QKeyEventTransition(QObject *object, QEvent::Type type, int key, + QState *sourceState = 0); + QKeyEventTransition(QObject *object, QEvent::Type type, int key, + const QList<QAbstractState*> &targets, + QState *sourceState = 0); + ~QKeyEventTransition(); + + int key() const; + void setKey(int key); + + Qt::KeyboardModifiers modifiersMask() const; + void setModifiersMask(Qt::KeyboardModifiers modifiers); + +protected: + void onTransition(QEvent *event); + bool eventTest(QEvent *event); + +private: + Q_DISABLE_COPY(QKeyEventTransition) + Q_DECLARE_PRIVATE(QKeyEventTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/statemachine/qmouseeventtransition.cpp b/src/gui/statemachine/qmouseeventtransition.cpp new file mode 100644 index 0000000..e4e18eb --- /dev/null +++ b/src/gui/statemachine/qmouseeventtransition.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** 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 "qmouseeventtransition.h" +#include "qbasicmouseeventtransition_p.h" +#include <QtCore/qwrappedevent.h> +#include <QtGui/qpainterpath.h> +#include <private/qeventtransition_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QMouseEventTransition + + \brief The QMouseEventTransition class provides a transition for mouse events. + + \since 4.6 + \ingroup statemachine + + QMouseEventTransition is part of \l{The State Machine Framework}. + + \sa QState::addTransition() +*/ + +/*! + \property QMouseEventTransition::button + + \brief the button that this mouse event transition is associated with +*/ + +/*! + \property QMouseEventTransition::modifiersMask + + \brief the keyboard modifiers mask that this mouse event transition checks for +*/ + +class QMouseEventTransitionPrivate : public QEventTransitionPrivate +{ + Q_DECLARE_PUBLIC(QMouseEventTransition) +public: + QMouseEventTransitionPrivate(); + + QBasicMouseEventTransition *transition; +}; + +QMouseEventTransitionPrivate::QMouseEventTransitionPrivate() +{ +} + +/*! + Constructs a new mouse event transition with the given \a sourceState. +*/ +QMouseEventTransition::QMouseEventTransition(QState *sourceState) + : QEventTransition(*new QMouseEventTransitionPrivate, sourceState) +{ + Q_D(QMouseEventTransition); + d->transition = new QBasicMouseEventTransition(); +} + +/*! + Constructs a new mouse event transition for events of the given \a type for + the given \a object, with the given \a button and \a sourceState. +*/ +QMouseEventTransition::QMouseEventTransition(QObject *object, QEvent::Type type, + Qt::MouseButton button, + QState *sourceState) + : QEventTransition(*new QMouseEventTransitionPrivate, object, type, sourceState) +{ + Q_D(QMouseEventTransition); + d->transition = new QBasicMouseEventTransition(type, button); +} + +/*! + Constructs a new mouse event transition for events of the given \a type for + the given \a object, with the given \a button, \a targets and \a + sourceState. +*/ +QMouseEventTransition::QMouseEventTransition(QObject *object, QEvent::Type type, + Qt::MouseButton button, + const QList<QAbstractState*> &targets, + QState *sourceState) + : QEventTransition(*new QMouseEventTransitionPrivate, object, type, targets, sourceState) +{ + Q_D(QMouseEventTransition); + d->transition = new QBasicMouseEventTransition(type, button); +} + +/*! + Destroys this mouse event transition. +*/ +QMouseEventTransition::~QMouseEventTransition() +{ + Q_D(QMouseEventTransition); + delete d->transition; +} + +/*! + Returns the button that this mouse event transition checks for. +*/ +Qt::MouseButton QMouseEventTransition::button() const +{ + Q_D(const QMouseEventTransition); + return d->transition->button(); +} + +/*! + Sets the \a button that this mouse event transition will check for. +*/ +void QMouseEventTransition::setButton(Qt::MouseButton button) +{ + Q_D(QMouseEventTransition); + d->transition->setButton(button); +} + +/*! + Returns the keyboard modifiers mask that this mouse event transition checks + for. +*/ +Qt::KeyboardModifiers QMouseEventTransition::modifiersMask() const +{ + Q_D(const QMouseEventTransition); + return d->transition->modifiersMask(); +} + +/*! + Sets the keyboard \a modifiers mask that this mouse event transition will + check for. +*/ +void QMouseEventTransition::setModifiersMask(Qt::KeyboardModifiers modifiersMask) +{ + Q_D(QMouseEventTransition); + d->transition->setModifiersMask(modifiersMask); +} + +/*! + Returns the path for this mouse event transition. +*/ +QPainterPath QMouseEventTransition::path() const +{ + Q_D(const QMouseEventTransition); + return d->transition->path(); +} + +/*! + Sets the \a path for this mouse event transition. + If a valid path has been set, the transition will only trigger if the mouse + event position (QMouseEvent::pos()) is inside the path. + + \sa QPainterPath::contains() +*/ +void QMouseEventTransition::setPath(const QPainterPath &path) +{ + Q_D(QMouseEventTransition); + d->transition->setPath(path); +} + +/*! + \reimp +*/ +bool QMouseEventTransition::eventTest(QEvent *event) +{ + Q_D(const QMouseEventTransition); + if (!QEventTransition::eventTest(event)) + return false; + QWrappedEvent *we = static_cast<QWrappedEvent*>(event); + d->transition->setEventType(we->event()->type()); + return QAbstractTransitionPrivate::get(d->transition)->callEventTest(we->event()); +} + +/*! + \reimp +*/ +void QMouseEventTransition::onTransition(QEvent *event) +{ + QEventTransition::onTransition(event); +} + +QT_END_NAMESPACE diff --git a/src/gui/statemachine/qmouseeventtransition.h b/src/gui/statemachine/qmouseeventtransition.h new file mode 100644 index 0000000..3f5f3ac --- /dev/null +++ b/src/gui/statemachine/qmouseeventtransition.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMOUSEEVENTTRANSITION_H +#define QMOUSEEVENTTRANSITION_H + +#include <QtCore/qeventtransition.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QMouseEventTransitionPrivate; +class QPainterPath; +class Q_GUI_EXPORT QMouseEventTransition : public QEventTransition +{ + Q_OBJECT + Q_PROPERTY(Qt::MouseButton button READ button WRITE setButton) + Q_PROPERTY(Qt::KeyboardModifiers modifiersMask READ modifiersMask WRITE setModifiersMask) +public: + QMouseEventTransition(QState *sourceState = 0); + QMouseEventTransition(QObject *object, QEvent::Type type, + Qt::MouseButton button, QState *sourceState = 0); + QMouseEventTransition(QObject *object, QEvent::Type type, + Qt::MouseButton button, + const QList<QAbstractState*> &targets, + QState *sourceState = 0); + ~QMouseEventTransition(); + + Qt::MouseButton button() const; + void setButton(Qt::MouseButton button); + + Qt::KeyboardModifiers modifiersMask() const; + void setModifiersMask(Qt::KeyboardModifiers modifiers); + + QPainterPath path() const; + void setPath(const QPainterPath &path); + +protected: + void onTransition(QEvent *event); + bool eventTest(QEvent *event); + +private: + Q_DISABLE_COPY(QMouseEventTransition) + Q_DECLARE_PRIVATE(QMouseEventTransition) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/statemachine/statemachine.pri b/src/gui/statemachine/statemachine.pri new file mode 100644 index 0000000..2eb1e05 --- /dev/null +++ b/src/gui/statemachine/statemachine.pri @@ -0,0 +1,13 @@ +SOURCES += $$PWD/qguistatemachine.cpp +!contains(DEFINES, QT_NO_STATEMACHINE_EVENTFILTER) { + HEADERS += \ + $$PWD/qkeyeventtransition.h \ + $$PWD/qmouseeventtransition.h \ + $$PWD/qbasickeyeventtransition_p.h \ + $$PWD/qbasicmouseeventtransition_p.h + SOURCES += \ + $$PWD/qkeyeventtransition.cpp \ + $$PWD/qmouseeventtransition.cpp \ + $$PWD/qbasickeyeventtransition.cpp \ + $$PWD/qbasicmouseeventtransition.cpp +} diff --git a/src/gui/styles/gtksymbols.cpp b/src/gui/styles/gtksymbols.cpp index d8a67c2..07cec93 100644 --- a/src/gui/styles/gtksymbols.cpp +++ b/src/gui/styles/gtksymbols.cpp @@ -207,118 +207,118 @@ static QString classPath(GtkWidget *widget) static void resolveGtk() { - const QString GTK_PATH(QLS("gtk-x11-2.0")); - QGtk::gtk_init = (Ptr_gtk_init)QLibrary::resolve(GTK_PATH, 0, "gtk_init"); - QGtk::gtk_window_new = (Ptr_gtk_window_new)QLibrary::resolve(GTK_PATH, 0, "gtk_window_new"); - QGtk::gtk_style_attach = (Ptr_gtk_style_attach)QLibrary::resolve(GTK_PATH, 0, "gtk_style_attach"); - QGtk::gtk_widget_destroy = (Ptr_gtk_widget_destroy)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_destroy"); - QGtk::gtk_widget_realize = (Ptr_gtk_widget_realize)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_realize"); - - QGtk::gtk_file_chooser_set_current_folder = (Ptr_gtk_file_chooser_set_current_folder)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_current_folder"); - QGtk::gtk_file_filter_new = (Ptr_gtk_file_filter_new)QLibrary::resolve(GTK_PATH, 0, "gtk_file_filter_new"); - QGtk::gtk_file_filter_set_name = (Ptr_gtk_file_filter_set_name)QLibrary::resolve(GTK_PATH, 0, "gtk_file_filter_set_name"); - QGtk::gtk_file_filter_add_pattern = (Ptr_gtk_file_filter_add_pattern)QLibrary::resolve(GTK_PATH, 0, "gtk_file_filter_add_pattern"); - QGtk::gtk_file_chooser_add_filter = (Ptr_gtk_file_chooser_add_filter)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_add_filter"); - QGtk::gtk_file_chooser_set_filter = (Ptr_gtk_file_chooser_set_filter)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_filter"); - QGtk::gtk_file_chooser_dialog_new = (Ptr_gtk_file_chooser_dialog_new)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_dialog_new"); - QGtk::gtk_file_chooser_set_current_folder = (Ptr_gtk_file_chooser_set_current_folder)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_current_folder"); - QGtk::gtk_file_chooser_get_filename = (Ptr_gtk_file_chooser_get_filename)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_get_filename"); - QGtk::gtk_file_chooser_get_filenames = (Ptr_gtk_file_chooser_get_filenames)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_get_filenames"); - QGtk::gtk_file_chooser_set_current_name = (Ptr_gtk_file_chooser_set_current_name)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_current_name"); - QGtk::gtk_dialog_run = (Ptr_gtk_dialog_run)QLibrary::resolve(GTK_PATH, 0, "gtk_dialog_run"); - QGtk::gtk_file_chooser_set_filename = (Ptr_gtk_file_chooser_set_filename)QLibrary::resolve(GTK_PATH, 0, "gtk_file_chooser_set_filename"); - - QGtk::gdk_pixbuf_get_pixels = (Ptr_gdk_pixbuf_get_pixels)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_get_pixels"); - QGtk::gdk_pixbuf_get_width = (Ptr_gdk_pixbuf_get_width)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_get_width"); - QGtk::gdk_pixbuf_get_height = (Ptr_gdk_pixbuf_get_height)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_get_height"); - QGtk::gdk_pixmap_new = (Ptr_gdk_pixmap_new)QLibrary::resolve(GTK_PATH, 0, "gdk_pixmap_new"); - QGtk::gdk_pixbuf_new = (Ptr_gdk_pixbuf_new)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_new"); - QGtk::gdk_pixbuf_get_from_drawable = (Ptr_gdk_pixbuf_get_from_drawable)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_get_from_drawable"); - QGtk::gdk_draw_rectangle = (Ptr_gdk_draw_rectangle)QLibrary::resolve(GTK_PATH, 0, "gdk_draw_rectangle"); - QGtk::gdk_pixbuf_unref = (Ptr_gdk_pixbuf_unref)QLibrary::resolve(GTK_PATH, 0, "gdk_pixbuf_unref"); - QGtk::gdk_drawable_unref = (Ptr_gdk_drawable_unref)QLibrary::resolve(GTK_PATH, 0, "gdk_drawable_unref"); - QGtk::gdk_drawable_get_depth = (Ptr_gdk_drawable_get_depth)QLibrary::resolve(GTK_PATH, 0, "gdk_drawable_get_depth"); - QGtk::gdk_color_free = (Ptr_gdk_color_free)QLibrary::resolve(GTK_PATH, 0, "gdk_color_free"); - QGtk::gdk_x11_window_set_user_time = (Ptr_gdk_x11_window_set_user_time)QLibrary::resolve(GTK_PATH, 0, "gdk_x11_window_set_user_time"); - QGtk::gdk_x11_drawable_get_xid = (Ptr_gdk_x11_drawable_get_xid)QLibrary::resolve(GTK_PATH, 0, "gdk_x11_drawable_get_xid"); - QGtk::gdk_x11_drawable_get_xdisplay = (Ptr_gdk_x11_drawable_get_xdisplay)QLibrary::resolve(GTK_PATH, 0, "gdk_x11_drawable_get_xdisplay"); - - QGtk::gtk_widget_set_default_direction = (Ptr_gtk_widget_set_default_direction)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_set_default_direction"); - QGtk::gtk_widget_modify_fg = (Ptr_gtk_widget_modify_color)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_modify_fg"); - QGtk::gtk_widget_modify_bg = (Ptr_gtk_widget_modify_color)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_modify_bg"); - QGtk::gtk_arrow_new = (Ptr_gtk_arrow_new)QLibrary::resolve(GTK_PATH, 0, "gtk_arrow_new"); - QGtk::gtk_menu_item_new = (Ptr_gtk_menu_item_new)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_item_new"); - QGtk::gtk_check_menu_item_new = (Ptr_gtk_check_menu_item_new)QLibrary::resolve(GTK_PATH, 0, "gtk_check_menu_item_new"); - QGtk::gtk_menu_bar_new = (Ptr_gtk_menu_bar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_bar_new"); - QGtk::gtk_menu_new = (Ptr_gtk_menu_new)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_new"); - QGtk::gtk_toolbar_new = (Ptr_gtk_toolbar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_toolbar_new"); - QGtk::gtk_separator_tool_item_new = (Ptr_gtk_separator_tool_item_new)QLibrary::resolve(GTK_PATH, 0, "gtk_separator_tool_item_new"); - QGtk::gtk_toolbar_insert = (Ptr_gtk_toolbar_insert)QLibrary::resolve(GTK_PATH, 0, "gtk_toolbar_insert"); - QGtk::gtk_button_new = (Ptr_gtk_button_new)QLibrary::resolve(GTK_PATH, 0, "gtk_button_new"); - QGtk::gtk_hbutton_box_new = (Ptr_gtk_hbutton_box_new)QLibrary::resolve(GTK_PATH, 0, "gtk_hbutton_box_new"); - QGtk::gtk_check_button_new = (Ptr_gtk_check_button_new)QLibrary::resolve(GTK_PATH, 0, "gtk_check_button_new"); - QGtk::gtk_radio_button_new = (Ptr_gtk_radio_button_new)QLibrary::resolve(GTK_PATH, 0, "gtk_radio_button_new"); - QGtk::gtk_notebook_new = (Ptr_gtk_notebook_new)QLibrary::resolve(GTK_PATH, 0, "gtk_notebook_new"); - QGtk::gtk_progress_bar_new = (Ptr_gtk_progress_bar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_progress_bar_new"); - QGtk::gtk_spin_button_new = (Ptr_gtk_spin_button_new)QLibrary::resolve(GTK_PATH, 0, "gtk_spin_button_new"); - QGtk::gtk_hscale_new = (Ptr_gtk_hscale_new)QLibrary::resolve(GTK_PATH, 0, "gtk_hscale_new"); - QGtk::gtk_vscale_new = (Ptr_gtk_vscale_new)QLibrary::resolve(GTK_PATH, 0, "gtk_vscale_new"); - QGtk::gtk_hscrollbar_new = (Ptr_gtk_hscrollbar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_hscrollbar_new"); - QGtk::gtk_vscrollbar_new = (Ptr_gtk_vscrollbar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_vscrollbar_new"); - QGtk::gtk_scrolled_window_new = (Ptr_gtk_scrolled_window_new)QLibrary::resolve(GTK_PATH, 0, "gtk_scrolled_window_new"); - QGtk::gtk_menu_shell_append = (Ptr_gtk_menu_shell_append)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_shell_append"); - QGtk::gtk_entry_new = (Ptr_gtk_entry_new)QLibrary::resolve(GTK_PATH, 0, "gtk_entry_new"); - QGtk::gtk_tree_view_new = (Ptr_gtk_tree_view_new)QLibrary::resolve(GTK_PATH, 0, "gtk_tree_view_new"); - QGtk::gtk_combo_box_new = (Ptr_gtk_combo_box_new)QLibrary::resolve(GTK_PATH, 0, "gtk_combo_box_new"); - QGtk::gtk_progress_set_adjustment = (Ptr_gtk_progress_set_adjustment)QLibrary::resolve(GTK_PATH, 0, "gtk_progress_set_adjustment"); - QGtk::gtk_range_set_adjustment = (Ptr_gtk_range_set_adjustment)QLibrary::resolve(GTK_PATH, 0, "gtk_range_set_adjustment"); - QGtk::gtk_range_set_inverted = (Ptr_gtk_range_set_inverted)QLibrary::resolve(GTK_PATH, 0, "gtk_range_set_inverted"); - QGtk::gtk_container_add = (Ptr_gtk_container_add)QLibrary::resolve(GTK_PATH, 0, "gtk_container_add"); - QGtk::gtk_icon_factory_lookup_default = (Ptr_gtk_icon_factory_lookup_default)QLibrary::resolve(GTK_PATH, 0, "gtk_icon_factory_lookup_default"); - QGtk::gtk_widget_style_get = (Ptr_gtk_widget_style_get)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_style_get"); - QGtk::gtk_icon_set_render_icon = (Ptr_gtk_icon_set_render_icon)QLibrary::resolve(GTK_PATH, 0, "gtk_icon_set_render_icon"); - QGtk::gtk_fixed_new = (Ptr_gtk_fixed_new)QLibrary::resolve(GTK_PATH, 0, "gtk_fixed_new"); - QGtk::gtk_tree_view_column_new = (Ptr_gtk_tree_view_column_new)QLibrary::resolve(GTK_PATH, 0, "gtk_tree_view_column_new"); - QGtk::gtk_tree_view_append_column= (Ptr_gtk_tree_view_append_column )QLibrary::resolve(GTK_PATH, 0, "gtk_tree_view_append_column"); - QGtk::gtk_tree_view_get_column = (Ptr_gtk_tree_view_get_column )QLibrary::resolve(GTK_PATH, 0, "gtk_tree_view_get_column"); - QGtk::gtk_paint_check = (Ptr_gtk_paint_check)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_check"); - QGtk::gtk_paint_box = (Ptr_gtk_paint_box)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_box"); - QGtk::gtk_paint_flat_box = (Ptr_gtk_paint_flat_box)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_flat_box"); - QGtk::gtk_paint_check = (Ptr_gtk_paint_check)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_check"); - QGtk::gtk_paint_box = (Ptr_gtk_paint_box)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_box"); - QGtk::gtk_paint_resize_grip = (Ptr_gtk_paint_resize_grip)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_resize_grip"); - QGtk::gtk_paint_focus = (Ptr_gtk_paint_focus)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_focus"); - QGtk::gtk_paint_shadow = (Ptr_gtk_paint_shadow)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_shadow"); - QGtk::gtk_paint_slider = (Ptr_gtk_paint_slider)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_slider"); - QGtk::gtk_paint_expander = (Ptr_gtk_paint_expander)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_expander"); - QGtk::gtk_paint_handle = (Ptr_gtk_paint_handle)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_handle"); - QGtk::gtk_paint_option = (Ptr_gtk_paint_option)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_option"); - QGtk::gtk_paint_arrow = (Ptr_gtk_paint_arrow)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_arrow"); - QGtk::gtk_paint_box_gap = (Ptr_gtk_paint_box_gap)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_box_gap"); - QGtk::gtk_paint_extension = (Ptr_gtk_paint_extension)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_extension"); - QGtk::gtk_paint_hline = (Ptr_gtk_paint_hline)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_hline"); - QGtk::gtk_paint_vline = (Ptr_gtk_paint_vline)QLibrary::resolve(GTK_PATH, 0, "gtk_paint_vline"); - QGtk::gtk_adjustment_new = (Ptr_gtk_adjustment_new)QLibrary::resolve(GTK_PATH, 0, "gtk_adjustment_new"); - QGtk::gtk_menu_item_set_submenu = (Ptr_gtk_menu_item_set_submenu)QLibrary::resolve(GTK_PATH, 0, "gtk_menu_item_set_submenu"); - QGtk::gtk_settings_get_default = (Ptr_gtk_settings_get_default)QLibrary::resolve(GTK_PATH, 0, "gtk_settings_get_default"); - QGtk::gtk_separator_menu_item_new = (Ptr_gtk_separator_menu_item_new)QLibrary::resolve(GTK_PATH, 0, "gtk_separator_menu_item_new"); - QGtk::gtk_frame_new = (Ptr_gtk_frame_new)QLibrary::resolve(GTK_PATH, 0, "gtk_frame_new"); - QGtk::gtk_expander_new = (Ptr_gtk_expander_new)QLibrary::resolve(GTK_PATH, 0, "gtk_expander_new"); - QGtk::gtk_statusbar_new = (Ptr_gtk_statusbar_new)QLibrary::resolve(GTK_PATH, 0, "gtk_statusbar_new"); - QGtk::gtk_combo_box_entry_new = (Ptr_gtk_combo_box_entry_new)QLibrary::resolve(GTK_PATH, 0, "gtk_combo_box_entry_new"); - QGtk::gtk_container_forall = (Ptr_gtk_container_forall)QLibrary::resolve(GTK_PATH, 0, "gtk_container_forall"); - QGtk::gtk_widget_size_allocate =(Ptr_gtk_widget_size_allocate)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_size_allocate"); - QGtk::gtk_widget_set_direction =(Ptr_gtk_widget_set_direction)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_set_direction"); - QGtk::gtk_widget_path =(Ptr_gtk_widget_path)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_path"); - QGtk::gtk_container_get_type =(Ptr_gtk_container_get_type)QLibrary::resolve(GTK_PATH, 0, "gtk_container_get_type"); - QGtk::gtk_window_get_type =(Ptr_gtk_window_get_type)QLibrary::resolve(GTK_PATH, 0, "gtk_window_get_type"); - QGtk::gtk_widget_get_type =(Ptr_gtk_widget_get_type)QLibrary::resolve(GTK_PATH, 0, "gtk_widget_get_type"); - QGtk::gtk_rc_get_style_by_paths =(Ptr_gtk_rc_get_style_by_paths)QLibrary::resolve(GTK_PATH, 0, "gtk_rc_get_style_by_paths"); - QGtk::gtk_check_version =(Ptr_gtk_check_version)QLibrary::resolve(GTK_PATH, 0, "gtk_check_version"); - QGtk::pango_font_description_get_size = (Ptr_pango_font_description_get_size)QLibrary::resolve(GTK_PATH, 0, "pango_font_description_get_size"); - QGtk::pango_font_description_get_weight = (Ptr_pango_font_description_get_weight)QLibrary::resolve(GTK_PATH, 0, "pango_font_description_get_weight"); - QGtk::pango_font_description_get_family = (Ptr_pango_font_description_get_family)QLibrary::resolve(GTK_PATH, 0, "pango_font_description_get_family"); - QGtk::pango_font_description_get_style = (Ptr_pango_font_description_get_style)QLibrary::resolve(GTK_PATH, 0, "pango_font_description_get_style"); + QLibrary libgtk(QLS("gtk-x11-2.0")); + QGtk::gtk_init = (Ptr_gtk_init)libgtk.resolve("gtk_init"); + QGtk::gtk_window_new = (Ptr_gtk_window_new)libgtk.resolve("gtk_window_new"); + QGtk::gtk_style_attach = (Ptr_gtk_style_attach)libgtk.resolve("gtk_style_attach"); + QGtk::gtk_widget_destroy = (Ptr_gtk_widget_destroy)libgtk.resolve("gtk_widget_destroy"); + QGtk::gtk_widget_realize = (Ptr_gtk_widget_realize)libgtk.resolve("gtk_widget_realize"); + + QGtk::gtk_file_chooser_set_current_folder = (Ptr_gtk_file_chooser_set_current_folder)libgtk.resolve("gtk_file_chooser_set_current_folder"); + QGtk::gtk_file_filter_new = (Ptr_gtk_file_filter_new)libgtk.resolve("gtk_file_filter_new"); + QGtk::gtk_file_filter_set_name = (Ptr_gtk_file_filter_set_name)libgtk.resolve("gtk_file_filter_set_name"); + QGtk::gtk_file_filter_add_pattern = (Ptr_gtk_file_filter_add_pattern)libgtk.resolve("gtk_file_filter_add_pattern"); + QGtk::gtk_file_chooser_add_filter = (Ptr_gtk_file_chooser_add_filter)libgtk.resolve("gtk_file_chooser_add_filter"); + QGtk::gtk_file_chooser_set_filter = (Ptr_gtk_file_chooser_set_filter)libgtk.resolve("gtk_file_chooser_set_filter"); + QGtk::gtk_file_chooser_dialog_new = (Ptr_gtk_file_chooser_dialog_new)libgtk.resolve("gtk_file_chooser_dialog_new"); + QGtk::gtk_file_chooser_set_current_folder = (Ptr_gtk_file_chooser_set_current_folder)libgtk.resolve("gtk_file_chooser_set_current_folder"); + QGtk::gtk_file_chooser_get_filename = (Ptr_gtk_file_chooser_get_filename)libgtk.resolve("gtk_file_chooser_get_filename"); + QGtk::gtk_file_chooser_get_filenames = (Ptr_gtk_file_chooser_get_filenames)libgtk.resolve("gtk_file_chooser_get_filenames"); + QGtk::gtk_file_chooser_set_current_name = (Ptr_gtk_file_chooser_set_current_name)libgtk.resolve("gtk_file_chooser_set_current_name"); + QGtk::gtk_dialog_run = (Ptr_gtk_dialog_run)libgtk.resolve("gtk_dialog_run"); + QGtk::gtk_file_chooser_set_filename = (Ptr_gtk_file_chooser_set_filename)libgtk.resolve("gtk_file_chooser_set_filename"); + + QGtk::gdk_pixbuf_get_pixels = (Ptr_gdk_pixbuf_get_pixels)libgtk.resolve("gdk_pixbuf_get_pixels"); + QGtk::gdk_pixbuf_get_width = (Ptr_gdk_pixbuf_get_width)libgtk.resolve("gdk_pixbuf_get_width"); + QGtk::gdk_pixbuf_get_height = (Ptr_gdk_pixbuf_get_height)libgtk.resolve("gdk_pixbuf_get_height"); + QGtk::gdk_pixmap_new = (Ptr_gdk_pixmap_new)libgtk.resolve("gdk_pixmap_new"); + QGtk::gdk_pixbuf_new = (Ptr_gdk_pixbuf_new)libgtk.resolve("gdk_pixbuf_new"); + QGtk::gdk_pixbuf_get_from_drawable = (Ptr_gdk_pixbuf_get_from_drawable)libgtk.resolve("gdk_pixbuf_get_from_drawable"); + QGtk::gdk_draw_rectangle = (Ptr_gdk_draw_rectangle)libgtk.resolve("gdk_draw_rectangle"); + QGtk::gdk_pixbuf_unref = (Ptr_gdk_pixbuf_unref)libgtk.resolve("gdk_pixbuf_unref"); + QGtk::gdk_drawable_unref = (Ptr_gdk_drawable_unref)libgtk.resolve("gdk_drawable_unref"); + QGtk::gdk_drawable_get_depth = (Ptr_gdk_drawable_get_depth)libgtk.resolve("gdk_drawable_get_depth"); + QGtk::gdk_color_free = (Ptr_gdk_color_free)libgtk.resolve("gdk_color_free"); + QGtk::gdk_x11_window_set_user_time = (Ptr_gdk_x11_window_set_user_time)libgtk.resolve("gdk_x11_window_set_user_time"); + QGtk::gdk_x11_drawable_get_xid = (Ptr_gdk_x11_drawable_get_xid)libgtk.resolve("gdk_x11_drawable_get_xid"); + QGtk::gdk_x11_drawable_get_xdisplay = (Ptr_gdk_x11_drawable_get_xdisplay)libgtk.resolve("gdk_x11_drawable_get_xdisplay"); + + QGtk::gtk_widget_set_default_direction = (Ptr_gtk_widget_set_default_direction)libgtk.resolve("gtk_widget_set_default_direction"); + QGtk::gtk_widget_modify_fg = (Ptr_gtk_widget_modify_color)libgtk.resolve("gtk_widget_modify_fg"); + QGtk::gtk_widget_modify_bg = (Ptr_gtk_widget_modify_color)libgtk.resolve("gtk_widget_modify_bg"); + QGtk::gtk_arrow_new = (Ptr_gtk_arrow_new)libgtk.resolve("gtk_arrow_new"); + QGtk::gtk_menu_item_new = (Ptr_gtk_menu_item_new)libgtk.resolve("gtk_menu_item_new"); + QGtk::gtk_check_menu_item_new = (Ptr_gtk_check_menu_item_new)libgtk.resolve("gtk_check_menu_item_new"); + QGtk::gtk_menu_bar_new = (Ptr_gtk_menu_bar_new)libgtk.resolve("gtk_menu_bar_new"); + QGtk::gtk_menu_new = (Ptr_gtk_menu_new)libgtk.resolve("gtk_menu_new"); + QGtk::gtk_toolbar_new = (Ptr_gtk_toolbar_new)libgtk.resolve("gtk_toolbar_new"); + QGtk::gtk_separator_tool_item_new = (Ptr_gtk_separator_tool_item_new)libgtk.resolve("gtk_separator_tool_item_new"); + QGtk::gtk_toolbar_insert = (Ptr_gtk_toolbar_insert)libgtk.resolve("gtk_toolbar_insert"); + QGtk::gtk_button_new = (Ptr_gtk_button_new)libgtk.resolve("gtk_button_new"); + QGtk::gtk_hbutton_box_new = (Ptr_gtk_hbutton_box_new)libgtk.resolve("gtk_hbutton_box_new"); + QGtk::gtk_check_button_new = (Ptr_gtk_check_button_new)libgtk.resolve("gtk_check_button_new"); + QGtk::gtk_radio_button_new = (Ptr_gtk_radio_button_new)libgtk.resolve("gtk_radio_button_new"); + QGtk::gtk_notebook_new = (Ptr_gtk_notebook_new)libgtk.resolve("gtk_notebook_new"); + QGtk::gtk_progress_bar_new = (Ptr_gtk_progress_bar_new)libgtk.resolve("gtk_progress_bar_new"); + QGtk::gtk_spin_button_new = (Ptr_gtk_spin_button_new)libgtk.resolve("gtk_spin_button_new"); + QGtk::gtk_hscale_new = (Ptr_gtk_hscale_new)libgtk.resolve("gtk_hscale_new"); + QGtk::gtk_vscale_new = (Ptr_gtk_vscale_new)libgtk.resolve("gtk_vscale_new"); + QGtk::gtk_hscrollbar_new = (Ptr_gtk_hscrollbar_new)libgtk.resolve("gtk_hscrollbar_new"); + QGtk::gtk_vscrollbar_new = (Ptr_gtk_vscrollbar_new)libgtk.resolve("gtk_vscrollbar_new"); + QGtk::gtk_scrolled_window_new = (Ptr_gtk_scrolled_window_new)libgtk.resolve("gtk_scrolled_window_new"); + QGtk::gtk_menu_shell_append = (Ptr_gtk_menu_shell_append)libgtk.resolve("gtk_menu_shell_append"); + QGtk::gtk_entry_new = (Ptr_gtk_entry_new)libgtk.resolve("gtk_entry_new"); + QGtk::gtk_tree_view_new = (Ptr_gtk_tree_view_new)libgtk.resolve("gtk_tree_view_new"); + QGtk::gtk_combo_box_new = (Ptr_gtk_combo_box_new)libgtk.resolve("gtk_combo_box_new"); + QGtk::gtk_progress_set_adjustment = (Ptr_gtk_progress_set_adjustment)libgtk.resolve("gtk_progress_set_adjustment"); + QGtk::gtk_range_set_adjustment = (Ptr_gtk_range_set_adjustment)libgtk.resolve("gtk_range_set_adjustment"); + QGtk::gtk_range_set_inverted = (Ptr_gtk_range_set_inverted)libgtk.resolve("gtk_range_set_inverted"); + QGtk::gtk_container_add = (Ptr_gtk_container_add)libgtk.resolve("gtk_container_add"); + QGtk::gtk_icon_factory_lookup_default = (Ptr_gtk_icon_factory_lookup_default)libgtk.resolve("gtk_icon_factory_lookup_default"); + QGtk::gtk_widget_style_get = (Ptr_gtk_widget_style_get)libgtk.resolve("gtk_widget_style_get"); + QGtk::gtk_icon_set_render_icon = (Ptr_gtk_icon_set_render_icon)libgtk.resolve("gtk_icon_set_render_icon"); + QGtk::gtk_fixed_new = (Ptr_gtk_fixed_new)libgtk.resolve("gtk_fixed_new"); + QGtk::gtk_tree_view_column_new = (Ptr_gtk_tree_view_column_new)libgtk.resolve("gtk_tree_view_column_new"); + QGtk::gtk_tree_view_append_column= (Ptr_gtk_tree_view_append_column )libgtk.resolve("gtk_tree_view_append_column"); + QGtk::gtk_tree_view_get_column = (Ptr_gtk_tree_view_get_column )libgtk.resolve("gtk_tree_view_get_column"); + QGtk::gtk_paint_check = (Ptr_gtk_paint_check)libgtk.resolve("gtk_paint_check"); + QGtk::gtk_paint_box = (Ptr_gtk_paint_box)libgtk.resolve("gtk_paint_box"); + QGtk::gtk_paint_flat_box = (Ptr_gtk_paint_flat_box)libgtk.resolve("gtk_paint_flat_box"); + QGtk::gtk_paint_check = (Ptr_gtk_paint_check)libgtk.resolve("gtk_paint_check"); + QGtk::gtk_paint_box = (Ptr_gtk_paint_box)libgtk.resolve("gtk_paint_box"); + QGtk::gtk_paint_resize_grip = (Ptr_gtk_paint_resize_grip)libgtk.resolve("gtk_paint_resize_grip"); + QGtk::gtk_paint_focus = (Ptr_gtk_paint_focus)libgtk.resolve("gtk_paint_focus"); + QGtk::gtk_paint_shadow = (Ptr_gtk_paint_shadow)libgtk.resolve("gtk_paint_shadow"); + QGtk::gtk_paint_slider = (Ptr_gtk_paint_slider)libgtk.resolve("gtk_paint_slider"); + QGtk::gtk_paint_expander = (Ptr_gtk_paint_expander)libgtk.resolve("gtk_paint_expander"); + QGtk::gtk_paint_handle = (Ptr_gtk_paint_handle)libgtk.resolve("gtk_paint_handle"); + QGtk::gtk_paint_option = (Ptr_gtk_paint_option)libgtk.resolve("gtk_paint_option"); + QGtk::gtk_paint_arrow = (Ptr_gtk_paint_arrow)libgtk.resolve("gtk_paint_arrow"); + QGtk::gtk_paint_box_gap = (Ptr_gtk_paint_box_gap)libgtk.resolve("gtk_paint_box_gap"); + QGtk::gtk_paint_extension = (Ptr_gtk_paint_extension)libgtk.resolve("gtk_paint_extension"); + QGtk::gtk_paint_hline = (Ptr_gtk_paint_hline)libgtk.resolve("gtk_paint_hline"); + QGtk::gtk_paint_vline = (Ptr_gtk_paint_vline)libgtk.resolve("gtk_paint_vline"); + QGtk::gtk_adjustment_new = (Ptr_gtk_adjustment_new)libgtk.resolve("gtk_adjustment_new"); + QGtk::gtk_menu_item_set_submenu = (Ptr_gtk_menu_item_set_submenu)libgtk.resolve("gtk_menu_item_set_submenu"); + QGtk::gtk_settings_get_default = (Ptr_gtk_settings_get_default)libgtk.resolve("gtk_settings_get_default"); + QGtk::gtk_separator_menu_item_new = (Ptr_gtk_separator_menu_item_new)libgtk.resolve("gtk_separator_menu_item_new"); + QGtk::gtk_frame_new = (Ptr_gtk_frame_new)libgtk.resolve("gtk_frame_new"); + QGtk::gtk_expander_new = (Ptr_gtk_expander_new)libgtk.resolve("gtk_expander_new"); + QGtk::gtk_statusbar_new = (Ptr_gtk_statusbar_new)libgtk.resolve("gtk_statusbar_new"); + QGtk::gtk_combo_box_entry_new = (Ptr_gtk_combo_box_entry_new)libgtk.resolve("gtk_combo_box_entry_new"); + QGtk::gtk_container_forall = (Ptr_gtk_container_forall)libgtk.resolve("gtk_container_forall"); + QGtk::gtk_widget_size_allocate =(Ptr_gtk_widget_size_allocate)libgtk.resolve("gtk_widget_size_allocate"); + QGtk::gtk_widget_set_direction =(Ptr_gtk_widget_set_direction)libgtk.resolve("gtk_widget_set_direction"); + QGtk::gtk_widget_path =(Ptr_gtk_widget_path)libgtk.resolve("gtk_widget_path"); + QGtk::gtk_container_get_type =(Ptr_gtk_container_get_type)libgtk.resolve("gtk_container_get_type"); + QGtk::gtk_window_get_type =(Ptr_gtk_window_get_type)libgtk.resolve("gtk_window_get_type"); + QGtk::gtk_widget_get_type =(Ptr_gtk_widget_get_type)libgtk.resolve("gtk_widget_get_type"); + QGtk::gtk_rc_get_style_by_paths =(Ptr_gtk_rc_get_style_by_paths)libgtk.resolve("gtk_rc_get_style_by_paths"); + QGtk::gtk_check_version =(Ptr_gtk_check_version)libgtk.resolve("gtk_check_version"); + QGtk::pango_font_description_get_size = (Ptr_pango_font_description_get_size)libgtk.resolve("pango_font_description_get_size"); + QGtk::pango_font_description_get_weight = (Ptr_pango_font_description_get_weight)libgtk.resolve("pango_font_description_get_weight"); + QGtk::pango_font_description_get_family = (Ptr_pango_font_description_get_family)libgtk.resolve("pango_font_description_get_family"); + QGtk::pango_font_description_get_style = (Ptr_pango_font_description_get_style)libgtk.resolve("pango_font_description_get_style"); } void QGtk::cleanup_gtk_widgets() diff --git a/src/gui/styles/qcleanlooksstyle.cpp b/src/gui/styles/qcleanlooksstyle.cpp index 468ada9..5c37794 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,9 +1645,9 @@ 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()); - pixmapName += QLatin1String("-") + QString::number(int(header->position)); - pixmapName += QLatin1String("-") + QString::number(int(header->orientation)); + QString pixmapName = QStyleHelper::uniqueName(QLatin1String("headersection"), option, option->rect.size()); + pixmapName += QString::number(- int(header->position)); + pixmapName += QString::number(- int(header->orientation)); QRect r = option->rect; QColor gradientStopColor; QColor gradientStartColor = option->palette.button().color(); @@ -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; @@ -3781,6 +3768,20 @@ QSize QCleanlooksStyle::sizeFromContents(ContentsType type, const QStyleOption * } break; case CT_GroupBox: + // Since we use a bold font we have to recalculate base width + if (const QGroupBox *gb = qobject_cast<const QGroupBox*>(widget)) { + QFont font = gb->font(); + font.setBold(true); + QFontMetrics metrics(font); + int baseWidth = metrics.width(gb->title()) + metrics.width(QLatin1Char(' ')); + if (gb->isCheckable()) { + baseWidth += pixelMetric(QStyle::PM_IndicatorWidth, option, widget); + baseWidth += pixelMetric(QStyle::PM_CheckBoxLabelSpacing, option, widget); + } + newSize.setWidth(qMax(baseWidth, newSize.width())); + } + newSize += QSize(0, 1); + break; case CT_RadioButton: case CT_CheckBox: newSize += QSize(0, 1); @@ -3857,7 +3858,7 @@ void QCleanlooksStyle::polish(QApplication *app) dataDirs = QLatin1String("/usr/local/share/:/usr/share/"); dataDirs.prepend(QDir::homePath() + QLatin1String("/:")); - d->iconDirs = dataDirs.split(QLatin1String(":")); + d->iconDirs = dataDirs.split(QLatin1Char(':')); #endif } diff --git a/src/gui/styles/qcommonstyle.cpp b/src/gui/styles/qcommonstyle.cpp index f3d1537..1285598 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> @@ -850,7 +851,7 @@ void QCommonStylePrivate::lookupIconTheme() const dataDirs.prepend(QDir::homePath() + QLatin1String("/:")); QStringList kdeDirs = QString::fromLocal8Bit(getenv("KDEDIRS")).split(QLatin1Char(':')); foreach (const QString &dirName, kdeDirs) - dataDirs.append(QLatin1String(":") + dirName + QLatin1String("/share")); + dataDirs.append(QLatin1Char(':') + dirName + QLatin1String("/share")); iconDirs = dataDirs.split(QLatin1Char(':')); QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde")); @@ -892,7 +893,7 @@ QIconTheme QCommonStylePrivate::parseIndexFile(const QString &themeName) const parents = line.split(QLatin1Char(',')); } - if (line.startsWith(QLatin1String("["))) { + if (line.startsWith(QLatin1Char('['))) { line = line.trimmed(); line.chop(1); QString dirName = line.right(line.length() - 1); @@ -1663,6 +1664,7 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, if (!styleHint(SH_UnderlineShortcut, opt, widget)) alignment |= Qt::TextHideMnemonic; rect.translate(shiftX, shiftY); + p->setFont(toolbutton->font); drawItemText(p, rect, alignment, toolbutton->palette, opt->state & State_Enabled, toolbutton->text, QPalette::ButtonText); @@ -1979,7 +1981,9 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, : QIcon::Disabled); QPixmap tabIcon = tabV2.icon.pixmap(iconSize, (tabV2.state & State_Enabled) ? QIcon::Normal - : QIcon::Disabled); + : QIcon::Disabled, + (tabV2.state & State_Selected) ? QIcon::On + : QIcon::Off); int offset = 4; int left = opt->rect.left(); @@ -2267,7 +2271,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; @@ -2849,9 +2853,11 @@ QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt, tr.setRect(0, 0, tr.height(), tr.width()); int verticalShift = pixelMetric(QStyle::PM_TabBarTabShiftVertical, tab, widget); int horizontalShift = pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, tab, widget); + int hpadding = pixelMetric(QStyle::PM_TabBarTabHSpace, opt, widget) / 2; + int vpadding = pixelMetric(QStyle::PM_TabBarTabVSpace, opt, widget) / 2; if (tabV2.shape == QTabBar::RoundedSouth || tabV2.shape == QTabBar::TriangularSouth) verticalShift = -verticalShift; - tr.adjust(0, 0, horizontalShift, verticalShift); + tr.adjust(hpadding, vpadding, horizontalShift - hpadding, verticalShift - vpadding); bool selected = tabV2.state & State_Selected; if (selected) { tr.setBottom(tr.bottom() - verticalShift); @@ -3182,6 +3188,25 @@ QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt, } break; #endif //QT_NO_ITEMVIEWS +#ifndef QT_NO_TOOLBAR + case SE_ToolBarHandle: + if (const QStyleOptionToolBar *tbopt = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) { + if (tbopt->features & QStyleOptionToolBar::Movable) { + ///we need to access the widget here because the style option doesn't + //have all the information we need (ie. the layout's margin) + const QToolBar *tb = qobject_cast<const QToolBar*>(widget); + const int margin = tb && tb->layout() ? tb->layout()->margin() : 2; + const int handleExtent = pixelMetric(QStyle::PM_ToolBarExtensionExtent, opt, tb); + if (tbopt->state & QStyle::State_Horizontal) { + r = QRect(margin, margin, handleExtent, tbopt->rect.height() - 2*margin); + r = QStyle::visualRect(tbopt->direction, tbopt->rect, r); + } else { + r = QRect(margin, margin, tbopt->rect.width() - 2*margin, handleExtent); + } + } + } + break; +#endif //QT_NO_TOOLBAR default: break; } @@ -3189,47 +3214,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 +3234,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 +3249,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 +3739,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 +3761,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 1be3d6e..2478f20 100644 --- a/src/gui/styles/qmacstyle_mac.mm +++ b/src/gui/styles/qmacstyle_mac.mm @@ -55,6 +55,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> @@ -130,6 +131,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 *); @@ -2567,6 +2582,9 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW case PM_MenuHMargin: ret = 0; break; + case PM_ToolBarFrameWidth: + ret = 0; + break; default: ret = QWindowsStyle::pixelMetric(metric, opt, widget); break; @@ -3508,7 +3526,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); } @@ -3516,51 +3537,76 @@ 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); + pr.setHeight(pixmap.size().height()); + cr.adjust(0, pr.bottom() + 1, 0, 1); alignment |= Qt::AlignCenter; } else { pr.setWidth(pixmap.width() + 8); 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 (opt->state & State_Sunken) { + 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 && + (tb->state & State_Sunken)) { + // 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); } @@ -4381,9 +4427,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter case CE_ToolBar: { // For unified tool bars, draw nothing. if (w) { - if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(w->window())) + if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(w->window())) { if (mainWindow->unifiedTitleAndToolBarOnMac()) break; + } } // draw background gradient @@ -5109,16 +5156,22 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex drawToolbarButtonArrow(tb->rect, tds, cg); } if (tb->state & State_On) { - QPen oldPen = p->pen(); - p->setPen(QColor(0, 0, 0, 0x3a)); - p->fillRect(tb->rect.adjusted(1, 1, -1, -1), QColor(0, 0, 0, 0x12)); - p->drawLine(tb->rect.left() + 1, tb->rect.top(), - tb->rect.right() - 1, tb->rect.top()); - p->drawLine(tb->rect.left() + 1, tb->rect.bottom(), - tb->rect.right() - 1, tb->rect.bottom()); - p->drawLine(tb->rect.topLeft(), tb->rect.bottomLeft()); - p->drawLine(tb->rect.topRight(), tb->rect.bottomRight()); - p->setPen(oldPen); + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { + static QPixmap pm(QLatin1String(":/trolltech/mac/style/images/leopard-unified-toolbar-on.png")); + p->setRenderHint(QPainter::SmoothPixmapTransform); + QStyleHelper::drawBorderPixmap(pm, p, tb->rect, 2, 2, 2, 2); + } else { + QPen oldPen = p->pen(); + p->setPen(QColor(0, 0, 0, 0x3a)); + p->fillRect(tb->rect.adjusted(1, 1, -1, -1), QColor(0, 0, 0, 0x12)); + p->drawLine(tb->rect.left() + 1, tb->rect.top(), + tb->rect.right() - 1, tb->rect.top()); + p->drawLine(tb->rect.left() + 1, tb->rect.bottom(), + tb->rect.right() - 1, tb->rect.bottom()); + p->drawLine(tb->rect.topLeft(), tb->rect.bottomLeft()); + p->drawLine(tb->rect.topRight(), tb->rect.bottomRight()); + p->setPen(oldPen); + } } drawControl(CE_ToolButtonLabel, opt, p, widget); } else { @@ -5213,6 +5266,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; @@ -5851,6 +5908,14 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, } break; case CT_ToolButton: + if (widget && qobject_cast<const QToolBar *>(widget->parentWidget())) { + sz.rwidth() += 4; + if (sz.height() <= 32) { + // Workaround strange HIToolBar bug when getting constraints. + sz.rheight() += 1; + } + return sz; + } sz.rwidth() += 10; sz.rheight() += 10; return sz; diff --git a/src/gui/styles/qmotifstyle.cpp b/src/gui/styles/qmotifstyle.cpp index 7d4fab8..be0e3eb 100644 --- a/src/gui/styles/qmotifstyle.cpp +++ b/src/gui/styles/qmotifstyle.cpp @@ -2026,10 +2026,6 @@ QMotifStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, QSize sz(contentsSize); switch(ct) { - case CT_Splitter: - sz = QSize(10, 10); - break; - case CT_RadioButton: case CT_CheckBox: sz = QCommonStyle::sizeFromContents(ct, opt, contentsSize, widget); diff --git a/src/gui/styles/qplastiquestyle.cpp b/src/gui/styles/qplastiquestyle.cpp index 24d7748..91ad64e 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> @@ -2844,8 +2845,8 @@ void QPlastiqueStyle::drawControl(ControlElement element, const QStyleOption *op if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { QPixmap cache; QString pixmapName = uniqueName(QLatin1String("headersection"), option, option->rect.size()); - pixmapName += QLatin1String("-") + QString::number(int(header->position)); - pixmapName += QLatin1String("-") + QString::number(int(header->orientation)); + pixmapName += QString::number(- int(header->position)); + pixmapName += QString::number(- int(header->orientation)); if (!UsePixmapCache || !QPixmapCache::find(pixmapName, cache)) { cache = QPixmap(option->rect.size()); @@ -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; @@ -5453,6 +5460,11 @@ int QPlastiqueStyle::styleHint(StyleHint hint, const QStyleOption *option, const case SH_Menu_SubMenuPopupDelay: ret = 96; // from Plastik break; +#ifndef Q_OS_WIN + case SH_Menu_AllowActiveAndDisabled: + ret = false; + break; +#endif default: ret = QWindowsStyle::styleHint(hint, option, widget, returnData); break; diff --git a/src/gui/styles/qstyle.cpp b/src/gui/styles/qstyle.cpp index 982f48f..3fab682 100644 --- a/src/gui/styles/qstyle.cpp +++ b/src/gui/styles/qstyle.cpp @@ -1048,6 +1048,8 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, \value SE_TabBarTabRightButton Area for a widget on the right side of a tab in a tab bar. \value SE_TabBarTabText Area for the text on a tab in a tab bar. + \value SE_ToolBarHandle Area for the handle of a tool bar. + \sa subElementRect() */ @@ -1330,7 +1332,7 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, \value PM_LayoutVerticalSpacing Default \l{QLayout::spacing}{vertical spacing} for a QLayout. \value PM_MaximumDragDistance The maximum allowed distance between - the mouse and a slider when dragging. Exceeding the specified + the mouse and a scrollbar when dragging. Exceeding the specified distance will cause the slider to jump back to the original position; a value of -1 disables this behavior. @@ -2437,7 +2439,7 @@ QDebug operator<<(QDebug debug, QStyle::State state) qSort(states); debug << states.join(QLatin1String(" | ")); - debug << ")"; + debug << ')'; return debug; } #endif diff --git a/src/gui/styles/qstyle.h b/src/gui/styles/qstyle.h index 6191d51..cc92459 100644 --- a/src/gui/styles/qstyle.h +++ b/src/gui/styles/qstyle.h @@ -373,6 +373,8 @@ public: SE_ShapedFrameContents, + SE_ToolBarHandle, + // do not add any values below/greater than this SE_CustomBase = 0xf0000000 }; @@ -453,6 +455,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..69f8cd2 --- /dev/null +++ b/src/gui/styles/qstylehelper.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** 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 + +namespace QStyleHelper { +const bool UsePixmapCache = true; + +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; +} + +#ifndef QT_NO_DIAL + +int 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 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 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 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 + +void drawBorderPixmap(const QPixmap &pixmap, QPainter *painter, const QRect &rect, + int left, int top, int right, + int bottom) +{ + QSize size = pixmap.size(); + //painter->setRenderHint(QPainter::SmoothPixmapTransform); + + //top + if (top > 0) { + painter->drawPixmap(QRect(rect.left() + left, rect.top(), rect.width() -right - left, top), pixmap, + QRect(left, 0, size.width() -right - left, top)); + + //top-left + if(left > 0) + painter->drawPixmap(QRect(rect.left(), rect.top(), left, top), pixmap, + QRect(0, 0, left, top)); + + //top-right + if (right > 0) + painter->drawPixmap(QRect(rect.left() + rect.width() - right, rect.top(), right, top), pixmap, + QRect(size.width() - right, 0, right, top)); + } + + //left + if (left > 0) + painter->drawPixmap(QRect(rect.left(), rect.top()+top, left, rect.height() - top - bottom), pixmap, + QRect(0, top, left, size.height() - bottom - top)); + + //center + painter->drawPixmap(QRect(rect.left() + left, rect.top()+top, rect.width() -right - left, + rect.height() - bottom - top), pixmap, + QRect(left, top, size.width() -right -left, + size.height() - bottom - top)); + //right + if (right > 0) + painter->drawPixmap(QRect(rect.left() +rect.width() - right, rect.top()+top, right, rect.height() - top - bottom), pixmap, + QRect(size.width() - right, top, right, size.height() - bottom - top)); + + //bottom + if (bottom > 0) { + painter->drawPixmap(QRect(rect.left() +left, rect.top() + rect.height() - bottom, + rect.width() - right - left, bottom), pixmap, + QRect(left, size.height() - bottom, + size.width() - right - left, bottom)); + //bottom-left + if (left > 0) + painter->drawPixmap(QRect(rect.left(), rect.top() + rect.height() - bottom, left, bottom), pixmap, + QRect(0, size.height() - bottom, left, bottom)); + + //bottom-right + if (right > 0) + painter->drawPixmap(QRect(rect.left() + rect.width() - right, rect.top() + rect.height() - bottom, right, bottom), pixmap, + QRect(size.width() - right, size.height() - bottom, right, bottom)); + + } +} +} +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..5385d9f --- /dev/null +++ b/src/gui/styles/qstylehelper_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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 <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 QPixmap; +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 + void drawBorderPixmap(const QPixmap &pixmap, QPainter *painter, const QRect &rect, + int left = 0, int top = 0, int right = 0, + int bottom = 0); +} + +QT_END_NAMESPACE + +#endif // QSTYLEHELPER_P_H diff --git a/src/gui/styles/qstyleoption.cpp b/src/gui/styles/qstyleoption.cpp index ce053ae..e441101 100644 --- a/src/gui/styles/qstyleoption.cpp +++ b/src/gui/styles/qstyleoption.cpp @@ -48,6 +48,7 @@ #ifndef QT_NO_DEBUG #include <qdebug.h> #endif +#include <QtCore/qmath.h> QT_BEGIN_NAMESPACE @@ -4998,6 +4999,34 @@ QStyleOptionGraphicsItem::QStyleOptionGraphicsItem(int version) } /*! + \since 4.6 + + Returns the level of detail from the \a worldTransform. + + Its value represents the maximum value of the height and + width of a unity rectangle, mapped using the \a worldTransform + of the painter used to draw the item. By default, if no + transformations are applied, its value is 1. If zoomed out 1:2, the level + of detail will be 0.5, and if zoomed in 2:1, its value is 2. + + For more advanced level-of-detail metrics, use + QStyleOptionGraphicsItem::matrix directly. + + \sa QStyleOptionGraphicsItem::matrix +*/ +qreal QStyleOptionGraphicsItem::levelOfDetailFromTransform(const QTransform &worldTransform) +{ + if (worldTransform.type() <= QTransform::TxTranslate) + return 1; // Translation only? The LOD is 1. + + // Two unit vectors. + QLineF v1(0, 0, 1, 0); + QLineF v2(0, 0, 0, 1); + // LOD is the transformed area of a 1x1 rectangle. + return qSqrt(worldTransform.map(v1).length() * worldTransform.map(v2).length()); +} + +/*! \fn QStyleOptionGraphicsItem::QStyleOptionGraphicsItem(const QStyleOptionGraphicsItem &other) Constructs a copy of \a other. @@ -5029,19 +5058,10 @@ QStyleOptionGraphicsItem::QStyleOptionGraphicsItem(int version) /*! \variable QStyleOptionGraphicsItem::levelOfDetail - \brief a simple metric for determining an item's level of detail - - This simple metric provides an easy way to determine the level of detail - for an item. Its value represents the maximum value of the height and - width of a unity rectangle, mapped using the complete transformation - matrix of the painter used to draw the item. By default, if no - transformations are applied, its value is 1. If zoomed out 1:2, the level - of detail will be 0.5, and if zoomed in 2:1, its value is 2. - - For more advanced level-of-detail metrics, use - QStyleOptionGraphicsItem::matrix directly. + \obsolete - \sa QStyleOptionGraphicsItem::matrix + Use QStyleOptionGraphicsItem::levelOfDetailFromTransform + together with QPainter::worldTransform() instead. */ /*! @@ -5342,10 +5362,10 @@ QDebug operator<<(QDebug debug, const QStyleOption &option) { debug << "QStyleOption("; debug << QStyleOption::OptionType(option.type); - debug << "," << (option.direction == Qt::RightToLeft ? "RightToLeft" : "LeftToRight"); - debug << "," << option.state; - debug << "," << option.rect; - debug << ")"; + debug << ',' << (option.direction == Qt::RightToLeft ? "RightToLeft" : "LeftToRight"); + debug << ',' << option.state; + debug << ',' << option.rect; + debug << ')'; return debug; } #endif diff --git a/src/gui/styles/qstyleoption.h b/src/gui/styles/qstyleoption.h index 5759a05..eb05324 100644 --- a/src/gui/styles/qstyleoption.h +++ b/src/gui/styles/qstyleoption.h @@ -856,6 +856,7 @@ public: QStyleOptionGraphicsItem(); QStyleOptionGraphicsItem(const QStyleOptionGraphicsItem &other) : QStyleOption(Version, Type) { *this = other; } + static qreal levelOfDetailFromTransform(const QTransform &worldTransform); protected: QStyleOptionGraphicsItem(int version); }; diff --git a/src/gui/styles/qstylesheetstyle.cpp b/src/gui/styles/qstylesheetstyle.cpp index bd80bb6..1d6dc8a 100644 --- a/src/gui/styles/qstylesheetstyle.cpp +++ b/src/gui/styles/qstylesheetstyle.cpp @@ -81,6 +81,7 @@ #include <private/qwidget_p.h> #include <QAbstractSpinBox> #include <QLabel> +#include "qdrawutil.h" #include <limits.h> @@ -312,15 +313,10 @@ struct QStyleSheetBorderImageData : public QSharedData for (int i = 0; i < 4; i++) cuts[i] = -1; } - QPixmap topEdge, bottomEdge, leftEdge, rightEdge, middle; - QRect topEdgeRect, bottomEdgeRect, leftEdgeRect, rightEdgeRect, middleRect; - QRect topLeftCorner, topRightCorner, bottomRightCorner, bottomLeftCorner; int cuts[4]; QPixmap pixmap; QImage image; QCss::TileMode horizStretch, vertStretch; - - void cutBorderImage(); }; struct QStyleSheetBackgroundData : public QSharedData @@ -1122,176 +1118,27 @@ void QRenderRule::fixupBorder(int nativeWidth) for (int i = 0; i < 4; i++) // assume, cut = border bi->cuts[i] = int(border()->borders[i]); } - bi->cutBorderImage(); -} - -void QStyleSheetBorderImageData::cutBorderImage() -{ - const int w = pixmap.width(); - const int h = pixmap.height(); - const int &l = cuts[LeftEdge], &r = cuts[RightEdge], - &t = cuts[TopEdge], &b = cuts[BottomEdge]; - - topEdgeRect = QRect(l, 0, w - r - l, t); - bottomEdgeRect = QRect(l, h - b, w - l - r, b); - if (horizStretch != TileMode_Stretch) { - if (topEdgeRect.isValid()) - topEdge = pixmap.copy(topEdgeRect).scaledToHeight(t); - if (bottomEdgeRect.isValid()) - bottomEdge = pixmap.copy(bottomEdgeRect).scaledToHeight(b); - } - - leftEdgeRect = QRect(0, t, l, h - b - t); - rightEdgeRect = QRect(w - r, t, r, h - t- b); - if (vertStretch != TileMode_Stretch) { - if (leftEdgeRect.isValid()) - leftEdge = pixmap.copy(leftEdgeRect).scaledToWidth(l); - if (rightEdgeRect.isValid()) - rightEdge = pixmap.copy(rightEdgeRect).scaledToWidth(r); - } - - middleRect = QRect(l, t, w - r -l, h - t - b); - if (middleRect.isValid() - && !(horizStretch == TileMode_Stretch && vertStretch == TileMode_Stretch)) { - middle = pixmap.copy(middleRect); - } } -static void qDrawCenterTiledPixmap(QPainter *p, const QRectF& r, const QPixmap& pix) -{ - p->drawTiledPixmap(r, pix, QPoint(pix.width() - int(r.width())%pix.width(), - pix.height() - int(r.height())%pix.height())); -} - -// Note: Round is not supported void QRenderRule::drawBorderImage(QPainter *p, const QRect& rect) { - setClip(p, rect); - const QRectF br(rect); - const int *borders = border()->borders; - const int &l = borders[LeftEdge], &r = borders[RightEdge], - &t = borders[TopEdge], &b = borders[BottomEdge]; - QRectF pr = br.adjusted(l, t, -r, -b); + static const Qt::TileRule tileMode2TileRule[] = { + Qt::Stretch, Qt::Round, Qt::Stretch, Qt::Repeat, Qt::Stretch }; + + const QStyleSheetBorderImageData *borderImageData = border()->borderImage(); + const int *targetBorders = border()->borders; + const int *sourceBorders = borderImageData->cuts; + QMargins sourceMargins(sourceBorders[TopEdge], sourceBorders[LeftEdge], + sourceBorders[BottomEdge], sourceBorders[RightEdge]); + QMargins targetMargins(targetBorders[TopEdge], targetBorders[LeftEdge], + targetBorders[BottomEdge], targetBorders[RightEdge]); bool wasSmoothPixmapTransform = p->renderHints() & QPainter::SmoothPixmapTransform; p->setRenderHint(QPainter::SmoothPixmapTransform); - - const QStyleSheetBorderImageData *bi = border()->borderImage(); - const QPixmap& pix = bi->pixmap; - const int *c = bi->cuts; - QRectF tlc(0, 0, c[LeftEdge], c[TopEdge]); - if (tlc.isValid()) - p->drawPixmap(QRectF(br.topLeft(), QSizeF(l, t)), pix, tlc); - QRectF trc(pix.width() - c[RightEdge], 0, c[RightEdge], c[TopEdge]); - if (trc.isValid()) - p->drawPixmap(QRectF(br.left() + br.width() - r, br.y(), r, t), pix, trc); - QRectF blc(0, pix.height() - c[BottomEdge], c[LeftEdge], c[BottomEdge]); - if (blc.isValid()) - p->drawPixmap(QRectF(br.x(), br.y() + br.height() - b, l, b), pix, blc); - QRectF brc(pix.width() - c[RightEdge], pix.height() - c[BottomEdge], - c[RightEdge], c[BottomEdge]); - if (brc.isValid()) - p->drawPixmap(QRectF(br.x() + br.width() - r, br.y() + br.height() - b, r, b), - pix, brc); - - QRectF topEdgeRect(br.x() + l, br.y(), pr.width(), t); - QRectF bottomEdgeRect(br.x() + l, br.y() + br.height() - b, pr.width(), b); - - switch (bi->horizStretch) { - case TileMode_Stretch: - if (bi->topEdgeRect.isValid()) - p->drawPixmap(topEdgeRect, pix, bi->topEdgeRect); - if (bi->bottomEdgeRect.isValid()) - p->drawPixmap(bottomEdgeRect, pix, bi->bottomEdgeRect); - if (bi->middleRect.isValid()) { - if (bi->vertStretch == TileMode_Stretch) - p->drawPixmap(pr, pix, bi->middleRect); - else if (bi->vertStretch == TileMode_Repeat) { - QPixmap scaled = bi->middle.scaled(int(pr.width()), bi->middle.height()); - qDrawCenterTiledPixmap(p, pr, scaled); - } - } - break; - case TileMode_Repeat: - if (!bi->topEdge.isNull() && !topEdgeRect.isEmpty()) { - QPixmap scaled = bi->topEdge.scaled(bi->topEdge.width(), t); - qDrawCenterTiledPixmap(p, topEdgeRect, scaled); - } - if (!bi->bottomEdge.isNull() && !bottomEdgeRect.isEmpty()) { - QPixmap scaled = bi->bottomEdge.scaled(bi->bottomEdge.width(), b); - qDrawCenterTiledPixmap(p, bottomEdgeRect, scaled); - } - if (bi->middleRect.isValid()) { - if (bi->vertStretch == TileMode_Repeat) { - qDrawCenterTiledPixmap(p, pr, bi->middle); - } else if (bi->vertStretch == TileMode_Stretch) { - QPixmap scaled = bi->middle.scaled(bi->middle.width(), int(pr.height())); - qDrawCenterTiledPixmap(p, pr, scaled); - } - } - break; - case TileMode_Round: - if (!bi->topEdge.isNull()) { - int rwh = (int)pr.width()/ceil(pr.width()/bi->topEdge.width()); - QPixmap scaled = bi->topEdge.scaled(rwh, bi->topEdge.height()); - int blank = int(pr.width()) % rwh; - p->drawTiledPixmap(QRectF(br.x() + l + blank/2, br.y(), pr.width() - blank, t), - scaled); - } - if (!bi->bottomEdge.isNull()) { - int rwh = (int) pr.width()/ceil(pr.width()/bi->bottomEdge.width()); - QPixmap scaled = bi->bottomEdge.scaled(rwh, bi->bottomEdge.height()); - int blank = int(pr.width()) % rwh; - p->drawTiledPixmap(QRectF(br.x() + l+ blank/2, br.y()+br.height()-b, - pr.width() - blank, b), scaled); - } - break; - default: - break; - } - - QRectF leftEdgeRect(br.x(), br.y() + t, l, pr.height()); - QRectF rightEdgeRect(br.x() + br.width()- r, br.y() + t, r, pr.height()); - - switch (bi->vertStretch) { - case TileMode_Stretch: - if (bi->leftEdgeRect.isValid()) - p->drawPixmap(leftEdgeRect, pix, bi->leftEdgeRect); - if (bi->rightEdgeRect.isValid()) - p->drawPixmap(rightEdgeRect, pix, bi->rightEdgeRect); - break; - case TileMode_Repeat: - if (!bi->leftEdge.isNull() && !leftEdgeRect.isEmpty()) { - QPixmap scaled = bi->leftEdge.scaled(l, bi->leftEdge.height()); - qDrawCenterTiledPixmap(p, leftEdgeRect, scaled); - } - if (!bi->rightEdge.isNull() && !rightEdgeRect.isEmpty()) { - QPixmap scaled = bi->rightEdge.scaled(r, bi->rightEdge.height()); - qDrawCenterTiledPixmap(p, rightEdgeRect, scaled); - } - break; - case TileMode_Round: - if (!bi->leftEdge.isNull()) { - int rwh = (int) pr.height()/ceil(pr.height()/bi->leftEdge.height()); - QPixmap scaled = bi->leftEdge.scaled(bi->leftEdge.width(), rwh); - int blank = int(pr.height()) % rwh; - p->drawTiledPixmap(QRectF(br.x(), br.y() + t + blank/2, l, pr.height() - blank), - scaled); - } - if (!bi->rightEdge.isNull()) { - int rwh = (int) pr.height()/ceil(pr.height()/bi->rightEdge.height()); - QPixmap scaled = bi->rightEdge.scaled(bi->rightEdge.width(), rwh); - int blank = int(pr.height()) % rwh; - p->drawTiledPixmap(QRectF(br.x() + br.width() - r, br.y()+t+blank/2, r, - pr.height() - blank), scaled); - } - break; - default: - break; - } - + qDrawBorderPixmap(p, rect, targetMargins, borderImageData->pixmap, + QRect(QPoint(), borderImageData->pixmap.size()), sourceMargins, + QTileRules(tileMode2TileRule[borderImageData->horizStretch], tileMode2TileRule[borderImageData->vertStretch])); p->setRenderHint(QPainter::SmoothPixmapTransform, wasSmoothPixmapTransform); - unsetClip(p); } QRect QRenderRule::originRect(const QRect &rect, Origin origin) const @@ -1525,7 +1372,7 @@ void QRenderRule::configurePalette(QPalette *p, QPalette::ColorGroup cg, const Q /* For embedded widgets (ComboBox, SpinBox and ScrollArea) we want the embedded widget * to be transparent when we have a transparent background or border image */ if ((hasBackground() && background()->isTransparent()) - || (hasBorder() && border()->hasBorderImage() && border()->borderImage()->middleRect.isValid())) + || (hasBorder() && border()->hasBorderImage() && !border()->borderImage()->pixmap.isNull())) p->setBrush(cg, w->backgroundRole(), Qt::NoBrush); } @@ -1708,7 +1555,7 @@ QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const if (widCacheIt == styleSheetCache->constEnd()) { parser.init(wid->styleSheet()); if (!parser.parse(&ss)) { - parser.init(QLatin1String("* {") + wid->styleSheet() + QLatin1String("}")); + parser.init(QLatin1String("* {") + wid->styleSheet() + QLatin1Char('}')); if (!parser.parse(&ss)) qWarning("Could not parse stylesheet of widget %p", wid); } @@ -2876,12 +2723,6 @@ void QStyleSheetStyle::polish(QWidget *w) QRenderRule rule = renderRule(w, PseudoElement_None, PseudoClass_Any); if (rule.hasDrawable() || rule.hasBox()) { if (w->metaObject() == &QWidget::staticMetaObject -#ifndef QT_NO_MENUBAR - || qobject_cast<QMenuBar *>(w) -#endif -#ifndef QT_NO_MENU - || qobject_cast<QMenu *>(w) -#endif #ifndef QT_NO_ITEMVIEWS || qobject_cast<QHeaderView *>(w) #endif @@ -3189,6 +3030,7 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC if (const QStyleOptionToolButton *tool = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { QStyleOptionToolButton toolOpt(*tool); rule.configurePalette(&toolOpt.palette, QPalette::ButtonText, QPalette::Button); + toolOpt.font = rule.font.resolve(toolOpt.font); toolOpt.rect = rule.borderRect(opt->rect); bool customArrow = (tool->features & (QStyleOptionToolButton::HasMenu | QStyleOptionToolButton::MenuButtonPopup)); bool customDropDown = tool->features & QStyleOptionToolButton::MenuButtonPopup; @@ -4164,9 +4006,8 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q rule.configurePalette(&frmOpt.palette, QPalette::Text, QPalette::Base); frmOpt.rect = rule.borderRect(frmOpt.rect); baseStyle()->drawControl(ce, &frmOpt, p, w); - } else { - rule.drawBorder(p, rule.borderRect(opt->rect)); } + // else, borders are already drawn in PE_Widget } return; @@ -4220,12 +4061,6 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op QRect rect = opt->rect; switch (pe) { - case PE_PanelStatusBar: - if (rule.hasDrawable()) { - rule.drawRule(p, opt->rect); - return; - } - break; case PE_FrameStatusBar: { QRenderRule subRule = renderRule(w->parentWidget(), opt, PseudoElement_Item); @@ -4337,36 +4172,34 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op return; case PE_Widget: - if (!rule.hasBackground()) { + if (!rule.hasDrawable()) { QWidget *container = containerWidget(w); if (autoFillDisabledWidgets->contains(container) - && (container == w || !renderRule(container, opt).hasBackground())) { + && (container == w || !renderRule(container, opt).hasDrawable())) { //we do not have a background, but we disabled the autofillbackground anyway. so fill the background now. // (this may happen if we have rules like :focus) p->fillRect(opt->rect, opt->palette.brush(w->backgroundRole())); } break; } - #ifndef QT_NO_SCROLLAREA if (const QAbstractScrollArea *sa = qobject_cast<const QAbstractScrollArea *>(w)) { const QAbstractScrollAreaPrivate *sap = sa->d_func(); rule.drawBackground(p, opt->rect, sap->contentsOffset()); - } else -#endif - { - rule.drawBackground(p, opt->rect); + if (rule.hasBorder()) + rule.drawBorder(p, rule.borderRect(opt->rect)); + break; } - - return; - - case PE_FrameMenu: +#endif + //fall tghought + case PE_PanelMenu: case PE_PanelMenuBar: - if (!rule.hasNativeBorder()) { - rule.drawBorder(p, rule.borderRect(opt->rect)); + case PE_PanelStatusBar: + if(rule.hasDrawable()) { + rule.drawRule(p, opt->rect); return; } - break; + break; case PE_IndicatorToolBarSeparator: case PE_IndicatorToolBarHandle: { @@ -4973,13 +4806,10 @@ QSize QStyleSheetStyle::sizeFromContents(ContentsType ct, const QStyleOption *op if ((pe == PseudoElement_MenuSeparator) && subRule.hasContentsSize()) { return QSize(sz.width(), subRule.size().height()); } else if ((pe == PseudoElement_Item) && (subRule.hasBox() || subRule.hasBorder())) { - int width = csz.width(), height = qMax(csz.height(), mi->fontMetrics.height()); - if (!mi->icon.isNull()) { - int iconExtent = pixelMetric(PM_SmallIconSize); - height = qMax(height, mi->icon.actualSize(QSize(iconExtent, iconExtent)).height()); - } - width += mi->tabWidth; - return subRule.boxSize(csz.expandedTo(subRule.minimumContentsSize())); + int width = csz.width(); + if (mi->text.contains(QLatin1Char('\t'))) + width += 12; //as in QCommonStyle + return subRule.boxSize(subRule.adjustSize(QSize(width, csz.height()))); } } break; @@ -5890,13 +5720,11 @@ void QStyleSheetStyle::clearWidgetFont(QWidget* w) const w->setProperty("_q_styleSheetWidgetFont", QVariant(QVariant::Invalid)); } -// Returns the palette that should be used when the particular widget is focused. -// This needs to be called by some widgets that do drawing themselves instead -// of through the style. -// ### This should be removed ideally by Qt 4.5, and at least by Qt 5, and fixed -// for good by letting the style draw everything. +// Polish palette that should be used for a particular widget, with particular states +// (eg. :focus, :hover, ...) +// this is called by widgets that paint themself in their paint event // Returns true if there is a new palette in pal. -bool QStyleSheetStyle::focusPalette(const QWidget* w, const QStyleOption* opt, QPalette* pal) +bool QStyleSheetStyle::styleSheetPalette(const QWidget* w, const QStyleOption* opt, QPalette* pal) { if (!w || !opt || !pal) return false; diff --git a/src/gui/styles/qstylesheetstyle_p.h b/src/gui/styles/qstylesheetstyle_p.h index 1f61445..e057274 100644 --- a/src/gui/styles/qstylesheetstyle_p.h +++ b/src/gui/styles/qstylesheetstyle_p.h @@ -131,7 +131,7 @@ public: void saveWidgetFont(QWidget* w, const QFont& font) const; void clearWidgetFont(QWidget* w) const; - bool focusPalette(const QWidget* w, const QStyleOption* opt, QPalette* pal); + bool styleSheetPalette(const QWidget* w, const QStyleOption* opt, QPalette* pal); protected Q_SLOTS: QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *opt = 0, diff --git a/src/gui/styles/qwindowsmobilestyle.cpp b/src/gui/styles/qwindowsmobilestyle.cpp index 1c03b9e..f56d46c 100644 --- a/src/gui/styles/qwindowsmobilestyle.cpp +++ b/src/gui/styles/qwindowsmobilestyle.cpp @@ -72,12 +72,12 @@ #include "qdebug.h" #include "qtabwidget.h" -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #include "qt_windows.h" #include "qguifunctions_wince.h" extern bool qt_wince_is_high_dpi(); //defined in qguifunctions_wince.cpp extern bool qt_wince_is_smartphone(); //defined in qguifunctions_wince.cpp -#endif // Q_OS_WINCE +#endif // Q_WS_WINCE QT_BEGIN_NAMESPACE @@ -685,13 +685,13 @@ QWindowsMobileStyle::QWindowsMobileStyle() : QWindowsStyle(*new QWindowsMobileSt QWindowsMobileStylePrivate::QWindowsMobileStylePrivate() :QWindowsStylePrivate() { -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE doubleControls = qt_wince_is_high_dpi(); smartphone = qt_wince_is_smartphone(); #else doubleControls = false; smartphone = false; -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE #ifndef QT_NO_IMAGEFORMAT_XPM 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 2f4254e..3dac9f5 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> @@ -1791,7 +1792,12 @@ case PE_Frame: return; case PE_IndicatorToolBarSeparator: - + if (option->rect.height() < 3) { + // XP style requires a few pixels for the separator + // to be visible. + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } name = QLatin1String("TOOLBAR"); partId = TP_SEPARATOR; @@ -3164,6 +3170,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/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 38621c1..0494b72 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -1508,7 +1508,7 @@ QRect Declaration::rectValue() const QStringList func = v.variant.toStringList(); if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0) return QRect(); - QStringList args = func[1].split(QLatin1String(" "), QString::SkipEmptyParts); + QStringList args = func[1].split(QLatin1Char(' '), QString::SkipEmptyParts); if (args.count() != 4) return QRect(); QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()); @@ -2153,7 +2153,7 @@ void Parser::init(const QString &css, bool isFile) if (isFile) { QFile file(css); if (file.open(QFile::ReadOnly)) { - sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1String("/"); + sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/'); QTextStream stream(&file); styleSheet = stream.readAll(); } else { diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index 97a0aef..72bd637 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -403,7 +403,7 @@ struct BorderData { // 4. QVector<Declaration> - { prop1: value1; prop2: value2; } // 5. Declaration - prop1: value1; -struct Q_GUI_EXPORT Declaration +struct Q_AUTOTEST_EXPORT Declaration { struct DeclarationData : public QSharedData { @@ -495,7 +495,7 @@ const quint64 PseudoClass_Alternate = Q_UINT64_C(0x0000100000000000); const quint64 PseudoClass_Any = Q_UINT64_C(0x0000ffffffffffff); const int NumPseudos = 46; -struct Q_GUI_EXPORT Pseudo +struct Pseudo { Pseudo() : negated(false) { } quint64 type; @@ -504,7 +504,7 @@ struct Q_GUI_EXPORT Pseudo bool negated; }; -struct Q_GUI_EXPORT AttributeSelector +struct AttributeSelector { enum ValueMatchType { NoMatch, @@ -519,7 +519,7 @@ struct Q_GUI_EXPORT AttributeSelector ValueMatchType valueMatchCriterium; }; -struct Q_GUI_EXPORT BasicSelector +struct BasicSelector { inline BasicSelector() : relationToNext(NoRelation) {} @@ -539,7 +539,7 @@ struct Q_GUI_EXPORT BasicSelector Relation relationToNext; }; -struct Q_GUI_EXPORT Selector +struct Q_AUTOTEST_EXPORT Selector { QVector<BasicSelector> basicSelectors; int specificity() const; @@ -552,7 +552,7 @@ struct MediaRule; struct PageRule; struct ImportRule; -struct Q_GUI_EXPORT ValueExtractor +struct Q_AUTOTEST_EXPORT ValueExtractor { ValueExtractor(const QVector<Declaration> &declarations, const QPalette & = QPalette()); @@ -586,7 +586,7 @@ private: QPalette pal; }; -struct Q_GUI_EXPORT StyleRule +struct StyleRule { StyleRule() : order(0) { } QVector<Selector> selectors; @@ -594,19 +594,19 @@ struct Q_GUI_EXPORT StyleRule int order; }; -struct Q_GUI_EXPORT MediaRule +struct MediaRule { QStringList media; QVector<StyleRule> styleRules; }; -struct Q_GUI_EXPORT PageRule +struct PageRule { QString selector; QVector<Declaration> declarations; }; -struct Q_GUI_EXPORT ImportRule +struct ImportRule { QString href; QStringList media; @@ -620,7 +620,7 @@ enum StyleSheetOrigin { StyleSheetOrigin_Inline }; -struct Q_GUI_EXPORT StyleSheet +struct StyleSheet { StyleSheet() : origin(StyleSheetOrigin_Unspecified), depth(0) { } QVector<StyleRule> styleRules; //only contains rules that are not indexed diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index c3fc9f5..d3020b0 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -792,7 +792,7 @@ static void initFontDef(const QtFontDesc &desc, const QFontDef &request, QFontDe if (! desc.foundry->name.isEmpty() && desc.family->count > 1) { fontDef->family += QString::fromLatin1(" ["); fontDef->family += desc.foundry->name; - fontDef->family += QString::fromLatin1("]"); + fontDef->family += QLatin1Char(']'); } if (desc.style->smoothScalable) @@ -1495,7 +1495,7 @@ QStringList QFontDatabase::families(WritingSystem writingSystem) const if (!foundry.isEmpty()) { str += QLatin1String(" ["); str += foundry; - str += QLatin1String("]"); + str += QLatin1Char(']'); } flist.append(str); } 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/qfontdatabase_win.cpp b/src/gui/text/qfontdatabase_win.cpp index 780ae28..fe1c08c 100644 --- a/src/gui/text/qfontdatabase_win.cpp +++ b/src/gui/text/qfontdatabase_win.cpp @@ -364,7 +364,7 @@ void addFontToDatabase(QString familyName, const QString &scriptName, signature->fsUsb[0], signature->fsUsb[1], signature->fsUsb[2], signature->fsUsb[3] }; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (signature->fsUsb[0] == 0) { // If the unicode ranges bit mask is zero then // EnumFontFamiliesEx failed to determine it properly. @@ -715,7 +715,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ f = deffnt; else if (fam == QLatin1String("system")) f = SYSTEM_FONT; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE else if (fam == QLatin1String("system_fixed")) f = SYSTEM_FIXED_FONT; else if (fam == QLatin1String("ansi_fixed")) @@ -774,7 +774,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ int strat = OUT_DEFAULT_PRECIS; if (request.styleStrategy & QFont::PreferBitmap) { strat = OUT_RASTER_PRECIS; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE } else if (request.styleStrategy & QFont::PreferDevice) { strat = OUT_DEVICE_PRECIS; } else if (request.styleStrategy & QFont::PreferOutline) { @@ -794,7 +794,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ if (request.styleStrategy & QFont::PreferMatch) qual = DRAFT_QUALITY; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE else if (request.styleStrategy & QFont::PreferQuality) qual = PROOF_QUALITY; #endif @@ -872,7 +872,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed"); } -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE if (hfont == 0) { hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); stockFont = true; diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index d7a9c23..6e8adcf 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -915,7 +915,7 @@ void QFontEngine::loadKerningPairs(QFixed scalingFactor) end: qSort(kerning_pairs); // for (int i = 0; i < kerning_pairs.count(); ++i) -// qDebug() << "i" << i << "left_right" << hex << kerning_pairs.at(i).left_right; +// qDebug() << 'i' << i << "left_right" << hex << kerning_pairs.at(i).left_right; } #else @@ -1646,12 +1646,6 @@ bool QFontEngineMulti::canRender(const QChar *string, int len) return allExist; } -QFontEngine *QFontEngineMulti::engine(int at) const -{ - Q_ASSERT(at < engines.size()); - return engines.at(at); -} - QImage QFontEngineMulti::alphaMapForGlyph(glyph_t) { Q_ASSERT(false); diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index d397e4a..2e62086 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -135,12 +135,12 @@ QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const ATSFontFamilyRef &, con symbolicTraits |= kCTFontItalicTrait; break; } - + QCFString name; ATSFontGetName(atsFontRef, kATSOptionFlagsDefault, &name); - QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize); - QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, 0); - ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, 0, symbolicTraits, symbolicTraits); + QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pointSize); + QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pointSize, 0); + ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pointSize, 0, symbolicTraits, symbolicTraits); // CTFontCreateCopyWithSymbolicTraits returns NULL if we ask for a trait that does // not exist for the given font. (for example italic) @@ -162,7 +162,7 @@ QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const ATSFontFamilyRef &, con QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef, this); fe->ref.ref(); engines.append(fe); - + } QCoreTextFontEngineMulti::~QCoreTextFontEngineMulti() @@ -176,7 +176,7 @@ uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef id) const if (CFEqual(engineAt(i)->ctfont, id)) return i; } - + QCoreTextFontEngineMulti *that = const_cast<QCoreTextFontEngineMulti *>(this); QCoreTextFontEngine *fe = new QCoreTextFontEngine(id, fontDef, that); fe->ref.ref(); @@ -227,7 +227,7 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttribs, NSFontAttributeName)); const uint fontIndex = (fontIndexForFont(runFont) << 24); //NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont)); - QVarLengthArray<CGGlyph, 512> cgglyphs(0); + QVarLengthArray<CGGlyph, 512> cgglyphs(0); const CGGlyph *tmpGlyphs = CTRunGetGlyphsPtr(run); if (!tmpGlyphs) { cgglyphs.resize(glyphCount); @@ -260,7 +260,7 @@ bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLay CFIndex k = 0; CFIndex i = 0; - for (i = stringRange.location; + for (i = stringRange.location; (i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) { if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location) { logClusters[i] = k + firstGlyphIndex; @@ -425,28 +425,28 @@ void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextIt getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); if (glyphs.size() == 0) return; - + CGContextSetFontSize(ctx, fontDef.pixelSize); - + CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx); - + CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight); - + CGAffineTransformConcat(cgMatrix, oldTextMatrix); - + if (synthesisFlags & QFontEngine::SynthesizedItalic) cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0)); - + // ### cgMatrix = CGAffineTransformConcat(cgMatrix, transform); - + CGContextSetTextMatrix(ctx, cgMatrix); - + CGContextSetTextDrawingMode(ctx, kCGTextFill); - - + + QVarLengthArray<CGSize> advances(glyphs.size()); QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size()); - + for (int i = 0; i < glyphs.size() - 1; ++i) { advances[i].width = (positions[i + 1].x - positions[i].x).toReal(); advances[i].height = (positions[i + 1].y - positions[i].y).toReal(); @@ -455,21 +455,21 @@ void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextIt advances[glyphs.size() - 1].width = 0; advances[glyphs.size() - 1].height = 0; cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1]; - + CGContextSetFont(ctx, cgFont); //NSLog(@"Font inDraw %@ ctfont %@", CGFontCopyFullName(cgFont), CTFontCopyFamilyName(ctfont)); - + CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal()); - + CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); - + if (synthesisFlags & QFontEngine::SynthesizedBold) { CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(), positions[0].y.toReal()); - + CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); } - + CGContextSetTextMatrix(ctx, oldTextMatrix); } @@ -624,7 +624,7 @@ QFontEngine::FaceId QCoreTextFontEngine::faceId() const bool QCoreTextFontEngine::canRender(const QChar *string, int len) { - QCFType<CTFontRef> retFont = CTFontCreateForString(ctfont, + QCFType<CTFontRef> retFont = CTFontCreateForString(ctfont, QCFType<CFStringRef>(CFStringCreateWithCharactersNoCopy(0, reinterpret_cast<const UniChar *>(string), len, kCFAllocatorNull)), @@ -674,7 +674,7 @@ QFontEngineMacMulti::QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, cons } else { if (fontDef.weight >= QFont::Bold) fntStyle |= ::bold; - if (fontDef.style != QFont::StyleNormal) + if (fontDef.style != QFont::StyleNormal) fntStyle |= ::italic; FMFontStyle intrinsicStyle; @@ -957,7 +957,7 @@ bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout * tmpItem.length = charCount; tmpItem.glyphs = shaperItem.glyphs.mid(glyphIdx, glyphCount); tmpItem.log_clusters = shaperItem.log_clusters + charIdx; - if (!stringToCMapInternal(tmpItem.string + tmpItem.from, tmpItem.length, + if (!stringToCMapInternal(tmpItem.string + tmpItem.from, tmpItem.length, &tmpItem.glyphs, &glyphCount, flags, &tmpItem)) { *nglyphs = glyphIdx + glyphCount; @@ -1223,12 +1223,12 @@ QFontEngineMac::QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFo transform = multiEngine->transform; else transform = CGAffineTransformIdentity; - + ATSUTextMeasurement metric; ATSUGetAttribute(style, kATSUAscentTag, sizeof(metric), &metric, 0); m_ascent = FixRound(metric); - + ATSUGetAttribute(style, kATSUDescentTag, sizeof(metric), &metric, 0); m_descent = FixRound(metric); @@ -1424,11 +1424,16 @@ void QFontEngineMac::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, in addGlyphsToPathHelper(style, glyphs, positions, numGlyphs, path); } -QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) + +/*! + Helper function for alphaMapForGlyph and alphaRGBMapForGlyph. The two are identical, except for + the subpixel antialiasing... +*/ +QImage QFontEngineMac::imageForGlyph(glyph_t glyph, int margin, bool colorful) { const glyph_metrics_t br = boundingBox(glyph); QImage im(qRound(br.width)+2, qRound(br.height)+4, QImage::Format_RGB32); - im.fill(0); + im.fill(0xff000000); CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace(); #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) @@ -1446,7 +1451,7 @@ QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) CGContextSetFontSize(ctx, fontDef.pixelSize); CGContextSetShouldAntialias(ctx, fontDef.pointSize > qt_antialiasing_threshold && !(fontDef.styleStrategy & QFont::NoAntialias)); // turn off sub-pixel hinting - no support for that in OpenGL - CGContextSetShouldSmoothFonts(ctx, false); + CGContextSetShouldSmoothFonts(ctx, colorful); CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx); CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0); CGAffineTransformConcat(cgMatrix, oldTextMatrix); @@ -1478,6 +1483,13 @@ QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) CGContextRelease(ctx); + return im; +} + +QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) +{ + QImage im = imageForGlyph(glyph, 2, false); + QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); QVector<QRgb> colors(256); for (int i=0; i<256; ++i) @@ -1497,6 +1509,32 @@ QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) return indexed; } +QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &t) +{ + QImage im = imageForGlyph(glyph, margin, true); + + if (t.type() >= QTransform::TxScale) { + im = im.transformed(t); + } + + extern uchar qt_pow_rgb_gamma[256]; + + // gamma correct the pixels back to linear color space... + for (int y=0; y<im.height(); ++y) { + uint *pixels = (uint *) im.scanLine(y); + for (int x=0; x<im.width(); ++x) { + uint p = pixels[x]; + uint r = qt_pow_rgb_gamma[qRed(p)]; + uint g = qt_pow_rgb_gamma[qGreen(p)]; + uint b = qt_pow_rgb_gamma[qBlue(p)]; + pixels[x] = (r << 16) | (g << 8) | b | 0xff000000; + } + } + + return im; +} + + bool QFontEngineMac::canRender(const QChar *string, int len) { Q_ASSERT(false); @@ -1650,7 +1688,7 @@ QFontEngine::Properties QFontEngineMac::properties() const if (ATSFontGetTable(atsFont, MAKE_TAG('p', 'o', 's', 't'), 10, 2, &lw, 0) == noErr) lw = qFromBigEndian<quint16>(lw); props.lineWidth = lw; - + // CTFontCopyPostScriptName QCFString psName; if (ATSFontGetPostScriptName(FMGetATSFontRefFromFont(fontID), kATSOptionFlagsDefault, &psName) == noErr) @@ -1665,7 +1703,7 @@ void QFontEngineMac::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_m ATSUCreateAndCopyStyle(style, &unscaledStyle); int emSquare = properties().emSquare.toInt(); - + const int maxAttributeCount = 4; ATSUAttributeTag tags[maxAttributeCount + 1]; ByteCount sizes[maxAttributeCount + 1]; @@ -1677,7 +1715,7 @@ void QFontEngineMac::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_m sizes[attributeCount] = sizeof(size); values[attributeCount] = &size; ++attributeCount; - + Q_ASSERT(attributeCount < maxAttributeCount + 1); OSStatus err = ATSUSetAttributes(unscaledStyle, attributeCount, tags, sizes, values); Q_ASSERT(err == noErr); diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index dc18991..0f8d81c 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -353,7 +353,7 @@ private: int _size; }; -class Q_GUI_EXPORT QFontEngineMulti : public QFontEngine +class QFontEngineMulti : public QFontEngine { public: explicit QFontEngineMulti(int engineCount); @@ -389,7 +389,9 @@ public: inline virtual const char *name() const { return "Multi"; } - QFontEngine *engine(int at) const; + QFontEngine *engine(int at) const + {Q_ASSERT(at < engines.size()); return engines.at(at); } + protected: friend class QPSPrintEnginePrivate; @@ -525,8 +527,11 @@ public: virtual Properties properties() const; virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics); virtual QImage alphaMapForGlyph(glyph_t); + virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t); private: + QImage imageForGlyph(glyph_t glyph, int margin, bool colorful); + ATSUFontID fontID; QCFType<CGFontRef> cgFont; ATSUStyle style; 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/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp index 7341665..002e670 100644 --- a/src/gui/text/qfontengine_win.cpp +++ b/src/gui/text/qfontengine_win.cpp @@ -65,7 +65,7 @@ #include <private/qpaintengine_raster_p.h> #include <private/qnativeimage_p.h> -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) #include "qguifunctions_wince.h" #endif @@ -205,7 +205,7 @@ QFixed QFontEngineWin::lineThickness() const return QFontEngine::lineThickness(); } -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) static OUTLINETEXTMETRICW *getOutlineTextMetric(HDC hdc) { int size; @@ -249,7 +249,7 @@ void QFontEngineWin::getCMap() designToDevice = 1; _faceId.index = 0; if(cmap) { -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) OUTLINETEXTMETRICW *otm = getOutlineTextMetric(hdc); #else OUTLINETEXTMETRICA *otm = getOutlineTextMetric(hdc); @@ -286,7 +286,7 @@ int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout int i = 0; int glyph_pos = 0; if (mirrored) { -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) { #else if (symbol) { @@ -314,7 +314,7 @@ int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout for (; i < numChars; ++i, ++glyph_pos) { uint ucs = QChar::mirroredChar(getChar(str, i, numChars)); if ( -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE tm.w.tmFirstChar > 60000 || // see line 375 #endif ucs >= first && ucs <= last) @@ -324,7 +324,7 @@ int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout } } } else { -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) { #else if (symbol) { @@ -352,7 +352,7 @@ int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout for (; i < numChars; ++i, ++glyph_pos) { uint uc = getChar(str, i, numChars); if ( -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE tm.w.tmFirstChar > 60000 || // see comment in QFontEngineWin #endif uc >= first && uc <= last) @@ -482,7 +482,7 @@ bool QFontEngineWin::stringToCMap(const QChar *str, int len, QGlyphLayout *glyph if (flags & QTextEngine::GlyphIndicesOnly) return true; -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) HDC hdc = shared_dc(); if (flags & QTextEngine::DesignMetrics) { HGDIOBJ oldFont = 0; @@ -585,7 +585,7 @@ void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFla designAdvances[glyph] = QFixed(width) / designToDevice; } else { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE GLYPHMETRICS gm; DWORD res = GDI_ERROR; MAT2 mat; @@ -647,7 +647,7 @@ void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFla width -= overhang; } else { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE GLYPHMETRICS gm; DWORD res = GDI_ERROR; MAT2 mat; @@ -693,14 +693,14 @@ glyph_metrics_t QFontEngineWin::boundingBox(const QGlyphLayout &glyphs) -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE typedef HRESULT (WINAPI *pGetCharABCWidthsFloat)(HDC, UINT, UINT, LPABCFLOAT); static pGetCharABCWidthsFloat qt_GetCharABCWidthsFloat = 0; #endif glyph_metrics_t QFontEngineWin::boundingBox(glyph_t glyph, const QTransform &t) { -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE GLYPHMETRICS gm; HDC hdc = shared_dc(); @@ -871,7 +871,7 @@ qreal QFontEngineWin::minLeftBearing() const qreal QFontEngineWin::minRightBearing() const { -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (rbearing == SHRT_MIN) { int ml = 0; int mr = 0; @@ -1047,7 +1047,7 @@ static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale) { static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc, QPainterPath *path, bool ttf, glyph_metrics_t *metric = 0, qreal scale = 1) { -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) Q_UNUSED(glyph); Q_UNUSED(hdc); #endif @@ -1064,7 +1064,7 @@ static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc, GLYPHMETRICS gMetric; memset(&gMetric, 0, sizeof(GLYPHMETRICS)); int bufferSize = GDI_ERROR; -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) QT_WA( { bufferSize = GetGlyphOutlineW(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat); }, { @@ -1077,7 +1077,7 @@ static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc, void *dataBuffer = new char[bufferSize]; DWORD ret = GDI_ERROR; -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) QT_WA( { ret = GetGlyphOutlineW(hdc, glyph, glyphFormat, &gMetric, bufferSize, dataBuffer, &mat); @@ -1199,7 +1199,7 @@ void QFontEngineWin::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, in void QFontEngineWin::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) { -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) if(tm.w.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) { hasOutline = true; QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); @@ -1267,7 +1267,7 @@ QFontEngine::Properties QFontEngineWin::properties() const }); HDC hdc = shared_dc(); HGDIOBJ oldfont = SelectObject(hdc, hf); -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) OUTLINETEXTMETRICW *otm = getOutlineTextMetric(hdc); #else OUTLINETEXTMETRICA *otm = getOutlineTextMetric(hdc); @@ -1351,7 +1351,7 @@ QNativeImage *QFontEngineWin::drawGDIGlyph(HFONT font, glyph_t glyph, int margin bool has_transformation = t.type() > QTransform::TxTranslate; -#ifndef Q_OS_WINCE +#ifndef Q_WS_WINCE unsigned int options = ttf ? ETO_GLYPH_INDEX : 0; XFORM xform; diff --git a/src/gui/text/qfontsubset.cpp b/src/gui/text/qfontsubset.cpp index 0d1a884..e4d813d 100644 --- a/src/gui/text/qfontsubset.cpp +++ b/src/gui/text/qfontsubset.cpp @@ -333,17 +333,17 @@ QByteArray QFontSubset::glyphName(unsigned int glyph, const QVector<int> reverse name[0] = 0; } if (name[0]) { - s << "/" << name; + s << '/' << name; } else #endif #if defined(Q_WS_X11) if (fontEngine->type() == QFontEngine::XLFD) { uint uc = static_cast<QFontEngineXLFD *>(fontEngine)->toUnicode(glyphIndex); - s << "/" << glyphName(uc, false /* ### */); + s << '/' << glyphName(uc, false /* ### */); } else #endif if (reverseMap[glyphIndex] && reverseMap[glyphIndex] < 0x10000) { - s << "/" << glyphName(reverseMap[glyphIndex], false); + s << '/' << glyphName(reverseMap[glyphIndex], false); } else { s << "/gl" << (int)glyphIndex; } @@ -395,13 +395,13 @@ QByteArray QFontSubset::widthArray() const int endnonlinear = startLinear ? startLinear : g; // qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear); if (endnonlinear > start) { - s << start << "["; + s << start << '['; for (int i = start; i < endnonlinear; ++i) s << (widths[i]*scale).toInt(); s << "]\n"; } if (startLinear) - s << startLinear << g - 1 << (widths[startLinear]*scale).toInt() << "\n"; + s << startLinear << g - 1 << (widths[startLinear]*scale).toInt() << '\n'; } s << "]\n"; } @@ -488,14 +488,14 @@ QByteArray QFontSubset::createToUnicodeMap() const int endnonlinear = startLinear ? startLinear : g; // qDebug(" startLinear=%x endnonlinear=%x", startLinear,endnonlinear); if (endnonlinear > start) { - s << "<" << QPdf::toHex((ushort)start, buf) << "> <"; + s << '<' << QPdf::toHex((ushort)start, buf) << "> <"; s << QPdf::toHex((ushort)(endnonlinear - 1), buf) << "> "; if (endnonlinear == start + 1) { - s << "<" << QPdf::toHex((ushort)reverseMap[start], buf) << ">\n"; + s << '<' << QPdf::toHex((ushort)reverseMap[start], buf) << ">\n"; } else { - s << "["; + s << '['; for (int i = start; i < endnonlinear; ++i) { - s << "<" << QPdf::toHex((ushort)reverseMap[i], buf) << "> "; + s << '<' << QPdf::toHex((ushort)reverseMap[i], buf) << "> "; } s << "]\n"; } @@ -508,9 +508,9 @@ QByteArray QFontSubset::createToUnicodeMap() const int uc_end = uc_start + len - 1; if ((uc_end >> 8) != (uc_start >> 8)) len = 256 - (uc_start & 0xff); - s << "<" << QPdf::toHex((ushort)startLinear, buf) << "> <"; + s << '<' << QPdf::toHex((ushort)startLinear, buf) << "> <"; s << QPdf::toHex((ushort)(startLinear + len - 1), buf) << "> "; - s << "<" << QPdf::toHex((ushort)reverseMap[startLinear], buf) << ">\n"; + s << '<' << QPdf::toHex((ushort)reverseMap[startLinear], buf) << ">\n"; checkRanges(ts, ranges, nranges); startLinear += len; } @@ -1655,7 +1655,7 @@ QByteArray QFontSubset::toType1() const QByteArray id = QByteArray::number(object_id); QByteArray psname = properties.postscriptName; - psname.replace(" ", ""); + psname.replace(' ', ""); standard_font = false; @@ -1681,12 +1681,12 @@ QByteArray QFontSubset::toType1() const #endif s << "/F" << id << "-Base\n"; if (standard_font) { - s << "/" << psname << " findfont\n" + s << '/' << psname << " findfont\n" "0 dict copy dup /NumGlyphs 0 put dup /CMap 256 array put def\n"; } else { s << "<<\n"; if(!psname.isEmpty()) - s << "/FontName /" << psname << "\n"; + s << "/FontName /" << psname << '\n'; s << "/FontInfo <</FsType " << (int)fontEngine->fsType << ">>\n" "/FontType 1\n" "/PaintType 0\n" @@ -1722,7 +1722,7 @@ QByteArray QFontSubset::type1AddedGlyphs() const int nGlyphs = glyph_indices.size(); QByteArray id = QByteArray::number(object_id); - s << "F" << id << "-Base [\n"; + s << 'F' << id << "-Base [\n"; for (int i = downloaded_glyphs; i < nGlyphs; ++i) { glyph_t g = glyph_indices.at(i); QPainterPath path; diff --git a/src/gui/text/qtextcontrol.cpp b/src/gui/text/qtextcontrol.cpp index f3d025c..0958b52 100644 --- a/src/gui/text/qtextcontrol.cpp +++ b/src/gui/text/qtextcontrol.cpp @@ -82,7 +82,7 @@ #include "private/qapplication_p.h" #include "private/qshortcutmap_p.h" #include <qkeysequence.h> -#define ACCEL_KEY(k) (!qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? QLatin1String("\t") + QString(QKeySequence(k)) : QString()) +#define ACCEL_KEY(k) (!qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? QLatin1Char('\t') + QString(QKeySequence(k)) : QString()) #else #define ACCEL_KEY(k) QString() #endif diff --git a/src/gui/text/qtextcontrol_p.h b/src/gui/text/qtextcontrol_p.h index e50540a..4dac4f7 100644 --- a/src/gui/text/qtextcontrol_p.h +++ b/src/gui/text/qtextcontrol_p.h @@ -83,7 +83,7 @@ class QAbstractScrollArea; class QEvent; class QTimerEvent; -class Q_GUI_EXPORT QTextControl : public QObject +class Q_AUTOTEST_EXPORT QTextControl : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QTextControl) diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp index 48963bb..b9e1c89 100644 --- a/src/gui/text/qtextcursor.cpp +++ b/src/gui/text/qtextcursor.cpp @@ -145,7 +145,6 @@ void QTextCursorPrivate::remove() { if (anchor == position) return; - priv->beginEditBlock(); currentCharFormat = -1; int pos1 = position; int pos2 = adjusted_anchor; @@ -159,15 +158,18 @@ void QTextCursorPrivate::remove() // deleting inside table? -> delete only content QTextTable *table = complexSelectionTable(); if (table) { + priv->beginEditBlock(); int startRow, startCol, numRows, numCols; selectedTableCells(&startRow, &numRows, &startCol, &numCols); clearCells(table, startRow, startCol, numRows, numCols, op); + adjusted_anchor = anchor = position; + priv->endEditBlock(); } else { priv->remove(pos1, pos2-pos1, op); + adjusted_anchor = anchor = position; + priv->finishEdit(); } - adjusted_anchor = anchor = position; - priv->endEditBlock(); } void QTextCursorPrivate::clearCells(QTextTable *table, int startRow, int startCol, int numRows, int numCols, QTextUndoCommand::Operation op) @@ -1291,9 +1293,14 @@ void QTextCursor::insertText(const QString &text, const QTextCharFormat &_format QTextCharFormat format = _format; format.clearProperty(QTextFormat::ObjectIndex); - d->priv->beginEditBlock(); + bool hasEditBlock = false; + + if (d->anchor != d->position) { + hasEditBlock = true; + d->priv->beginEditBlock(); + d->remove(); + } - d->remove(); if (!text.isEmpty()) { QTextFormatCollection *formats = d->priv->formatCollection(); int formatIdx = formats->indexForFormat(format); @@ -1323,6 +1330,11 @@ void QTextCursor::insertText(const QString &text, const QTextCharFormat &_format || ch == QChar::ParagraphSeparator || ch == QLatin1Char('\r')) { + if (!hasEditBlock) { + hasEditBlock = true; + d->priv->beginEditBlock(); + } + if (blockEnd > blockStart) d->priv->insert(d->position, textStart + blockStart, blockEnd - blockStart, formatIdx); @@ -1333,7 +1345,8 @@ void QTextCursor::insertText(const QString &text, const QTextCharFormat &_format if (textStart + blockStart < textEnd) d->priv->insert(d->position, textStart + blockStart, textEnd - textStart - blockStart, formatIdx); } - d->priv->endEditBlock(); + if (hasEditBlock) + d->priv->endEditBlock(); d->setX(); } @@ -1348,12 +1361,15 @@ void QTextCursor::deleteChar() if (!d || !d->priv) return; - if (d->position == d->anchor) { - if (!d->canDelete(d->position)) - return; - d->adjusted_anchor = d->anchor = - d->priv->nextCursorPosition(d->anchor, QTextLayout::SkipCharacters); + if (d->position != d->anchor) { + removeSelectedText(); + return; } + + if (!d->canDelete(d->position)) + return; + d->adjusted_anchor = d->anchor = + d->priv->nextCursorPosition(d->anchor, QTextLayout::SkipCharacters); d->remove(); d->setX(); } @@ -1368,27 +1384,29 @@ void QTextCursor::deletePreviousChar() { if (!d || !d->priv) return; - - if (d->position == d->anchor) { - if (d->anchor < 1 || !d->canDelete(d->anchor-1)) - return; - d->anchor--; - - QTextDocumentPrivate::FragmentIterator fragIt = d->priv->find(d->anchor); - const QTextFragmentData * const frag = fragIt.value(); - int fpos = fragIt.position(); - QChar uc = d->priv->buffer().at(d->anchor - fpos + frag->stringPosition); - if (d->anchor > fpos && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) { - // second half of a surrogate, check if we have the first half as well, - // if yes delete both at once - uc = d->priv->buffer().at(d->anchor - 1 - fpos + frag->stringPosition); - if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) - --d->anchor; - } - - d->adjusted_anchor = d->anchor; + + if (d->position != d->anchor) { + removeSelectedText(); + return; } - + + if (d->anchor < 1 || !d->canDelete(d->anchor-1)) + return; + d->anchor--; + + QTextDocumentPrivate::FragmentIterator fragIt = d->priv->find(d->anchor); + const QTextFragmentData * const frag = fragIt.value(); + int fpos = fragIt.position(); + QChar uc = d->priv->buffer().at(d->anchor - fpos + frag->stringPosition); + if (d->anchor > fpos && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) { + // second half of a surrogate, check if we have the first half as well, + // if yes delete both at once + uc = d->priv->buffer().at(d->anchor - 1 - fpos + frag->stringPosition); + if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) + --d->anchor; + } + + d->adjusted_anchor = d->anchor; d->remove(); d->setX(); } @@ -1504,7 +1522,9 @@ void QTextCursor::removeSelectedText() if (!d || !d->priv || d->position == d->anchor) return; + d->priv->beginEditBlock(); d->remove(); + d->priv->endEditBlock(); d->setX(); } diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 873f846..23c3c20 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2201,7 +2201,7 @@ void QTextHtmlExporter::emitTextLength(const char *attribute, const QTextLength if (length.type() == QTextLength::PercentageLength) html += QLatin1String("%\""); else - html += QLatin1String("\""); + html += QLatin1Char('\"'); } void QTextHtmlExporter::emitAlignment(Qt::Alignment align) diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 05ddf47..e1da4be 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -404,7 +404,7 @@ int QTextDocumentPrivate::insertBlock(const QChar &blockSeparator, int b = blocks.findNode(pos); QTextBlockData *B = blocks.fragment(b); - QTextUndoCommand c = { QTextUndoCommand::BlockInserted, true, + QTextUndoCommand c = { QTextUndoCommand::BlockInserted, editBlock != 0, op, charFormat, strPos, pos, { blockFormat }, B->revision }; @@ -439,20 +439,19 @@ void QTextDocumentPrivate::insert(int pos, int strPos, int strLength, int format Q_ASSERT(pos >= 0 && pos < fragments.length()); Q_ASSERT(formats.format(format).isCharFormat()); - beginEditBlock(); insert_string(pos, strPos, strLength, format, QTextUndoCommand::MoveCursor); if (undoEnabled) { int b = blocks.findNode(pos); QTextBlockData *B = blocks.fragment(b); - QTextUndoCommand c = { QTextUndoCommand::Inserted, true, + QTextUndoCommand c = { QTextUndoCommand::Inserted, editBlock != 0, QTextUndoCommand::MoveCursor, format, strPos, pos, { strLength }, B->revision }; appendUndoItem(c); B->revision = undoState; Q_ASSERT(undoState == undoStack.size()); } - endEditBlock(); + finishEdit(); } void QTextDocumentPrivate::insert(int pos, const QString &str, int format) @@ -584,8 +583,6 @@ void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::O Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell); #endif - beginEditBlock(); - split(pos); split(pos+length); @@ -605,10 +602,10 @@ void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::O int blockRevision = B->revision; QTextFragmentData *X = fragments.fragment(x); - QTextUndoCommand c = { QTextUndoCommand::Removed, true, + QTextUndoCommand c = { QTextUndoCommand::Removed, editBlock != 0, op, X->format, X->stringPosition, key, { X->size_array[0] }, blockRevision }; - QTextUndoCommand cInsert = { QTextUndoCommand::Inserted, true, + QTextUndoCommand cInsert = { QTextUndoCommand::Inserted, editBlock != 0, op, X->format, X->stringPosition, dstKey, { X->size_array[0] }, blockRevision }; @@ -648,7 +645,7 @@ void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::O Q_ASSERT(blocks.length() == fragments.length()); - endEditBlock(); + finishEdit(); } void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operation op) @@ -1004,8 +1001,12 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c) if (!undoStack.isEmpty() && modified) { QTextUndoCommand &last = undoStack[undoState - 1]; - if (last.tryMerge(c)) - return; + if ( (last.block && c.block) // part of the same block => can merge + || (!c.block && !last.block // two single undo items => can merge + && (undoState < 2 || !undoStack[undoState-2].block))) { + if (last.tryMerge(c)) + return; + } } if (modifiedState > undoState) modifiedState = -1; @@ -1013,6 +1014,9 @@ void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c) undoState++; emitUndoAvailable(true); emitRedoAvailable(false); + + if (!c.block) + emit document()->undoCommandAdded(); } void QTextDocumentPrivate::truncateUndoStack() @@ -1082,7 +1086,6 @@ void QTextDocumentPrivate::joinPreviousEditBlock() void QTextDocumentPrivate::endEditBlock() { - Q_Q(QTextDocument); if (--editBlock) return; @@ -1093,6 +1096,16 @@ void QTextDocumentPrivate::endEditBlock() emit document()->undoCommandAdded(); } + finishEdit(); +} + +void QTextDocumentPrivate::finishEdit() +{ + Q_Q(QTextDocument); + + if (editBlock) + return; + if (framesDirty) scan_frames(docChangeFrom, docChangeOldLength, docChangeLength); @@ -1279,7 +1292,7 @@ void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format) if (f) documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition()); - QTextUndoCommand c = { QTextUndoCommand::GroupFormatChange, true, QTextUndoCommand::MoveCursor, oldFormatIndex, + QTextUndoCommand c = { QTextUndoCommand::GroupFormatChange, editBlock != 0, QTextUndoCommand::MoveCursor, oldFormatIndex, 0, 0, { obj->d_func()->objectIndex }, 0 }; appendUndoItem(c); diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index 25763e1..d754ff0 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -139,7 +139,7 @@ public: MoveCursor = 1 }; quint16 command; - quint8 block; ///< All undo commands that have this set to zero/false are combined with the preceding command on undo/redo. + quint8 block; ///< All undo commands that have this set to true are combined with the preceding command on undo/redo. quint8 operation; int format; quint32 strPos; @@ -202,6 +202,7 @@ public: inline void beginEditBlock() { editBlock++; } void joinPreviousEditBlock(); void endEditBlock(); + void finishEdit(); inline bool isInEditBlock() const { return editBlock; } void enableUndoRedo(bool enable); inline bool isUndoRedoEnabled() const { return undoEnabled; } diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index d41d414..a28c655 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -363,7 +363,7 @@ static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiCon #if (BIDI_DEBUG >= 2) // qDebug() << "pos=" << current << " dir=" << directions[dir] // << " current=" << directions[dirCurrent] << " last=" << directions[status.last] -// << " eor=" << eor << "/" << directions[status.eor] +// << " eor=" << eor << '/' << directions[status.eor] // << " sor=" << sor << " lastStrong=" // << directions[status.lastStrong] // << " level=" << (int)control.level << " override=" << (bool)control.override; @@ -868,7 +868,7 @@ void QTextEngine::shapeText(int item) const #if defined(Q_WS_MAC) shapeTextMac(item); -#elif defined(Q_OS_WINCE) +#elif defined(Q_WS_WINCE) shapeTextWithCE(item); #else shapeTextWithHarfbuzz(item); @@ -923,7 +923,7 @@ void QTextEngine::shapeText(int item) const si.width += glyphs.advances_x[i]; } -#if defined(Q_OS_WINCE) //TODO +#if defined(Q_WS_WINCE) //TODO // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs // and no reordering. // also computes logClusters heuristically diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index cf241fa..6be525f 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -581,7 +581,7 @@ private: void addRequiredBoundaries() const; void shapeText(int item) const; void shapeTextWithHarfbuzz(int item) const; -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) void shapeTextWithCE(int item) const; #endif #if defined(Q_WS_MAC) diff --git a/src/gui/text/qtextimagehandler_p.h b/src/gui/text/qtextimagehandler_p.h index f5426b5..1f29642 100644 --- a/src/gui/text/qtextimagehandler_p.h +++ b/src/gui/text/qtextimagehandler_p.h @@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE class QTextImageFormat; -class Q_GUI_EXPORT QTextImageHandler : public QObject, +class QTextImageHandler : public QObject, public QTextObjectInterface { Q_OBJECT @@ -72,7 +72,7 @@ public: virtual void drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format); typedef QImage (*ExternalImageLoaderFunction)(const QString &name, const QString &context); - static ExternalImageLoaderFunction externalLoader; + static Q_GUI_EXPORT ExternalImageLoaderFunction externalLoader; //this is needed by Qt3Support }; QT_END_NAMESPACE diff --git a/src/gui/text/qtextodfwriter.cpp b/src/gui/text/qtextodfwriter.cpp index 1edc3b8..75e89d2 100644 --- a/src/gui/text/qtextodfwriter.cpp +++ b/src/gui/text/qtextodfwriter.cpp @@ -477,7 +477,7 @@ void QTextOdfWriter::writeBlockFormat(QXmlStreamWriter &writer, QTextBlockFormat if (format.hasProperty(QTextFormat::BlockRightMargin)) writer.writeAttribute(foNS, QString::fromLatin1("margin-right"), pixelToPoint(qMax(qreal(0.), format.rightMargin())) ); if (format.hasProperty(QTextFormat::TextIndent)) - writer.writeAttribute(foNS, QString::fromLatin1("text-indent"), QString::number(format.textIndent())); + writer.writeAttribute(foNS, QString::fromLatin1("text-indent"), pixelToPoint(format.textIndent())); if (format.hasProperty(QTextFormat::PageBreakPolicy)) { if (format.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore) writer.writeAttribute(foNS, QString::fromLatin1("break-before"), QString::fromLatin1("page")); diff --git a/src/gui/util/qcompleter.cpp b/src/gui/util/qcompleter.cpp index faa4e7b..bed10dc 100644 --- a/src/gui/util/qcompleter.cpp +++ b/src/gui/util/qcompleter.cpp @@ -1664,7 +1664,7 @@ QStringList QCompleter::splitPath(const QString& path) const doubleSlash.clear(); #endif - QRegExp re(QLatin1String("[") + QRegExp::escape(sep) + QLatin1String("]")); + QRegExp re(QLatin1Char('[') + QRegExp::escape(sep) + QLatin1Char(']')); QStringList parts = pathCopy.split(re); #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) 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/util/qsystemtrayicon.cpp b/src/gui/util/qsystemtrayicon.cpp index 2e072c5..bfafe44 100644 --- a/src/gui/util/qsystemtrayicon.cpp +++ b/src/gui/util/qsystemtrayicon.cpp @@ -434,13 +434,13 @@ QBalloonTip::QBalloonTip(QSystemTrayIcon::MessageIcon icon, const QString& title titleLabel->setText(title); QFont f = titleLabel->font(); f.setBold(true); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE f.setPointSize(f.pointSize() - 2); #endif titleLabel->setFont(f); titleLabel->setTextFormat(Qt::PlainText); // to maintain compat with windows -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE const int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); const int closeButtonSize = style()->pixelMetric(QStyle::PM_SmallIconSize) - 2; #else @@ -456,7 +456,7 @@ QBalloonTip::QBalloonTip(QSystemTrayIcon::MessageIcon icon, const QString& title QObject::connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); QLabel *msgLabel = new QLabel; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE f.setBold(false); msgLabel->setFont(f); #endif @@ -466,7 +466,7 @@ QBalloonTip::QBalloonTip(QSystemTrayIcon::MessageIcon icon, const QString& title msgLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); // smart size for the message label -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE int limit = QApplication::desktop()->availableGeometry(msgLabel).size().width() / 2; #else int limit = QApplication::desktop()->availableGeometry(msgLabel).size().width() / 3; @@ -481,7 +481,7 @@ QBalloonTip::QBalloonTip(QSystemTrayIcon::MessageIcon icon, const QString& title control->document()->setDefaultTextOption(opt); } } -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE // Make sure that the text isn't wrapped "somewhere" in the balloon widget // in the case that we have a long title label. setMaximumWidth(limit); diff --git a/src/gui/util/qsystemtrayicon_win.cpp b/src/gui/util/qsystemtrayicon_win.cpp index 84f9de4..c46c929 100644 --- a/src/gui/util/qsystemtrayicon_win.cpp +++ b/src/gui/util/qsystemtrayicon_win.cpp @@ -62,13 +62,13 @@ #include <QDesktopWidget> #include <QSettings> -#if defined(Q_OS_WINCE) && !defined(STANDARDSHELL_UI_MODEL) +#if defined(Q_WS_WINCE) && !defined(STANDARDSHELL_UI_MODEL) # include <streams.h> #endif QT_BEGIN_NAMESPACE -#if defined(Q_OS_WINCE) +#if defined(Q_WS_WINCE) static const UINT q_uNOTIFYICONID = 13; // IDs from 0 to 12 are reserved on WinCE. #else static const UINT q_uNOTIFYICONID = 0; @@ -331,7 +331,7 @@ bool QSystemTrayIconSys::showMessageA(const QString &title, const QString &messa bool QSystemTrayIconSys::trayMessageA(DWORD msg) { -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) NOTIFYICONDATAA tnd; memset(&tnd, 0, notifyIconSizeA); tnd.uID = q_uNOTIFYICONID; @@ -462,7 +462,7 @@ bool QSystemTrayIconSys::winEvent( MSG *m, long *result ) emit q->activated(QSystemTrayIcon::Trigger); break; -#if !defined(Q_OS_WINCE) +#if !defined(Q_WS_WINCE) case WM_LBUTTONDBLCLK: emit q->activated(QSystemTrayIcon::DoubleClick); break; @@ -662,7 +662,7 @@ void QSystemTrayIconPrivate::showMessage_sys(const QString &title, const QString //message is limited to 255 chars + NULL QString messageString; if (message.isEmpty() && !title.isEmpty()) - messageString = QLatin1String(" "); //ensures that the message shows when only title is set + messageString = QLatin1Char(' '); //ensures that the message shows when only title is set else messageString = message.left(255) + QChar(); @@ -726,7 +726,7 @@ void QSystemTrayIconPrivate::updateMenu_sys() void QSystemTrayIconPrivate::updateToolTip_sys() { -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE // Calling sys->trayMessage(NIM_MODIFY) on an existing icon is broken on Windows CE. // So we need to call updateIcon_sys() which creates a new icon handle. updateIcon_sys(); diff --git a/src/gui/widgets/qabstractbutton.cpp b/src/gui/widgets/qabstractbutton.cpp index 61ed0ea..1900016 100644 --- a/src/gui/widgets/qabstractbutton.cpp +++ b/src/gui/widgets/qabstractbutton.cpp @@ -215,11 +215,8 @@ void QButtonGroup::setExclusive(bool exclusive) d->exclusive = exclusive; } -/*! - Adds the given \a button to the end of the group's internal list of buttons. - \sa removeButton() -*/ +// TODO: Qt 5: Merge with addButton(QAbstractButton *button, int id) void QButtonGroup::addButton(QAbstractButton *button) { addButton(button, -1); @@ -232,8 +229,18 @@ void QButtonGroup::addButton(QAbstractButton *button, int id) previous->removeButton(button); button->d_func()->group = this; d->buttonList.append(button); - if (id != -1) + if (id == -1) { + QList<int> ids = d->mapping.values(); + if (ids.isEmpty()) + d->mapping[button] = -2; + else { + qSort(ids); + d->mapping[button] = ids.first()-1; + } + } else { d->mapping[button] = id; + } + if (d->exclusive && button->isChecked()) button->d_func()->notifyChecked(); } @@ -1236,7 +1243,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/qabstractscrollarea.cpp b/src/gui/widgets/qabstractscrollarea.cpp index 9886969..0d8b4de 100644 --- a/src/gui/widgets/qabstractscrollarea.cpp +++ b/src/gui/widgets/qabstractscrollarea.cpp @@ -873,21 +873,22 @@ bool QAbstractScrollArea::event(QEvent *e) case QEvent::Resize: d->layoutChildren(); break; - case QEvent::Paint: + case QEvent::Paint: { + QStyleOption option; + option.initFrom(this); if (d->cornerPaintingRect.isValid()) { - QStyleOption option; option.rect = d->cornerPaintingRect; QPainter p(this); style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, &p, this); } #ifdef Q_WS_MAC if (d->reverseCornerPaintingRect.isValid()) { - QStyleOption option; option.rect = d->reverseCornerPaintingRect; QPainter p(this); style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, &p, this); } #endif + } QFrame::paintEvent((QPaintEvent*)e); break; #ifndef QT_NO_CONTEXTMENU diff --git a/src/gui/widgets/qabstractscrollarea_p.h b/src/gui/widgets/qabstractscrollarea_p.h index e4c47e9..f153711 100644 --- a/src/gui/widgets/qabstractscrollarea_p.h +++ b/src/gui/widgets/qabstractscrollarea_p.h @@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE class QScrollBar; class QAbstractScrollAreaScrollBarContainer; -class Q_GUI_EXPORT QAbstractScrollAreaPrivate: public QFramePrivate +class QAbstractScrollAreaPrivate: public QFramePrivate { Q_DECLARE_PUBLIC(QAbstractScrollArea) diff --git a/src/gui/widgets/qabstractspinbox.cpp b/src/gui/widgets/qabstractspinbox.cpp index 347f89a..09accc7 100644 --- a/src/gui/widgets/qabstractspinbox.cpp +++ b/src/gui/widgets/qabstractspinbox.cpp @@ -193,6 +193,7 @@ void QAbstractSpinBox::setButtonSymbols(ButtonSymbols buttonSymbols) Q_D(QAbstractSpinBox); if (d->buttonSymbols != buttonSymbols) { d->buttonSymbols = buttonSymbols; + d->updateEditFieldGeometry(); update(); } } @@ -1780,8 +1781,8 @@ void QAbstractSpinBoxPrivate::interpret(EmitPolicy ep) q->fixup(tmp); QASBDEBUG() << "QAbstractSpinBoxPrivate::interpret() text '" << edit->displayText() - << "' >> '" << copy << "'" - << "' >> '" << tmp << "'"; + << "' >> '" << copy << '\'' + << "' >> '" << tmp << '\''; doInterpret = tmp != copy && (q->validate(tmp, pos) == QValidator::Acceptable); if (!doInterpret) { diff --git a/src/gui/widgets/qbuttongroup.cpp b/src/gui/widgets/qbuttongroup.cpp index 06bcf1e..ebfafe3 100644 --- a/src/gui/widgets/qbuttongroup.cpp +++ b/src/gui/widgets/qbuttongroup.cpp @@ -176,11 +176,21 @@ */ /*! - \fn void QButtonGroup::addButton(QAbstractButton *button, int id = -1); + \fn void QButtonGroup::addButton(QAbstractButton *button); + + Adds the given \a button to the end of the group's internal list of buttons. + An \a id will be assigned to the button by this QButtonGroup. Automatically + assigned ids are guaranteed to be negative, starting with -2. If you are also + assigning your own ids, use positive values to avoid conflicts. + + \sa removeButton() buttons() +*/ + +/*! + \fn void QButtonGroup::addButton(QAbstractButton *button, int id); Adds the given \a button to the button group, with the given \a - id. If \a id is -1 (the default), an id will be assigned to the - button by this QButtonGroup. + id. It is recommended to assign only positive ids. \sa removeButton() buttons() */ diff --git a/src/gui/widgets/qcalendarwidget.cpp b/src/gui/widgets/qcalendarwidget.cpp index 4436c04..8703139 100644 --- a/src/gui/widgets/qcalendarwidget.cpp +++ b/src/gui/widgets/qcalendarwidget.cpp @@ -198,7 +198,7 @@ QString QCalendarDayValidator::text() const { QString str; if (m_day / 10 == 0) - str += QLatin1String("0"); + str += QLatin1Char('0'); str += QString::number(m_day); return highlightString(str, m_pos); } @@ -210,7 +210,7 @@ QString QCalendarDayValidator::text(const QDate &date, int repeat) const } else if (repeat == 2) { QString str; if (date.day() / 10 == 0) - str += QLatin1String("0"); + str += QLatin1Char('0'); return str + QString::number(date.day()); } else if (repeat == 3) { return m_locale.dayName(date.dayOfWeek(), QLocale::ShortFormat); @@ -316,7 +316,7 @@ QString QCalendarMonthValidator::text() const { QString str; if (m_month / 10 == 0) - str += QLatin1String("0"); + str += QLatin1Char('0'); str += QString::number(m_month); return highlightString(str, m_pos); } @@ -328,7 +328,7 @@ QString QCalendarMonthValidator::text(const QDate &date, int repeat) const } else if (repeat == 2) { QString str; if (date.month() / 10 == 0) - str += QLatin1String("0"); + str += QLatin1Char('0'); return str + QString::number(date.month()); } else if (repeat == 3) { return m_locale.standaloneMonthName(date.month(), QLocale::ShortFormat); @@ -432,7 +432,7 @@ QString QCalendarYearValidator::text() const int pow = 10; for (int i = 0; i < 3; i++) { if (m_year / pow == 0) - str += QLatin1String("0"); + str += QLatin1Char('0'); pow *= 10; } str += QString::number(m_year); @@ -445,7 +445,7 @@ QString QCalendarYearValidator::text(const QDate &date, int repeat) const QString str; int year = date.year() % 100; if (year / 10 == 0) - str = QLatin1String("0"); + str = QLatin1Char('0'); return str + QString::number(year); } return QString::number(date.year()); @@ -577,7 +577,7 @@ void QCalendarDateValidator::setFormat(const QString &format) clear(); int pos = 0; - const QLatin1String quote("'"); + const QLatin1Char quote('\''); bool quoting = false; QString separator; while (pos < format.size()) { diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp index a5a98d4..01fe9d2 100644 --- a/src/gui/widgets/qcombobox.cpp +++ b/src/gui/widgets/qcombobox.cpp @@ -949,6 +949,7 @@ QComboBoxPrivateContainer* QComboBoxPrivate::viewContainer() container->itemView()->setTextElideMode(Qt::ElideMiddle); updateDelegate(true); updateLayoutDirection(); + updateViewContainerPaletteAndOpacity(); QObject::connect(container, SIGNAL(itemSelected(QModelIndex)), q, SLOT(_q_itemSelected(QModelIndex))); QObject::connect(container->itemView()->selectionModel(), @@ -1051,6 +1052,27 @@ void QComboBoxPrivate::_q_rowsRemoved(const QModelIndex &parent, int /*start*/, } +void QComboBoxPrivate::updateViewContainerPaletteAndOpacity() +{ + if (!container) + return; + Q_Q(QComboBox); + QStyleOptionComboBox opt; + q->initStyleOption(&opt); +#ifndef QT_NO_MENU + if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) { + QMenu menu; + menu.ensurePolished(); + container->setPalette(menu.palette()); + container->setWindowOpacity(menu.windowOpacity()); + } else +#endif + { + container->setPalette(q->palette()); + container->setWindowOpacity(1.0); + } +} + /*! Initialize \a option with the values from this QComboBox. This method is useful for subclasses when they need a QStyleOptionComboBox, but don't want @@ -2582,20 +2604,7 @@ void QComboBox::changeEvent(QEvent *e) hidePopup(); break; case QEvent::PaletteChange: { - QStyleOptionComboBox opt; - initStyleOption(&opt); -#ifndef QT_NO_MENU - if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) { - QMenu menu; - menu.ensurePolished(); - d->viewContainer()->setPalette(menu.palette()); - d->viewContainer()->setWindowOpacity(menu.windowOpacity()); - } else -#endif - { - d->viewContainer()->setPalette(palette()); - d->viewContainer()->setWindowOpacity(1.0); - } + d->updateViewContainerPaletteAndOpacity(); break; } case QEvent::FontChange: diff --git a/src/gui/widgets/qcombobox_p.h b/src/gui/widgets/qcombobox_p.h index a55b439..ee0da62 100644 --- a/src/gui/widgets/qcombobox_p.h +++ b/src/gui/widgets/qcombobox_p.h @@ -370,6 +370,7 @@ public: void updateDelegate(bool force = false); void keyboardSearchString(const QString &text); void modelChanged(); + void updateViewContainerPaletteAndOpacity(); QAbstractItemModel *model; QLineEdit *lineEdit; 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/qdockwidget.cpp b/src/gui/widgets/qdockwidget.cpp index a5be5f8..d573b8b 100644 --- a/src/gui/widgets/qdockwidget.cpp +++ b/src/gui/widgets/qdockwidget.cpp @@ -203,7 +203,7 @@ bool QDockWidgetLayout::nativeWindowDeco() const bool QDockWidgetLayout::nativeWindowDeco(bool floating) const { -#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_WINCE) +#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_WINCE) Q_UNUSED(floating); return false; #else diff --git a/src/gui/widgets/qframe.cpp b/src/gui/widgets/qframe.cpp index 6f81331..22a990b 100644 --- a/src/gui/widgets/qframe.cpp +++ b/src/gui/widgets/qframe.cpp @@ -59,8 +59,7 @@ QFramePrivate::QFramePrivate() midLineWidth(0), frameWidth(0), leftFrameWidth(0), rightFrameWidth(0), - topFrameWidth(0), bottomFrameWidth(0), - oldFrameStyle(QFrame::NoFrame | QFrame::Plain) + topFrameWidth(0), bottomFrameWidth(0) { } @@ -333,7 +332,6 @@ void QFrame::setFrameStyle(int style) d->frameStyle = (short)style; update(); d->updateFrameWidth(); - d->oldFrameStyle = (short)style; } /*! diff --git a/src/gui/widgets/qframe_p.h b/src/gui/widgets/qframe_p.h index 4fd341d..537f5bf 100644 --- a/src/gui/widgets/qframe_p.h +++ b/src/gui/widgets/qframe_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -class Q_GUI_EXPORT QFramePrivate : public QWidgetPrivate +class QFramePrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QFrame) public: @@ -74,7 +74,6 @@ public: short frameWidth; short leftFrameWidth, rightFrameWidth; short topFrameWidth, bottomFrameWidth; - short oldFrameStyle; inline void init(); 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/qlabel.cpp b/src/gui/widgets/qlabel.cpp index 63c1315..1aef133 100644 --- a/src/gui/widgets/qlabel.cpp +++ b/src/gui/widgets/qlabel.cpp @@ -971,6 +971,13 @@ void QLabel::paintEvent(QPaintEvent *) #endif if (d->isTextLabel) { QRectF lr = d->layoutRect(); + QStyleOption opt; + opt.initFrom(this); +#ifndef QT_NO_STYLE_STYLESHEET + if (QStyleSheetStyle* cssStyle = qobject_cast<QStyleSheetStyle*>(style)) { + cssStyle->styleSheetPalette(this, &opt, &opt.palette); + } +#endif if (d->control) { #ifndef QT_NO_SHORTCUT const bool underline = (bool)style->styleHint(QStyle::SH_UnderlineShortcut, 0, this, 0); @@ -984,11 +991,9 @@ void QLabel::paintEvent(QPaintEvent *) d->ensureTextLayouted(); QAbstractTextDocumentLayout::PaintContext context; - QStyleOption opt(0); - opt.init(this); if (!isEnabled() && style->styleHint(QStyle::SH_EtchDisabledText, &opt, this)) { - context.palette = palette(); + context.palette = opt.palette; context.palette.setColor(QPalette::Text, context.palette.light().color()); painter.save(); painter.translate(lr.x() + 1, lr.y() + 1); @@ -999,12 +1004,7 @@ void QLabel::paintEvent(QPaintEvent *) } // Adjust the palette - context.palette = palette(); -#ifndef QT_NO_STYLE_STYLESHEET - if (QStyleSheetStyle* cssStyle = qobject_cast<QStyleSheetStyle*>(style)) { - cssStyle->focusPalette(this, &opt, &context.palette); - } -#endif + context.palette = opt.palette; if (foregroundRole() != QPalette::Text && isEnabled()) context.palette.setColor(QPalette::Text, context.palette.color(foregroundRole())); @@ -1019,12 +1019,10 @@ void QLabel::paintEvent(QPaintEvent *) int flags = align; if (d->hasShortcut) { flags |= Qt::TextShowMnemonic; - QStyleOption opt; - opt.initFrom(this); if (!style->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) flags |= Qt::TextHideMnemonic; } - style->drawItemText(&painter, lr.toRect(), flags, palette(), isEnabled(), d->text, foregroundRole()); + style->drawItemText(&painter, lr.toRect(), flags, opt.palette, isEnabled(), d->text, foregroundRole()); } } else #ifndef QT_NO_PICTURE @@ -1172,7 +1170,7 @@ void QLabelPrivate::updateShortcut() shortcutCursor.deleteChar(); // remove the ampersand shortcutCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); } else { - if (!text.contains(QLatin1String("&"))) + if (!text.contains(QLatin1Char('&'))) return; hasShortcut = true; shortcutId = q->grabShortcut(QKeySequence::mnemonic(text)); diff --git a/src/gui/widgets/qlcdnumber.cpp b/src/gui/widgets/qlcdnumber.cpp index af80963..6686d7e 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 b76d019..a4b0b4f 100644 --- a/src/gui/widgets/qlineedit.cpp +++ b/src/gui/widgets/qlineedit.cpp @@ -79,7 +79,7 @@ #include "private/qapplication_p.h" #include "private/qshortcutmap_p.h" #include "qkeysequence.h" -#define ACCEL_KEY(k) (!qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? QLatin1String("\t") + QString(QKeySequence(k)) : QString()) +#define ACCEL_KEY(k) (!qApp->d_func()->shortcutMap.hasShortcutForKeySequence(k) ? QLatin1Char('\t') + QString(QKeySequence(k)) : QString()) #else #define ACCEL_KEY(k) QString() #endif @@ -2518,7 +2518,7 @@ void QLineEdit::paintEvent(QPaintEvent *) // draw text, selections and cursors #ifndef QT_NO_STYLE_STYLESHEET if (QStyleSheetStyle* cssStyle = qobject_cast<QStyleSheetStyle*>(style())) { - cssStyle->focusPalette(this, &panel, &pal); + cssStyle->styleSheetPalette(this, &panel, &pal); } #endif p.setPen(pal.text().color()); @@ -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/qmainwindowlayout_mac.mm b/src/gui/widgets/qmainwindowlayout_mac.mm index c807afb..53e1ad5 100644 --- a/src/gui/widgets/qmainwindowlayout_mac.mm +++ b/src/gui/widgets/qmainwindowlayout_mac.mm @@ -502,11 +502,11 @@ void QMainWindowLayout::fixSizeInUnifiedToolbar(QToolBar *tb) const QMacCocoaAutoReleasePool pool; QWidgetItem layoutItem(tb); QSize size = layoutItem.maximumSize(); - NSSize nssize = NSMakeSize(size.width(), size.height()); + NSSize nssize = NSMakeSize(size.width(), size.height() - 2); [item setMaxSize:nssize]; size = layoutItem.minimumSize(); nssize.width = size.width(); - nssize.height = size.height(); + nssize.height = size.height() - 2; [item setMinSize:nssize]; } } diff --git a/src/gui/widgets/qmdisubwindow.cpp b/src/gui/widgets/qmdisubwindow.cpp index 6bf7633..56df8ea 100644 --- a/src/gui/widgets/qmdisubwindow.cpp +++ b/src/gui/widgets/qmdisubwindow.cpp @@ -1772,7 +1772,7 @@ bool QMdiSubWindowPrivate::drawTitleBarWhenMaximized() const if (isChildOfTabbedQMdiArea(q)) return false; -#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) || defined(Q_OS_WINCE_WM) +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) || defined(Q_WS_WINCE_WM) return true; #else if (q->style()->styleHint(QStyle::SH_Workspace_FillSpaceOnMaximize, 0, q)) diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp index 7396a9d..3004841 100644 --- a/src/gui/widgets/qmenu.cpp +++ b/src/gui/widgets/qmenu.cpp @@ -2837,7 +2837,7 @@ void QMenu::actionEvent(QActionEvent *e) } #endif -#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR) +#if defined(Q_WS_WINCE) && !defined(QT_NO_MENUBAR) if (!d->wce_menu) d->wce_menu = new QMenuPrivate::QWceMenuPrivate; if (e->type() == QEvent::ActionAdded) diff --git a/src/gui/widgets/qmenu.h b/src/gui/widgets/qmenu.h index 867baee..e1a6256 100644 --- a/src/gui/widgets/qmenu.h +++ b/src/gui/widgets/qmenu.h @@ -141,7 +141,7 @@ public: OSMenuRef macMenu(OSMenuRef merge=0); #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE HMENU wceMenu(bool create = false); #endif @@ -174,7 +174,7 @@ protected: bool focusNextPrevChild(bool next); void initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE QAction* wceCommands(uint command); #endif diff --git a/src/gui/widgets/qmenu_mac.mm b/src/gui/widgets/qmenu_mac.mm index ad848c9..cce083f 100644 --- a/src/gui/widgets/qmenu_mac.mm +++ b/src/gui/widgets/qmenu_mac.mm @@ -71,7 +71,6 @@ QT_BEGIN_NAMESPACE /***************************************************************************** QMenu globals *****************************************************************************/ -bool qt_mac_no_native_menubar = false; bool qt_mac_no_menubar_merge = false; bool qt_mac_quit_menu_item_enabled = true; int qt_mac_menus_open_count = 0; @@ -143,6 +142,39 @@ static int qt_mac_CountMenuItems(OSMenuRef menu) return 0; } +static quint32 constructModifierMask(quint32 accel_key) +{ + quint32 ret = 0; + const bool dontSwap = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta); +#ifndef QT_MAC_USE_COCOA + if ((accel_key & Qt::ALT) == Qt::ALT) + ret |= kMenuOptionModifier; + if ((accel_key & Qt::SHIFT) == Qt::SHIFT) + ret |= kMenuShiftModifier; + if (dontSwap) { + if ((accel_key & Qt::META) != Qt::META) + ret |= kMenuNoCommandModifier; + if ((accel_key & Qt::CTRL) == Qt::CTRL) + ret |= kMenuControlModifier; + } else { + if ((accel_key & Qt::CTRL) != Qt::CTRL) + ret |= kMenuNoCommandModifier; + if ((accel_key & Qt::META) == Qt::META) + ret |= kMenuControlModifier; + } +#else + if ((accel_key & Qt::CTRL) == Qt::CTRL) + ret |= (dontSwap ? NSControlKeyMask : NSCommandKeyMask); + if ((accel_key & Qt::META) == Qt::META) + ret |= (dontSwap ? NSCommandKeyMask : NSControlKeyMask); + if ((accel_key & Qt::ALT) == Qt::ALT) + ret |= NSAlternateKeyMask; + if ((accel_key & Qt::SHIFT) == Qt::SHIFT) + ret |= NSShiftKeyMask; +#endif + return ret; +} + static bool actualMenuItemVisibility(const QMenuBarPrivate::QMacMenuBarPrivate *mbp, const QMacMenuAction *action) { @@ -166,7 +198,7 @@ bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent act QMenuMergeList *list = 0; GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, sizeof(list), 0, &list); - if (!list && qt_mac_current_menubar.qmenubar) { + if (!list && qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) { MenuRef apple_menu = qt_mac_current_menubar.qmenubar->d_func()->mac_menubar->apple_menu; GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, sizeof(list), 0, &list); if (list) @@ -526,15 +558,7 @@ static bool qt_mac_auto_apple_menu(MenuCommand cmd) static void qt_mac_get_accel(quint32 accel_key, quint32 *modif, quint32 *key) { if (modif) { - *modif = 0; - if ((accel_key & Qt::CTRL) != Qt::CTRL) - *modif |= kMenuNoCommandModifier; - if ((accel_key & Qt::META) == Qt::META) - *modif |= kMenuControlModifier; - if ((accel_key & Qt::ALT) == Qt::ALT) - *modif |= kMenuOptionModifier; - if ((accel_key & Qt::SHIFT) == Qt::SHIFT) - *modif |= kMenuShiftModifier; + *modif = constructModifierMask(accel_key); } accel_key &= ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL); @@ -728,6 +752,18 @@ QMacMenuAction::~QMacMenuAction() { #ifdef QT_MAC_USE_COCOA [menu release]; + if (action) { + QAction::MenuRole role = action->menuRole(); + // Check if the item is owned by Qt, and should be hidden to keep it from causing + // problems. Do it for everything but the quit menu item since that should always + // be visible. + if (role > QAction::ApplicationSpecificRole && role < QAction::QuitRole) { + [menuItem setHidden:YES]; + } else if (role == QAction::TextHeuristicRole + && menuItem != [getMenuLoader() quitMenuItem]) { + [menuItem setHidden:YES]; + } + } [menuItem setTag:nil]; [menuItem release]; #endif @@ -917,21 +953,22 @@ static QKeySequence qt_mac_menu_merge_accel(QMacMenuAction *action) ret = action->action->shortcut(); #ifndef QT_MAC_USE_COCOA else if (action->command == kHICommandPreferences) - ret = QKeySequence(Qt::CTRL+Qt::Key_Comma); + ret = QKeySequence(QKeySequence::Preferences); else if (action->command == kHICommandQuit) - ret = QKeySequence(Qt::CTRL+Qt::Key_Q); + ret = QKeySequence(QKeySequence::Quit); #else else if (action->menuItem == [loader preferencesMenuItem]) - ret = QKeySequence(Qt::CTRL+Qt::Key_Comma); + ret = QKeySequence(QKeySequence::Preferences); else if (action->menuItem == [loader quitMenuItem]) - ret = QKeySequence(Qt::CTRL+Qt::Key_Q); + ret = QKeySequence(QKeySequence::Quit); #endif return ret; } void Q_GUI_EXPORT qt_mac_set_menubar_icons(bool b) { QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !b); } -void Q_GUI_EXPORT qt_mac_set_native_menubar(bool b) { qt_mac_no_native_menubar = !b; } +void Q_GUI_EXPORT qt_mac_set_native_menubar(bool b) +{ QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, !b); } void Q_GUI_EXPORT qt_mac_set_menubar_merge(bool b) { qt_mac_no_menubar_merge = !b; } /***************************************************************************** @@ -1208,58 +1245,21 @@ QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction NSString *keySequenceToKeyEqivalent(const QKeySequence &accel) { quint32 accel_key = (accel[0] & ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL)); - unichar keyEquiv[1] = { 0 }; - if (accel_key == Qt::Key_Return) - keyEquiv[0] = kReturnCharCode; - else if (accel_key == Qt::Key_Enter) - keyEquiv[0] = kEnterCharCode; - else if (accel_key == Qt::Key_Tab) - keyEquiv[0] = kTabCharCode; - else if (accel_key == Qt::Key_Backspace) - keyEquiv[0] = kBackspaceCharCode; - else if (accel_key == Qt::Key_Delete) - keyEquiv[0] = NSDeleteFunctionKey; - else if (accel_key == Qt::Key_Escape) - keyEquiv[0] = kEscapeCharCode; - else if (accel_key == Qt::Key_PageUp) - keyEquiv[0] = NSPageUpFunctionKey; - else if (accel_key == Qt::Key_PageDown) - keyEquiv[0] = NSPageDownFunctionKey; - else if (accel_key == Qt::Key_Up) - keyEquiv[0] = NSUpArrowFunctionKey; - else if (accel_key == Qt::Key_Down) - keyEquiv[0] = NSDownArrowFunctionKey; - else if (accel_key == Qt::Key_Left) - keyEquiv[0] = NSLeftArrowFunctionKey; - else if (accel_key == Qt::Key_Right) - keyEquiv[0] = NSRightArrowFunctionKey; - else if (accel_key == Qt::Key_CapsLock) - keyEquiv[0] = kMenuCapsLockGlyph; // ### Cocoa has no equivalent - else if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15) - keyEquiv[0] = (accel_key - Qt::Key_F1) + NSF1FunctionKey; - else if (accel_key == Qt::Key_Home) - keyEquiv[0] = NSHomeFunctionKey; - else if (accel_key == Qt::Key_End) - keyEquiv[0] = NSEndFunctionKey; - else - keyEquiv[0] = unichar(QChar(accel_key).toLower().unicode()); - return [NSString stringWithCharacters:keyEquiv length:1]; + extern QChar qt_macSymbolForQtKey(int key); // qkeysequence.cpp + QChar keyEquiv = qt_macSymbolForQtKey(accel_key); + if (keyEquiv.isNull()) { + if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15) + keyEquiv = (accel_key - Qt::Key_F1) + NSF1FunctionKey; + else + keyEquiv = unichar(QChar(accel_key).toLower().unicode()); + } + return [NSString stringWithCharacters:&keyEquiv.unicode() length:1]; } // return the cocoa modifier mask for the QKeySequence (currently only looks at the first one). NSUInteger keySequenceModifierMask(const QKeySequence &accel) { - NSUInteger ret = 0; - quint32 accel_key = accel[0]; - if ((accel_key & Qt::CTRL) == Qt::CTRL) - ret |= NSCommandKeyMask; - if ((accel_key & Qt::META) == Qt::META) - ret |= NSControlKeyMask; - if ((accel_key & Qt::ALT) == Qt::ALT) - ret |= NSAlternateKeyMask; - if ((accel_key & Qt::SHIFT) == Qt::SHIFT) - ret |= NSShiftKeyMask; - return ret; + return constructModifierMask(accel[0]); } void @@ -1728,9 +1728,14 @@ QMenuBarPrivate::macCreateMenuBar(QWidget *parent) { Q_Q(QMenuBar); static int checkEnv = -1; + // We call the isNativeMenuBar function here + // becasue that will make sure that local overrides + // are dealt with correctly. + bool qt_mac_no_native_menubar = !q->isNativeMenuBar(); if (qt_mac_no_native_menubar == false && checkEnv < 0) { checkEnv = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty(); - qt_mac_no_native_menubar = checkEnv; + QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, checkEnv); + qt_mac_no_native_menubar = !q->isNativeMenuBar(); } if (!qt_mac_no_native_menubar) { extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp @@ -1765,7 +1770,7 @@ void QMenuBarPrivate::macDestroyMenuBar() OSMenuRef QMenuBarPrivate::macMenu() { Q_Q(QMenuBar); - if (!mac_menubar) { + if (!q->isNativeMenuBar() || !mac_menubar) { return 0; } else if (!mac_menubar->menu) { mac_menubar->menu = qt_mac_create_menu(q); @@ -1886,9 +1891,6 @@ static void cancelAllMenuTracking() */ bool QMenuBar::macUpdateMenuBar() { - if (qt_mac_no_native_menubar) //nothing to be done.. - return true; - cancelAllMenuTracking(); QMenuBar *mb = 0; //find a menu bar @@ -1922,7 +1924,7 @@ bool QMenuBar::macUpdateMenuBar() mb = fallback; //now set it bool ret = false; - if (mb) { + if (mb && mb->isNativeMenuBar()) { #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; #endif @@ -1943,7 +1945,7 @@ bool QMenuBar::macUpdateMenuBar() qt_mac_current_menubar.qmenubar = mb; qt_mac_current_menubar.modal = QApplicationPrivate::modalState(); ret = true; - } else if (qt_mac_current_menubar.qmenubar) { + } else if (qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) { const bool modal = QApplicationPrivate::modalState(); if (modal != qt_mac_current_menubar.modal) { ret = true; diff --git a/src/gui/widgets/qmenu_p.h b/src/gui/widgets/qmenu_p.h index 74367c4..edfeee7 100644 --- a/src/gui/widgets/qmenu_p.h +++ b/src/gui/widgets/qmenu_p.h @@ -112,7 +112,7 @@ struct QMenuMergeItem typedef QList<QMenuMergeItem> QMenuMergeList; #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE struct QWceMenuAction { uint command; QPointer<QAction> action; @@ -132,7 +132,7 @@ public: #ifdef Q_WS_MAC ,mac_menu(0) #endif -#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR) +#if defined(Q_WS_WINCE) && !defined(QT_NO_MENUBAR) ,wce_menu(0) #endif #ifdef QT3_SUPPORT @@ -145,7 +145,7 @@ public: #ifdef Q_WS_MAC delete mac_menu; #endif -#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR) +#if defined(Q_WS_WINCE) && !defined(QT_NO_MENUBAR) delete wce_menu; #endif } @@ -293,7 +293,7 @@ public: bool emitHighlighted; #endif -#if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR) +#if defined(Q_WS_WINCE) && !defined(QT_NO_MENUBAR) struct QWceMenuPrivate { QList<QWceMenuAction*> actionItems; HMENU menuHandle; diff --git a/src/gui/widgets/qmenu_wince.cpp b/src/gui/widgets/qmenu_wince.cpp index 42a4e0b..2ce89f9 100644 --- a/src/gui/widgets/qmenu_wince.cpp +++ b/src/gui/widgets/qmenu_wince.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ //Native menubars are only supported for Windows Mobile not the standard SDK/generic WinCE -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #include "qmenu.h" #include "qt_windows.h" #include "qapplication.h" @@ -621,4 +621,4 @@ void QMenuBarPrivate::QWceMenuBarPrivate::rebuild() { QT_END_NAMESPACE #endif //QT_NO_MENUBAR -#endif //Q_OS_WINCE +#endif //Q_WS_WINCE diff --git a/src/gui/widgets/qmenubar.cpp b/src/gui/widgets/qmenubar.cpp index c63da64..cffc3d5 100644 --- a/src/gui/widgets/qmenubar.cpp +++ b/src/gui/widgets/qmenubar.cpp @@ -67,7 +67,7 @@ #include "qmenubar_p.h" #include "qdebug.h" -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE extern bool qt_wince_is_mobile(); //defined in qguifunctions_wce.cpp #endif @@ -194,7 +194,7 @@ void QMenuBarPrivate::updateGeometries() } #ifdef Q_WS_MAC - if(mac_menubar) {//nothing to see here folks, move along.. + if(q->isNativeMenuBar()) {//nothing to see here folks, move along.. itemsDirty = false; return; } @@ -725,7 +725,7 @@ void QMenuBarPrivate::init() if(mac_menubar) q->hide(); #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (qt_wince_is_mobile()) { wceCreateMenuBar(q->parentWidget()); if(wce_menubar) @@ -776,7 +776,7 @@ QMenuBar::~QMenuBar() Q_D(QMenuBar); d->macDestroyMenuBar(); #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE Q_D(QMenuBar); if (qt_wince_is_mobile()) d->wceDestroyMenuBar(); @@ -1025,14 +1025,8 @@ void QMenuBar::paintEvent(QPaintEvent *e) */ void QMenuBar::setVisible(bool visible) { -#ifdef Q_WS_MAC - Q_D(QMenuBar); - if(d->mac_menubar) - return; -#endif -#ifdef Q_OS_WINCE - Q_D(QMenuBar); - if(d->wce_menubar) +#if defined(Q_WS_MAC) || defined(Q_OS_WINCE) + if (isNativeMenuBar()) return; #endif QWidget::setVisible(visible); @@ -1272,24 +1266,21 @@ void QMenuBar::actionEvent(QActionEvent *e) { Q_D(QMenuBar); d->itemsDirty = true; +#if defined (Q_WS_MAC) || defined(Q_OS_WINCE) + if (isNativeMenuBar()) { #ifdef Q_WS_MAC - if(d->mac_menubar) { - if(e->type() == QEvent::ActionAdded) - d->mac_menubar->addAction(e->action(), d->mac_menubar->findAction(e->before())); - else if(e->type() == QEvent::ActionRemoved) - d->mac_menubar->removeAction(e->action()); - else if(e->type() == QEvent::ActionChanged) - d->mac_menubar->syncAction(e->action()); - } + QMenuBarPrivate::QMacMenuBarPrivate *nativeMenuBar = d->mac_menubar; +#else + QMenuBarPrivate::QWceMenuBarPrivate *nativeMenuBar = d->wce_menubar; #endif -#ifdef Q_OS_WINCE - if(d->wce_menubar) { + if (!nativeMenuBar) + return; if(e->type() == QEvent::ActionAdded) - d->wce_menubar->addAction(e->action(), d->wce_menubar->findAction(e->before())); + nativeMenuBar->addAction(e->action(), nativeMenuBar->findAction(e->before())); else if(e->type() == QEvent::ActionRemoved) - d->wce_menubar->removeAction(e->action()); + nativeMenuBar->removeAction(e->action()); else if(e->type() == QEvent::ActionChanged) - d->wce_menubar->syncAction(e->action()); + nativeMenuBar->syncAction(e->action()); } #endif if(e->type() == QEvent::ActionAdded) { @@ -1374,7 +1365,7 @@ void QMenuBarPrivate::handleReparent() macCreateMenuBar(newParent); #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (qt_wince_is_mobile() && wce_menubar) wce_menubar->rebuild(); #endif @@ -1612,10 +1603,8 @@ QRect QMenuBar::actionGeometry(QAction *act) const QSize QMenuBar::minimumSizeHint() const { Q_D(const QMenuBar); -#ifdef Q_WS_MAC - const bool as_gui_menubar = !d->mac_menubar; -#elif defined (Q_OS_WINCE) - const bool as_gui_menubar = !d->wce_menubar; +#if defined(Q_WS_MAC) || defined(Q_WS_WINCE) + const bool as_gui_menubar = !isNativeMenuBar(); #else const bool as_gui_menubar = true; #endif @@ -1672,14 +1661,13 @@ QSize QMenuBar::minimumSizeHint() const QSize QMenuBar::sizeHint() const { Q_D(const QMenuBar); -#ifdef Q_WS_MAC - const bool as_gui_menubar = !d->mac_menubar; -#elif defined (Q_OS_WINCE) - const bool as_gui_menubar = !d->wce_menubar; +#if defined(Q_WS_MAC) || defined(Q_WS_WINCE) + const bool as_gui_menubar = !isNativeMenuBar(); #else const bool as_gui_menubar = true; #endif + ensurePolished(); QSize ret(0, 0); const int hmargin = style()->pixelMetric(QStyle::PM_MenuBarHMargin, 0, this); @@ -1735,13 +1723,12 @@ QSize QMenuBar::sizeHint() const int QMenuBar::heightForWidth(int) const { Q_D(const QMenuBar); -#ifdef Q_WS_MAC - const bool as_gui_menubar = !d->mac_menubar; -#elif defined (Q_OS_WINCE) - const bool as_gui_menubar = !d->wce_menubar; +#if defined(Q_WS_MAC) || defined(Q_WS_WINCE) + const bool as_gui_menubar = !isNativeMenuBar(); #else const bool as_gui_menubar = true; #endif + int height = 0; const int vmargin = style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, this); int fw = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, this); @@ -1856,6 +1843,60 @@ QWidget *QMenuBar::cornerWidget(Qt::Corner corner) const } /*! + \property QMenuBar::nativeMenuBar + \brief Whether or not a menubar will be used as a native menubar on platforms that support it + \since 4.6 + + This property specifies whether or not the menubar should be used as a native menubar on platforms + that support it. The currently supported platforms are Mac OS X and Windows CE. On these platforms + if this property is true, the menubar is used in the native menubar and is not in the window of + its parent, if false the menubar remains in the window. On other platforms the value of this + attribute has no effect. + + The default is to follow whether the Qt::AA_DontUseNativeMenuBar attribute + is set for the application. Explicitly settings this property overrides + the presence (or abscence) of the attribute. +*/ + +void QMenuBar::setNativeMenuBar(bool nativeMenuBar) +{ + Q_D(QMenuBar); + if (d->nativeMenuBar == -1 || (nativeMenuBar != bool(d->nativeMenuBar))) { + d->nativeMenuBar = nativeMenuBar; +#ifdef Q_WS_MAC + if (!d->nativeMenuBar) { + extern void qt_mac_clear_menubar(); + qt_mac_clear_menubar(); + d->macDestroyMenuBar(); + const QList<QAction *> &menubarActions = actions(); + for (int i = 0; i < menubarActions.size(); ++i) { + const QAction *action = menubarActions.at(i); + if (QMenu *menu = action->menu()) { + delete menu->d_func()->mac_menu; + menu->d_func()->mac_menu = 0; + } + } + } else { + d->macCreateMenuBar(parentWidget()); + } + macUpdateMenuBar(); + updateGeometry(); + setVisible(false); + setVisible(true); +#endif + } +} + +bool QMenuBar::isNativeMenuBar() const +{ + Q_D(const QMenuBar); + if (d->nativeMenuBar == -1) { + return !QApplication::instance()->testAttribute(Qt::AA_DontUseNativeMenuBar); + } + return d->nativeMenuBar; +} + +/*! \since 4.4 Sets the default action to \a act. @@ -1869,13 +1910,13 @@ QWidget *QMenuBar::cornerWidget(Qt::Corner corner) const \sa defaultAction() */ -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE void QMenuBar::setDefaultAction(QAction *act) { Q_D(QMenuBar); if (d->defaultAction == act) return; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (qt_wince_is_mobile()) if (d->defaultAction) { disconnect(d->defaultAction, SIGNAL(changed()), this, SLOT(_q_updateDefaultAction())); @@ -1883,7 +1924,7 @@ void QMenuBar::setDefaultAction(QAction *act) } #endif d->defaultAction = act; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE if (qt_wince_is_mobile()) if (d->defaultAction) { connect(d->defaultAction, SIGNAL(changed()), this, SLOT(_q_updateDefaultAction())); diff --git a/src/gui/widgets/qmenubar.h b/src/gui/widgets/qmenubar.h index 42f0c0c..58a03ff 100644 --- a/src/gui/widgets/qmenubar.h +++ b/src/gui/widgets/qmenubar.h @@ -64,6 +64,7 @@ class Q_GUI_EXPORT QMenuBar : public QWidget Q_OBJECT Q_PROPERTY(bool defaultUp READ isDefaultUp WRITE setDefaultUp) + Q_PROPERTY(bool nativeMenuBar READ isNativeMenuBar WRITE setNativeMenuBar) public: explicit QMenuBar(QWidget *parent = 0); @@ -110,7 +111,7 @@ public: static bool macUpdateMenuBar(); #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE void setDefaultAction(QAction *); QAction *defaultAction() const; @@ -118,6 +119,9 @@ public: static void wceRefresh(); #endif + bool isNativeMenuBar() const; + void setNativeMenuBar(bool nativeMenuBar); + public Q_SLOTS: virtual void setVisible(bool visible); @@ -339,7 +343,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_internalShortcutActivated(int)) Q_PRIVATE_SLOT(d_func(), void _q_updateLayout()) -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE Q_PRIVATE_SLOT(d_func(), void _q_updateDefaultAction()) #endif diff --git a/src/gui/widgets/qmenubar_p.h b/src/gui/widgets/qmenubar_p.h index 223346b..5dab310 100644 --- a/src/gui/widgets/qmenubar_p.h +++ b/src/gui/widgets/qmenubar_p.h @@ -57,7 +57,7 @@ #include "QtGui/qstyleoption.h" #include <private/qmenu_p.h> // Mac needs what in this file! -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE #include "qguifunctions_wince.h" #endif @@ -70,12 +70,13 @@ class QMenuBarPrivate : public QWidgetPrivate Q_DECLARE_PUBLIC(QMenuBar) public: QMenuBarPrivate() : itemsDirty(0), itemsWidth(0), itemsStart(-1), currentAction(0), mouseDown(0), - closePopupMode(0), defaultPopDown(1), popupState(0), keyboardState(0), altPressed(0) + closePopupMode(0), defaultPopDown(1), popupState(0), keyboardState(0), altPressed(0), + nativeMenuBar(-1) #ifdef Q_WS_MAC , mac_menubar(0) #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE , wce_menubar(0), wceClassicMenu(false) #endif { } @@ -84,7 +85,7 @@ public: #ifdef Q_WS_MAC delete mac_menubar; #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE delete wce_menubar; #endif } @@ -119,6 +120,8 @@ public: uint keyboardState : 1, altPressed : 1; QPointer<QWidget> keyboardFocusWidget; + + int nativeMenuBar : 3; // Only has values -1, 0, and 1 //firing of events void activateAction(QAction *, QAction::ActionEvent); @@ -127,7 +130,7 @@ public: void _q_internalShortcutActivated(int); void _q_updateLayout(); -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE void _q_updateDefaultAction(); #endif @@ -181,7 +184,7 @@ public: void macDestroyMenuBar(); OSMenuRef macMenu(); #endif -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE void wceCreateMenuBar(QWidget *); void wceDestroyMenuBar(); struct QWceMenuBarPrivate { diff --git a/src/gui/widgets/qplaintextedit.cpp b/src/gui/widgets/qplaintextedit.cpp index e563fa1..f317742 100644 --- a/src/gui/widgets/qplaintextedit.cpp +++ b/src/gui/widgets/qplaintextedit.cpp @@ -370,11 +370,16 @@ void QPlainTextDocumentLayout::layoutBlock(const QTextBlock &block) extraMargin += fm.width(QChar(0x21B5)); } tl->beginLayout(); + qreal availableWidth = d->width; + if (availableWidth <= 0) { + availableWidth = INT_MAX; // similar to text edit with pageSize.width == 0 + } + availableWidth -= 2*margin + extraMargin; while (1) { QTextLine line = tl->createLine(); if (!line.isValid()) break; - line.setLineWidth(d->width - 2*margin - extraMargin); + line.setLineWidth(availableWidth); height += leading; line.setPosition(QPointF(margin, height)); @@ -555,7 +560,8 @@ QRectF QPlainTextEditControl::blockBoundingRect(const QTextBlock &block) const { if (!currentBlock.isValid()) return QRectF(); Q_ASSERT(currentBlock.blockNumber() == currentBlockNumber); - QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout()); + QTextDocument *doc = document(); + QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout()); Q_ASSERT(documentLayout); QPointF offset; @@ -566,13 +572,22 @@ QRectF QPlainTextEditControl::blockBoundingRect(const QTextBlock &block) const { offset.ry() += r.height(); currentBlock = currentBlock.next(); ++currentBlockNumber; + if (!currentBlock.isVisible()) { + currentBlock = doc->findBlockByLineNumber(currentBlock.firstLineNumber()); + currentBlockNumber = currentBlock.blockNumber(); + } r = documentLayout->blockBoundingRect(currentBlock); } while (currentBlockNumber > blockNumber && offset.y() >= -textEdit->viewport()->height()) { currentBlock = currentBlock.previous(); + --currentBlockNumber; + while (!currentBlock.isVisible()) { + currentBlock = currentBlock.previous(); + --currentBlockNumber; + } if (!currentBlock.isValid()) break; - --currentBlockNumber; + r = documentLayout->blockBoundingRect(currentBlock); offset.ry() -= r.height(); } diff --git a/src/gui/widgets/qprintpreviewwidget.cpp b/src/gui/widgets/qprintpreviewwidget.cpp index 16334b8..d4e5122 100644 --- a/src/gui/widgets/qprintpreviewwidget.cpp +++ b/src/gui/widgets/qprintpreviewwidget.cpp @@ -98,29 +98,8 @@ void PageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QRectF paperRect(0,0, paperSize.width(), paperSize.height()); - painter->setClipRect(paperRect & option->exposedRect); - painter->fillRect(paperRect, Qt::white); - if (!pagePicture) - return; - painter->drawPicture(pageRect.topLeft(), *pagePicture); - - // Effect: make anything drawn in the margins look washed out. - QPainterPath path; - path.addRect(paperRect); - path.addRect(pageRect); - painter->setPen(QPen(Qt::NoPen)); - painter->setBrush(QColor(255, 255, 255, 180)); - painter->drawPath(path); - - painter->setClipRect(option->exposedRect); -#if 0 - // Draw frame around paper. - painter->setPen(QPen(Qt::black, 0)); - painter->setBrush(Qt::NoBrush); - painter->drawRect(paperRect); -#endif - // Draw shadow + painter->setClipRect(option->exposedRect); qreal shWidth = paperRect.width()/100; QRectF rshadow(paperRect.topRight() + QPointF(0, shWidth), paperRect.bottomRight() + QPointF(shWidth, 0)); @@ -141,6 +120,27 @@ void PageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, cgrad.setColorAt(1.0, QColor(0,0,0,0)); painter->fillRect(cshadow, QBrush(cgrad)); + painter->setClipRect(paperRect & option->exposedRect); + painter->fillRect(paperRect, Qt::white); + if (!pagePicture) + return; + painter->drawPicture(pageRect.topLeft(), *pagePicture); + + // Effect: make anything drawn in the margins look washed out. + QPainterPath path; + path.addRect(paperRect); + path.addRect(pageRect); + painter->setPen(QPen(Qt::NoPen)); + painter->setBrush(QColor(255, 255, 255, 180)); + painter->drawPath(path); + +#if 0 + // Draw frame around paper. + painter->setPen(QPen(Qt::black, 0)); + painter->setBrush(Qt::NoBrush); + painter->drawRect(paperRect); +#endif + // todo: drawtext "Page N" below paper } diff --git a/src/gui/widgets/qslider.cpp b/src/gui/widgets/qslider.cpp index 32b9021..5b9c8a4 100644 --- a/src/gui/widgets/qslider.cpp +++ b/src/gui/widgets/qslider.cpp @@ -62,7 +62,6 @@ public: int tickInterval; QSlider::TickPosition tickPosition; int clickOffset; - int snapBackPosition; void init(); void resetLayoutItemMargins(); int pixelPosToRangeValue(int pos) const; @@ -493,7 +492,6 @@ void QSlider::mousePressEvent(QMouseEvent *ev) setRepeatAction(SliderNoAction); QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); d->clickOffset = d->pick(ev->pos() - sr.topLeft()); - d->snapBackPosition = d->position; update(sr); setSliderDown(true); } @@ -513,14 +511,6 @@ void QSlider::mouseMoveEvent(QMouseEvent *ev) int newPosition = d->pixelPosToRangeValue(d->pick(ev->pos()) - d->clickOffset); QStyleOptionSlider opt; initStyleOption(&opt); - int m = style()->pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this); - if (m >= 0) { - QRect r = rect(); - r.adjust(-m, -m, m, m); - if (!r.contains(ev->pos())) { - newPosition = d->snapBackPosition; - } - } setSliderPosition(newPosition); } diff --git a/src/gui/widgets/qspinbox.cpp b/src/gui/widgets/qspinbox.cpp index c691eaf..f12946c 100644 --- a/src/gui/widgets/qspinbox.cpp +++ b/src/gui/widgets/qspinbox.cpp @@ -1030,7 +1030,7 @@ QVariant QSpinBoxPrivate::validateAndInterpret(QString &input, int &pos, { if (cachedText == input && !input.isEmpty()) { state = cachedState; - QSBDEBUG() << "cachedText was" << "'" << cachedText << "'" << "state was " + QSBDEBUG() << "cachedText was '" << cachedText << "' state was " << state << " and value was " << cachedValue; return cachedValue; @@ -1048,7 +1048,7 @@ QVariant QSpinBoxPrivate::validateAndInterpret(QString &input, int &pos, || (min >= 0 && copy == QLatin1String("+")))) { state = QValidator::Intermediate; QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num; - } else if (copy.startsWith(QLatin1String("-")) && min >= 0) { + } else if (copy.startsWith(QLatin1Char('-')) && min >= 0) { state = QValidator::Invalid; // special-case -0 will be interpreted as 0 and thus not be invalid with a range from 0-100 } else { bool ok = false; @@ -1254,9 +1254,7 @@ QVariant QDoubleSpinBoxPrivate::valueFromText(const QString &f) const double QDoubleSpinBoxPrivate::round(double value) const { - Q_Q(const QDoubleSpinBox); - const QString strDbl = q->locale().toString(value, 'f', decimals); - return q->locale().toDouble(strDbl); + return QString::number(value, 'f', decimals).toDouble(); } @@ -1271,7 +1269,7 @@ QVariant QDoubleSpinBoxPrivate::validateAndInterpret(QString &input, int &pos, { if (cachedText == input && !input.isEmpty()) { state = cachedState; - QSBDEBUG() << "cachedText was" << "'" << cachedText << "'" << "state was " + QSBDEBUG() << "cachedText was '" << cachedText << "' state was " << state << " and value was " << cachedValue; return cachedValue; } diff --git a/src/gui/widgets/qsplitter.cpp b/src/gui/widgets/qsplitter.cpp index 45e838f..400d78a 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/qtabbar.cpp b/src/gui/widgets/qtabbar.cpp index 785d772..bd8a1b0 100644 --- a/src/gui/widgets/qtabbar.cpp +++ b/src/gui/widgets/qtabbar.cpp @@ -176,12 +176,11 @@ void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner)) option->cornerWidgets |= QStyleOptionTab::RightCornerWidget; } +#endif QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option, this); - option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(), Qt::TextShowMnemonic); -#endif } /*! @@ -1944,8 +1943,10 @@ void QTabBar::changeEvent(QEvent *event) if (event->type() == QEvent::StyleChange) { d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, 0, this)); d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, 0, this); + d->refresh(); + } else if (event->type() == QEvent::FontChange) { + d->refresh(); } - d->refresh(); QWidget::changeEvent(event); } diff --git a/src/gui/widgets/qtoolbar.cpp b/src/gui/widgets/qtoolbar.cpp index 1babb6d..d765794 100644 --- a/src/gui/widgets/qtoolbar.cpp +++ b/src/gui/widgets/qtoolbar.cpp @@ -274,9 +274,11 @@ void QToolBarPrivate::endDrag() bool QToolBarPrivate::mousePressEvent(QMouseEvent *event) { - if (layout->handleRect().contains(event->pos()) == false) { + Q_Q(QToolBar); + QStyleOptionToolBar opt; + q->initStyleOption(&opt); + if (q->style()->subElementRect(QStyle::SE_ToolBarHandle, &opt, q).contains(event->pos()) == false) { #ifdef Q_WS_MAC - Q_Q(QToolBar); // When using the unified toolbar on Mac OS X the user can can click and // drag between toolbar contents to move the window. Make this work by // implementing the standard mouse-dragging code and then call @@ -1041,7 +1043,7 @@ void QToolBar::paintEvent(QPaintEvent *) style->drawControl(QStyle::CE_ToolBar, &opt, &p, this); } - opt.rect = d->layout->handleRect(); + opt.rect = style->subElementRect(QStyle::SE_ToolBarHandle, &opt, this); if (opt.rect.isValid()) style->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this); } @@ -1142,7 +1144,9 @@ bool QToolBar::event(QEvent *event) case QEvent::HoverMove: { #ifndef QT_NO_CURSOR QHoverEvent *e = static_cast<QHoverEvent*>(event); - if (d->layout->handleRect().contains(e->pos())) + QStyleOptionToolBar opt; + initStyleOption(&opt); + if (style()->subElementRect(QStyle::SE_ToolBarHandle, &opt, this).contains(e->pos())) setCursor(Qt::SizeAllCursor); else unsetCursor(); @@ -1153,7 +1157,7 @@ bool QToolBar::event(QEvent *event) if (d->mouseMoveEvent(static_cast<QMouseEvent*>(event))) return true; break; -#ifdef Q_OS_WINCE +#ifdef Q_WS_WINCE case QEvent::ContextMenu: { QContextMenuEvent* contextMenuEvent = static_cast<QContextMenuEvent*>(event); diff --git a/src/gui/widgets/qtoolbarlayout.cpp b/src/gui/widgets/qtoolbarlayout.cpp index 7771f46..0bfa493 100644 --- a/src/gui/widgets/qtoolbarlayout.cpp +++ b/src/gui/widgets/qtoolbarlayout.cpp @@ -334,7 +334,7 @@ void QToolBarLayout::updateGeomArray() const if (QMainWindow *mw = qobject_cast<QMainWindow *>(parentWidget()->parentWidget())) { if (mw->unifiedTitleAndToolBarOnMac() && mw->toolBarArea(static_cast<QToolBar *>(parentWidget())) == Qt::TopToolBarArea) { - if (that->expandFlag) { + if (expandFlag) { tb->setMaximumSize(0xFFFFFF, 0xFFFFFF); } else { tb->setMaximumSize(hint); @@ -360,23 +360,11 @@ void QToolBarLayout::setGeometry(const QRect &rect) QStyle *style = tb->style(); QStyleOptionToolBar opt; tb->initStyleOption(&opt); - const int handleExtent = movable() - ? style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb) : 0; const int margin = this->margin(); const int extensionExtent = style->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, tb); Qt::Orientation o = tb->orientation(); QLayout::setGeometry(rect); - if (movable()) { - if (o == Qt::Horizontal) { - handRect = QRect(margin, margin, handleExtent, rect.height() - 2*margin); - handRect = QStyle::visualRect(parentWidget()->layoutDirection(), rect, handRect); - } else { - handRect = QRect(margin, margin, rect.width() - 2*margin, handleExtent); - } - } else { - handRect = QRect(); - } bool ranOutOfSpace = false; if (!animating) @@ -742,11 +730,6 @@ QToolBarItem *QToolBarLayout::createItem(QAction *action) return result; } -QRect QToolBarLayout::handleRect() const -{ - return handRect; -} - QT_END_NAMESPACE #endif // QT_NO_TOOLBAR diff --git a/src/gui/widgets/qtoolbarlayout_p.h b/src/gui/widgets/qtoolbarlayout_p.h index 2eca773..37755b1 100644 --- a/src/gui/widgets/qtoolbarlayout_p.h +++ b/src/gui/widgets/qtoolbarlayout_p.h @@ -65,7 +65,7 @@ class QAction; class QToolBarExtension; class QMenu; -class Q_GUI_EXPORT QToolBarItem : public QWidgetItem +class QToolBarItem : public QWidgetItem { public: QToolBarItem(QWidget *widget); @@ -75,7 +75,7 @@ public: bool customWidget; }; -class Q_GUI_EXPORT QToolBarLayout : public QLayout +class QToolBarLayout : public QLayout { Q_OBJECT @@ -100,8 +100,6 @@ public: int indexOf(QAction *action) const; int indexOf(QWidget *widget) const { return QLayout::indexOf(widget); } - QRect handleRect() const; - bool layoutActions(const QSize &size); QSize expandedSize(const QSize &size) const; bool expanded, animating; 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/qhttp.cpp b/src/network/access/qhttp.cpp index 30befb3..49ce5a3 100644 --- a/src/network/access/qhttp.cpp +++ b/src/network/access/qhttp.cpp @@ -1446,7 +1446,7 @@ QString QHttpRequestHeader::toString() const that indicates if the request finished with an error. To make an HTTP request you must set up suitable HTTP headers. The - following example demonstrates, how to request the main HTML page + following example demonstrates how to request the main HTML page from the Trolltech home page (i.e., the URL \c http://qtsoftware.com/index.html): diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index ae518df..43fbb16 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qhttpnetworkconnection_p.h" +#include "private/qnoncontiguousbytedevice_p.h" #include <private/qnetworkrequest_p.h> #include <private/qobject_p.h> #include <private/qauthenticator_p.h> @@ -71,6 +72,7 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host #ifndef QT_NO_NETWORKPROXY , networkProxy(QNetworkProxy::NoProxy) #endif + { } @@ -205,12 +207,19 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) // add missing fields for the request QByteArray value; // check if Content-Length is provided - QIODevice *data = request.data(); - if (data && request.contentLength() == -1) { - if (!data->isSequential()) - request.setContentLength(data->size()); - else - bufferData(messagePair); // ### or do chunked upload + QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); + if (uploadByteDevice) { + if (request.contentLength() != -1 && uploadByteDevice->size() != -1) { + // both values known, take the smaller one. + request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength())); + } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) { + // content length not supplied by user, but the upload device knows it + request.setContentLength(uploadByteDevice->size()); + } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) { + // everything OK, the user supplied us the contentLength + } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) { + qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given"); + } } // set the Connection/Proxy-Connection: Keep-Alive headers #ifndef QT_NO_NETWORKPROXY @@ -361,18 +370,12 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) false); #endif socket->write(header); - QIODevice *data = channels[i].request.d->data; - QHttpNetworkReply *reply = channels[i].reply; - if (reply && reply->d_func()->requestDataBuffer.size()) - data = &channels[i].reply->d_func()->requestDataBuffer; - if (data && (data->isOpen() || data->open(QIODevice::ReadOnly))) { - if (data->isSequential()) { - channels[i].bytesTotal = -1; - QObject::connect(data, SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadNoBuffer())); - QObject::connect(data, SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadNoBuffer())); - } else { - channels[i].bytesTotal = data->size(); - } + QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); + if (uploadByteDevice) { + // connect the signals so this function gets called again + QObject::connect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead())); + + channels[i].bytesTotal = channels[i].request.contentLength(); } else { channels[i].state = WaitingState; break; @@ -380,30 +383,81 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) // write the initial chunk together with the headers // fall through } - case WritingState: { // write the data - QIODevice *data = channels[i].request.d->data; - if (channels[i].reply->d_func()->requestDataBuffer.size()) - data = &channels[i].reply->d_func()->requestDataBuffer; - if (!data || channels[i].bytesTotal == channels[i].written) { + case WritingState: + { + // write the data + QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); + if (!uploadByteDevice || channels[i].bytesTotal == channels[i].written) { + if (uploadByteDevice) + emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal); channels[i].state = WaitingState; // now wait for response + sendRequest(socket); break; } - QByteArray chunk; - chunk.resize(ChunkSize); - qint64 readSize = data->read(chunk.data(), ChunkSize); - if (readSize == -1) { - // source has reached EOF - channels[i].state = WaitingState; // now wait for response - } else if (readSize > 0) { - // source gave us something useful - channels[i].written += socket->write(chunk.data(), readSize); - if (channels[i].reply) - emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal); + // only feed the QTcpSocket buffer when there is less than 32 kB in it + const qint64 socketBufferFill = 32*1024; + const qint64 socketWriteMaxSize = 16*1024; + + +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + while ((sslSocket->encryptedBytesToWrite() + sslSocket->bytesToWrite()) <= socketBufferFill + && channels[i].bytesTotal != channels[i].written) +#else + while (socket->bytesToWrite() <= socketBufferFill + && channels[i].bytesTotal != channels[i].written) +#endif + { + // get pointer to upload data + qint64 currentReadSize; + qint64 desiredReadSize = qMin(socketWriteMaxSize, channels[i].bytesTotal - channels[i].written); + const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize); + + if (currentReadSize == -1) { + // premature eof happened + emitReplyError(socket, channels[i].reply, QNetworkReply::UnknownNetworkError); + return false; + break; + } else if (readPointer == 0 || currentReadSize == 0) { + // nothing to read currently, break the loop + break; + } else { + qint64 currentWriteSize = socket->write(readPointer, currentReadSize); + if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { + // socket broke down + emitReplyError(socket, channels[i].reply, QNetworkReply::UnknownNetworkError); + return false; + } else { + channels[i].written += currentWriteSize; + uploadByteDevice->advanceReadPointer(currentWriteSize); + + emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal); + + if (channels[i].written == channels[i].bytesTotal) { + // make sure this function is called once again + channels[i].state = WaitingState; + sendRequest(socket); + break; + } + } + } } break; } + case WaitingState: + { + QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); + if (uploadByteDevice) { + QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead())); + } + // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called + // this is needed if the sends an reply before we have finished sending the request. In that + // case receiveReply had been called before but ignored the server reply + receiveReply(socket, channels[i].reply); + break; + } case ReadingState: case Wait4AuthState: // ignore _q_bytesWritten in these states @@ -479,6 +533,9 @@ bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetwork // make sure that the reply is valid if (channels[i].reply != reply) return true; + // emit dataReadProgress signal (signal is currently not connected + // to the rest of QNAM) since readProgress of the + // QNonContiguousByteDevice is used emit reply->dataReadProgress(reply->d_func()->totalProgress, 0); // make sure that the reply is valid if (channels[i].reply != reply) @@ -579,6 +636,9 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN // make sure that the reply is valid if (channels[i].reply != reply) return; + // emit dataReadProgress signal (signal is currently not connected + // to the rest of QNAM) since readProgress of the + // QNonContiguousByteDevice is used emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); // make sure that the reply is valid if (channels[i].reply != reply) @@ -645,8 +705,24 @@ void QHttpNetworkConnectionPrivate::handleStatus(QAbstractSocket *socket, QHttpN case 407: if (handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) { if (resend) { + int i = indexOf(socket); + + QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); + if (uploadByteDevice) { + if (uploadByteDevice->reset()) { + channels[i].written = 0; + } else { + emitReplyError(socket, reply, QNetworkReply::ContentReSendError); + break; + } + } + eraseData(reply); - sendRequest(socket); + + // also use async _q_startNextRequest so we dont break with closed + // proxy or server connections.. + channels[i].resendCurrent = true; + QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); } } else { int i = indexOf(socket); @@ -990,6 +1066,7 @@ void QHttpNetworkConnectionPrivate::_q_bytesWritten(qint64 bytes) QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); if (!socket) return; // ### error + // bytes have been written to the socket. write even more of them :) if (isSocketWriting(socket)) sendRequest(socket); // otherwise we do nothing @@ -1148,80 +1225,21 @@ void QHttpNetworkConnectionPrivate::_q_proxyAuthenticationRequired(const QNetwor } #endif -void QHttpNetworkConnectionPrivate::_q_dataReadyReadNoBuffer() +void QHttpNetworkConnectionPrivate::_q_uploadDataReadyRead() { Q_Q(QHttpNetworkConnection); - // data emitted either readyRead() + // upload data emitted readyRead() // find out which channel it is for - QIODevice *sender = qobject_cast<QIODevice *>(q->sender()); + QObject *sender = q->sender(); - // won't match anything if the qobject_cast above failed for (int i = 0; i < channelCount; ++i) { - if (sender == channels[i].request.data()) { + if (sender == channels[i].request.uploadByteDevice()) { sendRequest(channels[i].socket); break; } } } -void QHttpNetworkConnectionPrivate::_q_dataReadyReadBuffer() -{ - Q_Q(QHttpNetworkConnection); - QIODevice *sender = qobject_cast<QIODevice *>(q->sender()); - HttpMessagePair *thePair = 0; - for (int i = 0; !thePair && i < lowPriorityQueue.size(); ++i) - if (lowPriorityQueue.at(i).first.data() == sender) - thePair = &lowPriorityQueue[i]; - - for (int i = 0; !thePair && i < highPriorityQueue.size(); ++i) - if (highPriorityQueue.at(i).first.data() == sender) - thePair = &highPriorityQueue[i]; - - if (thePair) { - bufferData(*thePair); - - // are we finished buffering? - if (!thePair->second->d_func()->requestIsBuffering) - _q_startNextRequest(); - } -} - -void QHttpNetworkConnectionPrivate::bufferData(HttpMessagePair &messagePair) -{ - Q_Q(QHttpNetworkConnection); - QHttpNetworkRequest &request = messagePair.first; - QHttpNetworkReply *reply = messagePair.second; - Q_ASSERT(request.data()); - if (!reply->d_func()->requestIsBuffering) { // first time - QObject::connect(request.data(), SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadBuffer())); - QObject::connect(request.data(), SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadBuffer())); - reply->d_func()->requestIsBuffering = true; - reply->d_func()->requestDataBuffer.open(QIODevice::ReadWrite); - } - - // always try to read at least one byte - // ### FIXME! use a QRingBuffer - qint64 bytesToRead = qMax<qint64>(1, request.data()->bytesAvailable()); - QByteArray newData; - newData.resize(bytesToRead); - qint64 bytesActuallyRead = request.data()->read(newData.data(), bytesToRead); - - if (bytesActuallyRead > 0) { - // we read something - newData.chop(bytesToRead - bytesActuallyRead); - reply->d_func()->requestDataBuffer.write(newData); - } else if (bytesActuallyRead == -1) { // last time - QObject::disconnect(request.data(), SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadBuffer())); - QObject::disconnect(request.data(), SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadBuffer())); - - request.setContentLength(reply->d_func()->requestDataBuffer.size()); - reply->d_func()->requestDataBuffer.seek(0); - reply->d_func()->requestIsBuffering = false; - } -} - -// QHttpNetworkConnection - QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent) : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent) { diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 09bd459..9b127dd 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -146,8 +146,7 @@ private: #ifndef QT_NO_NETWORKPROXY Q_PRIVATE_SLOT(d_func(), void _q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)) #endif - Q_PRIVATE_SLOT(d_func(), void _q_dataReadyReadBuffer()) - Q_PRIVATE_SLOT(d_func(), void _q_dataReadyReadNoBuffer()) + Q_PRIVATE_SLOT(d_func(), void _q_uploadDataReadyRead()) #ifndef QT_NO_OPENSSL Q_PRIVATE_SLOT(d_func(), void _q_encrypted()) @@ -209,8 +208,8 @@ public: #ifndef QT_NO_NETWORKPROXY void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy #endif - void _q_dataReadyReadNoBuffer(); - void _q_dataReadyReadBuffer(); + + void _q_uploadDataReadyRead(); void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request); bool ensureConnection(QAbstractSocket *socket); @@ -219,7 +218,6 @@ public: #ifndef QT_NO_COMPRESS bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete); #endif - void bufferData(HttpMessagePair &request); void removeReply(QHttpNetworkReply *reply); QString hostName; diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index 69e0a4c..310994c 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -544,13 +544,13 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *ou { qint64 bytes = 0; if (isChunked()) { - bytes += transferChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6) + bytes += readReplyBodyChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6) } else if (bodyLength > 0) { // we have a Content-Length - bytes += transferRaw(socket, out, bodyLength - contentRead); + bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead); if (contentRead + bytes == bodyLength) state = AllDoneState; } else { - bytes += transferRaw(socket, out, socket->bytesAvailable()); + bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable()); } if (state == AllDoneState) socket->readAll(); // Read the rest to clean (CRLF) @@ -558,7 +558,7 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *ou return bytes; } -qint64 QHttpNetworkReplyPrivate::transferRaw(QIODevice *in, QIODevice *out, qint64 size) +qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QIODevice *out, qint64 size) { qint64 bytes = 0; Q_ASSERT(in); @@ -584,7 +584,7 @@ qint64 QHttpNetworkReplyPrivate::transferRaw(QIODevice *in, QIODevice *out, qint } -qint64 QHttpNetworkReplyPrivate::transferChunked(QIODevice *in, QIODevice *out) +qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QIODevice *out) { qint64 bytes = 0; while (in->bytesAvailable()) { // while we can read from input diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index cb4d34f..cc5cce8 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -139,7 +139,7 @@ Q_SIGNALS: void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString()); void headerChanged(); void dataReadProgress(int done, int total); - void dataSendProgress(int done, int total); + void dataSendProgress(qint64 done, qint64 total); private: Q_DECLARE_PRIVATE(QHttpNetworkReply) @@ -162,8 +162,8 @@ public: QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const; void clear(); - qint64 transferRaw(QIODevice *in, QIODevice *out, qint64 size); - qint64 transferChunked(QIODevice *in, QIODevice *out); + qint64 readReplyBodyRaw(QIODevice *in, QIODevice *out, qint64 size); + qint64 readReplyBodyChunked(QIODevice *in, QIODevice *out); qint64 getChunkSize(QIODevice *in, qint64 *chunkSize); qint64 bytesAvailable() const; @@ -206,7 +206,6 @@ public: QByteArray responseData; // uncompressed body QByteArray compressedData; // compressed body (temporary) - QBuffer requestDataBuffer; bool requestIsBuffering; bool requestIsPrepared; }; diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index 420cb69..7df68fc 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -40,12 +40,13 @@ ****************************************************************************/ #include "qhttpnetworkrequest_p.h" +#include "private/qnoncontiguousbytedevice_p.h" QT_BEGIN_NAMESPACE QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op, QHttpNetworkRequest::Priority pri, const QUrl &newUrl) - : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), data(0), + : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0), autoDecompress(false) { } @@ -55,7 +56,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest { operation = other.operation; priority = other.priority; - data = other.data; + uploadByteDevice = other.uploadByteDevice; autoDecompress = other.autoDecompress; } @@ -67,7 +68,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot { return QHttpNetworkHeaderPrivate::operator==(other) && (operation == other.operation) - && (data == other.data); + && (uploadByteDevice == other.uploadByteDevice); } QByteArray QHttpNetworkRequestPrivate::methodName() const @@ -109,7 +110,7 @@ QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const QUrl::FormattingOptions format(QUrl::RemoveFragment); // for POST, query data is send as content - if (operation == QHttpNetworkRequest::Post && !data) + if (operation == QHttpNetworkRequest::Post && !uploadByteDevice) format |= QUrl::RemoveQuery; // for requests through proxy, the Request-URI contains full url if (throughProxy) @@ -140,7 +141,7 @@ QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request // add content type, if not set in the request if (request.headerField("content-type").isEmpty()) ba += "Content-Type: application/x-www-form-urlencoded\r\n"; - if (!request.d->data && request.d->url.hasQuery()) { + if (!request.d->uploadByteDevice && request.d->url.hasQuery()) { QByteArray query = request.d->url.encodedQuery(); ba += "Content-Length: "+ QByteArray::number(query.size()) + "\r\n"; ba += "\r\n"; @@ -236,14 +237,14 @@ void QHttpNetworkRequest::setPriority(Priority priority) d->priority = priority; } -QIODevice *QHttpNetworkRequest::data() const +void QHttpNetworkRequest::setUploadByteDevice(QNonContiguousByteDevice *bd) { - return d->data; + d->uploadByteDevice = bd; } -void QHttpNetworkRequest::setData(QIODevice *data) +QNonContiguousByteDevice* QHttpNetworkRequest::uploadByteDevice() const { - d->data = data; + return d->uploadByteDevice; } int QHttpNetworkRequest::majorVersion() const diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index d18e116..ed4325a 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -58,6 +58,8 @@ QT_BEGIN_NAMESPACE +class QNonContiguousByteDevice; + class QHttpNetworkRequestPrivate; class Q_AUTOTEST_EXPORT QHttpNetworkRequest: public QHttpNetworkHeader { @@ -104,8 +106,8 @@ public: Priority priority() const; void setPriority(Priority priority); - QIODevice *data() const; - void setData(QIODevice *data); + void setUploadByteDevice(QNonContiguousByteDevice *bd); + QNonContiguousByteDevice* uploadByteDevice() const; private: QSharedDataPointer<QHttpNetworkRequestPrivate> d; @@ -113,7 +115,6 @@ private: friend class QHttpNetworkConnectionPrivate; }; - class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate { public: @@ -129,7 +130,7 @@ public: QHttpNetworkRequest::Operation operation; QHttpNetworkRequest::Priority priority; - mutable QIODevice *data; + mutable QNonContiguousByteDevice* uploadByteDevice; bool autoDecompress; }; diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index df468b8..b9d1b85 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -50,6 +50,8 @@ #include "qnetworkaccesscachebackend_p.h" #include "qabstractnetworkcache.h" +#include "private/qnoncontiguousbytedevice_p.h" + QT_BEGIN_NAMESPACE static bool factoryDataShutdown = false; @@ -109,17 +111,43 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM return 0; } -QNetworkAccessBackend::QNetworkAccessBackend() + +QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice() { + QNonContiguousByteDevice* device = 0; + + if (reply->outgoingDataBuffer) + device = QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer); + else + device = QNonContiguousByteDeviceFactory::create(reply->outgoingData); + + bool bufferDisallowed = + reply->request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute, + QVariant(false)) == QVariant(true); + if (bufferDisallowed) + device->disableReset(); + + // make sure we delete this later + device->setParent(this); + + connect(device, SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64))); + + return device; } -QNetworkAccessBackend::~QNetworkAccessBackend() +// need to have this function since the reply is a private member variable +// and the special backends need to access this. +void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal) { + reply->emitUploadProgress(bytesSent, bytesTotal); } -void QNetworkAccessBackend::upstreamReadyRead() +QNetworkAccessBackend::QNetworkAccessBackend() +{ +} + +QNetworkAccessBackend::~QNetworkAccessBackend() { - // do nothing } void QNetworkAccessBackend::downstreamReadyWrite() @@ -184,23 +212,6 @@ bool QNetworkAccessBackend::isCachingEnabled() const return reply->isCachingEnabled(); } -qint64 QNetworkAccessBackend::upstreamBytesAvailable() const -{ - return reply->writeBuffer.size(); -} - -void QNetworkAccessBackend::upstreamBytesConsumed(qint64 count) -{ - // remove count bytes from the write buffer - reply->consume(count); -} - -QByteArray QNetworkAccessBackend::readUpstream() -{ - // ### this is expensive. Consider making QRingBuffer::peekAll keep the buffer it returns - return reply->writeBuffer.peek(upstreamBytesAvailable()); -} - qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const { return reply->nextDownstreamBlockSize(); @@ -213,12 +224,12 @@ qint64 QNetworkAccessBackend::downstreamBytesToConsume() const void QNetworkAccessBackend::writeDownstreamData(const QByteArray &data) { - reply->feed(data); + reply->appendDownstreamData(data); } void QNetworkAccessBackend::writeDownstreamData(QIODevice *data) { - reply->feed(data); + reply->appendDownstreamData(data); } QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h index 9012396..6035f3a 100644 --- a/src/network/access/qnetworkaccessbackend_p.h +++ b/src/network/access/qnetworkaccessbackend_p.h @@ -70,6 +70,8 @@ class QNetworkAccessManagerPrivate; class QNetworkReplyImplPrivate; class QAbstractNetworkCache; class QNetworkCacheMetaData; +class QNetworkAccessBackendUploadIODevice; +class QNonContiguousByteDevice; // Should support direct file upload from disk or download to disk. // @@ -86,14 +88,13 @@ public: // have different names. The Connection has two streams: // // - Upstream: - // Upstream is data that is being written into this connection, - // from the user. Upstream operates in a "pull" mechanism: the - // connection will be notified that there is more data available - // by a call to "upstreamReadyRead". The number of bytes - // available is given by upstreamBytesAvailable(). A call to - // readUpstream() always yields the entire upstream buffer. When - // the connection has processed a certain amount of bytes from - // that buffer, it should call upstreamBytesConsumed(). + // The upstream uses a QNonContiguousByteDevice provided + // by the backend. This device emits the usual readyRead() + // signal when the backend has data available for the connection + // to write. The different backends can listen on this signal + // and then pull upload data from the QNonContiguousByteDevice and + // deal with it. + // // // - Downstream: // Downstream is the data that is being read from this @@ -111,12 +112,9 @@ public: virtual void open() = 0; virtual void closeDownstreamChannel() = 0; - virtual void closeUpstreamChannel() = 0; virtual bool waitForDownstreamReadyRead(int msecs) = 0; - virtual bool waitForUpstreamBytesWritten(int msecs) = 0; // slot-like: - virtual void upstreamReadyRead(); virtual void downstreamReadyWrite(); virtual void copyFinished(QIODevice *); virtual void ignoreSslErrors(); @@ -155,18 +153,24 @@ public: QVariant attribute(QNetworkRequest::Attribute code) const; void setAttribute(QNetworkRequest::Attribute code, const QVariant &value); + // return true if the QNonContiguousByteDevice of the upload + // data needs to support reset(). Currently needed for HTTP. + // This will possibly enable buffering of the upload data. + virtual bool needsResetableUploadData() {return false;}; + protected: - // these functions control the upstream mechanism - // that is, data coming into the backend and out via the connection - qint64 upstreamBytesAvailable() const; - void upstreamBytesConsumed(qint64 count); - QByteArray readUpstream(); + // Create the device used for reading the upload data + QNonContiguousByteDevice* createUploadByteDevice(); + // these functions control the downstream mechanism // that is, data that has come via the connection and is going out the backend qint64 nextDownstreamBlockSize() const; qint64 downstreamBytesToConsume() const; void writeDownstreamData(const QByteArray &data); + +public slots: + // for task 251801, needs to be a slot to be called asynchronously void writeDownstreamData(QIODevice *data); protected slots: @@ -179,10 +183,12 @@ protected slots: void metaDataChanged(); void redirectionRequested(const QUrl &destination); void sslErrors(const QList<QSslError> &errors); + void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal); private: friend class QNetworkAccessManager; friend class QNetworkAccessManagerPrivate; + friend class QNetworkAccessBackendUploadIODevice; QNetworkAccessManagerPrivate *manager; QNetworkReplyImplPrivate *reply; }; diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp index 2e5f1b1..d4bda9a 100644 --- a/src/network/access/qnetworkaccessdebugpipebackend.cpp +++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp @@ -41,6 +41,8 @@ #include "qnetworkaccessdebugpipebackend_p.h" #include "QtCore/qdatastream.h" +#include <QCoreApplication> +#include "private/qnoncontiguousbytedevice_p.h" QT_BEGIN_NAMESPACE @@ -51,12 +53,6 @@ enum { WriteBufferSize = ReadBufferSize }; -struct QNetworkAccessDebugPipeBackend::DataPacket -{ - QList<QPair<QByteArray, QByteArray> > headers; - QByteArray data; -}; - QNetworkAccessBackend * QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation op, const QNetworkRequest &request) const @@ -79,12 +75,14 @@ QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation o } QNetworkAccessDebugPipeBackend::QNetworkAccessDebugPipeBackend() - : incomingPacketSize(0), bareProtocol(false) + : bareProtocol(false), hasUploadFinished(false), hasDownloadFinished(false), + hasEverythingFinished(false), bytesDownloaded(0), bytesUploaded(0) { } QNetworkAccessDebugPipeBackend::~QNetworkAccessDebugPipeBackend() { + // this is signals disconnect, not network! socket.disconnect(this); // we're not interested in the signals at this point } @@ -92,160 +90,150 @@ void QNetworkAccessDebugPipeBackend::open() { socket.connectToHost(url().host(), url().port(12345)); socket.setReadBufferSize(ReadBufferSize); + + // socket ready read -> we can push from socket to downstream connect(&socket, SIGNAL(readyRead()), SLOT(socketReadyRead())); - connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64))); connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError())); connect(&socket, SIGNAL(disconnected()), SLOT(socketDisconnected())); + connect(&socket, SIGNAL(connected()), SLOT(socketConnected())); + // socket bytes written -> we can push more from upstream to socket + connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64))); bareProtocol = url().queryItemValue(QLatin1String("bare")) == QLatin1String("1"); - if (!bareProtocol) { - // "Handshake": - // send outgoing metadata and the URL being requested - DataPacket packet; - //packet.metaData = request().metaData(); - packet.data = url().toEncoded(); - send(packet); + if (operation() == QNetworkAccessManager::PutOperation) { + uploadByteDevice = createUploadByteDevice(); + QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot())); + QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection); } } -void QNetworkAccessDebugPipeBackend::closeDownstreamChannel() +void QNetworkAccessDebugPipeBackend::socketReadyRead() { - if (operation() == QNetworkAccessManager::GetOperation) - socket.disconnectFromHost(); + pushFromSocketToDownstream(); } -void QNetworkAccessDebugPipeBackend::closeUpstreamChannel() +void QNetworkAccessDebugPipeBackend::downstreamReadyWrite() { - if (operation() == QNetworkAccessManager::PutOperation) - socket.disconnectFromHost(); - else if (operation() == QNetworkAccessManager::PostOperation) { - send(DataPacket()); - } + pushFromSocketToDownstream(); } -bool QNetworkAccessDebugPipeBackend::waitForDownstreamReadyRead(int ms) +void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64) { - readyReadEmitted = false; - if (socket.bytesAvailable()) { - socketReadyRead(); - if (readyReadEmitted) - return true; - } - socket.waitForReadyRead(ms); - return readyReadEmitted; + pushFromUpstreamToSocket(); } -bool QNetworkAccessDebugPipeBackend::waitForUpstreamBytesWritten(int ms) +void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot() { - bytesWrittenEmitted = false; - upstreamReadyRead(); - if (bytesWrittenEmitted) - return true; - - socket.waitForBytesWritten(ms); - return bytesWrittenEmitted; + pushFromUpstreamToSocket(); } -void QNetworkAccessDebugPipeBackend::upstreamReadyRead() +void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream() { - int maxWrite = WriteBufferSize - socket.bytesToWrite(); - if (maxWrite <= 0) - return; // can't write yet, wait for the socket to write - - if (bareProtocol) { - QByteArray data = readUpstream(); - if (data.isEmpty()) - return; + QByteArray buffer; - socket.write(data); - upstreamBytesConsumed(data.size()); - bytesWrittenEmitted = true; + if (socket.state() == QAbstractSocket::ConnectingState) { return; } - DataPacket packet; - packet.data = readUpstream(); - if (packet.data.isEmpty()) - return; // we'll be called again when there's data - if (packet.data.size() > maxWrite) - packet.data.truncate(maxWrite); - - if (!send(packet)) { - QString msg = QObject::tr("Write error writing to %1: %2") - .arg(url().toString(), socket.errorString()); - error(QNetworkReply::ProtocolFailure, msg); + forever { + if (hasDownloadFinished) + return; - finished(); - return; + buffer.resize(ReadBufferSize); + qint64 haveRead = socket.read(buffer.data(), ReadBufferSize); + + if (haveRead == -1) { + hasDownloadFinished = true; + // this ensures a good last downloadProgress is emitted + setHeader(QNetworkRequest::ContentLengthHeader, QVariant()); + possiblyFinish(); + break; + } else if (haveRead == 0) { + break; + } else { + // have read something + buffer.resize(haveRead); + bytesDownloaded += haveRead; + writeDownstreamData(buffer); + } } - upstreamBytesConsumed(packet.data.size()); - bytesWrittenEmitted = true; } -void QNetworkAccessDebugPipeBackend::downstreamReadyWrite() +void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket() { - socketReadyRead(); -} + // FIXME + if (operation() == QNetworkAccessManager::PutOperation) { + if (hasUploadFinished) + return; -void QNetworkAccessDebugPipeBackend::socketReadyRead() -{ - if (bareProtocol) { - qint64 bytesToRead = socket.bytesAvailable(); - if (bytesToRead) { - QByteArray buffer; - buffer.resize(bytesToRead); - qint64 bytesRead = socket.read(buffer.data(), bytesToRead); - if (bytesRead < bytesToRead) - buffer.truncate(bytesRead); - writeDownstreamData(buffer); - readyReadEmitted = true; + forever { + if (socket.bytesToWrite() >= WriteBufferSize) + return; + + qint64 haveRead; + const char *readPointer = uploadByteDevice->readPointer(WriteBufferSize, haveRead); + if (haveRead == -1) { + // EOF + hasUploadFinished = true; + emitReplyUploadProgress(bytesUploaded, bytesUploaded); + possiblyFinish(); + break; + } else if (haveRead == 0 || readPointer == 0) { + // nothing to read right now, we will be called again later + break; + } else { + qint64 haveWritten; + haveWritten = socket.write(readPointer, haveRead); + + if (haveWritten < 0) { + // write error! + QString msg = QCoreApplication::translate("QNetworkAccessDebugPipeBackend", "Write error writing to %1: %2") + .arg(url().toString(), socket.errorString()); + error(QNetworkReply::ProtocolFailure, msg); + finished(); + return; + } else { + uploadByteDevice->advanceReadPointer(haveWritten); + bytesUploaded += haveWritten; + emitReplyUploadProgress(bytesUploaded, -1); + } + + //QCoreApplication::processEvents(); + + } } - return; } +} - while (canReceive() && - (socket.state() == QAbstractSocket::UnconnectedState || nextDownstreamBlockSize())) { - DataPacket packet; - if (receive(packet)) { - if (!packet.headers.isEmpty()) { - QList<QPair<QByteArray, QByteArray> >::ConstIterator - it = packet.headers.constBegin(), - end = packet.headers.constEnd(); - for ( ; it != end; ++it) - setRawHeader(it->first, it->second); - metaDataChanged(); - } +void QNetworkAccessDebugPipeBackend::possiblyFinish() +{ + if (hasEverythingFinished) + return; + hasEverythingFinished = true; - if (!packet.data.isEmpty()) { - writeDownstreamData(packet.data); - readyReadEmitted = true; - } + if ((operation() == QNetworkAccessManager::GetOperation) && hasDownloadFinished) { + socket.close(); + finished(); + } else if ((operation() == QNetworkAccessManager::PutOperation) && hasUploadFinished) { + socket.close(); + finished(); + } - if (packet.headers.isEmpty() && packet.data.isEmpty()) { - // it's an eof - socket.close(); - readyReadEmitted = true; - } - } else { - // got an error - QString msg = QObject::tr("Read error reading from %1: %2") - .arg(url().toString(), socket.errorString()); - error(QNetworkReply::ProtocolFailure, msg); - finished(); - return; - } - } } -void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64) +void QNetworkAccessDebugPipeBackend::closeDownstreamChannel() { - upstreamReadyRead(); + qWarning() << "QNetworkAccessDebugPipeBackend::closeDownstreamChannel()" << operation(); + //if (operation() == QNetworkAccessManager::GetOperation) + // socket.disconnectFromHost(); } + void QNetworkAccessDebugPipeBackend::socketError() { + qWarning() << "QNetworkAccessDebugPipeBackend::socketError()" << socket.error(); QNetworkReply::NetworkError code; switch (socket.error()) { case QAbstractSocket::RemoteHostClosedError: @@ -269,76 +257,27 @@ void QNetworkAccessDebugPipeBackend::socketError() void QNetworkAccessDebugPipeBackend::socketDisconnected() { - socketReadyRead(); - if (incomingPacketSize == 0 && socket.bytesToWrite() == 0) { + pushFromSocketToDownstream(); + + if (socket.bytesToWrite() == 0) { // normal close - finished(); } else { // abnormal close QString msg = QObject::tr("Remote host closed the connection prematurely on %1") .arg(url().toString()); error(QNetworkReply::RemoteHostClosedError, msg); - finished(); } } -bool QNetworkAccessDebugPipeBackend::send(const DataPacket &packet) -{ - QByteArray ba; - { - QDataStream stream(&ba, QIODevice::WriteOnly); - stream.setVersion(QDataStream::Qt_4_4); - - stream << packet.headers << packet.data; - } - - qint32 outgoingPacketSize = ba.size(); - qint64 written = socket.write((const char*)&outgoingPacketSize, sizeof outgoingPacketSize); - written += socket.write(ba); - return quint64(written) == (outgoingPacketSize + sizeof outgoingPacketSize); -} - -bool QNetworkAccessDebugPipeBackend::receive(DataPacket &packet) +void QNetworkAccessDebugPipeBackend::socketConnected() { - if (!canReceive()) - return false; - - // canReceive() does the setting up for us - Q_ASSERT(socket.bytesAvailable() >= incomingPacketSize); - QByteArray incomingPacket = socket.read(incomingPacketSize); - QDataStream stream(&incomingPacket, QIODevice::ReadOnly); - stream.setVersion(QDataStream::Qt_4_4); - stream >> packet.headers >> packet.data; - - // reset for next packet: - incomingPacketSize = 0; - socket.setReadBufferSize(ReadBufferSize); - return true; } -bool QNetworkAccessDebugPipeBackend::canReceive() +bool QNetworkAccessDebugPipeBackend::waitForDownstreamReadyRead(int ms) { - if (incomingPacketSize == 0) { - // read the packet size - if (quint64(socket.bytesAvailable()) >= sizeof incomingPacketSize) - socket.read((char*)&incomingPacketSize, sizeof incomingPacketSize); - else - return false; - } - - if (incomingPacketSize == 0) { - QString msg = QObject::tr("Protocol error: packet of size 0 received"); - error(QNetworkReply::ProtocolFailure, msg); - finished(); - - socket.blockSignals(true); - socket.abort(); - socket.blockSignals(false); - return false; - } - - return socket.bytesAvailable() >= incomingPacketSize; + qCritical("QNetworkAccess: Debug pipe backend does not support waitForReadyRead()"); + return false; } #endif diff --git a/src/network/access/qnetworkaccessdebugpipebackend_p.h b/src/network/access/qnetworkaccessdebugpipebackend_p.h index 73a35cf..a13edc4 100644 --- a/src/network/access/qnetworkaccessdebugpipebackend_p.h +++ b/src/network/access/qnetworkaccessdebugpipebackend_p.h @@ -66,35 +66,38 @@ class QNetworkAccessDebugPipeBackend: public QNetworkAccessBackend { Q_OBJECT public: - struct DataPacket; QNetworkAccessDebugPipeBackend(); virtual ~QNetworkAccessDebugPipeBackend(); virtual void open(); virtual void closeDownstreamChannel(); - virtual void closeUpstreamChannel(); virtual bool waitForDownstreamReadyRead(int msecs); - virtual bool waitForUpstreamBytesWritten(int msecs); - virtual void upstreamReadyRead(); virtual void downstreamReadyWrite(); +protected: + void pushFromSocketToDownstream(); + void pushFromUpstreamToSocket(); + void possiblyFinish(); + QNonContiguousByteDevice *uploadByteDevice; + private slots: + void uploadReadyReadSlot(); void socketReadyRead(); void socketBytesWritten(qint64 bytes); void socketError(); void socketDisconnected(); + void socketConnected(); private: QTcpSocket socket; - qint32 incomingPacketSize; - bool readyReadEmitted; - bool bytesWrittenEmitted; bool bareProtocol; + bool hasUploadFinished; + bool hasDownloadFinished; + bool hasEverythingFinished; - bool send(const DataPacket &packet); - bool canReceive(); - bool receive(DataPacket &packet); + qint64 bytesDownloaded; + qint64 bytesUploaded; }; class QNetworkAccessDebugPipeBackendFactory: public QNetworkAccessBackendFactory diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp index 8a5a665..6374fde 100644 --- a/src/network/access/qnetworkaccessfilebackend.cpp +++ b/src/network/access/qnetworkaccessfilebackend.cpp @@ -43,6 +43,7 @@ #include "qfileinfo.h" #include "qurlinfo.h" #include "qdir.h" +#include "private/qnoncontiguousbytedevice_p.h" #include <QtCore/QCoreApplication> @@ -77,7 +78,7 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op, } QNetworkAccessFileBackend::QNetworkAccessFileBackend() - : totalBytes(0) + : uploadByteDevice(0), totalBytes(0), hasUploadFinished(false) { } @@ -126,6 +127,9 @@ void QNetworkAccessFileBackend::open() break; case QNetworkAccessManager::PutOperation: mode = QIODevice::WriteOnly | QIODevice::Truncate; + uploadByteDevice = createUploadByteDevice(); + QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot())); + QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection); break; default: Q_ASSERT_X(false, "QNetworkAccessFileBackend::open", @@ -152,19 +156,50 @@ void QNetworkAccessFileBackend::open() } } -void QNetworkAccessFileBackend::closeDownstreamChannel() +void QNetworkAccessFileBackend::uploadReadyReadSlot() { - if (operation() == QNetworkAccessManager::GetOperation) { - file.close(); - //downstreamChannelClosed(); + if (hasUploadFinished) + return; + + forever { + qint64 haveRead; + const char *readPointer = uploadByteDevice->readPointer(-1, haveRead); + if (haveRead == -1) { + // EOF + hasUploadFinished = true; + file.flush(); + file.close(); + finished(); + break; + } else if (haveRead == 0 || readPointer == 0) { + // nothing to read right now, we will be called again later + break; + } else { + qint64 haveWritten; + haveWritten = file.write(readPointer, haveRead); + + if (haveWritten < 0) { + // write error! + QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Write error writing to %1: %2") + .arg(url().toString(), file.errorString()); + error(QNetworkReply::ProtocolFailure, msg); + + finished(); + return; + } else { + uploadByteDevice->advanceReadPointer(haveWritten); + } + + + file.flush(); + } } } -void QNetworkAccessFileBackend::closeUpstreamChannel() +void QNetworkAccessFileBackend::closeDownstreamChannel() { - if (operation() == QNetworkAccessManager::PutOperation) { + if (operation() == QNetworkAccessManager::GetOperation) { file.close(); - finished(); } } @@ -174,40 +209,6 @@ bool QNetworkAccessFileBackend::waitForDownstreamReadyRead(int) return readMoreFromFile(); } -bool QNetworkAccessFileBackend::waitForUpstreamBytesWritten(int) -{ - Q_ASSERT_X(false, "QNetworkAccessFileBackend::waitForUpstreamBytesWritten", - "This function should never have been called, since there is never anything " - "left to be written!"); - return false; -} - -void QNetworkAccessFileBackend::upstreamReadyRead() -{ - Q_ASSERT_X(operation() == QNetworkAccessManager::PutOperation, "QNetworkAccessFileBackend", - "We're being told to upload data but operation isn't PUT!"); - - // there's more data to be written to the file - while (upstreamBytesAvailable()) { - // write everything and let QFile handle it - int written = file.write(readUpstream()); - - if (written < 0) { - // write error! - QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Write error writing to %1: %2") - .arg(url().toString(), file.errorString()); - error(QNetworkReply::ProtocolFailure, msg); - - finished(); - return; - } - - // successful write - file.flush(); - upstreamBytesConsumed(written); - } -} - void QNetworkAccessFileBackend::downstreamReadyWrite() { Q_ASSERT_X(operation() == QNetworkAccessManager::GetOperation, "QNetworkAccessFileBackend", diff --git a/src/network/access/qnetworkaccessfilebackend_p.h b/src/network/access/qnetworkaccessfilebackend_p.h index ce7d351..4615c5f 100644 --- a/src/network/access/qnetworkaccessfilebackend_p.h +++ b/src/network/access/qnetworkaccessfilebackend_p.h @@ -62,22 +62,25 @@ QT_BEGIN_NAMESPACE class QNetworkAccessFileBackend: public QNetworkAccessBackend { + Q_OBJECT public: QNetworkAccessFileBackend(); virtual ~QNetworkAccessFileBackend(); virtual void open(); virtual void closeDownstreamChannel(); - virtual void closeUpstreamChannel(); virtual bool waitForDownstreamReadyRead(int msecs); - virtual bool waitForUpstreamBytesWritten(int msecs); - virtual void upstreamReadyRead(); virtual void downstreamReadyWrite(); +public slots: + void uploadReadyReadSlot(); +protected: + QNonContiguousByteDevice *uploadByteDevice; private: QFile file; qint64 totalBytes; + bool hasUploadFinished; bool loadFileInfo(); bool readMoreFromFile(); diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp index ea39dec..ad55b85 100644 --- a/src/network/access/qnetworkaccessftpbackend.cpp +++ b/src/network/access/qnetworkaccessftpbackend.cpp @@ -42,6 +42,7 @@ #include "qnetworkaccessftpbackend_p.h" #include "qnetworkaccessmanager_p.h" #include "QtNetwork/qauthenticator.h" +#include "private/qnoncontiguousbytedevice_p.h" #ifndef QT_NO_FTP @@ -81,41 +82,6 @@ QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op, return 0; } -class QNetworkAccessFtpIODevice: public QIODevice -{ - //Q_OBJECT -public: - QNetworkAccessFtpBackend *backend; - bool eof; - - inline QNetworkAccessFtpIODevice(QNetworkAccessFtpBackend *parent) - : QIODevice(parent), backend(parent), eof(false) - { open(ReadOnly); } - - bool isSequential() const { return true; } - bool atEnd() const { return backend->upstreamBytesAvailable() == 0; } - - qint64 bytesAvailable() const { return backend->upstreamBytesAvailable(); } - qint64 bytesToWrite() const { return backend->downstreamBytesToConsume(); } -protected: - qint64 readData(char *data, qint64 maxlen) - { - const QByteArray toSend = backend->readUpstream(); - maxlen = qMin<qint64>(maxlen, toSend.size()); - if (!maxlen) - return eof ? -1 : 0; - - backend->upstreamBytesConsumed(maxlen); - memcpy(data, toSend.constData(), maxlen); - return maxlen; - } - - qint64 writeData(const char *, qint64) - { return -1; } - - friend class QNetworkAccessFtpBackend; -}; - class QNetworkAccessFtpFtp: public QFtp, public QNetworkAccessCache::CacheableObject { // Q_OBJECT @@ -198,7 +164,11 @@ void QNetworkAccessFtpBackend::open() ftpConnectionReady(ftp); } - uploadDevice = new QNetworkAccessFtpIODevice(this); + // Put operation + if (operation() == QNetworkAccessManager::PutOperation) { + uploadDevice = QNonContiguousByteDeviceFactory::wrap(createUploadByteDevice()); + uploadDevice->setParent(this); + } } void QNetworkAccessFtpBackend::closeDownstreamChannel() @@ -212,16 +182,6 @@ void QNetworkAccessFtpBackend::closeDownstreamChannel() #endif } -void QNetworkAccessFtpBackend::closeUpstreamChannel() -{ - if (operation() == QNetworkAccessManager::PutOperation) { - Q_ASSERT(uploadDevice); - uploadDevice->eof = true; - if (!upstreamBytesAvailable()) - emit uploadDevice->readyRead(); - } -} - bool QNetworkAccessFtpBackend::waitForDownstreamReadyRead(int ms) { if (!ftp) @@ -239,18 +199,6 @@ bool QNetworkAccessFtpBackend::waitForDownstreamReadyRead(int ms) return false; } -bool QNetworkAccessFtpBackend::waitForUpstreamBytesWritten(int ms) -{ - Q_UNUSED(ms); - qCritical("QNetworkAccess: FTP backend does not support waitForBytesWritten()"); - return false; -} - -void QNetworkAccessFtpBackend::upstreamReadyRead() -{ - // uh... how does QFtp operate? -} - void QNetworkAccessFtpBackend::downstreamReadyWrite() { if (state == Transferring && ftp && ftp->bytesAvailable()) diff --git a/src/network/access/qnetworkaccessftpbackend_p.h b/src/network/access/qnetworkaccessftpbackend_p.h index 9ec2dd8..1bb7ff2 100644 --- a/src/network/access/qnetworkaccessftpbackend_p.h +++ b/src/network/access/qnetworkaccessftpbackend_p.h @@ -87,11 +87,8 @@ public: virtual void open(); virtual void closeDownstreamChannel(); - virtual void closeUpstreamChannel(); virtual bool waitForDownstreamReadyRead(int msecs); - virtual bool waitForUpstreamBytesWritten(int msecs); - virtual void upstreamReadyRead(); virtual void downstreamReadyWrite(); void disconnectFromFtp(); @@ -105,7 +102,7 @@ public slots: private: friend class QNetworkAccessFtpIODevice; QPointer<QNetworkAccessFtpFtp> ftp; - QNetworkAccessFtpIODevice *uploadDevice; + QIODevice *uploadDevice; qint64 totalBytes; int helpId, sizeId, mdtmId; bool supportsSize, supportsMdtm; diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index f214699..bd364cb 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -286,37 +286,6 @@ public: } }; -class QNetworkAccessHttpBackendIODevice: public QIODevice -{ - // Q_OBJECT -public: - bool eof; - QNetworkAccessHttpBackendIODevice(QNetworkAccessHttpBackend *parent) - : QIODevice(parent), eof(false) - { - setOpenMode(ReadOnly); - } - bool isSequential() const { return true; } - qint64 bytesAvailable() const - { return static_cast<QNetworkAccessHttpBackend *>(parent())->upstreamBytesAvailable(); } - -protected: - virtual qint64 readData(char *buffer, qint64 maxlen) - { - qint64 ret = static_cast<QNetworkAccessHttpBackend *>(parent())->deviceReadData(buffer, maxlen); - if (!ret && eof) - return -1; - return ret; - } - - virtual qint64 writeData(const char *, qint64) - { - return -1; // cannot write - } - - friend class QNetworkAccessHttpBackend; -}; - QNetworkAccessHttpBackend::QNetworkAccessHttpBackend() : QNetworkAccessBackend(), httpReply(0), http(0), uploadDevice(0) #ifndef QT_NO_OPENSSL @@ -507,20 +476,19 @@ void QNetworkAccessHttpBackend::postRequest() case QNetworkAccessManager::PostOperation: invalidateCache(); httpRequest.setOperation(QHttpNetworkRequest::Post); - uploadDevice = new QNetworkAccessHttpBackendIODevice(this); + httpRequest.setUploadByteDevice(createUploadByteDevice()); break; case QNetworkAccessManager::PutOperation: invalidateCache(); httpRequest.setOperation(QHttpNetworkRequest::Put); - uploadDevice = new QNetworkAccessHttpBackendIODevice(this); + httpRequest.setUploadByteDevice(createUploadByteDevice()); break; default: break; // can't happen } - httpRequest.setData(uploadDevice); httpRequest.setUrl(url()); QList<QByteArray> headers = request().rawHeaderList(); @@ -528,7 +496,9 @@ void QNetworkAccessHttpBackend::postRequest() httpRequest.setHeaderField(header, request().rawHeader(header)); if (loadedFromCache) { - QNetworkAccessBackend::finished(); + // commented this out since it will be called later anyway + // by copyFinished() + //QNetworkAccessBackend::finished(); return; // no need to send the request! :) } @@ -624,14 +594,6 @@ void QNetworkAccessHttpBackend::closeDownstreamChannel() // this indicates that the user closed the stream while the reply isn't finished yet } -void QNetworkAccessHttpBackend::closeUpstreamChannel() -{ - // this indicates that the user finished uploading the data for POST - Q_ASSERT(uploadDevice); - uploadDevice->eof = true; - emit uploadDevice->readChannelFinished(); -} - bool QNetworkAccessHttpBackend::waitForDownstreamReadyRead(int msecs) { Q_ASSERT(http); @@ -651,38 +613,6 @@ bool QNetworkAccessHttpBackend::waitForDownstreamReadyRead(int msecs) return false; } -bool QNetworkAccessHttpBackend::waitForUpstreamBytesWritten(int msecs) -{ - - // ### FIXME: not implemented in QHttpNetworkAccess - Q_UNUSED(msecs); - qCritical("QNetworkAccess: HTTP backend does not support waitForBytesWritten()"); - return false; -} - -void QNetworkAccessHttpBackend::upstreamReadyRead() -{ - // There is more data available from the user to be uploaded - // QHttpNetworkAccess implements the upload rate control: - // we simply tell QHttpNetworkAccess that there is more data available - // it'll pull from us when it can (through uploadDevice) - - Q_ASSERT(uploadDevice); - emit uploadDevice->readyRead(); -} - -qint64 QNetworkAccessHttpBackend::deviceReadData(char *buffer, qint64 maxlen) -{ - QByteArray toBeUploaded = readUpstream(); - if (toBeUploaded.isEmpty()) - return 0; // nothing to be uploaded - - maxlen = qMin<qint64>(maxlen, toBeUploaded.length()); - - memcpy(buffer, toBeUploaded.constData(), maxlen); - upstreamBytesConsumed(maxlen); - return maxlen; -} void QNetworkAccessHttpBackend::downstreamReadyWrite() { @@ -904,7 +834,14 @@ bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &m checkForRedirect(status); - writeDownstreamData(contents); + emit metaDataChanged(); + + // invoke this asynchronously, else Arora/QtDemoBrowser don't like cached downloads + // see task 250221 / 251801 + qRegisterMetaType<QIODevice*>("QIODevice*"); + QMetaObject::invokeMethod(this, "writeDownstreamData", Qt::QueuedConnection, Q_ARG(QIODevice*, contents)); + + #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) qDebug() << "Successfully sent cache:" << url() << contents->size() << "bytes"; #endif diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h index 02915e7..225f944 100644 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -79,11 +79,8 @@ public: virtual void open(); virtual void closeDownstreamChannel(); - virtual void closeUpstreamChannel(); virtual bool waitForDownstreamReadyRead(int msecs); - virtual bool waitForUpstreamBytesWritten(int msecs); - virtual void upstreamReadyRead(); virtual void downstreamReadyWrite(); virtual void copyFinished(QIODevice *); #ifndef QT_NO_OPENSSL @@ -96,6 +93,9 @@ public: qint64 deviceReadData(char *buffer, qint64 maxlen); + // we return true since HTTP needs to send PUT/POST data again after having authenticated + bool needsResetableUploadData() {return true;}; + private slots: void replyReadyRead(); void replyFinished(); @@ -108,7 +108,8 @@ private: QHttpNetworkReply *httpReply; QPointer<QNetworkAccessHttpBackendCache> http; QByteArray cacheKey; - QNetworkAccessHttpBackendIODevice *uploadDevice; + QNetworkAccessBackendUploadIODevice *uploadDevice; + #ifndef QT_NO_OPENSSL QSslConfiguration *pendingSslConfiguration; bool pendingIgnoreSslErrors; @@ -122,8 +123,6 @@ private: void postRequest(); void readFromHttp(); void checkForRedirect(const int statusCode); - - friend class QNetworkAccessHttpBackendIODevice; }; class QNetworkAccessHttpBackendFactory : public QNetworkAccessBackendFactory diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index bcbeef1..bf06ede 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -686,7 +686,10 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera priv->urlForLastAuthentication = url; } - // third step: setup the reply + // third step: find a backend + priv->backend = d->findBackend(op, request); + + // fourth step: setup the reply priv->setup(op, request, outgoingData); if (request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt() != QNetworkRequest::AlwaysNetwork) @@ -695,9 +698,6 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera QList<QNetworkProxy> proxyList = d->queryProxy(QNetworkProxyQuery(request.url())); priv->proxyList = proxyList; #endif - - // fourth step: find a backend - priv->backend = d->findBackend(op, request); if (priv->backend) { priv->backend->setParent(reply); priv->backend->reply = priv; diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp index 44a8298..0ca6b13 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" @@ -669,3 +671,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/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp index f4dad3c..0990b17 100644 --- a/src/network/access/qnetworkreply.cpp +++ b/src/network/access/qnetworkreply.cpp @@ -151,6 +151,10 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() authentication to serve the content but the credentials provided were not accepted (if any) + \value ContentReSendError the request needed to be sent + again, but this failed for example because the upload data + could not be read a second time. + \value ProtocolUnknownError the Network Access API cannot honor the request because the protocol is not known diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h index 6f763b3..2f864fe 100644 --- a/src/network/access/qnetworkreply.h +++ b/src/network/access/qnetworkreply.h @@ -92,6 +92,7 @@ public: ContentOperationNotPermittedError, ContentNotFoundError, AuthenticationRequiredError, + ContentReSendError, UnknownContentError = 299, // protocol errors diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 6f715e5..a146358 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -46,13 +46,15 @@ #include "QtCore/qcoreapplication.h" #include "QtCore/qdatetime.h" #include "QtNetwork/qsslconfiguration.h" +#include "qnetworkaccesshttpbackend_p.h" #include <QtCore/QCoreApplication> QT_BEGIN_NAMESPACE inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() - : copyDevice(0), networkCache(0), + : backend(0), outgoingData(0), outgoingDataBuffer(0), + copyDevice(0), networkCache(0), cacheEnabled(false), cacheSaveDevice(0), bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), state(Idle) @@ -61,8 +63,13 @@ inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() void QNetworkReplyImplPrivate::_q_startOperation() { - // This function is called exactly once + // ensure this function is only being called once + if (state == Working) { + qDebug("QNetworkReplyImpl::_q_startOperation was called more than once"); + return; + } state = Working; + if (!backend) { error(QNetworkReplyImpl::ProtocolUnknownError, QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!; @@ -74,57 +81,11 @@ void QNetworkReplyImplPrivate::_q_startOperation() if (state != Finished) { if (operation == QNetworkAccessManager::GetOperation) pendingNotifications.append(NotifyDownstreamReadyWrite); - if (outgoingData) { - _q_sourceReadyRead(); -#if 0 // ### FIXME - if (outgoingData->atEndOfStream() && writeBuffer.isEmpty()) - // empty upload - emit q->uploadProgress(0, 0); -#endif - } handleNotifications(); } } -void QNetworkReplyImplPrivate::_q_sourceReadyRead() -{ - // read data from the outgoingData QIODevice into our internal buffer - enum { DesiredBufferSize = 32 * 1024 }; - - if (writeBuffer.size() >= DesiredBufferSize) - return; // don't grow the buffer too much - - // read as many bytes are available or up until we fill up the buffer - // but always read at least one byte - qint64 bytesToRead = qBound<qint64>(1, outgoingData->bytesAvailable(), - DesiredBufferSize - writeBuffer.size()); - char *ptr = writeBuffer.reserve(bytesToRead); - qint64 bytesActuallyRead = outgoingData->read(ptr, bytesToRead); - if (bytesActuallyRead == -1) { - // EOF - writeBuffer.chop(bytesToRead); - backendNotify(NotifyCloseUpstreamChannel); - return; - } - - if (bytesActuallyRead < bytesToRead) - writeBuffer.chop(bytesToRead - bytesActuallyRead); - - // if we did read anything, let the backend know and handle it - if (bytesActuallyRead) - backendNotify(NotifyUpstreamReadyRead); - - // check for EOF again - if (!outgoingData->isSequential() && outgoingData->atEnd()) - backendNotify(NotifyCloseUpstreamChannel); -} - -void QNetworkReplyImplPrivate::_q_sourceReadChannelFinished() -{ - _q_sourceReadyRead(); -} - void QNetworkReplyImplPrivate::_q_copyReadyRead() { Q_Q(QNetworkReplyImpl); @@ -143,7 +104,7 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead() if (bytesActuallyRead == -1) { readBuffer.chop(bytesToRead); backendNotify(NotifyCopyFinished); - return; + break; } if (bytesActuallyRead != bytesToRead) @@ -151,6 +112,7 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead() if (!copyDevice->isSequential() && copyDevice->atEnd()) { backendNotify(NotifyCopyFinished); + bytesDownloaded += bytesActuallyRead; break; } @@ -174,6 +136,67 @@ void QNetworkReplyImplPrivate::_q_copyReadChannelFinished() _q_copyReadyRead(); } +void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished() +{ + Q_Q(QNetworkReplyImpl); + + // make sure this is only called once, ever. + //_q_bufferOutgoingData may call it or the readChannelFinished emission + if (state != Buffering) + return; + + // disconnect signals + QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData())); + QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished())); + + // finally, start the request + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); +} + +void QNetworkReplyImplPrivate::_q_bufferOutgoingData() +{ + Q_Q(QNetworkReplyImpl); + + if (!outgoingDataBuffer) { + // first call, create our buffer + outgoingDataBuffer = new QRingBuffer(); + + QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData())); + QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished())); + } + + qint64 bytesBuffered = 0; + qint64 bytesToBuffer = 0; + + // read data into our buffer + forever { + bytesToBuffer = outgoingData->bytesAvailable(); + // unknown? just try 2 kB, this also ensures we always try to read the EOF + if (bytesToBuffer <= 0) + bytesToBuffer = 2*1024; + + char *dst = outgoingDataBuffer->reserve(bytesToBuffer); + bytesBuffered = outgoingData->read(dst, bytesToBuffer); + + if (bytesBuffered == -1) { + // EOF has been reached. + outgoingDataBuffer->chop(bytesToBuffer); + + _q_bufferOutgoingDataFinished(); + break; + } else if (bytesBuffered == 0) { + // nothing read right now, just wait until we get called again + outgoingDataBuffer->chop(bytesToBuffer); + + break; + } else { + // don't break, try to read() again + outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered); + } + } +} + + void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *data) { @@ -184,13 +207,42 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const url = request.url(); operation = op; - if (outgoingData) { - q->connect(outgoingData, SIGNAL(readyRead()), SLOT(_q_sourceReadyRead())); - q->connect(outgoingData, SIGNAL(readChannelFinished()), SLOT(_q_sourceReadChannelFinished())); + if (outgoingData && backend) { + // there is data to be uploaded, e.g. HTTP POST. + + if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) { + // backend does not need upload buffering or + // fixed size non-sequential + // just start the operation + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); + } else { + bool bufferingDisallowed = + req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute, + false).toBool(); + + if (bufferingDisallowed) { + // if a valid content-length header for the request was supplied, we can disable buffering + // if not, we will buffer anyway + if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) { + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); + } else { + state = Buffering; + QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection); + } + } else { + // _q_startOperation will be called when the buffering has finished. + state = Buffering; + QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection); + } + } + } else { + // No outgoing data (e.g. HTTP GET request) + // or no backend + // if no backend, _q_startOperation will handle the error of this + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); } q->QIODevice::open(QIODevice::ReadOnly); - QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); } void QNetworkReplyImplPrivate::setNetworkCache(QAbstractNetworkCache *nc) @@ -226,18 +278,10 @@ void QNetworkReplyImplPrivate::handleNotifications() backend->downstreamReadyWrite(); break; - case NotifyUpstreamReadyRead: - backend->upstreamReadyRead(); - break; - case NotifyCloseDownstreamChannel: backend->closeDownstreamChannel(); break; - case NotifyCloseUpstreamChannel: - backend->closeUpstreamChannel(); - break; - case NotifyCopyFinished: { QIODevice *dev = copyDevice; copyDevice = 0; @@ -299,29 +343,14 @@ void QNetworkReplyImplPrivate::completeCacheSave() cacheEnabled = false; } -void QNetworkReplyImplPrivate::consume(qint64 count) +void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal) { Q_Q(QNetworkReplyImpl); - if (count <= 0) { - qWarning("QNetworkConnection: backend signalled that it consumed %ld bytes", long(count)); - return; - } - - if (outgoingData) - // schedule another read from the source - QMetaObject::invokeMethod(q_func(), "_q_sourceReadyRead", Qt::QueuedConnection); - - writeBuffer.skip(count); - if (bytesUploaded == -1) - bytesUploaded = count; - else - bytesUploaded += count; - - QVariant totalSize = request.header(QNetworkRequest::ContentLengthHeader); - emit q->uploadProgress(bytesUploaded, - totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); + bytesUploaded = bytesSent; + emit q->uploadProgress(bytesSent, bytesTotal); } + qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const { enum { DesiredBufferSize = 32 * 1024 }; @@ -331,7 +360,9 @@ qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const return qMax<qint64>(0, readBufferMaxSize - readBuffer.size()); } -void QNetworkReplyImplPrivate::feed(const QByteArray &data) +// we received downstream data and send this to the cache +// and to our readBuffer (which in turn gets read by the user of QNetworkReply) +void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data) { Q_Q(QNetworkReplyImpl); if (!q->isOpen()) @@ -379,7 +410,8 @@ void QNetworkReplyImplPrivate::feed(const QByteArray &data) } } -void QNetworkReplyImplPrivate::feed(QIODevice *data) +// this is used when it was fetched from the cache, right? +void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data) { Q_Q(QNetworkReplyImpl); if (!q->isOpen()) @@ -410,9 +442,11 @@ void QNetworkReplyImplPrivate::finished() pendingNotifications.clear(); QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); - if (bytesDownloaded != lastBytesDownloaded || totalSize.isNull()) + if (totalSize.isNull() || totalSize == -1) { emit q->downloadProgress(bytesDownloaded, bytesDownloaded); - if (bytesUploaded == -1 && outgoingData) + } + + if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer)) emit q->uploadProgress(0, 0); completeCacheSave(); diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h index ad06f78..8d3c90e 100644 --- a/src/network/access/qnetworkreplyimpl_p.h +++ b/src/network/access/qnetworkreplyimpl_p.h @@ -59,6 +59,7 @@ #include "qnetworkproxy.h" #include "QtCore/qmap.h" #include "QtCore/qqueue.h" +#include "QtCore/qbuffer.h" #include "private/qringbuffer_p.h" QT_BEGIN_NAMESPACE @@ -91,10 +92,10 @@ public: Q_DECLARE_PRIVATE(QNetworkReplyImpl) Q_PRIVATE_SLOT(d_func(), void _q_startOperation()) - Q_PRIVATE_SLOT(d_func(), void _q_sourceReadyRead()) - Q_PRIVATE_SLOT(d_func(), void _q_sourceReadChannelFinished()) Q_PRIVATE_SLOT(d_func(), void _q_copyReadyRead()) Q_PRIVATE_SLOT(d_func(), void _q_copyReadChannelFinished()) + Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData()) + Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished()) }; class QNetworkReplyImplPrivate: public QNetworkReplyPrivate @@ -102,15 +103,13 @@ class QNetworkReplyImplPrivate: public QNetworkReplyPrivate public: enum InternalNotifications { NotifyDownstreamReadyWrite, - NotifyUpstreamReadyRead, NotifyCloseDownstreamChannel, - NotifyCloseUpstreamChannel, NotifyCopyFinished }; enum State { Idle, - Opening, + Buffering, Working, Finished, Aborted @@ -125,6 +124,8 @@ public: void _q_sourceReadChannelFinished(); void _q_copyReadyRead(); void _q_copyReadChannelFinished(); + void _q_bufferOutgoingData(); + void _q_bufferOutgoingDataFinished(); void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData); @@ -138,9 +139,10 @@ public: void setCachingEnabled(bool enable); bool isCachingEnabled() const; void consume(qint64 count); + void emitUploadProgress(qint64 bytesSent, qint64 bytesTotal); qint64 nextDownstreamBlockSize() const; - void feed(const QByteArray &data); - void feed(QIODevice *data); + void appendDownstreamData(const QByteArray &data); + void appendDownstreamData(QIODevice *data); void finished(); void error(QNetworkReply::NetworkError code, const QString &errorString); void metaDataChanged(); @@ -149,6 +151,7 @@ public: QNetworkAccessBackend *backend; QIODevice *outgoingData; + QRingBuffer *outgoingDataBuffer; QIODevice *copyDevice; QAbstractNetworkCache *networkCache; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 56b793d..8b1afba 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -162,6 +162,13 @@ QT_BEGIN_NAMESPACE Indicates whether the data was obtained from cache or not. + \value DoNotBufferUploadDataAttribute + Requests only, type: QVariant::Bool (default: false) + Indicates whether the QNetworkAccessManager code is + allowed to buffer the upload data, e.g. when doing a HTTP POST. + When using this flag with sequential upload data, the ContentLengthHeader + header must be set. + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 6f34bce..5dea1df 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -75,6 +75,7 @@ public: CacheLoadControlAttribute, CacheSaveControlAttribute, SourceIsFromCacheAttribute, + DoNotBufferUploadDataAttribute, User = 1000, UserMax = 32767 diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp index d6b1507..77a999b 100644 --- a/src/network/socket/qlocalserver.cpp +++ b/src/network/socket/qlocalserver.cpp @@ -276,9 +276,13 @@ QLocalSocket *QLocalServer::nextPendingConnection() if (d->pendingConnections.isEmpty()) return 0; QLocalSocket *nextSocket = d->pendingConnections.dequeue(); +#ifndef QT_LOCALSOCKET_TCP + if (d->pendingConnections.size() <= d->maxPendingConnections) #ifndef Q_OS_WIN - d->socketNotifier->setEnabled(d->pendingConnections.size() - <= d->maxPendingConnections); + d->socketNotifier->setEnabled(true); +#else + d->connectionEventNotifier->setEnabled(true); +#endif #endif return nextSocket; } diff --git a/src/network/socket/qlocalserver.h b/src/network/socket/qlocalserver.h index 8e8babd..1488a75 100644 --- a/src/network/socket/qlocalserver.h +++ b/src/network/socket/qlocalserver.h @@ -86,15 +86,7 @@ protected: private: Q_DISABLE_COPY(QLocalServer) -#if defined(QT_LOCALSOCKET_TCP) Q_PRIVATE_SLOT(d_func(), void _q_onNewConnection()) -#elif defined(Q_OS_WIN) - Q_PRIVATE_SLOT(d_func(), void _q_openSocket(HANDLE handle)) - Q_PRIVATE_SLOT(d_func(), void _q_stoppedListening()) - Q_PRIVATE_SLOT(d_func(), void _q_setError(QAbstractSocket::SocketError error, const QString &errorString)) -#else - Q_PRIVATE_SLOT(d_func(), void _q_socketActivated()) -#endif }; #endif // QT_NO_LOCALSERVER diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h index 8e96401..7b31082 100644 --- a/src/network/socket/qlocalserver_p.h +++ b/src/network/socket/qlocalserver_p.h @@ -63,7 +63,7 @@ # include <qtcpserver.h> #elif defined(Q_OS_WIN) # include <qt_windows.h> -# include <qthread.h> +# include <private/qwineventnotifier_p.h> #else # include <private/qnativesocketengine_p.h> # include <qsocketnotifier.h> @@ -71,52 +71,13 @@ QT_BEGIN_NAMESPACE -#if defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) - -/*! - \internal - QLocalServerThread exists because Windows does not have a - way to provide notifications when there is a new connections to - the server. - */ -class QLocalServerThread : public QThread -{ - Q_OBJECT - -Q_SIGNALS: - void connected(HANDLE newSocket); - void error(QAbstractSocket::SocketError error, const QString &errorString); - -public: - QLocalServerThread(QObject *parent = 0); - ~QLocalServerThread(); - void closeServer(); - -public: - QString setName(const QString &name); - void run(); - void stop(); - bool makeHandle(); - - HANDLE gotConnectionEvent; - QQueue<HANDLE> pendingHandles; - int maxPendingConnections; -private: - HANDLE stopEvent; - QString fullServerName; -}; - -#endif - class QLocalServerPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QLocalServer) public: QLocalServerPrivate() : -#if defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) - inWaitingFunction(false), -#elif !defined(QT_LOCALSOCKET_TCP) +#if !defined(QT_LOCALSOCKET_TCP) && !defined(Q_OS_WIN) listenSocket(-1), socketNotifier(0), #endif maxPendingConnections(30), error(QAbstractSocket::UnknownSocketError) @@ -128,22 +89,26 @@ public: static bool removeServer(const QString &name); void closeServer(); void waitForNewConnection(int msec, bool *timedOut); + void _q_onNewConnection(); #if defined(QT_LOCALSOCKET_TCP) - void _q_onNewConnection(); QTcpServer tcpServer; QMap<quintptr, QTcpSocket*> socketMap; #elif defined(Q_OS_WIN) - void _q_openSocket(HANDLE socket); - void _q_stoppedListening(); - void _q_setError(QAbstractSocket::SocketError error, const QString &errorString); + struct Listener { + HANDLE handle; + OVERLAPPED overlapped; + }; + + void setError(const QString &function); + bool addListener(); - QLocalServerThread waitForConnection; - bool inWaitingFunction; + QList<Listener> listeners; + HANDLE eventHandle; + QWinEventNotifier *connectionEventNotifier; #else void setError(const QString &function); - void _q_socketActivated(); int listenSocket; QSocketNotifier *socketNotifier; diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp index e7d2252..53ee6b6 100644 --- a/src/network/socket/qlocalserver_unix.cpp +++ b/src/network/socket/qlocalserver_unix.cpp @@ -132,7 +132,7 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName) socketNotifier = new QSocketNotifier(listenSocket, QSocketNotifier::Read, q); q->connect(socketNotifier, SIGNAL(activated(int)), - q, SLOT(_q_socketActivated())); + q, SLOT(_q_onNewConnection())); socketNotifier->setEnabled(maxPendingConnections > 0); return true; } @@ -164,7 +164,7 @@ void QLocalServerPrivate::closeServer() We have received a notification that we can read on the listen socket. Accept the new socket. */ -void QLocalServerPrivate::_q_socketActivated() +void QLocalServerPrivate::_q_onNewConnection() { Q_Q(QLocalServer); if (-1 == listenSocket) @@ -209,7 +209,7 @@ void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut) break; } if (result > 0) - _q_socketActivated(); + _q_onNewConnection(); } if (timedOut) *timedOut = (result == 0); diff --git a/src/network/socket/qlocalserver_win.cpp b/src/network/socket/qlocalserver_win.cpp index 880cd7e..b14bbf7 100644 --- a/src/network/socket/qlocalserver_win.cpp +++ b/src/network/socket/qlocalserver_win.cpp @@ -44,68 +44,26 @@ #include "qlocalsocket.h" #include <qdebug.h> -#include <qdatetime.h> -#include <qcoreapplication.h> -#include <QMetaType> // The buffer size need to be 0 otherwise data could be // lost if the socket that has written data closes the connection // before it is read. Pipewriter is used for write buffering. #define BUFSIZE 0 -QT_BEGIN_NAMESPACE - -QLocalServerThread::QLocalServerThread(QObject *parent) : QThread(parent), - maxPendingConnections(1) -{ - stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - gotConnectionEvent = CreateEvent(NULL, TRUE, FALSE, NULL); -} +// ###: This should be a property. Should replace the insane 50 on unix as well. +#define SYSTEM_MAX_PENDING_SOCKETS 8 -QLocalServerThread::~QLocalServerThread() -{ - stop(); - closeServer(); - CloseHandle(stopEvent); - CloseHandle(gotConnectionEvent); -} - -void QLocalServerThread::stop() -{ - if (isRunning()) { - SetEvent(stopEvent); - wait(); - ResetEvent(stopEvent); - } -} - -void QLocalServerThread::closeServer() -{ - while (!pendingHandles.isEmpty()) - CloseHandle(pendingHandles.dequeue()); -} - -QString QLocalServerThread::setName(const QString &name) -{ - QString pipePath = QLatin1String("\\\\.\\pipe\\"); - if (name.startsWith(pipePath)) - fullServerName = name; - else - fullServerName = pipePath + name; - for (int i = pendingHandles.count(); i < maxPendingConnections; ++i) - if (!makeHandle()) - break; - return fullServerName; -} +QT_BEGIN_NAMESPACE -bool QLocalServerThread::makeHandle() +bool QLocalServerPrivate::addListener() { - if (pendingHandles.count() >= maxPendingConnections) - return false; + // The object must not change its address once the + // contained OVERLAPPED struct is passed to Windows. + listeners << Listener(); + Listener &listener = listeners.last(); - HANDLE handle = INVALID_HANDLE_VALUE; QT_WA({ - handle = CreateNamedPipeW( + listener.handle = CreateNamedPipeW( (TCHAR*)fullServerName.utf16(), // pipe name PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access PIPE_TYPE_MESSAGE | // message type pipe @@ -117,7 +75,7 @@ bool QLocalServerThread::makeHandle() 3000, // client time-out NULL); }, { - handle = CreateNamedPipeA( + listener.handle = CreateNamedPipeA( fullServerName.toLocal8Bit().constData(), // pipe name PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access PIPE_TYPE_MESSAGE | // message type pipe @@ -129,68 +87,43 @@ bool QLocalServerThread::makeHandle() 3000, // client time-out NULL); }); - - if (INVALID_HANDLE_VALUE == handle) { + if (listener.handle == INVALID_HANDLE_VALUE) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + listeners.removeLast(); return false; } - pendingHandles.enqueue(handle); + + memset(&listener.overlapped, 0, sizeof(listener.overlapped)); + listener.overlapped.hEvent = eventHandle; + if (!ConnectNamedPipe(listener.handle, &listener.overlapped)) { + switch (GetLastError()) { + case ERROR_IO_PENDING: + break; + case ERROR_PIPE_CONNECTED: + SetEvent(eventHandle); + break; + default: + CloseHandle(listener.handle); + setError(QLatin1String("QLocalServerPrivate::addListener")); + listeners.removeLast(); + return false; + } + } else { + Q_ASSERT_X(false, "QLocalServerPrivate::addListener", "The impossible happened"); + SetEvent(eventHandle); + } return true; } -void QLocalServerThread::run() +void QLocalServerPrivate::setError(const QString &function) { - OVERLAPPED op; - HANDLE handleArray[2]; - memset(&op, 0, sizeof(op)); - handleArray[0] = op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - handleArray[1] = stopEvent; - HANDLE handle = INVALID_HANDLE_VALUE; - - forever { - if (INVALID_HANDLE_VALUE == handle) { - makeHandle(); - if (!pendingHandles.isEmpty()) - handle = pendingHandles.dequeue(); - } - if (INVALID_HANDLE_VALUE == handle) { - int windowsError = GetLastError(); - QString function = QLatin1String("QLocalServer::run"); - QString errorString = QLocalServer::tr("%1: Unknown error %2").arg(function).arg(windowsError); - emit error(QAbstractSocket::UnknownSocketError, errorString); - CloseHandle(handleArray[0]); - SetEvent(gotConnectionEvent); - return; - } - - BOOL isConnected = ConnectNamedPipe(handle, &op) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); - if (!isConnected) { - switch (WaitForMultipleObjects(2, handleArray, FALSE, INFINITE)) - { - case WAIT_OBJECT_0 + 1: - CloseHandle(handle); - CloseHandle(handleArray[0]); - return; - } - } - emit connected(handle); - handle = INVALID_HANDLE_VALUE; - ResetEvent(handleArray[0]); - SetEvent(gotConnectionEvent); - } + int windowsError = GetLastError(); + errorString = QString::fromLatin1("%1: %2").arg(function).arg(qt_error_string(windowsError)); + error = QAbstractSocket::UnknownSocketError; } void QLocalServerPrivate::init() { - Q_Q(QLocalServer); - qRegisterMetaType<HANDLE>("HANDLE"); - q->connect(&waitForConnection, SIGNAL(connected(HANDLE)), - q, SLOT(_q_openSocket(HANDLE)), Qt::QueuedConnection); - q->connect(&waitForConnection, SIGNAL(finished()), - q, SLOT(_q_stoppedListening()), Qt::QueuedConnection); - q->connect(&waitForConnection, SIGNAL(terminated()), - q, SLOT(_q_stoppedListening()), Qt::QueuedConnection); - q->connect(&waitForConnection, SIGNAL(error(QAbstractSocket::SocketError, const QString &)), - q, SLOT(_q_setError(QAbstractSocket::SocketError, const QString &))); } bool QLocalServerPrivate::removeServer(const QString &name) @@ -201,35 +134,71 @@ bool QLocalServerPrivate::removeServer(const QString &name) bool QLocalServerPrivate::listen(const QString &name) { - fullServerName = waitForConnection.setName(name); - serverName = name; - waitForConnection.start(); - return true; -} + Q_Q(QLocalServer); -void QLocalServerPrivate::_q_setError(QAbstractSocket::SocketError e, const QString &eString) -{ - error = e; - errorString = eString; -} + QString pipePath = QLatin1String("\\\\.\\pipe\\"); + if (name.startsWith(pipePath)) + fullServerName = name; + else + fullServerName = pipePath + name; -void QLocalServerPrivate::_q_stoppedListening() -{ - Q_Q(QLocalServer); - if (!inWaitingFunction) - q->close(); + // Use only one event for all listeners of one socket. + // The idea is that listener events are rare, so polling all listeners once in a while is + // cheap compared to waiting for N additional events in each iteration of the main loop. + eventHandle = CreateEvent(NULL, TRUE, FALSE, NULL); + connectionEventNotifier = new QWinEventNotifier(eventHandle , q); + q->connect(connectionEventNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_onNewConnection())); + + for (int i = 0; i < SYSTEM_MAX_PENDING_SOCKETS; ++i) + if (!addListener()) + return false; + return true; } -void QLocalServerPrivate::_q_openSocket(HANDLE handle) +void QLocalServerPrivate::_q_onNewConnection() { Q_Q(QLocalServer); - q->incomingConnection((int)handle); + DWORD dummy; + + // Reset first, otherwise we could reset an event which was asserted + // immediately after we checked the conn status. + ResetEvent(eventHandle); + + // Testing shows that there is indeed absolutely no guarantee which listener gets + // a client connection first, so there is no way around polling all of them. + for (int i = 0; i < listeners.size(); ) { + HANDLE handle = listeners[i].handle; + if (GetOverlappedResult(handle, &listeners[i].overlapped, &dummy, FALSE)) { + listeners.removeAt(i); + + addListener(); + + if (pendingConnections.size() > maxPendingConnections) + connectionEventNotifier->setEnabled(false); + + // Make this the last thing so connected slots can wreak the least havoc + q->incomingConnection((quintptr)handle); + } else { + if (GetLastError() != ERROR_IO_INCOMPLETE) { + setError(QLatin1String("QLocalServerPrivate::_q_onNewConnection")); + closeServer(); + return; + } + + ++i; + } + } } void QLocalServerPrivate::closeServer() { - waitForConnection.stop(); - waitForConnection.closeServer(); + connectionEventNotifier->setEnabled(false); // Otherwise, closed handle is checked before deleter runs + connectionEventNotifier->deleteLater(); + connectionEventNotifier = 0; + CloseHandle(eventHandle); + for (int i = 0; i < listeners.size(); ++i) + CloseHandle(listeners[i].handle); + listeners.clear(); } void QLocalServerPrivate::waitForNewConnection(int msecs, bool *timedOut) @@ -238,14 +207,12 @@ void QLocalServerPrivate::waitForNewConnection(int msecs, bool *timedOut) if (!pendingConnections.isEmpty() || !q->isListening()) return; - DWORD result = WaitForSingleObject(waitForConnection.gotConnectionEvent, - (msecs == -1) ? INFINITE : msecs); + DWORD result = WaitForSingleObject(eventHandle, (msecs == -1) ? INFINITE : msecs); if (result == WAIT_TIMEOUT) { if (timedOut) *timedOut = true; } else { - ResetEvent(waitForConnection.gotConnectionEvent); - QCoreApplication::instance()->processEvents(); + _q_onNewConnection(); } } diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index ea64042..92054a4 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -113,8 +113,8 @@ readLine(), or getChar() to read decrypted data from QSslSocket's internal buffer, and you can call write() or putChar() to write data back to the peer. QSslSocket will automatically encrypt the - written data for you, and emit bytesWritten() once the data has - been written to the peer. + written data for you, and emit encryptedBytesWritten() once + the data has been written to the peer. As a convenience, QSslSocket supports QTcpSocket's blocking functions waitForConnected(), waitForReadyRead(), @@ -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 cb101af..32b02c5 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -500,7 +500,7 @@ void QSslSocketBackendPrivate::startClientEncryption() // Start connecting. This will place outgoing data in the BIO, so we // follow up with calling transmit(). - testConnection(); + startHandshake(); transmit(); } @@ -513,7 +513,7 @@ void QSslSocketBackendPrivate::startServerEncryption() // Start connecting. This will place outgoing data in the BIO, so we // follow up with calling transmit(). - testConnection(); + startHandshake(); transmit(); } @@ -601,7 +601,7 @@ void QSslSocketBackendPrivate::transmit() #ifdef QSSLSOCKET_DEBUG qDebug() << "QSslSocketBackendPrivate::transmit: testing encryption"; #endif - if (testConnection()) { + if (startHandshake()) { #ifdef QSSLSOCKET_DEBUG qDebug() << "QSslSocketBackendPrivate::transmit: encryption established"; #endif @@ -620,7 +620,7 @@ void QSslSocketBackendPrivate::transmit() } // If the request is small and the remote host closes the transmission - // after sending, there's a chance that testConnection() will already + // after sending, there's a chance that startHandshake() will already // have triggered a shutdown. if (!ssl) continue; @@ -720,7 +720,7 @@ static QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &c return error; } -bool QSslSocketBackendPrivate::testConnection() +bool QSslSocketBackendPrivate::startHandshake() { Q_Q(QSslSocket); @@ -761,7 +761,7 @@ bool QSslSocketBackendPrivate::testConnection() q->setErrorString(QSslSocket::tr("Error during SSL handshake: %1").arg(SSL_ERRORSTR())); q->setSocketError(QAbstractSocket::SslHandshakeFailedError); #ifdef QSSLSOCKET_DEBUG - qDebug() << "QSslSocketBackendPrivate::testConnection: error!" << q->errorString(); + qDebug() << "QSslSocketBackendPrivate::startHandshake: error!" << q->errorString(); #endif emit q->error(QAbstractSocket::SslHandshakeFailedError); q->abort(); @@ -792,7 +792,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_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index b3be42a..f53d4e8 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -102,7 +102,7 @@ public: void startClientEncryption(); void startServerEncryption(); void transmit(); - bool testConnection(); + bool startHandshake(); void disconnectFromHost(); void disconnected(); QSslCipher sessionCipher() const; 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/gl2paintengineex/glgc_shader_source.h b/src/opengl/gl2paintengineex/glgc_shader_source.h deleted file mode 100644 index 5b9d28b..0000000 --- a/src/opengl/gl2paintengineex/glgc_shader_source.h +++ /dev/null @@ -1,289 +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 QtOpenGL module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the 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 GLGC_SHADER_SOURCE_H -#define GLGC_SHADER_SOURCE_H - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(OpenGL) - -static const char* qglslImageVertexShader = "\ - attribute highp vec4 inputVertex; \ - attribute lowp vec2 textureCoord; \ - uniform highp mat4 pmvMatrix; \ - varying lowp vec2 fragTextureCoord; \ - void main(void) \ - {\ - gl_Position = pmvMatrix * inputVertex;\ - fragTextureCoord = textureCoord; \ - }"; - -static const char* qglslImageFragmentShader = "\ - varying lowp vec2 fragTextureCoord;\ - uniform sampler2D textureSampler;\ - uniform lowp float opacity; \ - void main(void) \ - {\ - gl_FragColor = texture2D(textureSampler, fragTextureCoord) * opacity; \ - }"; - -static const char* qglslTextFragmentShader = "\ - varying lowp vec2 fragTextureCoord;\ - uniform mediump vec4 fragmentColor;\ - uniform sampler2D textureSampler;\ - void main(void) \ - {\ - highp vec4 tex = texture2D(textureSampler, fragTextureCoord); \ - tex = fragmentColor * tex.r; \ - gl_FragColor = tex; \ - }"; - -static const char* qglslDefaultVertexShader = "\ - attribute highp vec4 inputVertex;\ - uniform highp mat4 pmvMatrix;\ - void main(void)\ - {\ - gl_Position = pmvMatrix * inputVertex;\ - }"; - -static const char* qglslSimpleFragmentShader = "\ - void main (void)\ - {\ - gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\ - }"; - - -/**** FRAGMENT SHADER MAIN FUNCTIONS ****/ -// NOTE: Currently, the engine assumes brushes return colors already in pre-multiplied -// format. However, this may change if we add support for non-premultiplied - -static const char* qglslNoOpacityFragmentShaderMain = "\n\ - mediump vec4 brush();\ - void main (void)\ - {\ - gl_FragColor = brush();\ - }\n"; - -static const char* qglslFragmentShaderMain = "\n\ - mediump vec4 brush();\ - uniform lowp float opacity; \ - void main (void)\ - {\ - gl_FragColor = brush() * opacity;\ - }\n"; - - - -/**** BRUSH SHADERS ****/ - -// This should never actually be used -static const char* qglslNoBrushFragmentShader = "\n\ - mediump vec4 brush() { \ - discard; \ - return vec4(1.0, 0.8, 0.8, 1.0);\ - }\n"; - -// Solid Fill Brush -static const char* qglslSolidBrushFragmentShader = "\n\ - uniform mediump vec4 fragmentColor; \ - mediump vec4 brush() { \ - return fragmentColor;\ - }\n"; - -// Texture Brush -static const char* qglslTextureBrushVertexShader = "\ - attribute highp vec4 inputVertex; \ - uniform highp mat4 pmvMatrix; \ - uniform mediump vec2 halfViewportSize; \ - uniform mediump vec2 invertedTextureSize; \ - uniform mediump mat3 brushTransform; \ - varying mediump vec2 texCoords; \ - void main(void) { \ - gl_Position = pmvMatrix * inputVertex;\ - gl_Position.xy = gl_Position.xy / gl_Position.w; \ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ - gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ - gl_Position.w = invertedHTexCoordsZ; \ - texCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \ - texCoords.y = -texCoords.y; \ - }"; - -static const char* qglslTextureBrushFragmentShader = "\n\ - varying mediump vec2 texCoords;\ - uniform sampler2D brushTexture;\ - mediump vec4 brush() { \ - return texture2D(brushTexture, texCoords); \ - }\n"; - - -// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125 -static const char* qglslPatternBrushVertexShader = "\ - attribute highp vec4 inputVertex; \ - uniform highp mat4 pmvMatrix; \ - uniform mediump vec2 halfViewportSize; \ - uniform mediump vec2 invertedTextureSize; \ - uniform mediump mat3 brushTransform; \ - varying mediump vec2 texCoords; \ - void main(void) { \ - gl_Position = pmvMatrix * inputVertex;\ - gl_Position.xy = gl_Position.xy / gl_Position.w; \ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ - gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ - gl_Position.w = invertedHTexCoordsZ; \ - texCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \ - texCoords.y = -texCoords.y; \ - }"; - -static const char* qglslPatternBrushFragmentShader = "\n\ - uniform sampler2D brushTexture;\ - uniform lowp vec4 patternColor; \ - varying mediump vec2 texCoords;\ - mediump vec4 brush() { \ - return patternColor * texture2D(brushTexture, texCoords).r; \ - }\n"; - - -// Linear Gradient Brush -static const char* qglslLinearGradientBrushVertexShader = "\ - attribute highp vec4 inputVertex; \ - uniform highp mat4 pmvMatrix; \ - uniform mediump vec2 halfViewportSize; \ - uniform highp vec3 linearData; \ - uniform mediump mat3 brushTransform; \ - varying mediump float index ; \ - void main() { \ - gl_Position = pmvMatrix * inputVertex;\ - gl_Position.xy = gl_Position.xy / gl_Position.w; \ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ - gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ - gl_Position.w = invertedHTexCoordsZ; \ - index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \ - }"; - -static const char* qglslLinearGradientBrushFragmentShader = "\n\ - uniform sampler2D brushTexture; \ - varying mediump float index; \ - mediump vec4 brush() { \ - mediump vec2 val = vec2(index, 0.5); \ - return texture2D(brushTexture, val); \ - }\n"; - - -static const char* qglslRadialGradientBrushVertexShader = "\ - attribute highp vec4 inputVertex;\ - uniform highp mat4 pmvMatrix;\ - uniform mediump vec2 halfViewportSize; \ - uniform highp mat3 brushTransform; \ - uniform highp vec2 fmp; \ - varying highp float b; \ - varying highp vec2 A; \ - void main(void) \ - {\ - gl_Position = pmvMatrix * inputVertex;\ - gl_Position.xy = gl_Position.xy / gl_Position.w; \ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ - gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ - gl_Position.w = invertedHTexCoordsZ; \ - A = hTexCoords.xy * invertedHTexCoordsZ; \ - b = 2.0 * fmp * (A.x + A.y); \ -\ - }"; - -static const char* qglslRadialGradientBrushFragmentShader = "\n\ - uniform sampler2D brushTexture; \ - uniform highp float fmp2_m_radius2; \ - uniform highp float inverse_2_fmp2_m_radius2; \ - varying highp float b; \ - varying highp vec2 A; \ -\ - mediump vec4 brush() { \ - highp float c = -dot(A, A); \ - highp vec2 val = vec2((-b + sqrt(b*b - 4.0*fmp2_m_radius2*c)) * inverse_2_fmp2_m_radius2, 0.5); \ - return texture2D(brushTexture, val); \ - }\n"; - -static const char* qglslConicalGradientBrushVertexShader = "\ - attribute highp vec4 inputVertex;\ - uniform highp mat4 pmvMatrix;\ - uniform mediump vec2 halfViewportSize; \ - uniform highp mat3 brushTransform; \ - varying highp vec2 A; \ - void main(void)\ - {\ - gl_Position = pmvMatrix * inputVertex;\ - gl_Position.xy = gl_Position.xy / gl_Position.w; \ - mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ - mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ - mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ - gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ - gl_Position.w = invertedHTexCoordsZ; \ - A = hTexCoords.xy * invertedHTexCoordsZ; \ - }"; - -static const char* qglslConicalGradientBrushFragmentShader = "\n\ - #define INVERSE_2PI 0.1591549430918953358 \n\ - uniform sampler2D brushTexture; \ - uniform mediump float angle; \ - varying highp vec2 A; \ - mediump vec4 brush() { \ - if (abs(A.y) == abs(A.x)) \ - A.y += 0.002; \ - highp float t = (atan2(-A.y, A.x) + angle) * INVERSE_2PI; \ - return texture2D(brushTexture, vec2(t - floor(t), 0.5)); \ - }\n"; - - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // GLGC_SHADER_SOURCE_H diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp index 0352d39..f237847 100644 --- a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp +++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp @@ -59,6 +59,12 @@ QGLRect QGL2PEXVertexArray::boundingRect() const return QGLRect(minX, minY, maxX, maxY); } +void QGL2PEXVertexArray::addRect(const QRectF &rect) +{ + vertexArray << rect.topLeft() << rect.topRight() << rect.bottomRight() + << rect.bottomRight() << rect.bottomLeft() << rect.topLeft(); +} + void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale) { const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h index c205022..a83f13e 100644 --- a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h +++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h @@ -62,7 +62,7 @@ public: QGLPoint(GLfloat new_x, GLfloat new_y) : x(new_x), y(new_y) {}; - QGLPoint(QPointF p) : + QGLPoint(const QPointF &p) : x(p.x()), y(p.y()) {}; QGLPoint(const QPointF* p) : @@ -77,7 +77,7 @@ public: struct QGLRect { - QGLRect(QRectF r) + QGLRect(const QRectF &r) : left(r.left()), top(r.top()), right(r.right()), bottom(r.bottom()) {} QGLRect(GLfloat l, GLfloat t, GLfloat r, GLfloat b) @@ -98,6 +98,7 @@ public: maxX(-2e10), maxY(-2e10), minX(2e10), minY(2e10), boundingRectDirty(true) {} + void addRect(const QRectF &rect); void addPath(const QVectorPath &path, GLfloat curveInverseScale); void clear(); diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp new file mode 100644 index 0000000..514aac0 --- /dev/null +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -0,0 +1,465 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qglengineshadermanager_p.h" +#include "qglengineshadersource_p.h" + +#if defined(QT_DEBUG) +#include <QMetaEnum> +#endif + + +const char* QGLEngineShaderManager::qglEngineShaderSourceCode[] = { + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0 +}; + +QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) + : ctx(context), + shaderProgNeedsChanging(true), + srcPixelType(Qt::NoBrush), + useGlobalOpacity(false), + maskType(NoMask), + useTextureCoords(false), + compositionMode(QPainter::CompositionMode_SourceOver), + simpleShaderProg(0), + currentShaderProg(0) +{ + memset(compiledShaders, 0, sizeof(compiledShaders)); + +/* + Rather than having the shader source array statically initialised, it is initialised + here instead. This is to allow new shader names to be inserted or existing names moved + around without having to change the order of the glsl strings. It is hoped this will + make future hard-to-find runtime bugs more obvious and generally give more solid code. +*/ + static bool qglEngineShaderSourceCodePopulated = false; + if (!qglEngineShaderSourceCodePopulated) { + + const char** code = qglEngineShaderSourceCode; // shortcut + + code[MainVertexShader] = qglslMainVertexShader; + code[MainWithTexCoordsVertexShader] = qglslMainWithTexCoordsVertexShader; + + code[PositionOnlyVertexShader] = qglslPositionOnlyVertexShader; + code[PositionWithPatternBrushVertexShader] = qglslPositionWithPatternBrushVertexShader; + code[PositionWithLinearGradientBrushVertexShader] = qglslPositionWithLinearGradientBrushVertexShader; + code[PositionWithConicalGradientBrushVertexShader] = qglslPositionWithConicalGradientBrushVertexShader; + code[PositionWithRadialGradientBrushVertexShader] = qglslPositionWithRadialGradientBrushVertexShader; + code[PositionWithTextureBrushVertexShader] = qglslPositionWithTextureBrushVertexShader; + code[AffinePositionWithPatternBrushVertexShader] = qglslAffinePositionWithPatternBrushVertexShader; + code[AffinePositionWithLinearGradientBrushVertexShader] = qglslAffinePositionWithLinearGradientBrushVertexShader; + code[AffinePositionWithConicalGradientBrushVertexShader] = qglslAffinePositionWithConicalGradientBrushVertexShader; + code[AffinePositionWithRadialGradientBrushVertexShader] = qglslAffinePositionWithRadialGradientBrushVertexShader; + code[AffinePositionWithTextureBrushVertexShader] = qglslAffinePositionWithTextureBrushVertexShader; + + code[MainFragmentShader_CMO] = qglslMainFragmentShader_CMO; + code[MainFragmentShader_CM] = qglslMainFragmentShader_CM; + code[MainFragmentShader_MO] = qglslMainFragmentShader_MO; + code[MainFragmentShader_M] = qglslMainFragmentShader_M; + code[MainFragmentShader_CO] = qglslMainFragmentShader_CO; + code[MainFragmentShader_C] = qglslMainFragmentShader_C; + code[MainFragmentShader_O] = qglslMainFragmentShader_O; + code[MainFragmentShader] = qglslMainFragmentShader; + + code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader; + code[NonPremultipliedImageSrcFragmentShader] = qglslNonPremultipliedImageSrcFragmentShader; + code[SolidBrushSrcFragmentShader] = qglslSolidBrushSrcFragmentShader; + code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader; + code[PatternBrushSrcFragmentShader] = qglslPatternBrushSrcFragmentShader; + code[LinearGradientBrushSrcFragmentShader] = qglslLinearGradientBrushSrcFragmentShader; + code[RadialGradientBrushSrcFragmentShader] = qglslRadialGradientBrushSrcFragmentShader; + code[ConicalGradientBrushSrcFragmentShader] = qglslConicalGradientBrushSrcFragmentShader; + code[ShockingPinkSrcFragmentShader] = qglslShockingPinkSrcFragmentShader; + + code[MaskFragmentShader] = qglslMaskFragmentShader; + code[RgbMaskFragmentShader] = ""; //### + code[RgbMaskWithGammaFragmentShader] = ""; //### + + code[MultiplyCompositionModeFragmentShader] = ""; //### + code[ScreenCompositionModeFragmentShader] = ""; //### + code[OverlayCompositionModeFragmentShader] = ""; //### + code[DarkenCompositionModeFragmentShader] = ""; //### + code[LightenCompositionModeFragmentShader] = ""; //### + code[ColorDodgeCompositionModeFragmentShader] = ""; //### + code[ColorBurnCompositionModeFragmentShader] = ""; //### + code[HardLightCompositionModeFragmentShader] = ""; //### + code[SoftLightCompositionModeFragmentShader] = ""; //### + code[DifferenceCompositionModeFragmentShader] = ""; //### + code[ExclusionCompositionModeFragmentShader] = ""; //### + +#if defined(QT_DEBUG) + // Check that all the elements have been filled: + for (int i = 0; i < TotalShaderCount; ++i) { + if (qglEngineShaderSourceCode[i] == 0) { + int enumIndex = staticMetaObject.indexOfEnumerator("ShaderName"); + QMetaEnum m = staticMetaObject.enumerator(enumIndex); + + qCritical() << "qglEngineShaderSourceCode: Source for" << m.valueToKey(i) + << "(shader" << i << ") missing!"; + } + } +#endif + qglEngineShaderSourceCodePopulated = true; + } + + // Compile up the simple shader: + simpleShaderProg = new QGLShaderProgram(ctx, this); + compileNamedShader(MainVertexShader, QGLShader::PartialVertexShader); + compileNamedShader(PositionOnlyVertexShader, QGLShader::PartialVertexShader); + compileNamedShader(MainFragmentShader, QGLShader::PartialFragmentShader); + compileNamedShader(ShockingPinkSrcFragmentShader, QGLShader::PartialFragmentShader); + simpleShaderProg->addShader(compiledShaders[MainVertexShader]); + simpleShaderProg->addShader(compiledShaders[PositionOnlyVertexShader]); + simpleShaderProg->addShader(compiledShaders[MainFragmentShader]); + simpleShaderProg->addShader(compiledShaders[ShockingPinkSrcFragmentShader]); + simpleShaderProg->link(); + if (!simpleShaderProg->isLinked()) { + qCritical() << "Errors linking simple shader:" + << simpleShaderProg->log(); + } +} + +QGLEngineShaderManager::~QGLEngineShaderManager() +{ + //### +} + + + + + +void QGLEngineShaderManager::optimiseForBrushTransform(const QTransform &transform) +{ + Q_UNUSED(transform); // Currently ignored +} + +void QGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style) +{ + srcPixelType = style; + shaderProgNeedsChanging = true; //### +} + +void QGLEngineShaderManager::setSrcPixelType(PixelSrcType type) +{ + srcPixelType = type; + shaderProgNeedsChanging = true; //### +} + +void QGLEngineShaderManager::setTextureCoordsEnabled(bool enabled) +{ + useTextureCoords = enabled; + shaderProgNeedsChanging = true; //### +} + +void QGLEngineShaderManager::setUseGlobalOpacity(bool useOpacity) +{ + useGlobalOpacity = useOpacity; + shaderProgNeedsChanging = true; //### +} + +void QGLEngineShaderManager::setMaskType(MaskType type) +{ + maskType = type; + shaderProgNeedsChanging = true; //### +} + +void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode) +{ + compositionMode = mode; + shaderProgNeedsChanging = true; //### +} + +QGLShaderProgram* QGLEngineShaderManager::currentProgram() +{ + return currentShaderProg; +} + +QGLShaderProgram* QGLEngineShaderManager::simpleProgram() +{ + return simpleShaderProg; +} + + + + +// Select & use the correct shader program using the current state. +// Returns true if program needed changing. +bool QGLEngineShaderManager::useCorrectShaderProg() +{ + if (!shaderProgNeedsChanging) + return false; + + QGLEngineShaderProg requiredProgram; + requiredProgram.program = 0; + + // Choose vertex shader main function + QGLEngineShaderManager::ShaderName mainVertexShaderName = InvalidShaderName; + if (useTextureCoords) + mainVertexShaderName = MainWithTexCoordsVertexShader; + else + mainVertexShaderName = MainVertexShader; + compileNamedShader(mainVertexShaderName, QGLShader::PartialVertexShader); + requiredProgram.mainVertexShader = compiledShaders[mainVertexShaderName]; + + // Choose vertex shader shader position function (which typically also sets + // varyings) and the source pixel (srcPixel) fragment shader function: + QGLEngineShaderManager::ShaderName positionVertexShaderName = InvalidShaderName; + QGLEngineShaderManager::ShaderName srcPixelFragShaderName = InvalidShaderName; + bool isAffine = brushTransform.isAffine(); + if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) { + if (isAffine) + positionVertexShaderName = AffinePositionWithPatternBrushVertexShader; + else + positionVertexShaderName = PositionWithPatternBrushVertexShader; + + srcPixelFragShaderName = PatternBrushSrcFragmentShader; + } + else switch (srcPixelType) { + default: + case Qt::NoBrush: + qCritical("QGLEngineShaderManager::useCorrectShaderProg() - I'm scared, Qt::NoBrush style is set"); + break; + case QGLEngineShaderManager::ImageSrc: + srcPixelFragShaderName = ImageSrcFragmentShader; + positionVertexShaderName = PositionOnlyVertexShader; + break; + case QGLEngineShaderManager::NonPremultipliedImageSrc: + srcPixelFragShaderName = NonPremultipliedImageSrcFragmentShader; + positionVertexShaderName = PositionOnlyVertexShader; + break; + case Qt::SolidPattern: + srcPixelFragShaderName = SolidBrushSrcFragmentShader; + positionVertexShaderName = PositionOnlyVertexShader; + break; + case Qt::LinearGradientPattern: + srcPixelFragShaderName = LinearGradientBrushSrcFragmentShader; + positionVertexShaderName = isAffine ? AffinePositionWithLinearGradientBrushVertexShader + : PositionWithLinearGradientBrushVertexShader; + break; + case Qt::ConicalGradientPattern: + srcPixelFragShaderName = ConicalGradientBrushSrcFragmentShader; + positionVertexShaderName = isAffine ? AffinePositionWithConicalGradientBrushVertexShader + : PositionWithConicalGradientBrushVertexShader; + break; + case Qt::RadialGradientPattern: + srcPixelFragShaderName = RadialGradientBrushSrcFragmentShader; + positionVertexShaderName = isAffine ? AffinePositionWithRadialGradientBrushVertexShader + : PositionWithRadialGradientBrushVertexShader; + break; + case Qt::TexturePattern: + srcPixelFragShaderName = TextureBrushSrcFragmentShader; + positionVertexShaderName = isAffine ? AffinePositionWithTextureBrushVertexShader + : PositionWithTextureBrushVertexShader; + break; + }; + compileNamedShader(positionVertexShaderName, QGLShader::PartialVertexShader); + compileNamedShader(srcPixelFragShaderName, QGLShader::PartialFragmentShader); + requiredProgram.positionVertexShader = compiledShaders[positionVertexShaderName]; + requiredProgram.srcPixelFragShader = compiledShaders[srcPixelFragShaderName]; + + + const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus; + const bool hasMask = maskType != QGLEngineShaderManager::NoMask; + + // Choose fragment shader main function: + QGLEngineShaderManager::ShaderName mainFragShaderName; + + if (hasCompose && hasMask && useGlobalOpacity) + mainFragShaderName = MainFragmentShader_CMO; + if (hasCompose && hasMask && !useGlobalOpacity) + mainFragShaderName = MainFragmentShader_CM; + if (!hasCompose && hasMask && useGlobalOpacity) + mainFragShaderName = MainFragmentShader_MO; + if (!hasCompose && hasMask && !useGlobalOpacity) + mainFragShaderName = MainFragmentShader_M; + if (hasCompose && !hasMask && useGlobalOpacity) + mainFragShaderName = MainFragmentShader_CO; + if (hasCompose && !hasMask && !useGlobalOpacity) + mainFragShaderName = MainFragmentShader_C; + if (!hasCompose && !hasMask && useGlobalOpacity) + mainFragShaderName = MainFragmentShader_O; + if (!hasCompose && !hasMask && !useGlobalOpacity) + mainFragShaderName = MainFragmentShader; + + compileNamedShader(mainFragShaderName, QGLShader::PartialFragmentShader); + requiredProgram.mainFragShader = compiledShaders[mainFragShaderName]; + + if (hasMask) { + QGLEngineShaderManager::ShaderName maskShaderName = QGLEngineShaderManager::InvalidShaderName; + if (maskType == PixelMask) + maskShaderName = MaskFragmentShader; + else if (maskType == SubPixelMask) + maskShaderName = RgbMaskFragmentShader; + else if (maskType == SubPixelWithGammaMask) + maskShaderName = RgbMaskWithGammaFragmentShader; + else + qCritical("QGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type"); + + compileNamedShader(maskShaderName, QGLShader::PartialFragmentShader); + requiredProgram.maskFragShader = compiledShaders[maskShaderName]; + } + else + requiredProgram.maskFragShader = 0; + + if (hasCompose) { + QGLEngineShaderManager::ShaderName compositionShaderName = QGLEngineShaderManager::InvalidShaderName; + switch (compositionMode) { + case QPainter::CompositionMode_Multiply: + compositionShaderName = MultiplyCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Screen: + compositionShaderName = ScreenCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Overlay: + compositionShaderName = OverlayCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Darken: + compositionShaderName = DarkenCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Lighten: + compositionShaderName = LightenCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_ColorDodge: + compositionShaderName = ColorDodgeCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_ColorBurn: + compositionShaderName = ColorBurnCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_HardLight: + compositionShaderName = HardLightCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_SoftLight: + compositionShaderName = SoftLightCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Difference: + compositionShaderName = DifferenceCompositionModeFragmentShader; + break; + case QPainter::CompositionMode_Exclusion: + compositionShaderName = ExclusionCompositionModeFragmentShader; + break; + default: + qWarning("QGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode"); + } + compileNamedShader(compositionShaderName, QGLShader::PartialFragmentShader); + requiredProgram.compositionFragShader = compiledShaders[compositionShaderName]; + } + else + requiredProgram.compositionFragShader = 0; + + + // At this point, requiredProgram is fully populated so try to find the program in the cache + foreach (const QGLEngineShaderProg &prog, cachedPrograms) { + if ( (prog.mainVertexShader == requiredProgram.mainVertexShader) + && (prog.positionVertexShader == requiredProgram.positionVertexShader) + && (prog.mainFragShader == requiredProgram.mainFragShader) + && (prog.srcPixelFragShader == requiredProgram.srcPixelFragShader) + && (prog.compositionFragShader == requiredProgram.compositionFragShader) ) + { + currentShaderProg = prog.program; + currentShaderProg->enable(); + shaderProgNeedsChanging = false; + return true; + } + } + + // Shader program not found in cache, create it now. + requiredProgram.program = new QGLShaderProgram(ctx, this); + requiredProgram.program->addShader(requiredProgram.mainVertexShader); + requiredProgram.program->addShader(requiredProgram.positionVertexShader); + requiredProgram.program->addShader(requiredProgram.mainFragShader); + requiredProgram.program->addShader(requiredProgram.srcPixelFragShader); + requiredProgram.program->addShader(requiredProgram.maskFragShader); + requiredProgram.program->addShader(requiredProgram.compositionFragShader); + + // We have to bind the vertex attribute names before the program is linked: + requiredProgram.program->bindAttributeLocation("inputVertex", QT_VERTEX_COORDS_ATTR); + if (useTextureCoords) + requiredProgram.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + + requiredProgram.program->link(); + if (!requiredProgram.program->isLinked()) { + QString error; + qWarning() << "Shader program failed to link," +#if defined(QT_DEBUG) + << '\n' + << " Shaders Used:" << '\n' + << " mainVertexShader = " << requiredProgram.mainVertexShader->objectName() << '\n' + << " positionVertexShader = " << requiredProgram.positionVertexShader->objectName() << '\n' + << " mainFragShader = " << requiredProgram.mainFragShader->objectName() << '\n' + << " srcPixelFragShader = " << requiredProgram.srcPixelFragShader->objectName() << '\n' + << " maskFragShader = " << requiredProgram.maskFragShader->objectName() << '\n' + << " compositionFragShader = "<< requiredProgram.compositionFragShader->objectName() << '\n' +#endif + << " Error Log:" << '\n' + << " " << requiredProgram.program->log(); + qWarning() << error; + } + else { + cachedPrograms.append(requiredProgram); + currentShaderProg = requiredProgram.program; + currentShaderProg->enable(); + } + shaderProgNeedsChanging = false; + return true; +} + +void QGLEngineShaderManager::compileNamedShader(QGLEngineShaderManager::ShaderName name, QGLShader::ShaderType type) +{ + if (compiledShaders[name]) + return; + + QGLShader *newShader = new QGLShader(type, ctx, this); + newShader->setSourceCode(qglEngineShaderSourceCode[name]); + // newShader->compile(); ### does not exist? + +#if defined(QT_DEBUG) + // Name the shader for easier debugging + QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("ShaderName")); + newShader->setObjectName(QLatin1String(m.valueToKey(name))); +#endif + + compiledShaders[name] = newShader; +} + + diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h new file mode 100644 index 0000000..b8b2745 --- /dev/null +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -0,0 +1,385 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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$ +** +****************************************************************************/ + +// +// 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. +// + +/* + VERTEX SHADERS + ============== + + Vertex shaders are specified as multiple (partial) shaders. On desktop, + this works fine. On ES, QGLShader & QGLShaderProgram will make partial + shaders work by concatenating the source in each QGLShader and compiling + it as a single shader. This is abstracted nicely by QGLShaderProgram and + the GL2 engine doesn't need to worry about it. + + Generally, there's two vertex shader objects. The position shaders are + the ones which set gl_Position. There's also two "main" vertex shaders, + one which just calls the position shader and another which also passes + through some texture coordinates from a vertex attribute array to a + varying. These texture coordinates are used for mask position in text + rendering and for the source coordinates in drawImage/drawPixmap. There's + also a "Simple" vertex shader for rendering a solid colour (used to render + into the stencil buffer where the actual colour value is discarded). + + The position shaders for brushes look scary. This is because many of the + calculations which logically belong in the fragment shader have been moved + into the vertex shader to improve performance. This is why the position + calculation is in a seperate shader. Not only does it calculate the + position, but it also calculates some data to be passed to the fragment + shader as a varying. It is optimal to move as much of the calculation as + possible into the vertex shader as this is executed less often. + + The varyings passed to the fragment shaders are interpolated (which is + cheap). Unfortunately, GL will apply perspective correction to the + interpolation calusing errors. To get around this, the vertex shader must + apply perspective correction itself and set the w-value of gl_Position to + zero. That way, GL will be tricked into thinking it doesn't need to apply a + perspective correction and use linear interpolation instead (which is what + we want). Of course, if the brush transform is affeine, no perspective + correction is needed and a simpler vertex shader can be used instead. + + So there are the following "main" vertex shaders: + qglslMainVertexShader + qglslMainWithTexCoordsVertexShader + + And the the following position vertex shaders: + qglslPositionOnlyVertexShader + qglslPositionWithTextureBrushVertexShader + qglslPositionWithPatternBrushVertexShader + qglslPositionWithLinearGradientBrushVertexShader + qglslPositionWithRadialGradientBrushVertexShader + qglslPositionWithConicalGradientBrushVertexShader + qglslAffinePositionWithTextureBrushVertexShader + qglslAffinePositionWithPatternBrushVertexShader + qglslAffinePositionWithLinearGradientBrushVertexShader + qglslAffinePositionWithRadialGradientBrushVertexShader + qglslAffinePositionWithConicalGradientBrushVertexShader + + Leading to 23 possible vertex shaders + + + FRAGMENT SHADERS + ================ + + Fragment shaders are also specified as multiple (partial) shaders. The + different fragment shaders represent the different stages in Qt's fragment + pipeline. There are 1-3 stages in this pipeline: First stage is to get the + fragment's colour value. The next stage is to get the fragment's mask value + (coverage value for anti-aliasing) and the final stage is to blend the + incoming fragment with the background (for composition modes not supported + by GL). + + Of these, the first stage will always be present. If Qt doesn't need to + apply anti-aliasing (because it's off or handled by multisampling) then + the coverage value doesn't need to be applied. (Note: There are two types + of mask, one for regular anti-aliasing and one for sub-pixel anti- + aliasing.) If the composition mode is one which GL supports natively then + the blending stage doesn't need to be applied. + + As eash stage can have multiple implementations, they are abstracted as + GLSL function calls with the following signatures: + + Brushes & image drawing are implementations of "qcolorp vec4 srcPixel()": + qglslImageSrcFragShader + qglslNonPremultipliedImageSrcFragShader + qglslSolidBrushSrcFragShader + qglslTextureBrushSrcFragShader + qglslPatternBrushSrcFragShader + qglslLinearGradientBrushSrcFragShader + qglslRadialGradientBrushSrcFragShader + qglslConicalGradientBrushSrcFragShader + NOTE: It is assumed the colour returned by srcPixel() is pre-multiplied + + Masks are implementations of "qcolorp vec4 applyMask(qcolorp vec4 src)": + qglslMaskFragmentShader + qglslRgbMaskFragmentShader + qglslRgbMaskWithGammaFragmentShader + + Composition modes are "qcolorp vec4 compose(qcolorp vec4 src)": + qglslColorBurnCompositionModeFragmentShader + qglslColorDodgeCompositionModeFragmentShader + qglslDarkenCompositionModeFragmentShader + qglslDifferenceCompositionModeFragmentShader + qglslExclusionCompositionModeFragmentShader + qglslHardLightCompositionModeFragmentShader + qglslLightenCompositionModeFragmentShader + qglslMultiplyCompositionModeFragmentShader + qglslOverlayCompositionModeFragmentShader + qglslScreenCompositionModeFragmentShader + qglslSoftLightCompositionModeFragmentShader + + + Note: In the future, some GLSL compilers will support an extension allowing + a new 'color' precision specifier. To support this, qcolorp is used for + all color components so it can be defined to colorp or lowp depending upon + the implementation. + + So there are differnt frament shader main functions, depending on the + number & type of pipelines the fragment needs to go through. + + The choice of which main() fragment shader string to use depends on: + - Use of global opacity + - Brush style (some brushes apply opacity themselves) + - Use & type of mask (TODO: Need to support high quality anti-aliasing & text) + - Use of non-GL Composition mode + + Leading to the following fragment shader main functions: + gl_FragColor = compose(applyMask(srcPixel()*globalOpacity)); + gl_FragColor = compose(applyMask(srcPixel())); + gl_FragColor = applyMask(srcPixel()*globalOpacity); + gl_FragColor = applyMask(srcPixel()); + gl_FragColor = compose(srcPixel()*globalOpacity); + gl_FragColor = compose(srcPixel()); + gl_FragColor = srcPixel()*globalOpacity; + gl_FragColor = srcPixel(); + + Called: + qglslMainFragmentShader_CMO + qglslMainFragmentShader_CM + qglslMainFragmentShader_MO + qglslMainFragmentShader_M + qglslMainFragmentShader_CO + qglslMainFragmentShader_C + qglslMainFragmentShader_O + qglslMainFragmentShader + + Where: + M = Mask + C = Composition + O = Global Opacity + + + CUSTOM SHADER CODE (idea, depricated) + ================== + + The use of custom shader code is supported by the engine for drawImage and + drawPixmap calls. This is implemented via hooks in the fragment pipeline. + The custom shader is passed to the engine as a partial fragment shader + (QGLCustomizedShader). The shader will implement a pre-defined method name + which Qt's fragment pipeline will call. There are two different hooks which + can be implemented as custom shader code: + + mediump vec4 customShader(sampler2d src, vec2 srcCoords) + mediump vec4 customShaderWithDest(sampler2d dest, sampler2d src, vec2 srcCoords) + +*/ + +#ifndef QGLENGINE_SHADER_MANAGER_H +#define QGLENGINE_SHADER_MANAGER_H + +#include <QGLShader> +#include <QGLShaderProgram> +#include <QPainter> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(OpenGL) + + +struct QGLEngineShaderProg +{ + QGLShader* mainVertexShader; + QGLShader* positionVertexShader; + QGLShader* mainFragShader; + QGLShader* srcPixelFragShader; + QGLShader* maskFragShader; // Can be null for no mask + QGLShader* compositionFragShader; // Can be null for GL-handled mode + QGLShaderProgram* program; +}; + +/* +struct QGLEngineCachedShaderProg +{ + QGLEngineCachedShaderProg(QGLEngineShaderManager::ShaderName vertexMain, + QGLEngineShaderManager::ShaderName vertexPosition, + QGLEngineShaderManager::ShaderName fragMain, + QGLEngineShaderManager::ShaderName pixelSrc, + QGLEngineShaderManager::ShaderName mask, + QGLEngineShaderManager::ShaderName composition); + + int cacheKey; + QGLShaderProgram* program; +} +*/ + +static const GLuint QT_VERTEX_COORDS_ATTR = 0; +static const GLuint QT_TEXTURE_COORDS_ATTR = 1; + +class QGLEngineShaderManager : public QObject +{ + Q_OBJECT; +public: + QGLEngineShaderManager(QGLContext* context); + ~QGLEngineShaderManager(); + + enum MaskType {NoMask, PixelMask, SubPixelMask, SubPixelWithGammaMask}; + enum PixelSrcType { + ImageSrc = Qt::TexturePattern+1, + NonPremultipliedImageSrc = Qt::TexturePattern+2 + }; + + // There are optimisations we can do, depending on the brush transform: + // 1) May not have to apply perspective-correction + // 2) Can use lower precision for matrix + void optimiseForBrushTransform(const QTransform &transform); + void setSrcPixelType(Qt::BrushStyle); + void setSrcPixelType(PixelSrcType); // For non-brush sources, like pixmaps & images + void setTextureCoordsEnabled(bool); // For images & text glyphs + void setUseGlobalOpacity(bool); + void setMaskType(MaskType); + void setCompositionMode(QPainter::CompositionMode); + + bool useCorrectShaderProg(); // returns true if the shader program needed to be changed + + QGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen + QGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers + + enum ShaderName { + MainVertexShader, + MainWithTexCoordsVertexShader, + + PositionOnlyVertexShader, + PositionWithPatternBrushVertexShader, + PositionWithLinearGradientBrushVertexShader, + PositionWithConicalGradientBrushVertexShader, + PositionWithRadialGradientBrushVertexShader, + PositionWithTextureBrushVertexShader, + AffinePositionWithPatternBrushVertexShader, + AffinePositionWithLinearGradientBrushVertexShader, + AffinePositionWithConicalGradientBrushVertexShader, + AffinePositionWithRadialGradientBrushVertexShader, + AffinePositionWithTextureBrushVertexShader, + + MainFragmentShader_CMO, + MainFragmentShader_CM, + MainFragmentShader_MO, + MainFragmentShader_M, + MainFragmentShader_CO, + MainFragmentShader_C, + MainFragmentShader_O, + MainFragmentShader, + + ImageSrcFragmentShader, + NonPremultipliedImageSrcFragmentShader, + SolidBrushSrcFragmentShader, + TextureBrushSrcFragmentShader, + PatternBrushSrcFragmentShader, + LinearGradientBrushSrcFragmentShader, + RadialGradientBrushSrcFragmentShader, + ConicalGradientBrushSrcFragmentShader, + ShockingPinkSrcFragmentShader, + + MaskFragmentShader, + RgbMaskFragmentShader, + RgbMaskWithGammaFragmentShader, + + MultiplyCompositionModeFragmentShader, + ScreenCompositionModeFragmentShader, + OverlayCompositionModeFragmentShader, + DarkenCompositionModeFragmentShader, + LightenCompositionModeFragmentShader, + ColorDodgeCompositionModeFragmentShader, + ColorBurnCompositionModeFragmentShader, + HardLightCompositionModeFragmentShader, + SoftLightCompositionModeFragmentShader, + DifferenceCompositionModeFragmentShader, + ExclusionCompositionModeFragmentShader, + + TotalShaderCount, InvalidShaderName + }; + +/* + // These allow the ShaderName enum to be used as a cache key + const int mainVertexOffset = 0; + const int positionVertexOffset = (1<<2) - PositionOnlyVertexShader; + const int mainFragOffset = (1<<6) - MainFragmentShader_CMO; + const int srcPixelOffset = (1<<10) - ImageSrcFragmentShader; + const int maskOffset = (1<<14) - NoMaskShader; + const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader; +*/ + +#if defined (QT_DEBUG) + Q_ENUMS(ShaderName); +#endif + + +private: + QGLContext* ctx; + bool shaderProgNeedsChanging; + + // Current state variables which influence the choice of shader: + QTransform brushTransform; + int srcPixelType; + bool useGlobalOpacity; + MaskType maskType; + bool useTextureCoords; + QPainter::CompositionMode compositionMode; + + QGLShaderProgram* simpleShaderProg; + QGLShaderProgram* currentShaderProg; + + // TODO: Possibly convert to a LUT + QList<QGLEngineShaderProg> cachedPrograms; + + QGLShader* compiledShaders[TotalShaderCount]; + + void compileNamedShader(QGLEngineShaderManager::ShaderName name, QGLShader::ShaderType type); + + static const char* qglEngineShaderSourceCode[TotalShaderCount]; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QGLENGINE_SHADER_MANAGER_H diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h new file mode 100644 index 0000000..fdbba72 --- /dev/null +++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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$ +** +****************************************************************************/ + +// +// 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. +// + + +#ifndef QGL_ENGINE_SHADER_SOURCE_H +#define QGL_ENGINE_SHADER_SOURCE_H + +#include "qglengineshadermanager_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(OpenGL) + + +static const char* const qglslMainVertexShader = "\ + void setPosition();\ + void main(void)\ + {\ + setPosition();\ + }"; + +static const char* const qglslMainWithTexCoordsVertexShader = "\ + attribute lowp vec2 textureCoordArray; \ + varying lowp vec2 textureCoords; \ + void setPosition();\ + void main(void) \ + {\ + setPosition();\ + textureCoords = textureCoordArray; \ + }"; + + +static const char* const qglslPositionOnlyVertexShader = "\ + attribute highp vec4 vertexCoordsArray;\ + uniform highp mat4 pmvMatrix;\ + void setPosition(void)\ + {\ + gl_Position = pmvMatrix * vertexCoordsArray;\ + }"; + + +// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125 +static const char* const qglslPositionWithPatternBrushVertexShader = "\ + attribute highp vec4 vertexCoordsArray; \ + uniform highp mat4 pmvMatrix; \ + uniform mediump vec2 halfViewportSize; \ + uniform mediump vec2 invertedTextureSize; \ + uniform mediump mat3 brushTransform; \ + varying mediump vec2 patternTexCoords; \ + void setPosition(void) { \ + gl_Position = pmvMatrix * vertexCoordsArray;\ + gl_Position.xy = gl_Position.xy / gl_Position.w; \ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ + gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ + gl_Position.w = invertedHTexCoordsZ; \ + patternTexCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \ + patternTexCoords.y = -patternTexCoords.y; \ + }"; + +static const char* const qglslAffinePositionWithPatternBrushVertexShader + = qglslPositionWithPatternBrushVertexShader; + +static const char* const qglslPatternBrushSrcFragmentShader = "\ + uniform sampler2D brushTexture;\ + uniform lowp vec4 patternColor; \ + varying mediump vec2 patternTexCoords;\ + lowp vec4 srcPixel() { \ + return patternColor * texture2D(brushTexture, patternTexCoords).r; \ + }\n"; + + +// Linear Gradient Brush +static const char* const qglslPositionWithLinearGradientBrushVertexShader = "\ + attribute highp vec4 vertexCoordsArray; \ + uniform highp mat4 pmvMatrix; \ + uniform mediump vec2 halfViewportSize; \ + uniform highp vec3 linearData; \ + uniform highp mat3 brushTransform; \ + varying mediump float index ; \ + void setPosition() { \ + gl_Position = pmvMatrix * vertexCoordsArray;\ + gl_Position.xy = gl_Position.xy / gl_Position.w; \ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ + gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ + gl_Position.w = invertedHTexCoordsZ; \ + index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \ + }"; + +static const char* const qglslAffinePositionWithLinearGradientBrushVertexShader + = qglslPositionWithLinearGradientBrushVertexShader; + +static const char* const qglslLinearGradientBrushSrcFragmentShader = "\ + uniform sampler2D brushTexture; \ + varying mediump float index; \ + lowp vec4 srcPixel() { \ + mediump vec2 val = vec2(index, 0.5); \ + return texture2D(brushTexture, val); \ + }\n"; + + +// Conical Gradient Brush +static const char* const qglslPositionWithConicalGradientBrushVertexShader = "\ + attribute highp vec4 vertexCoordsArray;\ + uniform highp mat4 pmvMatrix;\ + uniform mediump vec2 halfViewportSize; \ + uniform highp mat3 brushTransform; \ + varying highp vec2 A; \ + void setPosition(void)\ + {\ + gl_Position = pmvMatrix * vertexCoordsArray;\ + gl_Position.xy = gl_Position.xy / gl_Position.w; \ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ + gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ + gl_Position.w = invertedHTexCoordsZ; \ + A = hTexCoords.xy * invertedHTexCoordsZ; \ + }"; + +static const char* const qglslAffinePositionWithConicalGradientBrushVertexShader + = qglslPositionWithConicalGradientBrushVertexShader; + +static const char* const qglslConicalGradientBrushSrcFragmentShader = "\n\ + #define INVERSE_2PI 0.1591549430918953358 \n\ + uniform sampler2D brushTexture; \n\ + uniform mediump float angle; \ + varying highp vec2 A; \ + lowp vec4 srcPixel() { \ + highp float t; \ + if (abs(A.y) == abs(A.x)) \ + t = (atan(-A.y + 0.002, A.x) + angle) * INVERSE_2PI; \ + else \ + t = (atan(-A.y, A.x) + angle) * INVERSE_2PI; \ + return texture2D(brushTexture, vec2(t - floor(t), 0.5)); \ + }"; + + +// Radial Gradient Brush +static const char* const qglslPositionWithRadialGradientBrushVertexShader = "\ + attribute highp vec4 vertexCoordsArray;\ + uniform highp mat4 pmvMatrix;\ + uniform mediump vec2 halfViewportSize; \ + uniform highp mat3 brushTransform; \ + uniform highp vec2 fmp; \ + varying highp float b; \ + varying highp vec2 A; \ + void setPosition(void) \ + {\ + gl_Position = pmvMatrix * vertexCoordsArray;\ + gl_Position.xy = gl_Position.xy / gl_Position.w; \ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ + gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ + gl_Position.w = invertedHTexCoordsZ; \ + A = hTexCoords.xy * invertedHTexCoordsZ; \ + b = 2.0 * dot(A, fmp); \ + }"; + +static const char* const qglslAffinePositionWithRadialGradientBrushVertexShader + = qglslPositionWithRadialGradientBrushVertexShader; + +static const char* const qglslRadialGradientBrushSrcFragmentShader = "\ + uniform sampler2D brushTexture; \ + uniform highp float fmp2_m_radius2; \ + uniform highp float inverse_2_fmp2_m_radius2; \ + varying highp float b; \ + varying highp vec2 A; \ + lowp vec4 srcPixel() { \ + highp float c = -dot(A, A); \ + highp vec2 val = vec2((-b + sqrt(b*b - 4.0*fmp2_m_radius2*c)) * inverse_2_fmp2_m_radius2, 0.5); \ + return texture2D(brushTexture, val); \ + }"; + + +// Texture Brush +static const char* const qglslPositionWithTextureBrushVertexShader = "\ + attribute highp vec4 vertexCoordsArray; \ + uniform highp mat4 pmvMatrix; \ + uniform mediump vec2 halfViewportSize; \ + uniform mediump vec2 invertedTextureSize; \ + uniform mediump mat3 brushTransform; \ + varying mediump vec2 brushTextureCoords; \ + void setPosition(void) { \ + gl_Position = pmvMatrix * vertexCoordsArray;\ + gl_Position.xy = gl_Position.xy / gl_Position.w; \ + mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \ + mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \ + mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \ + gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \ + gl_Position.w = invertedHTexCoordsZ; \ + brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \ + brushTextureCoords.y = -brushTextureCoords.y; \ + }"; + +static const char* const qglslAffinePositionWithTextureBrushVertexShader + = qglslPositionWithTextureBrushVertexShader; + +static const char* const qglslTextureBrushSrcFragmentShader = "\ + varying mediump vec2 brushTextureCoords; \ + uniform sampler2D brushTexture; \ + lowp vec4 srcPixel() { \ + return texture2D(brushTexture, brushTextureCoords); \ + }"; + + +// Solid Fill Brush +static const char* const qglslSolidBrushSrcFragmentShader = "\ + uniform lowp vec4 fragmentColor; \ + lowp vec4 srcPixel() { \ + return fragmentColor; \ + }"; + +static const char* const qglslImageSrcFragmentShader = "\ + varying highp vec2 textureCoords; \ + uniform sampler2D imageTexture; \ + lowp vec4 srcPixel() { \ + return texture2D(imageTexture, textureCoords); \ + }"; + +static const char* const qglslNonPremultipliedImageSrcFragmentShader = "\ + varying highp vec2 textureCoords; \ + uniform sampler2D imageTexture; \ + lowp vec4 srcPixel() { \ + lowp vec4 sample = texture2D(imageTexture, textureCoords); \ + sample.rgb = sample.rgb * sample.a; \ + return sample; \ + }"; + +static const char* const qglslShockingPinkSrcFragmentShader = "\ + lowp vec4 srcPixel() { \ + return vec4(0.98, 0.06, 0.75, 1.0); \ + }"; + + +static const char* const qglslMainFragmentShader_CMO = "\ + uniform lowp float globalOpacity; \ + lowp vec4 srcPixel(); \ + lowp vec4 applyMask(lowp vec4); \ + lowp vec4 compose(lowp vec4); \ + void main() { \ + gl_FragColor = applyMask(compose(srcPixel()*globalOpacity))); \ + }"; + +static const char* const qglslMainFragmentShader_CM = "\ + lowp vec4 srcPixel(); \ + lowp vec4 applyMask(lowp vec4); \ + lowp vec4 compose(lowp vec4); \ + void main() { \ + gl_FragColor = applyMask(compose(srcPixel())); \ + }"; + +static const char* const qglslMainFragmentShader_MO = "\ + uniform lowp float globalOpacity; \ + lowp vec4 srcPixel(); \ + lowp vec4 applyMask(lowp vec4); \ + void main() { \ + gl_FragColor = applyMask(srcPixel()*globalOpacity); \ + }"; + +static const char* const qglslMainFragmentShader_M = "\ + lowp vec4 srcPixel(); \ + lowp vec4 applyMask(lowp vec4); \ + void main() { \ + gl_FragColor = applyMask(srcPixel()); \ + }"; + +static const char* const qglslMainFragmentShader_CO = "\ + uniform lowp float globalOpacity; \ + lowp vec4 srcPixel(); \ + lowp vec4 compose(lowp vec4); \ + void main() { \ + gl_FragColor = compose(srcPixel()*globalOpacity); \ + }"; + +static const char* const qglslMainFragmentShader_C = "\ + lowp vec4 srcPixel(); \ + lowp vec4 compose(lowp vec4); \ + void main() { \ + gl_FragColor = compose(srcPixel()); \ + }"; + +static const char* const qglslMainFragmentShader_O = "\ + uniform lowp float globalOpacity; \ + lowp vec4 srcPixel(); \ + void main() { \ + gl_FragColor = srcPixel()*globalOpacity; \ + }"; + +static const char* const qglslMainFragmentShader = "\ + lowp vec4 srcPixel(); \ + void main() { \ + gl_FragColor = srcPixel(); \ + }"; + +static const char* const qglslMaskFragmentShader = "\ + varying highp vec2 textureCoords;\ + uniform sampler2D maskTexture;\ + lowp vec4 applyMask(lowp vec4 src) \ + {\ + lowp vec4 mask = texture2D(maskTexture, textureCoords); \ + return src * mask.r; \ + }"; + +/* + Left to implement: + RgbMaskFragmentShader, + RgbMaskWithGammaFragmentShader, + + MultiplyCompositionModeFragmentShader, + ScreenCompositionModeFragmentShader, + OverlayCompositionModeFragmentShader, + DarkenCompositionModeFragmentShader, + LightenCompositionModeFragmentShader, + ColorDodgeCompositionModeFragmentShader, + ColorBurnCompositionModeFragmentShader, + HardLightCompositionModeFragmentShader, + SoftLightCompositionModeFragmentShader, + DifferenceCompositionModeFragmentShader, + ExclusionCompositionModeFragmentShader, +*/ + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // GLGC_SHADER_SOURCE_H diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp index b4591b2..2239c2f 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache.cpp +++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp @@ -44,7 +44,7 @@ #include "qglgradientcache_p.h" -void QGLGradientCache::cleanCache() { +void QGL2GradientCache::cleanCache() { QGLGradientColorTableHash::const_iterator it = cache.constBegin(); for (; it != cache.constEnd(); ++it) { const CacheInfo &cache_info = it.value(); @@ -53,7 +53,7 @@ void QGLGradientCache::cleanCache() { cache.clear(); } -GLuint QGLGradientCache::getBuffer(const QGradient &gradient, qreal opacity, const QGLContext *ctx) +GLuint QGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity, const QGLContext *ctx) { if (buffer_ctx && !qgl_share_reg()->checkSharing(buffer_ctx, ctx)) cleanCache(); @@ -84,7 +84,7 @@ GLuint QGLGradientCache::getBuffer(const QGradient &gradient, qreal opacity, con } -GLuint QGLGradientCache::addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity) +GLuint QGL2GradientCache::addCacheElement(quint64 hash_val, const QGradient &gradient, qreal opacity) { if (cache.size() == maxCacheSize()) { int elem_to_remove = qrand() % maxCacheSize(); @@ -109,34 +109,31 @@ GLuint QGLGradientCache::addCacheElement(quint64 hash_val, const QGradient &grad } -// GL's expects pixels in RGBA, bin-endian (ABGR on x86). -// Qt stores in ARGB using whatever byte-order the mancine uses. +// GL's expects pixels in RGBA (when using GL_RGBA), bin-endian (ABGR on x86). +// Qt always stores in ARGB reguardless of the byte-order the mancine uses. static inline uint qtToGlColor(uint c) { uint o; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - o = c & 0xFF00FF00; // alpha & green already in the right place - o |= (c >> 16) & 0x000000FF; // red - o |= (c << 16) & 0x00FF0000; // blue - + o = (c & 0xff00ff00) // alpha & green already in the right place + | ((c >> 16) & 0x000000ff) // red + | ((c << 16) & 0x00ff0000); // blue #else //Q_BIG_ENDIAN - o = c & 0x00FF00FF; // alpha & green already in the right place - o |= (c << 16) & 0xFF000000; // red - o |= (c >> 16) & 0x0000FF00; // blue -#error big endian not tested with QGLGraphicsContext + o = (c << 8) + | ((c >> 24) & 0x000000ff); #endif // Q_BYTE_ORDER return o; } //TODO: Let GL generate the texture using an FBO -void QGLGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const +void QGL2GradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, qreal opacity) const { int pos = 0; QGradientStops s = gradient.stops(); QVector<uint> colors(s.size()); for (int i = 0; i < s.size(); ++i) - colors[i] = s[i].second.rgba(); // Qt LIES! It returns ARGB + colors[i] = s[i].second.rgba(); // Qt LIES! It returns ARGB (on little-endian AND on big-endian) bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation); diff --git a/src/opengl/gl2paintengineex/qglgradientcache_p.h b/src/opengl/gl2paintengineex/qglgradientcache_p.h index 346ea13..6acaa00 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache_p.h +++ b/src/opengl/gl2paintengineex/qglgradientcache_p.h @@ -54,7 +54,7 @@ #include <QObject> #include <QtOpenGL> -class QGLGradientCache : public QObject +class QGL2GradientCache : public QObject { Q_OBJECT struct CacheInfo @@ -71,7 +71,7 @@ class QGLGradientCache : public QObject typedef QMultiHash<quint64, CacheInfo> QGLGradientColorTableHash; public: - QGLGradientCache() : QObject(), buffer_ctx(0) + QGL2GradientCache() : QObject(), buffer_ctx(0) { /* connect(QGLSignalProxy::instance(), diff --git a/src/opengl/gl2paintengineex/qglpexshadermanager.cpp b/src/opengl/gl2paintengineex/qglpexshadermanager.cpp deleted file mode 100644 index e460e08..0000000 --- a/src/opengl/gl2paintengineex/qglpexshadermanager.cpp +++ /dev/null @@ -1,450 +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 QtOpenGL module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the 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 "qglpexshadermanager_p.h" - -#include "glgc_shader_source.h" - -QGLPEXShaderManager::QGLPEXShaderManager(const QGLContext* context) -{ - ctx = const_cast<QGLContext*>(context); - - defaultVertexShader= new QGLShader(QGLShader::VertexShader, context); - defaultVertexShader->addSource(QLatin1String(qglslDefaultVertexShader)); - if (!defaultVertexShader->compile()) - qWarning() << "Default vertex shader failed to compile: " << defaultVertexShader->log(); - - noBrushShader = new QGLShader(QGLShader::FragmentShader, context); - noBrushShader->addSource(QLatin1String(qglslFragmentShaderMain)); - noBrushShader->addSource(QLatin1String(qglslNoBrushFragmentShader)); - if (!noBrushShader->compile()) - qWarning() << "No brush shader failed to compile:" << noBrushShader->log(); - - - // Create a program for noBrush: - QGLShaderProgram* noBrushProg = new QGLShaderProgram(ctx); - noBrushProg->addShader(defaultVertexShader); - noBrushProg->addShader(noBrushShader); - if (!noBrushProg->link()) - qWarning() << "NoBrush shader program failed to link:" << noBrushProg->log(); - - // Add noBrush Program to cache: - QGLCachedShaderProg cachedProg; - cachedProg.vertexShader = defaultVertexShader; - cachedProg.brushShader = noBrushShader; - cachedProg.compositionShader = 0; - cachedProg.shader = noBrushProg; - cachedPrograms.append(cachedProg); - - - // Set state - useGlobalOpacity = true; - currentBrushStyle = Qt::NoBrush; - currentTransformType = FullTransform; - shaderProgNeedsChanging = false; - activeProgram = noBrushProg; - - solidBrushShader = 0; - - conicalBrushVertexShader = 0; - conicalBrushFragmentShader = 0; - - radialBrushVertexShader = 0; - radialBrushFragmentShader = 0; - - linearBrushVertexShader = 0; - linearBrushFragmentShader = 0; - - patternBrushVertexShader = 0; - patternBrushFragmentShader = 0; - - textureBrushFragmentShader = 0; - textureBrushVertexShader = 0; - - simpleFragmentShader = 0; - simpleShaderProgram = 0; - - imageVertexShader = 0; - imageFragmentShader = 0; - imageShaderProgram = 0; - - textVertexShader = 0; - textFragmentShader = 0; - textShaderProgram = 0; -} - -QGLPEXShaderManager::~QGLPEXShaderManager() -{ - delete defaultVertexShader; - delete imageVertexShader; - delete imageFragmentShader; - delete imageShaderProgram; - delete textVertexShader; - delete textFragmentShader; - delete textShaderProgram; - delete noBrushShader; - delete solidBrushShader; - - delete conicalBrushVertexShader; - delete conicalBrushFragmentShader; - - delete radialBrushVertexShader; - delete radialBrushFragmentShader; - - delete linearBrushFragmentShader; - delete linearBrushVertexShader; - - delete patternBrushFragmentShader; - delete patternBrushVertexShader; - - delete textureBrushFragmentShader; - delete textureBrushVertexShader; - - delete simpleFragmentShader; - delete simpleShaderProgram; -} - -void QGLPEXShaderManager::setUseGlobalOpacity(bool value) -{ - if (value != useGlobalOpacity) - shaderProgNeedsChanging = true; - - useGlobalOpacity = value; -} - -void QGLPEXShaderManager::setBrushStyle(Qt::BrushStyle style) -{ - if (currentBrushStyle != style) - shaderProgNeedsChanging = true; - - currentBrushStyle = style; -} - -void QGLPEXShaderManager::setAffineOnlyBrushTransform(bool value) -{ - Q_UNUSED(value); - // TODO -} - -bool QGLPEXShaderManager::useCorrectShaderProg() -{ - if (!shaderProgNeedsChanging) { - activeProgram->use(); - return false; - } - - const char* fragmentShaderMainSrc = qglslFragmentShaderMain; - QGLShader* vertexShader = defaultVertexShader; - QGLShader* fragmentShader = noBrushShader; - - // Make sure we compile up the correct brush shader - switch (currentBrushStyle) { - case Qt::NoBrush: - break; - case Qt::SolidPattern: - if (!solidBrushShader) { - qDebug("Compiling qglslSolidBrushFragmentShader"); - solidBrushShader = new QGLShader(QGLShader::FragmentShader, ctx); - solidBrushShader->addSource(QLatin1String(qglslNoOpacityFragmentShaderMain)); - solidBrushShader->addSource(QLatin1String(qglslSolidBrushFragmentShader)); - if (!solidBrushShader->compile()) - qWarning() << "qglslSolidBrush failed to compile:" << solidBrushShader->log(); - } - fragmentShader = solidBrushShader; - break; - case Qt::TexturePattern: - if (!textureBrushVertexShader) { - qDebug("Compiling qglslTextureBrushVertexShader"); - textureBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx); - textureBrushVertexShader->addSource(QLatin1String(qglslTextureBrushVertexShader)); - if (!textureBrushVertexShader->compile()) { - qWarning() << "qglslTextureBrushVertexShader failed to compile: " - << textureBrushVertexShader->log(); - } - } - vertexShader = textureBrushVertexShader; - - if (!textureBrushFragmentShader) { - qDebug("Compiling qglslTextureBrushFragmentShader"); - textureBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx); - textureBrushFragmentShader->addSource(QLatin1String(fragmentShaderMainSrc)); - textureBrushFragmentShader->addSource(QLatin1String(qglslTextureBrushFragmentShader)); - if (!textureBrushFragmentShader->compile()) { - qWarning() << "qglslTextureBrushFragmentShader failed to compile:" - << textureBrushFragmentShader->log(); - } - } - fragmentShader = textureBrushFragmentShader; - break; - case Qt::LinearGradientPattern: - if (!linearBrushVertexShader) { - qDebug("Compiling qglslLinearGradientBrushVertexShader"); - linearBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx); - linearBrushVertexShader->addSource(QLatin1String(qglslLinearGradientBrushVertexShader)); - if (!linearBrushVertexShader->compile()) { - qWarning() << "qglslLinearGradientBrushVertexShader failed to compile: " - << linearBrushVertexShader->log(); - } - } - vertexShader = linearBrushVertexShader; - - if (!linearBrushFragmentShader) { - qDebug("Compiling qglslLinearGradientBrushFragmentShader"); - linearBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx); - linearBrushFragmentShader->addSource(QLatin1String(fragmentShaderMainSrc)); - linearBrushFragmentShader->addSource(QLatin1String(qglslLinearGradientBrushFragmentShader)); - if (!linearBrushFragmentShader->compile()) { - qWarning() << "qglslLinearGradientBrushFragmentShader failed to compile:" - << linearBrushFragmentShader->log(); - } - } - fragmentShader = linearBrushFragmentShader; - break; - case Qt::RadialGradientPattern: - if (!radialBrushVertexShader) { - qDebug("Compiling qglslRadialGradientBrushVertexShader"); - radialBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx); - radialBrushVertexShader->addSource(QLatin1String(qglslRadialGradientBrushVertexShader)); - if (!radialBrushVertexShader->compile()) { - qWarning() << "qglslRadialGradientBrushVertexShader failed to compile: " - << radialBrushVertexShader->log(); - } - } - vertexShader = radialBrushVertexShader; - - if (!radialBrushFragmentShader) { - qDebug("Compiling qglslRadialGradientBrushFragmentShader"); - radialBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx); - radialBrushFragmentShader->addSource(QLatin1String(fragmentShaderMainSrc)); - radialBrushFragmentShader->addSource(QLatin1String(qglslRadialGradientBrushFragmentShader)); - if (!radialBrushFragmentShader->compile()) { - qWarning() << "qglslRadialGradientBrushFragmentShader failed to compile:" - << radialBrushFragmentShader->log(); - } - } - fragmentShader = radialBrushFragmentShader; - break; - case Qt::ConicalGradientPattern: - // FIXME: We currently use the same vertex shader as radial brush - if (!conicalBrushVertexShader) { - qDebug("Compiling qglslConicalGradientBrushVertexShader"); - conicalBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx); - conicalBrushVertexShader->addSource(QLatin1String(qglslConicalGradientBrushVertexShader)); - if (!conicalBrushVertexShader->compile()) { - qWarning() << "qglslConicalGradientBrushVertexShader failed to compile: " - << conicalBrushVertexShader->log(); - } - } - vertexShader = conicalBrushVertexShader; - - if (!conicalBrushFragmentShader) { - qDebug("Compiling qglslConicalGradientBrushFragmentShader"); - conicalBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx); - conicalBrushFragmentShader->addSource(QLatin1String(fragmentShaderMainSrc)); - conicalBrushFragmentShader->addSource(QLatin1String(qglslConicalGradientBrushFragmentShader)); - if (!conicalBrushFragmentShader->compile()) { - qWarning() << "qglslConicalGradientBrushFragmentShader failed to compile:" - << conicalBrushFragmentShader->log(); - } - } - fragmentShader = conicalBrushFragmentShader; - break; - case Qt::Dense1Pattern: - case Qt::Dense2Pattern: - case Qt::Dense3Pattern: - case Qt::Dense4Pattern: - case Qt::Dense5Pattern: - case Qt::Dense6Pattern: - case Qt::Dense7Pattern: - case Qt::HorPattern: - case Qt::VerPattern: - case Qt::CrossPattern: - case Qt::BDiagPattern: - case Qt::FDiagPattern: - case Qt::DiagCrossPattern: - if (!patternBrushVertexShader) { - qDebug("Compiling qglslPatternBrushVertexShader"); - patternBrushVertexShader = new QGLShader(QGLShader::VertexShader, ctx); - patternBrushVertexShader->addSource(QLatin1String(qglslPatternBrushVertexShader)); - if (!patternBrushVertexShader->compile()) { - qWarning() << "qglslPatternBrushVertexShader failed to compile: " - << patternBrushVertexShader->log(); - } - } - vertexShader = patternBrushVertexShader; - - if (!patternBrushFragmentShader) { - qDebug("Compiling qglslPatternBrushFragmentShader"); - patternBrushFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx); - patternBrushFragmentShader->addSource(QLatin1String(qglslNoOpacityFragmentShaderMain)); - patternBrushFragmentShader->addSource(QLatin1String(qglslPatternBrushFragmentShader)); - if (!patternBrushFragmentShader->compile()) { - qWarning() << "qglslPatternBrushFragmentShader failed to compile:" - << patternBrushFragmentShader->log(); - } - } - fragmentShader = patternBrushFragmentShader; - break; - default: - qWarning("Unimplemented brush style (%d)", currentBrushStyle); - } - - // Now newBrushShader is set correctly, check to see if we already have the program - // already linked and ready to go in the cache: - bool foundProgram = false; - foreach (QGLCachedShaderProg cachedProg, cachedPrograms) { - if ((cachedProg.vertexShader == vertexShader) && - (cachedProg.brushShader == fragmentShader) && - (cachedProg.compositionShader == 0) ) { - - activeProgram = cachedProg.shader; - foundProgram = true; - break; - } - } - - if (!foundProgram) { - qDebug() << "Linking shader program for " << currentBrushStyle; - // Required program not found - create it. - QGLShaderProgram* newProg = new QGLShaderProgram(ctx); - - newProg->addShader(vertexShader); - newProg->addShader(fragmentShader); - - if (!newProg->link()) - qWarning() << "Shader program for " << currentBrushStyle << "failed to link:" << newProg->log(); - - QGLCachedShaderProg cachedProg; - cachedProg.vertexShader = vertexShader; - cachedProg.brushShader = fragmentShader; - cachedProg.compositionShader = 0; - cachedProg.shader = newProg; - - cachedPrograms.append(cachedProg); - activeProgram = newProg; - } - - activeProgram->use(); - shaderProgNeedsChanging = false; - return true; -} - -QGLShaderProgram* QGLPEXShaderManager::brushShader() -{ - return activeProgram; -} - -// The only uniform the simple shader has is the PMV matrix -QGLShaderProgram* QGLPEXShaderManager::simpleShader() -{ - if (!simpleShaderProgram) { - simpleShaderProgram = new QGLShaderProgram(ctx); - - if (!simpleFragmentShader) { - simpleFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx); - simpleFragmentShader->addSource(QLatin1String(qglslSimpleFragmentShader)); - if (!simpleFragmentShader->compile()) - qWarning() << "qglslSimpleFragmentShader failed to compile:" << simpleFragmentShader->log(); - } - - simpleShaderProgram->addShader(defaultVertexShader); - simpleShaderProgram->addShader(simpleFragmentShader); - if (!simpleShaderProgram->link()) - qWarning() << "Simple shader program failed to link:" << simpleShaderProgram->log(); - } - - return simpleShaderProgram; -} - -QGLShaderProgram* QGLPEXShaderManager::imageShader() -{ - if (!imageShaderProgram) { - if (!imageVertexShader) { - imageVertexShader = new QGLShader(QGLShader::VertexShader, ctx); - imageVertexShader->addSource(QLatin1String(qglslImageVertexShader)); - if (!imageVertexShader->compile()) - qWarning() << "Image/Pixmap vertex shader failed to compile:" << imageVertexShader->log(); - } - - if (!imageFragmentShader) { - imageFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx); - imageFragmentShader->addSource(QLatin1String(qglslImageFragmentShader)); - if (!imageFragmentShader->compile()) - qWarning() << "Image/Pixmap fragment shader failed to compile:" << imageFragmentShader->log(); - } - - imageShaderProgram = new QGLShaderProgram(ctx); - imageShaderProgram->addShader(imageVertexShader); - imageShaderProgram->addShader(imageFragmentShader); - if (!imageShaderProgram->link()) - qWarning() << "Image/Pixmap shader program failed to link:" << imageShaderProgram->log(); - } - - return imageShaderProgram; -} - -QGLShaderProgram* QGLPEXShaderManager::textShader() -{ - if (!textShaderProgram) { - if (!textVertexShader) { - textVertexShader = new QGLShader(QGLShader::VertexShader, ctx); - textVertexShader->addSource(QLatin1String(qglslImageVertexShader)); - if (!textVertexShader->compile()) - qWarning() << "Text vertex shader failed to compile:" << textVertexShader->log(); - } - - if (!textFragmentShader) { - textFragmentShader = new QGLShader(QGLShader::FragmentShader, ctx); - textFragmentShader->addSource(QLatin1String(qglslTextFragmentShader)); - if (!textFragmentShader->compile()) - qWarning() << "Text fragment shader failed to compile:" << textFragmentShader->log(); - } - - textShaderProgram = new QGLShaderProgram(ctx); - textShaderProgram->addShader(textVertexShader); - textShaderProgram->addShader(textFragmentShader); - if (!textShaderProgram->link()) - qWarning() << "Text shader program failed to link:" << textShaderProgram->log(); - } - - return textShaderProgram; -} - diff --git a/src/opengl/gl2paintengineex/qglpexshadermanager_p.h b/src/opengl/gl2paintengineex/qglpexshadermanager_p.h deleted file mode 100644 index c8f47b2..0000000 --- a/src/opengl/gl2paintengineex/qglpexshadermanager_p.h +++ /dev/null @@ -1,156 +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 QtOpenGL module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the 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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qglshader_p.h" - - -// Worstcase combo: Brush->Mask->Composition - -/* - Vertex shader source is specified with a single string. This string - contains the main function and sets the gl_Position. The choice of - which vertex shader to use depends on: - - Brush style - - Brush transform->isAffine() - - Fragment shaders are specified as multiple strings, one for the main - function, one for the brush calculation and optionally one for the - extended composition mode. Brushes are implementations of - "mediump vec4 brush()" - Composition modes are implemented as a - "mediump vec4 compose(mediump vec4 color)" - NOTE: Precision may change in future. - - The choice of which main() fragment shader string to use depends on: - - Global opacity - - Brush style (some brushes apply opacity themselves) - - Use of mask (TODO: Need to support high quality anti-aliasing & text) - - Composition mode - - The choice of which brush() fragment shader to use depends on: - - Brush style - -*/ - - -struct QGLCachedShaderProg -{ - QGLShader* vertexShader; - QGLShader* brushShader; - QGLShader* compositionShader; - QGLShaderProgram* shader; -}; - -class QGLPEXShaderManager -{ -public: - QGLPEXShaderManager(const QGLContext* context); - ~QGLPEXShaderManager(); - - enum TransformType {IdentityTransform, ScaleTransform, TranslateTransform, FullTransform}; - - void optimiseForBrushTransform(const QTransform& transform); - void setBrushStyle(Qt::BrushStyle style); - void setUseGlobalOpacity(bool value); - void setAffineOnlyBrushTransform(bool value); // I.e. Do we need to apply perspective-correction? - // Not doing so saves some vertex shader calculations. - - bool useCorrectShaderProg(); // returns true if the shader program has changed - - QGLShaderProgram* brushShader(); - QGLShaderProgram* simpleShader(); // Used to draw into e.g. stencil buffers - QGLShaderProgram* imageShader(); - QGLShaderProgram* textShader(); - -private: - QGLShader* defaultVertexShader; - - QGLShader* imageVertexShader; - QGLShader* imageFragmentShader; - QGLShaderProgram* imageShaderProgram; - - QGLShader* textVertexShader; - QGLShader* textFragmentShader; - QGLShaderProgram* textShaderProgram; - - QGLShader* noBrushShader; - QGLShader* solidBrushShader; - - QGLShader* conicalBrushVertexShader; - QGLShader* conicalBrushFragmentShader; - - QGLShader* radialBrushVertexShader; - QGLShader* radialBrushFragmentShader; - - QGLShader* linearBrushVertexShader; - QGLShader* linearBrushFragmentShader; - - QGLShader* patternBrushVertexShader; - QGLShader* patternBrushFragmentShader; - - QGLShader* textureBrushFragmentShader; - QGLShader* textureBrushVertexShader; - - QGLShader* simpleFragmentShader; - QGLShaderProgram* simpleShaderProgram; - - QGLShaderProgram* activeProgram; - - Qt::BrushStyle currentBrushStyle; - bool useGlobalOpacity; - TransformType currentTransformType; - bool shaderProgNeedsChanging; - - QList<QGLCachedShaderProg> cachedPrograms; - - QGLContext* ctx; -}; diff --git a/src/opengl/gl2paintengineex/qglshader.cpp b/src/opengl/gl2paintengineex/qglshader.cpp deleted file mode 100644 index 634be84..0000000 --- a/src/opengl/gl2paintengineex/qglshader.cpp +++ /dev/null @@ -1,605 +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 QtOpenGL module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the 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 "qglshader_p.h" - - -// Windows needs to resolve OpenGL 2.0 function pointers for each context. The -// QGL OpenGL 2.0 functions are actually macros, which take a "ctx" parameter. -#define Q_CTX QGLContext* ctx = d->ctx; \ - if (!ctx) \ - return false; \ - ctx->makeCurrent(); \ - - - - -class QGLShaderPrivate -{ -public: - QGLShaderPrivate() : shaderId(0), valid(false), ctx(0) {} - - GLuint shaderId; - QString source; - bool valid; - QGLShader::ShaderType type; - QGLContext* ctx; -}; - - -QGLShader::QGLShader(QGLShader::ShaderType type, const QGLContext* ctx) - : d_ptr(new QGLShaderPrivate) -{ - Q_D(QGLShader); - - if (!ctx) - ctx = QGLContext::currentContext(); - - if (!ctx) { - qWarning("QGLShader being created without a context"); - return; - } - - d->ctx = const_cast<QGLContext*>(ctx); - d->ctx->makeCurrent(); - - if (type == QGLShader::FragmentShader) - d->shaderId = glCreateShader(GL_FRAGMENT_SHADER); - else - d->shaderId = glCreateShader(GL_VERTEX_SHADER); - - if (d->shaderId == 0) { - qWarning("Error creating shader object"); - return; - } - - d->type = type; -} - -GLuint QGLShader::id() -{ - Q_D(QGLShader); - return d->shaderId; -} - -const QGLContext* QGLShader::context() -{ - Q_D(QGLShader); - return d->ctx; -} - -void QGLShader::clearSource() -{ - Q_D(QGLShader); - d->source.clear(); - d->valid = false; -} - -void QGLShader::addSource(const QLatin1String& newSource) -{ - Q_D(QGLShader); - d->source += newSource; - d->valid = false; -} - - -bool QGLShader::compile() -{ - Q_D(QGLShader); - - d->valid = false; - - if (d->source.size() == 0) - return false; - - const QByteArray src_ba = d->source.toAscii(); - const char* src = src_ba.constData(); - - glShaderSource(d->shaderId, 1, &src, 0); - - glCompileShader(d->shaderId); - - GLint shaderCompiled; - glGetShaderiv(d->shaderId, GL_COMPILE_STATUS, &shaderCompiled); - if (!shaderCompiled) - return false; - - d->valid = true; - return true; -} - -bool QGLShader::isValid() -{ - Q_D(QGLShader); - return d->valid; -} - -QString QGLShader::log() -{ - Q_D(QGLShader); - - char* logData; - GLint logSize; - GLint logLength; - - glGetShaderiv(d->shaderId, GL_INFO_LOG_LENGTH, &logSize); - - if (!logSize) - return QString(); - - logData = new char[logSize]; - glGetShaderInfoLog(d->shaderId, logSize, &logLength, logData); - QString result = QString::fromAscii(logData); - delete [] logData; - - return result; -} - - - - - - - - - - - - -class QGLShaderProgramPrivate -{ -public: - QGLShaderProgramPrivate() : valid(false), programId(0), ctx(0) {} - void populateVariableLists(); - - QVector<QGLShader*> shaders; - QGLUniformList uniforms; - QGLVertexAttributeList attributeArrays; - bool valid; - GLuint programId; - QGLContext* ctx; -}; - - - -void QGLShaderProgramPrivate::populateVariableLists() -{ - attributeArrays.clear(); - uniforms.clear(); - - int count; - int sizeOfNameBuff; - char* name; - GLint nameLength; - GLenum type; - GLint size; - GLint location; - - glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTES, &count); - glGetProgramiv(programId, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &sizeOfNameBuff); - name = new char[sizeOfNameBuff]; - - for (int i = 0; i < count; ++i) { - nameLength = -1; - glGetActiveAttrib(programId, i, sizeOfNameBuff, &nameLength, &size, &type, name); - if (nameLength == -1) - continue; - - location = glGetAttribLocation(programId, name); - attributeArrays.insert(QString::fromAscii(name), QGLVertexAttribute(type, location, ctx)); - } - - delete [] name; - - - glGetProgramiv(programId, GL_ACTIVE_UNIFORMS, &count); - glGetProgramiv(programId, GL_ACTIVE_UNIFORM_MAX_LENGTH, &sizeOfNameBuff); - - name = new char[sizeOfNameBuff]; - - for (int i = 0; i < count; ++i) { - nameLength = -1; - glGetActiveUniform(programId, i, sizeOfNameBuff, &nameLength, &size, &type, name); - if (nameLength == -1) - continue; - - location = glGetUniformLocation(programId, name); - uniforms.insert(QString::fromAscii(name), QGLUniform(type, location, ctx)); - } -} - - -QGLShaderProgram::QGLShaderProgram(const QGLContext* ctx) - : d_ptr(new QGLShaderProgramPrivate) -{ - Q_D(QGLShaderProgram); - if (!ctx) - ctx = QGLContext::currentContext(); - - if (!ctx) { - qWarning("QGLShaderProgram being created without a context"); - return; - } - - d->ctx = const_cast<QGLContext*>(ctx); - d->ctx->makeCurrent(); - - d->programId = glCreateProgram(); - - d->valid = false; -} - - -const QGLUniformList & QGLShaderProgram::uniforms() -{ - Q_D(QGLShaderProgram); - return const_cast<const QGLUniformList&>(d->uniforms); -} - - -const QGLVertexAttributeList& QGLShaderProgram::vertexAttributes() -{ - Q_D(QGLShaderProgram); - return const_cast<const QGLVertexAttributeList&>(d->attributeArrays); -} - - -bool QGLShaderProgram::addShader(QGLShader* newShader) -{ - Q_D(QGLShaderProgram); - if (!newShader || !d->ctx) - return false; - - if (newShader->context() != d->ctx) { - qWarning("Shader object's context does not match program's context"); - return false; - } - - if (!newShader->isValid()) - return false; - - QGLContext* ctx = d->ctx; - if (!ctx) - return false; - ctx->makeCurrent(); - - glAttachShader(d->programId, newShader->id()); - - d->shaders.append(newShader); - return true; -} - - -bool QGLShaderProgram::removeShader(QGLShader* oldShader) -{ - Q_D(QGLShaderProgram); - - int idx = d->shaders.indexOf(oldShader); - - if (idx == -1) - return false; - - d->shaders.remove(idx); - - QGLContext* ctx = d->ctx; - if (!ctx) - return false; - ctx->makeCurrent(); - - glDetachShader(d->programId, oldShader->id()); - return true; -} - - -bool QGLShaderProgram::removeAllShaders() -{ - Q_D(QGLShaderProgram); - - QGLContext* ctx = d->ctx; - if (!ctx) - return false; - ctx->makeCurrent(); - - foreach (QGLShader* shader, d->shaders) - glDetachShader(d->programId, shader->id()); - - d->shaders.clear(); - return true; -} - -#include <stdio.h> - -bool QGLShaderProgram::link() -{ - Q_D(QGLShaderProgram); - - QGLContext* ctx = d->ctx; - if (!ctx) - return false; - ctx->makeCurrent(); - - glLinkProgram(d->programId); - - - GLint linked; - glGetProgramiv(d->programId, GL_LINK_STATUS, &linked); - - if (!linked) - return false; - - d->populateVariableLists(); - - d->valid = true; - return true; -} - -void QGLShaderProgram::use() -{ - Q_D(QGLShaderProgram); - if (!d->valid) - return; - - glUseProgram(d->programId); -} - - -QString QGLShaderProgram::log() -{ - Q_D(QGLShaderProgram); - - QGLContext* ctx = d->ctx; - if (!ctx) - return QString(); - ctx->makeCurrent(); - - GLint logSize = -666; - glGetProgramiv(d->programId, GL_INFO_LOG_LENGTH, &logSize); - - char* logData = new char[logSize]; - GLint logLength; - - glGetProgramInfoLog(d->programId, logSize, &logLength, logData); - - QString result = QString::fromAscii(logData); - delete [] logData; - - return result; -} - -GLuint QGLShaderProgram::id() -{ - Q_D(QGLShaderProgram); - return d->programId; -} - -///////////////////////////////////////////////////////////////////////// - - - - -QGLUniform::QGLUniform() - : m_id(0), m_type(QGLInvalidType), ctx(0) -{ - qWarning("Unknown uniform! Either the uniform doesn't exist or it was removed at shader link"); -} - -const QGLUniform& QGLUniform::operator=(const GLfloat& rhs) const -{ - if (m_type != QGLFloatType) - return *this; - - glUniform1f(m_id, rhs); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const QGLVec2& rhs) const -{ - if (m_type != QGLVec2Type) - return *this; - - glUniform2fv(m_id, 1, (const GLfloat*)&rhs); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const QSizeF& rhs) const -{ - if (m_type != QGLVec2Type) - return *this; - - glUniform2f(m_id, rhs.width(), rhs.height()); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const QPointF& rhs) const -{ - if (m_type != QGLVec2Type) - return *this; - - glUniform2f(m_id, rhs.x(), rhs.y()); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const QGLVec3& rhs) const -{ - if (m_type != QGLVec3Type) - return *this; - - glUniform3fv(m_id, 1, (const GLfloat*)&rhs); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const QGLVec4& rhs) const -{ - if (m_type != QGLVec4Type) - return *this; - - glUniform4fv(m_id, 1, (const GLfloat*)&rhs); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const QColor& rhs) const -{ - if (m_type != QGLVec4Type) - return *this; - - glUniform4f(m_id, rhs.redF(), rhs.greenF(), rhs.blueF(), rhs.alphaF()); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const GLfloat rhs[2][2]) const -{ - if (m_type != QGLMat2Type) - return *this; - - glUniformMatrix2fv(m_id, 1, GL_FALSE, (GLfloat*)rhs); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const GLfloat rhs[3][3]) const -{ - if (m_type != QGLMat3Type) - return *this; - - glUniformMatrix3fv(m_id, 1, GL_FALSE, (GLfloat*)rhs); - - return *this; -} - -// Transposes ready for GL -const QGLUniform& QGLUniform::operator=(const QTransform& rhs) const -{ - if (m_type != QGLMat3Type) - return *this; - - GLfloat mat3[3][3] = { - {rhs.m11(), rhs.m12(), rhs.m13()}, - {rhs.m21(), rhs.m22(), rhs.m23()}, - {rhs.m31(), rhs.m32(), rhs.m33()} - }; - - glUniformMatrix3fv(m_id, 1, GL_FALSE, (GLfloat*)mat3); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const GLfloat rhs[4][4]) const -{ - if (m_type != QGLMat4Type) - return *this; - - glUniformMatrix4fv(m_id, 1, GL_FALSE, (GLfloat*)rhs); - - return *this; -} - -const QGLUniform& QGLUniform::operator=(const GLuint& rhs) const -{ - if ((m_type != QGLSampler2DType) || (m_type != QGLSamplerCubeType)) - return *this; - - glUniform1i(m_id, rhs); - - return *this; -} - - - - -///////////////////////////////////////////////////////////////////////// - -QGLVertexAttribute::QGLVertexAttribute() - : m_id(0), m_type(QGLInvalidType), ctx(0) -{ - qWarning("Unknown vertex attribute!"); -} - -void QGLVertexAttribute::enable() const -{ - glEnableVertexAttribArray(m_id); -} - -void QGLVertexAttribute::disable() const -{ - glDisableVertexAttribArray(m_id); -} - -// NOTE: Under PC emulation, QGLVec4Type is _always_ returned as the type, so this -// method isn't very useful. I.e. The datatypes are needed to distinguish the different -// sizes for the function signatures. -const QGLVertexAttribute& QGLVertexAttribute::operator=(const GLfloat* rhs) const -{ - int size = -1; - if (m_type == QGLFloatType) - size = 1; - else if (m_type == QGLVec2Type) - size = 2; - else if (m_type == QGLVec3Type) - size = 3; - else if (m_type == QGLVec4Type) - size = 4; - else if (m_type == QGLMat2Type) //### Not sure if this is right for matrix attributes... - size = 4; - else if (m_type == QGLMat3Type) //### Not sure if this is right for matrix attributes... - size = 9; - else if (m_type == QGLMat4Type) //### Not sure if this is right for matrix attributes... - size = 16; - else - return *this; - - glVertexAttribPointer(m_id, size, GL_FLOAT, GL_FALSE, 0, rhs); - - return *this; -} - -const QGLVertexAttribute& QGLVertexAttribute::operator=(const QGLVec3* rhs) const -{ - glVertexAttribPointer(m_id, 3, GL_FLOAT, GL_FALSE, 0, (GLfloat*)rhs); - - return *this; -} diff --git a/src/opengl/gl2paintengineex/qglshader_p.h b/src/opengl/gl2paintengineex/qglshader_p.h deleted file mode 100644 index 1625b84..0000000 --- a/src/opengl/gl2paintengineex/qglshader_p.h +++ /dev/null @@ -1,260 +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 QtOpenGL module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the 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$ -** -****************************************************************************/ - -// -// 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. -// - -/* -Uniform Types in OpenGL ES 2.0 -============================== -GL_FLOAT GLfloat GLfloat -GL_FLOAT_VEC2 QGLVec2 GLfloat[2] -GL_FLOAT_VEC3 QGLVec3 GLfloat[3] -GL_FLOAT_VEC4 QGLVec4 GLfloat[4] -GL_INT GLint GLint -GL_INT_VEC2 QGLIVec2 GLint[2] -GL_INT_VEC3 QGLIVec3 GLint[3] -GL_INT_VEC4 QGLIVec4 GLint[4] -GL_BOOL GLbool GLbool -GL_BOOL_VEC2 QGLBVec2 GLbool[2] -GL_BOOL_VEC3 QGLBVec3 GLbool[3] -GL_BOOL_VEC4 QGLBVec4 GLbool[4] -GL_FLOAT_MAT2 QGLMat2 GLfloat[2][2] -GL_FLOAT_MAT3 QGLMat3 GLfloat[3][3] -GL_FLOAT_MAT4 QGLMat4 GLfloat[4][4] -GL_SAMPLER_2D QGLSampler2D GLuint -GL_SAMPLER_CUBE QGLSamplerCube GLuint - -Additional Types in Desktop OpenGL 2.0 -====================================== -SAMPLER_1D, -SAMPLER_3D, -SAMPLER_1D_SHADOW, -SAMPLER_2D_SHADOW. -*/ - -#include <QtOpenGL> - - -typedef struct { - GLfloat a; - GLfloat b; -} QGLVec2; - -typedef struct { - GLfloat a; - GLfloat b; - GLfloat c; -} QGLVec3; - -typedef struct { - GLfloat a; - GLfloat b; - GLfloat c; - GLfloat d; -} QGLVec4; - - -class QGLShaderProgram; - -class QGLShaderPrivate; - -class QGLShader : QObject -{ - Q_OBJECT -public: - enum ShaderType {VertexShader, FragmentShader}; - - QGLShader(ShaderType type, const QGLContext* ctx = 0); - - GLuint id(); - void clearSource(); - void addSource(const QLatin1String& newSource); - bool compile(); - bool isValid(); - QString log(); - const QGLContext* context(); //maybe make private with prog a friend? - -private: - QGLShaderPrivate* d_ptr; - Q_DECLARE_PRIVATE(QGLShader); - -/* -public slots: - void cleanupGLContextRefs(const QGLContext *context); -*/ -}; - - -enum QGLType { - QGLInvalidType = 0, - QGLFloatType = GL_FLOAT, - QGLVec2Type = GL_FLOAT_VEC2, - QGLVec3Type = GL_FLOAT_VEC3, - QGLVec4Type = GL_FLOAT_VEC4, - QGLIntType = GL_INT, - QGLIVec2Type = GL_INT_VEC2, - QGLIVec3Type = GL_INT_VEC3, - QGLIVec4Type = GL_INT_VEC4, - QGLBoolType = GL_BOOL, - QGLBVec2Type = GL_BOOL_VEC2, - QGLBVec3Type = GL_BOOL_VEC3, - QGLBVec4Type = GL_BOOL_VEC4, - QGLMat2Type = GL_FLOAT_MAT2, - QGLMat3Type = GL_FLOAT_MAT3, - QGLMat4Type = GL_FLOAT_MAT4, - QGLSampler2DType = GL_SAMPLER_2D, - QGLSamplerCubeType = GL_SAMPLER_CUBE -}; - -class QGLUniform -{ -public: - - QGLUniform(GLenum glType, GLint location, QGLContext* context) - : m_id(location), m_type(QGLType(glType)), ctx(context) {} - - QGLUniform(); // Called by QMap when there's no match on the name - - QGLType type() const {return m_type;} - GLuint id() const {return m_id;} - - // Seems odd to be const, but it doesn't actually modify any of the - // class members, only the GL state! - const QGLUniform& operator=(const GLfloat&) const; - - const QGLUniform& operator=(const QGLVec2&) const; - const QGLUniform& operator=(const QSizeF&) const; - const QGLUniform& operator=(const QPointF&) const; - - const QGLUniform& operator=(const QGLVec3&) const; - - const QGLUniform& operator=(const QGLVec4&) const; - const QGLUniform& operator=(const QColor&) const; - - const QGLUniform& operator=(const GLfloat[2][2]) const; - - const QGLUniform& operator=(const GLfloat[3][3]) const; - const QGLUniform& operator=(const QTransform&) const; - - const QGLUniform& operator=(const GLfloat[4][4]) const; - - const QGLUniform& operator=(const GLuint&) const; // sampler2d, specifying a texture unit - - -protected: - GLuint m_id; - QGLType m_type; - QGLContext* ctx; -}; - -typedef QMap<QString, QGLUniform> QGLUniformList; -typedef QMapIterator<QString, QGLUniform> QGLUniformListIterator; - - -class QGLVertexAttribute -{ -public: - QGLVertexAttribute(GLenum glType, GLuint location, QGLContext* context) - : m_id(location), m_type(QGLType(glType)), ctx(context) {} - - QGLVertexAttribute(); // Called by QMap when there's no match on the name - - QGLType type() const {return m_type;} - GLuint id() const {return m_id;} - void enable() const; - void disable() const; - - const QGLVertexAttribute& operator=(const GLfloat* rhs) const; - const QGLVertexAttribute& operator=(const QGLVec3* rhs) const; - -protected: - GLuint m_id; - QGLType m_type; - QGLContext* ctx; -}; - -//TODO: Convert into setter overloads on QGLShaderProgram -typedef QMap<QString, QGLVertexAttribute> QGLVertexAttributeList; -typedef QMapIterator<QString, QGLVertexAttribute> QGLVertexAttributeListIterator; - - - -class QGLShaderProgramPrivate; - -class QGLShaderProgram : QObject -{ - Q_OBJECT -public: - QGLShaderProgram(const QGLContext* ctx = 0); - - const QGLUniformList & uniforms(); - const QGLVertexAttributeList& vertexAttributes(); - - bool addShader(QGLShader* newShader); - bool removeShader(QGLShader* oldShader); - bool removeAllShaders(); - - bool link(); - QString log(); - bool isValid(); - void use(); - - GLuint id(); - -private: - QGLShaderProgramPrivate* d_ptr; - Q_DECLARE_PRIVATE(QGLShaderProgram); - -/* -public slots: - void cleanupGLContextRefs(const QGLContext *context); -*/ -}; - diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index a74f044..f7dbed3 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -77,7 +77,7 @@ #include <private/qtextureglyphcache_p.h> #include "qglgradientcache_p.h" -#include "qglpexshadermanager_p.h" +#include "qglengineshadermanager_p.h" #include "qgl2pexvertexarray_p.h" @@ -86,10 +86,16 @@ extern QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp #include <QDebug> +enum EngineMode { + ImageDrawingMode, + TextDrawingMode, + BrushDrawingMode +}; -static const GLuint QT_VERTEX_COORDS_ATTR = 0; -static const GLuint QT_TEXTURE_COORDS_ATTR = 1; -static const GLuint QT_BRUSH_TEXTURE_UNIT = 0; +static const GLuint QT_BRUSH_TEXTURE_UNIT = 0; +static const GLuint QT_IMAGE_TEXTURE_UNIT = 0; //Can be the same as brush texture unit +static const GLuint QT_MASK_TEXTURE_UNIT = 1; +static const GLuint QT_BACKGROUND_TEXTURE_UNIT = 2; class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate { @@ -114,10 +120,13 @@ public: void setBrush(const QBrush* brush); - void drawTexture(const QGLRect& dest, const QGLRect& src, int txtWidth, int txtHeight); + void transferMode(EngineMode newMode); + // fill, drawOutline, drawTexture & drawCachedGlyphs are the rendering entry points: void fill(const QVectorPath &path); void drawOutline(const QVectorPath& path); + void drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize); + void drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti); void drawVertexArrays(QGL2PEXVertexArray& vertexArray, GLenum primitive); // ^ draws whatever is in the vertex array @@ -127,16 +136,17 @@ public: // ^ Calls drawVertexArrays to render into stencil buffer void cleanStencilBuffer(const QGLRect& area); - void prepareForDraw(); + void prepareForDraw(bool srcPixelsAreOpaque); inline void useSimpleShader(); inline QColor premultiplyColor(QColor c, GLfloat opacity); QGL2PaintEngineEx* q; - - //### Move into QGLDrawable + QGLDrawable drawable; int width, height; - QGLContext* ctx; + QGLContext *ctx; + + EngineMode mode; // Dirty flags bool matrixDirty; // Implies matrix uniforms are also dirty @@ -144,23 +154,26 @@ public: bool brushTextureDirty; bool brushUniformsDirty; bool simpleShaderMatrixUniformDirty; - bool brushShaderMatrixUniformDirty; - bool imageShaderMatrixUniformDirty; - bool textShaderMatrixUniformDirty; + bool shaderMatrixUniformDirty; bool stencilBuferDirty; const QBrush* currentBrush; // May not be the state's brush! GLfloat inverseScale; - QGL2PEXVertexArray pathVertexArray; + QGL2PEXVertexArray vertexCoordinateArray; + QGL2PEXVertexArray textureCoordinateArray; + + GLfloat staticVertexCoordinateArray[8]; + GLfloat staticTextureCoordinateArray[8]; GLfloat pmvMatrix[4][4]; - QGLPEXShaderManager* shaderManager; + QGLEngineShaderManager* shaderManager; // Clipping & state stuff stolen from QOpenGLPaintEngine: void updateDepthClip(); + void systemStateChanged(); uint use_system_clip : 1; }; @@ -177,7 +190,7 @@ QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate() void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform) { - glActiveTexture(QT_BRUSH_TEXTURE_UNIT); +// glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); //### Is it always this texture unit? if (smoothPixmapTransform) { glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -206,27 +219,27 @@ void QGL2PaintEngineExPrivate::setBrush(const QBrush* brush) currentBrush = brush; brushTextureDirty = true; brushUniformsDirty = true; - shaderManager->setBrushStyle(currentBrush->style()); - shaderManager->setAffineOnlyBrushTransform(currentBrush->transform().isAffine()); + shaderManager->setSrcPixelType(currentBrush->style()); + shaderManager->optimiseForBrushTransform(currentBrush->transform()); } // Unless this gets used elsewhere, it's probably best to merge it into fillStencilWithVertexArray void QGL2PaintEngineExPrivate::useSimpleShader() { - shaderManager->simpleShader()->use(); + shaderManager->simpleProgram()->enable(); if (matrixDirty) updateMatrix(); if (simpleShaderMatrixUniformDirty) { - shaderManager->simpleShader()->uniforms()[QLatin1String("pmvMatrix")] = pmvMatrix; + shaderManager->simpleProgram()->setUniformValue("pmvMatrix", pmvMatrix); simpleShaderMatrixUniformDirty = false; } } -Q_GLOBAL_STATIC(QGLGradientCache, qt_opengl_gradient_cache) +Q_GLOBAL_STATIC(QGL2GradientCache, qt_opengl_gradient_cache) void QGL2PaintEngineExPrivate::updateBrushTexture() { @@ -237,7 +250,7 @@ void QGL2PaintEngineExPrivate::updateBrushTexture() // Get the image data for the pattern QImage texImage = qt_imageForBrush(style, true); - glActiveTexture(QT_BRUSH_TEXTURE_UNIT); + glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); ctx->d_func()->bindTexture(texImage, GL_TEXTURE_2D, GL_RGBA, true); updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true); } @@ -257,12 +270,13 @@ void QGL2PaintEngineExPrivate::updateBrushTexture() else updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, true); + glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); glBindTexture(GL_TEXTURE_2D, texId); } else if (style == Qt::TexturePattern) { const QPixmap& texPixmap = currentBrush->texture(); - glActiveTexture(QT_BRUSH_TEXTURE_UNIT); + glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); ctx->d_func()->bindTexture(texPixmap, GL_TEXTURE_2D, GL_RGBA, true); updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true); } @@ -278,17 +292,11 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() if (style == Qt::NoBrush) return; - GLfloat opacity = 1.0; - if (q->state()->opacity < 0.99f) - opacity = (GLfloat)q->state()->opacity; - bool setOpacity = true; - QTransform brushQTransform = currentBrush->transform(); if (style == Qt::SolidPattern) { - QColor col = premultiplyColor(currentBrush->color(), opacity); - shaderManager->brushShader()->uniforms()[QLatin1String("fragmentColor")] = col; - setOpacity = false; + QColor col = premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity); + shaderManager->currentProgram()->setUniformValue("fragmentColor", col); } else { // All other brushes have a transform and thus need the translation point: @@ -297,13 +305,12 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() if (style <= Qt::DiagCrossPattern) { translationPoint = q->state()->brushOrigin; - QColor col = premultiplyColor(currentBrush->color(), opacity); + QColor col = premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity); - shaderManager->brushShader()->uniforms()[QLatin1String("patternColor")] = col; - setOpacity = false; //So code below doesn't try to set the opacity uniform + shaderManager->currentProgram()->setUniformValue("patternColor", col); - QGLVec2 halfViewportSize = { width*0.5, height*0.5 }; - shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize; + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue("halfViewportSize", halfViewportSize); } else if (style == Qt::LinearGradientPattern) { const QLinearGradient *g = static_cast<const QLinearGradient *>(currentBrush->gradient()); @@ -314,17 +321,16 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() QPointF l = realFinal - realStart; - // ### - QGLVec3 linearData = { + QVector3D linearData( l.x(), l.y(), 1.0f / (l.x() * l.x() + l.y() * l.y()) - }; + ); - shaderManager->brushShader()->uniforms()[QLatin1String("linearData")] = linearData; + shaderManager->currentProgram()->setUniformValue("linearData", linearData); - QGLVec2 halfViewportSize = { width*0.5, height*0.5 }; - shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize; + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue("halfViewportSize", halfViewportSize); } else if (style == Qt::ConicalGradientPattern) { const QConicalGradient *g = static_cast<const QConicalGradient *>(currentBrush->gradient()); @@ -332,10 +338,10 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() GLfloat angle = -(g->angle() * 2 * Q_PI) / 360.0; - shaderManager->brushShader()->uniforms()[QLatin1String("angle")] = angle; + shaderManager->currentProgram()->setUniformValue("angle", angle); - QGLVec2 halfViewportSize = { width*0.5, height*0.5 }; - shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize; + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue("halfViewportSize", halfViewportSize); } else if (style == Qt::RadialGradientPattern) { const QRadialGradient *g = static_cast<const QRadialGradient *>(currentBrush->gradient()); @@ -345,16 +351,15 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() translationPoint = realFocal; QPointF fmp = realCenter - realFocal; - shaderManager->brushShader()->uniforms()[QLatin1String("fmp")] = fmp; + shaderManager->currentProgram()->setUniformValue("fmp", fmp); GLfloat fmp2_m_radius2 = -fmp.x() * fmp.x() - fmp.y() * fmp.y() + realRadius*realRadius; - shaderManager->brushShader()->uniforms()[QLatin1String("fmp2_m_radius2")] = fmp2_m_radius2; + shaderManager->currentProgram()->setUniformValue("fmp2_m_radius2", fmp2_m_radius2); + shaderManager->currentProgram()->setUniformValue("inverse_2_fmp2_m_radius2", + GLfloat(1.0 / (2.0*fmp2_m_radius2))); - shaderManager->brushShader()->uniforms()[QLatin1String("inverse_2_fmp2_m_radius2")] = - GLfloat(1.0 / (2.0*fmp2_m_radius2)); - - QGLVec2 halfViewportSize = { width*0.5, height*0.5 }; - shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize; + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue("halfViewportSize", halfViewportSize); } else if (style == Qt::TexturePattern) { translationPoint = q->state()->brushOrigin; @@ -362,10 +367,10 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() const QPixmap& texPixmap = currentBrush->texture(); QSizeF invertedTextureSize( 1.0 / texPixmap.width(), 1.0 / texPixmap.height() ); - shaderManager->brushShader()->uniforms()[QLatin1String("invertedTextureSize")] = invertedTextureSize; + shaderManager->currentProgram()->setUniformValue("invertedTextureSize", invertedTextureSize); - QGLVec2 halfViewportSize = { width*0.5, height*0.5 }; - shaderManager->brushShader()->uniforms()[QLatin1String("halfViewportSize")] = halfViewportSize; + QVector2D halfViewportSize(width*0.5, height*0.5); + shaderManager->currentProgram()->setUniformValue("halfViewportSize", halfViewportSize); } else qWarning("QGL2PaintEngineEx: Unimplemented fill style"); @@ -374,11 +379,8 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() QTransform gl_to_qt(1, 0, 0, -1, 0, height); QTransform inv_matrix = gl_to_qt * (brushQTransform * q->state()->matrix).inverted() * translate; - shaderManager->brushShader()->uniforms()[QLatin1String("brushTransform")] = inv_matrix; - shaderManager->brushShader()->uniforms()[QLatin1String("brushTexture")] = QT_BRUSH_TEXTURE_UNIT; - - if (setOpacity) - shaderManager->brushShader()->uniforms()[QLatin1String("opacity")] = opacity; + shaderManager->currentProgram()->setUniformValue("brushTransform", inv_matrix); + shaderManager->currentProgram()->setUniformValue("brushTexture", QT_BRUSH_TEXTURE_UNIT); } brushUniformsDirty = false; } @@ -397,38 +399,51 @@ void QGL2PaintEngineExPrivate::updateMatrix() {0.0, 0.0, 0.0, 1.0} }; - // Use the (3x3) transform for the Model~View matrix: const QTransform& transform = q->state()->matrix; - GLfloat MV[4][4] = { - {transform.m11(), transform.m21(), 0.0, transform.dx() + 0.5}, - {transform.m12(), transform.m22(), 0.0, transform.dy() + 0.5}, - {0.0, 0.0, 1.0, 0.0}, - {transform.m13(), transform.m23(), 0.0, transform.m33()} - }; - // NOTE: OpenGL ES works with column-major matrices, so when we multiply the matrices, - // we also transpose them ready for GL. - for (int row = 0; row < 4; ++row) { - for (int col = 0; col < 4; ++col) { - pmvMatrix[col][row] = 0.0; - for (int n = 0; n < 4; ++n) - pmvMatrix[col][row] += P[row][n] * MV[n][col]; + if (mode == TextDrawingMode) { + // Text drawing mode is only used for non-scaling transforms + for (int row = 0; row < 4; ++row) + for (int col = 0; col < 4; ++col) + pmvMatrix[col][row] = P[row][col]; + + pmvMatrix[3][0] += P[0][0] * qRound(transform.dx()); + pmvMatrix[3][1] += P[1][1] * qRound(transform.dy()); + + inverseScale = 1; + } else { + // Use the (3x3) transform for the Model~View matrix: + GLfloat MV[4][4] = { + {transform.m11(), transform.m21(), 0.0, transform.dx()}, + {transform.m12(), transform.m22(), 0.0, transform.dy()}, + {0.0, 0.0, 1.0, 0.0}, + {transform.m13(), transform.m23(), 0.0, transform.m33()} + }; + + // NOTE: OpenGL ES works with column-major matrices, so when we multiply the matrices, + // we also transpose them ready for GL. + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 4; ++col) { + pmvMatrix[col][row] = 0.0; + + // P[row][n] is 0.0 for n < row + for (int n = row; n < 4; ++n) + pmvMatrix[col][row] += P[row][n] * MV[n][col]; + } } - } - // 1/10000 == 0.0001, so we have good enough res to cover curves - // that span the entire widget... - inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())), - qMax(qAbs(transform.m12()), qAbs(transform.m21())) ), - qreal(0.0001)); + // 1/10000 == 0.0001, so we have good enough res to cover curves + // that span the entire widget... + inverseScale = qMax(1 / qMax( qMax(qAbs(transform.m11()), qAbs(transform.m22())), + qMax(qAbs(transform.m12()), qAbs(transform.m21())) ), + qreal(0.0001)); + } matrixDirty = false; // The actual data has been updated so both shader program's uniforms need updating simpleShaderMatrixUniformDirty = true; - brushShaderMatrixUniformDirty = true; - imageShaderMatrixUniformDirty = true; - textShaderMatrixUniformDirty = true; + shaderMatrixUniformDirty = true; } @@ -485,120 +500,140 @@ void QGL2PaintEngineExPrivate::updateCompositionMode() compositionModeDirty = false; } +static inline void setCoords(GLfloat *coords, const QGLRect &rect) +{ + coords[0] = rect.left; + coords[1] = rect.top; + coords[2] = rect.right; + coords[3] = rect.top; + coords[4] = rect.right; + coords[5] = rect.bottom; + coords[6] = rect.left; + coords[7] = rect.bottom; +} -void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& src, int txtWidth, int txtHeight) +void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& src, const QSize &textureSize) { -// qDebug("QGL2PaintEngineExPrivate::drawImage()"); + transferMode(ImageDrawingMode); - // We have a shader specifically for drawPixmap/drawImage... - shaderManager->imageShader()->use(); + updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, q->state()->renderHints & QPainter::SmoothPixmapTransform); - updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false); + // Setup for texture drawing + shaderManager->setSrcPixelType(QGLEngineShaderManager::ImageSrc); + shaderManager->setTextureCoordsEnabled(true); + prepareForDraw(false); // ### - if (compositionModeDirty) - updateCompositionMode(); + shaderManager->currentProgram()->setUniformValue("imageTexture", QT_IMAGE_TEXTURE_UNIT); - if (matrixDirty) - updateMatrix(); + GLfloat dx = 1.0 / textureSize.width(); + GLfloat dy = 1.0 / textureSize.height(); - if (imageShaderMatrixUniformDirty) { - shaderManager->imageShader()->uniforms()[QLatin1String("pmvMatrix")] = pmvMatrix; - imageShaderMatrixUniformDirty = false; - } + QGLRect srcTextureRect(src.left*dx, 1.0 - src.top*dy, src.right*dx, 1.0 - src.bottom*dy); - shaderManager->imageShader()->uniforms()[QLatin1String("textureSampler")] = QT_BRUSH_TEXTURE_UNIT; + setCoords(staticVertexCoordinateArray, dest); + setCoords(staticTextureCoordinateArray, srcTextureRect); -// if (q->state()->opacity < 0.99f) - shaderManager->imageShader()->uniforms()[QLatin1String("opacity")] = (GLfloat)q->state()->opacity; + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} - GLfloat vertexCoords[] = { - dest.left, dest.top, - dest.left, dest.bottom, - dest.right, dest.bottom, - dest.right, dest.top - }; +void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode) +{ + if (newMode == mode) + return; - glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); - glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoords); + if (mode == TextDrawingMode || mode == ImageDrawingMode) { + glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); + glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + } - GLfloat dx = 1.0 / txtWidth; - GLfloat dy = 1.0 / txtHeight; + if (mode == TextDrawingMode) + matrixDirty = true; - QGLRect srcTextureRect(src.left*dx, 1.0 - src.top*dy, src.right*dx, 1.0 - src.bottom*dy); + if (newMode == TextDrawingMode) { + glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); - GLfloat textureCoords[] = { - srcTextureRect.left, srcTextureRect.top, - srcTextureRect.left, srcTextureRect.bottom, - srcTextureRect.right, srcTextureRect.bottom, - srcTextureRect.right, srcTextureRect.top - }; + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray.data()); + glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray.data()); - glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); - glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoords); + matrixDirty = true; + } - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + if (newMode == ImageDrawingMode) { + glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); - glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); - glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR); -} + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, staticVertexCoordinateArray); + glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, staticTextureCoordinateArray); + } + + // This needs to change when we implement high-quality anti-aliasing... + if (newMode != TextDrawingMode) + shaderManager->setMaskType(QGLEngineShaderManager::NoMask); + mode = newMode; +} void QGL2PaintEngineExPrivate::drawOutline(const QVectorPath& path) { -// qDebug("QGL2PaintEngineExPrivate::drawOutline()"); + transferMode(BrushDrawingMode); + + // Might need to call updateMatrix to re-calculate inverseScale if (matrixDirty) updateMatrix(); - pathVertexArray.clear(); - pathVertexArray.addPath(path, inverseScale); + vertexCoordinateArray.clear(); + vertexCoordinateArray.addPath(path, inverseScale); if (path.hasImplicitClose()) { // Close the path's outline - pathVertexArray.lineToArray(path.points()[0], path.points()[1]); - pathVertexArray.stops().last() += 1; + vertexCoordinateArray.lineToArray(path.points()[0], path.points()[1]); + vertexCoordinateArray.stops().last() += 1; } - prepareForDraw(); - drawVertexArrays(pathVertexArray, GL_LINE_STRIP); + prepareForDraw(currentBrush->isOpaque()); + drawVertexArrays(vertexCoordinateArray, GL_LINE_STRIP); } // Assumes everything is configured for the brush you want to use void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) { + transferMode(BrushDrawingMode); + + // Might need to call updateMatrix to re-calculate inverseScale if (matrixDirty) updateMatrix(); const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); - // Check to see if there's any hints if (path.shape() == QVectorPath::RectangleHint) { QGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y()); - prepareForDraw(); + prepareForDraw(currentBrush->isOpaque()); composite(rect); } else if (path.shape() == QVectorPath::EllipseHint) { - pathVertexArray.clear(); - pathVertexArray.addPath(path, inverseScale); - prepareForDraw(); - drawVertexArrays(pathVertexArray, GL_TRIANGLE_FAN); + vertexCoordinateArray.clear(); + vertexCoordinateArray.addPath(path, inverseScale); + prepareForDraw(currentBrush->isOpaque()); + drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN); } else { // The path is too complicated & needs the stencil technique - pathVertexArray.clear(); - pathVertexArray.addPath(path, inverseScale); + vertexCoordinateArray.clear(); + vertexCoordinateArray.addPath(path, inverseScale); - fillStencilWithVertexArray(pathVertexArray, path.hasWindingFill()); + fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); // Stencil the brush onto the dest buffer glStencilFunc(GL_NOTEQUAL, 0, 0xFFFF); // Pass if stencil buff value != 0 glEnable(GL_STENCIL_TEST); - prepareForDraw(); - composite(pathVertexArray.boundingRect()); + prepareForDraw(currentBrush->isOpaque()); + composite(vertexCoordinateArray.boundingRect()); glDisable(GL_STENCIL_TEST); - cleanStencilBuffer(pathVertexArray.boundingRect()); + cleanStencilBuffer(vertexCoordinateArray.boundingRect()); } } @@ -674,32 +709,53 @@ void QGL2PaintEngineExPrivate::cleanStencilBuffer(const QGLRect& area) glDisable(GL_STENCIL_TEST); } -void QGL2PaintEngineExPrivate::prepareForDraw() +void QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) { - if (brushTextureDirty) + if (brushTextureDirty && mode != ImageDrawingMode) updateBrushTexture(); if (compositionModeDirty) updateCompositionMode(); + if (matrixDirty) + updateMatrix(); + + const bool stateHasOpacity = q->state()->opacity < 0.99f; + if ( (!srcPixelsAreOpaque || stateHasOpacity) && + q->state()->compositionMode() != QPainter::CompositionMode_Source) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + bool useGlobalOpacityUniform = stateHasOpacity; + if (stateHasOpacity && (mode != ImageDrawingMode)) { + // Using a brush + bool brushIsPattern = (currentBrush->style() >= Qt::Dense1Pattern) && + (currentBrush->style() <= Qt::DiagCrossPattern); + + if ((currentBrush->style() == Qt::SolidPattern) || brushIsPattern) + useGlobalOpacityUniform = false; // Global opacity handled by srcPixel shader + } + shaderManager->setUseGlobalOpacity(useGlobalOpacityUniform); + + + // If the shader program needs changing, we change it and mark all uniforms as dirty if (shaderManager->useCorrectShaderProg()) { // The shader program has changed so mark all uniforms as dirty: brushUniformsDirty = true; - brushShaderMatrixUniformDirty = true; + shaderMatrixUniformDirty = true; } - if (brushUniformsDirty) + if (brushUniformsDirty && mode != ImageDrawingMode) updateBrushUniforms(); - if (brushShaderMatrixUniformDirty) { - shaderManager->brushShader()->uniforms()[QLatin1String("pmvMatrix")] = pmvMatrix; - brushShaderMatrixUniformDirty = false; + if (shaderMatrixUniformDirty) { + shaderManager->currentProgram()->setUniformValue("pmvMatrix", pmvMatrix); + shaderMatrixUniformDirty = false; } - if ((q->state()->opacity < 0.99f) || !currentBrush->isOpaque()) - glEnable(GL_BLEND); - else - glDisable(GL_BLEND); + if (useGlobalOpacityUniform) + shaderManager->currentProgram()->setUniformValue("globalOpacity", (GLfloat)q->state()->opacity); } void QGL2PaintEngineExPrivate::composite(const QGLRect& boundingRect) @@ -749,8 +805,6 @@ void QGL2PaintEngineExPrivate::drawVertexArrays(QGL2PEXVertexArray& vertexArray, QGL2PaintEngineEx::QGL2PaintEngineEx() : QPaintEngineEx(*(new QGL2PaintEngineExPrivate(this))) { - qDebug("QGL2PaintEngineEx::QGL2PaintEngineEx()"); - } QGL2PaintEngineEx::~QGL2PaintEngineEx() @@ -761,8 +815,10 @@ void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush) { Q_D(QGL2PaintEngineEx); - QTime startTime = QTime::currentTime(); + if (brush.style() == Qt::NoBrush) + return; + ensureActive(); d->setBrush(&brush); d->fill(path); d->setBrush(&(state()->brush)); // reset back to the state's brush @@ -775,6 +831,8 @@ void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) if (pen.style() == Qt::NoPen) return; + ensureActive(); + if ( (pen.isCosmetic() && (pen.style() == Qt::SolidLine)) && (pen.widthF() < 2.5f) ) { // We only handle solid, cosmetic pens with a width of 1 pixel @@ -790,7 +848,6 @@ void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) d->setBrush(&(state()->brush)); } else return QPaintEngineEx::stroke(path, pen); - } void QGL2PaintEngineEx::penChanged() @@ -819,7 +876,6 @@ void QGL2PaintEngineEx::opacityChanged() Q_D(QGL2PaintEngineEx); Q_ASSERT(d->shaderManager); - d->shaderManager->setUseGlobalOpacity(state()->opacity > 0.999); d->brushUniformsDirty = true; } @@ -845,9 +901,12 @@ void QGL2PaintEngineEx::transformChanged() void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, const QRectF & src) { Q_D(QGL2PaintEngineEx); - glActiveTexture(QT_BRUSH_TEXTURE_UNIT); + ensureActive(); + d->transferMode(ImageDrawingMode); - d->ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, true); + QGLContext *ctx = d->ctx; + glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, true); //FIXME: we should use hasAlpha() instead, but that's SLOW at the moment if ((state()->opacity < 0.99f) || pixmap.hasAlphaChannel()) @@ -855,35 +914,39 @@ void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, c else glDisable(GL_BLEND); - d->drawTexture(dest, src, pixmap.width(), pixmap.height()); + d->drawTexture(dest, src, pixmap.size()); } void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src, Qt::ImageConversionFlags) { Q_D(QGL2PaintEngineEx); - glActiveTexture(QT_BRUSH_TEXTURE_UNIT); - d->ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true); + ensureActive(); + d->transferMode(ImageDrawingMode); + + QGLContext *ctx = d->ctx; + glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true); if ((state()->opacity < 0.99f) || image.hasAlphaChannel()) glEnable(GL_BLEND); else glDisable(GL_BLEND); - d->drawTexture(dest, src, image.width(), image.height()); + d->drawTexture(dest, src, image.size()); } void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem) { - QOpenGLPaintEngineState *s = state(); + Q_D(QGL2PaintEngineEx); + + ensureActive(); + QOpenGL2PaintEngineState *s = state(); const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); bool drawCached = true; - if (state()->pen.brush().style() != Qt::SolidPattern) - drawCached = false; - if (s->matrix.type() > QTransform::TxTranslate) drawCached = false; @@ -892,17 +955,19 @@ void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem drawCached = false; if (drawCached) { - drawCachedGlyphs(p, ti); + d->drawCachedGlyphs(p, ti); return; } QPaintEngineEx::drawTextItem(p, ti); } -void QGL2PaintEngineEx::drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti) +void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti) { - Q_D(QGL2PaintEngineEx); - QOpenGLPaintEngineState *s = state(); + transferMode(TextDrawingMode); + + Q_Q(QGL2PaintEngineEx); + QOpenGL2PaintEngineState *s = q->state(); QVarLengthArray<QFixedPoint> positions; QVarLengthArray<glyph_t> glyphs; @@ -910,10 +975,22 @@ void QGL2PaintEngineEx::drawCachedGlyphs(const QPointF &p, const QTextItemInt &t matrix.translate(p.x(), p.y()); ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); + QFontEngineGlyphCache::Type glyphType = ti.fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat) : QFontEngineGlyphCache::Raster_A8; + GLenum maskFormat = GL_RGBA; + if (glyphType == QFontEngineGlyphCache::Raster_A8) { + shaderManager->setMaskType(QGLEngineShaderManager::PixelMask); +// maskFormat = GL_ALPHA; + } + else if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) + shaderManager->setMaskType(QGLEngineShaderManager::SubPixelMask); + //### TODO: Gamma correction + shaderManager->setTextureCoordsEnabled(true); + + QImageTextureGlyphCache *cache = (QImageTextureGlyphCache *) ti.fontEngine->glyphCache(glyphType, s->matrix); if (!cache) { @@ -926,66 +1003,46 @@ void QGL2PaintEngineEx::drawCachedGlyphs(const QPointF &p, const QTextItemInt &t const QImage &image = cache->image(); int margin = cache->glyphMargin(); - glActiveTexture(QT_BRUSH_TEXTURE_UNIT); - d->ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true); - - glEnable(GL_BLEND); - - d->shaderManager->textShader()->use(); - d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false); - - if (d->compositionModeDirty) - d->updateCompositionMode(); - - if (d->matrixDirty) - d->updateMatrix(); - - if (d->textShaderMatrixUniformDirty) { - d->shaderManager->textShader()->uniforms()[QLatin1String("pmvMatrix")] = d->pmvMatrix; - d->textShaderMatrixUniformDirty = false; - } - - d->shaderManager->textShader()->uniforms()[QLatin1String("textureSampler")] = QT_BRUSH_TEXTURE_UNIT; - QColor col = d->premultiplyColor(state()->pen.color(), (GLfloat)state()->opacity); - d->shaderManager->textShader()->uniforms()[QLatin1String("fragmentColor")] = col; + if (image.isNull()) + return; GLfloat dx = 1.0 / image.width(); GLfloat dy = 1.0 / image.height(); - glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); - glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); + QGLPoint *oldVertexCoordinateDataPtr = vertexCoordinateArray.data(); + QGLPoint *oldTextureCoordinateDataPtr = textureCoordinateArray.data(); + + vertexCoordinateArray.clear(); + textureCoordinateArray.clear(); + for (int i=0; i<glyphs.size(); ++i) { const QTextureGlyphCache::Coord &c = cache->coords.value(glyphs[i]); int x = positions[i].x.toInt() + c.baseLineX - margin; int y = positions[i].y.toInt() - c.baseLineY - margin; - QGLRect dest = QRectF(x, y, c.w, c.h); - QGLRect src = QRectF(c.x, c.y, c.w, c.h); + vertexCoordinateArray.addRect(QRectF(x, y, c.w, c.h)); + textureCoordinateArray.addRect(QRectF(c.x*dx, 1 - c.y*dy, c.w * dx, -c.h * dy)); + } - GLfloat vertexCoords[] = { - dest.left, dest.top, - dest.left, dest.bottom, - dest.right, dest.bottom, - dest.right, dest.top - }; + glActiveTexture(GL_TEXTURE0 + QT_MASK_TEXTURE_UNIT); + ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true); + updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false); - glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoords); + QBrush pensBrush = q->state()->pen.brush(); + setBrush(&pensBrush); - QGLRect srcTextureRect(src.left*dx, 1.0 - src.top*dy, src.right*dx, 1.0 - src.bottom*dy); + prepareForDraw(false); // Text always causes src pixels to be transparent - GLfloat textureCoords[] = { - srcTextureRect.left, srcTextureRect.top, - srcTextureRect.left, srcTextureRect.bottom, - srcTextureRect.right, srcTextureRect.bottom, - srcTextureRect.right, srcTextureRect.top - }; + shaderManager->currentProgram()->setUniformValue("maskTexture", QT_MASK_TEXTURE_UNIT); - glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoords); + if (vertexCoordinateArray.data() != oldVertexCoordinateDataPtr) + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray.data()); + if (textureCoordinateArray.data() != oldTextureCoordinateDataPtr) + glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray.data()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } - glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); - glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size()); + + setBrush(&(q->state()->brush)); //### } bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) @@ -993,15 +1050,30 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) Q_D(QGL2PaintEngineEx); // qDebug("QGL2PaintEngineEx::begin()"); + d->drawable.setDevice(pdev); + d->ctx = d->drawable.context(); + + if (d->ctx->d_ptr->active_engine) { + QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(d->ctx->d_ptr->active_engine); + QGL2PaintEngineExPrivate *p = static_cast<QGL2PaintEngineExPrivate *>(engine->d_ptr); + p->transferMode(BrushDrawingMode); + p->drawable.doneCurrent(); + } + + d->ctx->d_ptr->active_engine = this; + + d->drawable.makeCurrent(); + QSize sz = d->drawable.size(); + d->width = sz.width(); + d->height = sz.height(); + d->mode = BrushDrawingMode; - QGLWidget* widget = static_cast<QGLWidget*>(pdev); - d->ctx = const_cast<QGLContext*>(widget->context()); - d->ctx->makeCurrent(); - d->width = widget->width(); - d->height = widget->height(); +#if !defined(QT_OPENGL_ES_2) + qt_resolve_version_2_0_functions(d->ctx); +#endif if (!d->shaderManager) - d->shaderManager = new QGLPEXShaderManager(d->ctx); + d->shaderManager = new QGLEngineShaderManager(d->ctx); glViewport(0, 0, d->width, d->height); @@ -1020,17 +1092,81 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) d->use_system_clip = !systemClip().isEmpty(); glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + + QGLPixmapData *source = d->drawable.copyOnBegin(); + if (d->drawable.context()->d_func()->clear_on_painter_begin && d->drawable.autoFillBackground()) { + if (d->drawable.hasTransparentBackground()) + glClearColor(0.0, 0.0, 0.0, 0.0); + else { + const QColor &c = d->drawable.backgroundColor(); + float alpha = c.alphaF(); + glClearColor(c.redF() * alpha, c.greenF() * alpha, c.blueF() * alpha, alpha); + } + glClear(GL_COLOR_BUFFER_BIT); + } else if (source) { + d->transferMode(ImageDrawingMode); + + source->bind(false); + glDisable(GL_BLEND); + + QRect rect(0, 0, source->width(), source->height()); + d->drawTexture(QRectF(rect), QRectF(rect), rect.size()); + } + + updateClipRegion(QRegion(), Qt::NoClip); return true; } bool QGL2PaintEngineEx::end() { Q_D(QGL2PaintEngineEx); - d->ctx->swapBuffers(); + QGLContext *ctx = d->ctx; + if (ctx->d_ptr->active_engine != this) { + QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(ctx->d_ptr->active_engine); + if (engine && engine->isActive()) { + QGL2PaintEngineExPrivate *p = static_cast<QGL2PaintEngineExPrivate *>(engine->d_ptr); + p->transferMode(BrushDrawingMode); + p->drawable.doneCurrent(); + } + d->drawable.makeCurrent(); + } + + glUseProgram(0); + d->transferMode(BrushDrawingMode); + d->drawable.swapBuffers(); + d->drawable.doneCurrent(); + d->ctx->d_ptr->active_engine = 0; + return false; } +void QGL2PaintEngineEx::ensureActive() +{ + Q_D(QGL2PaintEngineEx); + QGLContext *ctx = d->ctx; + + if (isActive() && ctx->d_ptr->active_engine != this) { + QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(ctx->d_ptr->active_engine); + if (engine && engine->isActive()) { + QGL2PaintEngineExPrivate *p = static_cast<QGL2PaintEngineExPrivate *>(engine->d_ptr); + p->transferMode(BrushDrawingMode); + p->drawable.doneCurrent(); + } + d->drawable.makeCurrent(); + + ctx->d_ptr->active_engine = this; + + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + + glViewport(0, 0, d->width, d->height); + setState(state()); + d->updateDepthClip(); + } +} + /////////////////////////////////// State/Clipping stolen from QOpenGLPaintEngine ////////////////////////////////////////// @@ -1161,14 +1297,19 @@ void QGL2PaintEngineEx::updateClipRegion(const QRegion &clipRegion, Qt::ClipOper state()->hasClipping = op != Qt::NoClip || d->use_system_clip; } - if (state()->hasClipping && state()->clipRegion.rects().size() == 1) - state()->fastClip = state()->clipRegion.rects().at(0); - else - state()->fastClip = QRect(); - d->updateDepthClip(); } +void QGL2PaintEngineExPrivate::systemStateChanged() +{ + Q_Q(QGL2PaintEngineEx); + use_system_clip = !systemClip.isEmpty(); + + if (q->painter()->hasClipping()) + q->updateClipRegion(q->painter()->clipRegion(), Qt::ReplaceClip); + else + q->updateClipRegion(QRegion(), Qt::NoClip); +} void QGL2PaintEngineExPrivate::updateDepthClip() { @@ -1176,20 +1317,17 @@ void QGL2PaintEngineExPrivate::updateDepthClip() Q_Q(QGL2PaintEngineEx); + q->ensureActive(); glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); if (!q->state()->hasClipping) return; - QRect fastClip; - if (q->state()->clipEnabled) { - fastClip = q->state()->fastClip; - } else if (use_system_clip && q->systemClip().rects().count() == 1) { - fastClip = q->systemClip().rects().at(0); - } + const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects(); + if (rects.size() == 1) { + QRect fastClip = rects.at(0); - if (!fastClip.isEmpty()) { glEnable(GL_SCISSOR_TEST); const int left = fastClip.left(); @@ -1201,12 +1339,11 @@ void QGL2PaintEngineExPrivate::updateDepthClip() return; } - glClearDepthf(0x0); + glClearDepth(0x0); glDepthMask(true); glClear(GL_DEPTH_BUFFER_BIT); - glClearDepthf(0x1); + glClearDepth(0x1); - const QVector<QRect> rects = q->state()->clipEnabled ? q->state()->clipRegion.rects() : q->systemClip().rects(); glEnable(GL_SCISSOR_TEST); for (int i = 0; i < rects.size(); ++i) { QRect rect = rects.at(i); @@ -1229,50 +1366,56 @@ void QGL2PaintEngineExPrivate::updateDepthClip() -void QGL2PaintEngineEx::setState(QPainterState *s) +void QGL2PaintEngineEx::setState(QPainterState *new_state) { // qDebug("QGL2PaintEngineEx::setState()"); Q_D(QGL2PaintEngineEx); + + QOpenGL2PaintEngineState *s = static_cast<QOpenGL2PaintEngineState *>(new_state); + + QOpenGL2PaintEngineState *old_state = state(); + const bool needsDepthClipUpdate = !old_state + || s->clipEnabled != old_state->clipEnabled + || (s->clipEnabled && s->clipRegion != old_state->clipRegion); + QPaintEngineEx::setState(s); - d->updateDepthClip(); + if (needsDepthClipUpdate) + d->updateDepthClip(); d->matrixDirty = true; d->compositionModeDirty = true; d->brushTextureDirty = true; d->brushUniformsDirty = true; d->simpleShaderMatrixUniformDirty = true; - d->brushShaderMatrixUniformDirty = true; - d->imageShaderMatrixUniformDirty = true; - d->textShaderMatrixUniformDirty = true; + d->shaderMatrixUniformDirty = true; } QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const { - QOpenGLPaintEngineState *s; + QOpenGL2PaintEngineState *s; if (!orig) - s = new QOpenGLPaintEngineState(); + s = new QOpenGL2PaintEngineState(); else - s = new QOpenGLPaintEngineState(*static_cast<QOpenGLPaintEngineState *>(orig)); + s = new QOpenGL2PaintEngineState(*static_cast<QOpenGL2PaintEngineState *>(orig)); return s; } -QOpenGLPaintEngineState::QOpenGLPaintEngineState(QOpenGLPaintEngineState &other) +QOpenGL2PaintEngineState::QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other) : QPainterState(other) { clipRegion = other.clipRegion; hasClipping = other.hasClipping; - fastClip = other.fastClip; } -QOpenGLPaintEngineState::QOpenGLPaintEngineState() +QOpenGL2PaintEngineState::QOpenGL2PaintEngineState() { hasClipping = false; } -QOpenGLPaintEngineState::~QOpenGLPaintEngineState() +QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState() { } diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index ce66e4b..ccf89f0 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -58,16 +58,15 @@ class QGL2PaintEngineExPrivate; -class QOpenGLPaintEngineState : public QPainterState +class QOpenGL2PaintEngineState : public QPainterState { public: - QOpenGLPaintEngineState(QOpenGLPaintEngineState &other); - QOpenGLPaintEngineState(); - ~QOpenGLPaintEngineState(); + QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other); + QOpenGL2PaintEngineState(); + ~QOpenGL2PaintEngineState(); QRegion clipRegion; bool hasClipping; - QRect fastClip; }; @@ -81,6 +80,8 @@ public: bool begin(QPaintDevice *device); bool end(); + void ensureActive(); + virtual void fill(const QVectorPath &path, const QBrush &brush); virtual void stroke(const QVectorPath &path, const QPen &pen); virtual void clip(const QVectorPath &path, Qt::ClipOperation op); @@ -100,19 +101,17 @@ public: virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor); virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); - void drawCachedGlyphs(const QPointF &p, const QTextItemInt &ti); Type type() const { return OpenGL; } - // State stuff is just for clipping and ripped off from QGLPaintEngine void setState(QPainterState *s); QPainterState *createState(QPainterState *orig) const; - inline QOpenGLPaintEngineState *state() { - return static_cast<QOpenGLPaintEngineState *>(QPaintEngineEx::state()); + inline QOpenGL2PaintEngineState *state() { + return static_cast<QOpenGL2PaintEngineState *>(QPaintEngineEx::state()); } - inline const QOpenGLPaintEngineState *state() const { - return static_cast<const QOpenGLPaintEngineState *>(QPaintEngineEx::state()); + inline const QOpenGL2PaintEngineState *state() const { + return static_cast<const QOpenGL2PaintEngineState *>(QPaintEngineEx::state()); } void updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op); @@ -120,6 +119,4 @@ private: Q_DISABLE_COPY(QGL2PaintEngineEx) }; - - #endif diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index 78aaddb..73af174 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -13,18 +13,15 @@ include(../qbase.pri) !win32:!embedded:!mac:CONFIG += x11 contains(QT_CONFIG, opengl):CONFIG += opengl contains(QT_CONFIG, opengles1):CONFIG += opengles1 +contains(QT_CONFIG, opengles1):CONFIG += opengles1cl contains(QT_CONFIG, opengles2):CONFIG += opengles2 -!contains(QT_CONFIG, opengles2) { - HEADERS += qgraphicssystem_gl_p.h qwindowsurface_gl_p.h qpixmapdata_gl_p.h - SOURCES += qgraphicssystem_gl.cpp qwindowsurface_gl.cpp qpixmapdata_gl.cpp -} - HEADERS += qgl.h \ qgl_p.h \ qglcolormap.h \ qglpixelbuffer.h \ qglframebufferobject.h \ + qglextensions_p.h SOURCES += qgl.cpp \ qglcolormap.cpp \ @@ -33,25 +30,34 @@ SOURCES += qgl.cpp \ qglextensions.cpp \ !contains(QT_CONFIG, opengles2) { - HEADERS += qpaintengine_opengl_p.h qglpixmapfilter_p.h - SOURCES += qpaintengine_opengl.cpp qglpixmapfilter.cpp + HEADERS += qpaintengine_opengl_p.h + SOURCES += qpaintengine_opengl.cpp } -contains(QT_CONFIG, opengles2) { - SOURCES += gl2paintengineex/qglgradientcache.cpp \ - gl2paintengineex/qglpexshadermanager.cpp \ - gl2paintengineex/qglshader.cpp \ +!contains(QT_CONFIG, opengles1):!contains(QT_CONFIG, opengles1cl) { + HEADERS += qglshaderprogram.h \ + qglpixmapfilter_p.h \ + qgraphicssystem_gl_p.h \ + qwindowsurface_gl_p.h \ + qpixmapdata_gl_p.h \ + gl2paintengineex/qglgradientcache_p.h \ + gl2paintengineex/qglengineshadermanager_p.h \ + gl2paintengineex/qgl2pexvertexarray_p.h \ + gl2paintengineex/qpaintengineex_opengl2_p.h \ + gl2paintengineex/qglengineshadersource_p.h + + SOURCES += qglshaderprogram.cpp \ + qglpixmapfilter.cpp \ + qgraphicssystem_gl.cpp \ + qwindowsurface_gl.cpp \ + qpixmapdata_gl.cpp \ + gl2paintengineex/qglgradientcache.cpp \ + gl2paintengineex/qglengineshadermanager.cpp \ gl2paintengineex/qgl2pexvertexarray.cpp \ gl2paintengineex/qpaintengineex_opengl2.cpp - HEADERS += gl2paintengineex/qglgradientcache_p.h \ - gl2paintengineex/qglpexshadermanager_p.h \ - gl2paintengineex/qglshader_p.h \ - gl2paintengineex/qgl2pexvertexarray_p.h \ - gl2paintengineex/qpaintengineex_opengl2_p.h } - x11 { contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles1cl)|contains(QT_CONFIG, opengles2) { SOURCES += qgl_x11egl.cpp \ diff --git a/src/opengl/qegl.cpp b/src/opengl/qegl.cpp index 165a0f3..c6c258b 100644 --- a/src/opengl/qegl.cpp +++ b/src/opengl/qegl.cpp @@ -119,7 +119,7 @@ bool QEglContext::chooseConfig if (red == props.value(EGL_RED_SIZE) && green == props.value(EGL_GREEN_SIZE) && blue == props.value(EGL_BLUE_SIZE) && - (props.value(EGL_ALPHA_SIZE) == EGL_DONT_CARE || + (props.value(EGL_ALPHA_SIZE) == 0 || alpha == props.value(EGL_ALPHA_SIZE))) { cfg = configs[index]; delete [] configs; @@ -405,7 +405,54 @@ int QEglProperties::value(int name) const if (props[index] == name) return props[index + 1]; } - return EGL_DONT_CARE; + + // If the attribute has not been explicitly set, return the EGL default + // The following defaults were taken from the EGL 1.4 spec: + switch(name) { + case EGL_BUFFER_SIZE: return 0; + case EGL_RED_SIZE: return 0; + case EGL_GREEN_SIZE: return 0; + case EGL_BLUE_SIZE: return 0; + case EGL_LUMINANCE_SIZE: return 0; + case EGL_ALPHA_SIZE: return 0; + case EGL_ALPHA_MASK_SIZE: return 0; + case EGL_BIND_TO_TEXTURE_RGB: return EGL_DONT_CARE; + case EGL_BIND_TO_TEXTURE_RGBA: return EGL_DONT_CARE; + case EGL_COLOR_BUFFER_TYPE: return EGL_RGB_BUFFER; + case EGL_CONFIG_CAVEAT: return EGL_DONT_CARE; + case EGL_CONFIG_ID: return EGL_DONT_CARE; + case EGL_DEPTH_SIZE: return 0; + case EGL_LEVEL: return 0; + case EGL_NATIVE_RENDERABLE: return EGL_DONT_CARE; + case EGL_NATIVE_VISUAL_TYPE: return EGL_DONT_CARE; + case EGL_MAX_SWAP_INTERVAL: return EGL_DONT_CARE; + case EGL_MIN_SWAP_INTERVAL: return EGL_DONT_CARE; + case EGL_RENDERABLE_TYPE: return EGL_OPENGL_ES_BIT; + case EGL_SAMPLE_BUFFERS: return 0; + case EGL_SAMPLES: return 0; + case EGL_STENCIL_SIZE: return 0; + case EGL_SURFACE_TYPE: return EGL_WINDOW_BIT; + case EGL_TRANSPARENT_TYPE: return EGL_NONE; + case EGL_TRANSPARENT_RED_VALUE: return EGL_DONT_CARE; + case EGL_TRANSPARENT_GREEN_VALUE: return EGL_DONT_CARE; + case EGL_TRANSPARENT_BLUE_VALUE: return EGL_DONT_CARE; + +#if defined(EGL_VERSION_1_3) + case EGL_CONFORMANT: return 0; + case EGL_MATCH_NATIVE_PIXMAP: return EGL_NONE; +#endif + + case EGL_MAX_PBUFFER_HEIGHT: + case EGL_MAX_PBUFFER_WIDTH: + case EGL_MAX_PBUFFER_PIXELS: + case EGL_NATIVE_VISUAL_ID: + case EGL_NONE: + qWarning("QEglProperties::value() - Attibute %d does not affect config selection", name); + return EGL_DONT_CARE; + default: + qWarning("QEglProperties::value() - Attibute %d is unknown in EGL <=1.4", name); + return EGL_DONT_CARE; + } } // Set the value associated with a property, replacing an existing @@ -593,13 +640,13 @@ QString QEglProperties::toString() const #endif val = value(EGL_DEPTH_SIZE); - if (val != EGL_DONT_CARE) { + if (val != 0) { addTag(str, QLatin1String(" depth=")); str += QString::number(val); } val = value(EGL_STENCIL_SIZE); - if (val != EGL_DONT_CARE) { + if (val != 0) { addTag(str, QLatin1String(" stencil=")); str += QString::number(val); } @@ -649,7 +696,7 @@ QString QEglProperties::toString() const } val = value(EGL_LEVEL); - if (val != EGL_DONT_CARE) { + if (val != 0) { addTag(str, QLatin1String(" level=")); str += QString::number(val); } @@ -700,13 +747,13 @@ QString QEglProperties::toString() const #endif val = value(EGL_SAMPLES); - if (val != EGL_DONT_CARE) { + if (val != 0) { addTag(str, QLatin1String(" samples=")); str += QString::number(val); } val = value(EGL_SAMPLE_BUFFERS); - if (val != EGL_DONT_CARE) { + if (val != 0) { addTag(str, QLatin1String(" sample-buffers=")); str += QString::number(val); } @@ -755,7 +802,7 @@ QString QEglProperties::toString() const #ifdef EGL_LUMINANCE_SIZE val = value(EGL_LUMINANCE_SIZE); - if (val != EGL_DONT_CARE) { + if (val != 0) { addTag(str, QLatin1String(" luminance=")); str += QString::number(val); } @@ -763,7 +810,7 @@ QString QEglProperties::toString() const #ifdef EGL_ALPHA_MASK_SIZE val = value(EGL_ALPHA_MASK_SIZE); - if (val != EGL_DONT_CARE) { + if (val != 0) { addTag(str, QLatin1String(" alpha-mask=")); str += QString::number(val); } @@ -771,7 +818,7 @@ QString QEglProperties::toString() const #ifdef EGL_CONFORMANT val = value(EGL_CONFORMANT); - if (val != EGL_DONT_CARE) { + if (val != 0) { if (val) addTag(str, QLatin1String(" conformant=true")); else diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index ca5c732..431b488 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -65,15 +65,20 @@ #include "qimage.h" #include "qgl_p.h" -#if defined(QT_OPENGL_ES_2) #include "gl2paintengineex/qpaintengineex_opengl2_p.h" -#else + +#ifndef QT_OPENGL_ES_2 #include <private/qpaintengine_opengl_p.h> #endif +#include <qglpixelbuffer.h> +#include <qglframebufferobject.h> + #include <private/qimage_p.h> #include <private/qpixmapdata_p.h> #include <private/qpixmapdata_gl_p.h> +#include <private/qglpixelbuffer_p.h> +#include <private/qwindowsurface_gl_p.h> #include "qcolormap.h" #include "qcache.h" #include "qfile.h" @@ -1290,6 +1295,7 @@ void QGLContextPrivate::init(QPaintDevice *dev, const QGLFormat &format) eglContext = 0; #endif pbo = 0; + fbo = 0; crWin = false; initDone = false; sharing = false; @@ -1297,6 +1303,8 @@ 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; + active_engine = 0; } QGLContext* QGLContext::currentCtx = 0; @@ -1307,12 +1315,8 @@ QGLContext* QGLContext::currentCtx = 0; QGLFramebufferObject::toImage() */ -QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha) +static void convertFromGLImage(QImage &img, int w, int h, bool alpha_format, bool include_alpha) { - QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32); - int w = size.width(); - int h = size.height(); - glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { // OpenGL gives RGBA; Qt wants ARGB uint *p = (uint*)img.bits(); @@ -1343,7 +1347,30 @@ QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include } } - return img.mirrored(); + img = img.mirrored(); +} + +QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha) +{ + QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32); + int w = size.width(); + int h = size.height(); + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); + convertFromGLImage(img, w, h, alpha_format, include_alpha); + return img; +} + +QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha) +{ + QImage img(size, alpha_format ? QImage::Format_ARGB32 : QImage::Format_RGB32); + int w = size.width(); + int h = size.height(); +#if !defined(QT_OPENGL_ES_2) && !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + //### glGetTexImage not in GL ES 2.0, need to do something else here! + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); +#endif + convertFromGLImage(img, w, h, alpha_format, include_alpha); + return img; } // returns the highest number closest to v, which is a power of 2 @@ -1547,7 +1574,7 @@ void QGLContextPrivate::cleanup() Q_Q(QGLContext); if (pbo) { QGLContext *ctx = q; - glDeleteBuffersARB(1, &pbo); + glDeleteBuffers(1, &pbo); pbo = 0; } } @@ -1684,58 +1711,105 @@ static void qt_gl_clean_cache(qint64 cacheKey) static void convertToGLFormatHelper(QImage &dst, const QImage &img, GLenum texture_format) { - Q_ASSERT(dst.size() == img.size()); Q_ASSERT(dst.depth() == 32); Q_ASSERT(img.depth() == 32); - const int width = img.width(); - const int height = img.height(); - const uint *p = (const uint*) img.scanLine(img.height() - 1); - uint *q = (uint*) dst.scanLine(0); - - if (texture_format == GL_BGRA) { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - // mirror + swizzle - for (int i=0; i < height; ++i) { - const uint *end = p + width; - while (p < end) { - *q = ((*p << 24) & 0xff000000) - | ((*p >> 24) & 0x000000ff) - | ((*p << 8) & 0x00ff0000) - | ((*p >> 8) & 0x0000ff00); - p++; - q++; + if (dst.size() != img.size()) { + int target_width = dst.width(); + int target_height = dst.height(); + qreal sx = target_width / qreal(img.width()); + qreal sy = target_height / qreal(img.height()); + + quint32 *dest = (quint32 *) dst.scanLine(0); // NB! avoid detach here + uchar *srcPixels = (uchar *) img.scanLine(img.height() - 1); + int sbpl = img.bytesPerLine(); + int dbpl = dst.bytesPerLine(); + + int ix = 0x00010000 / sx; + int iy = 0x00010000 / sy; + + quint32 basex = int(0.5 * ix); + quint32 srcy = int(0.5 * iy); + + // scale, swizzle and mirror in one loop + while (target_height--) { + const uint *src = (const quint32 *) (srcPixels - (srcy >> 16) * sbpl); + int srcx = basex; + for (int x=0; x<target_width; ++x) { + uint src_pixel = src[srcx >> 16]; + if (texture_format == GL_BGRA) { + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + dest[x] = ((src_pixel << 24) & 0xff000000) + | ((src_pixel >> 24) & 0x000000ff) + | ((src_pixel << 8) & 0x00ff0000) + | ((src_pixel >> 8) & 0x0000ff00); + } else { + dest[x] = src_pixel; + } + } else { // GL_RGBA + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + dest[x] = (src_pixel << 8) | ((src_pixel >> 24) & 0xff); + } else { + dest[x] = ((src_pixel << 16) & 0xff0000) + | ((src_pixel >> 16) & 0xff) + | (src_pixel & 0xff00ff00); + } } - p -= 2 * width; - } - } else { - const uint bytesPerLine = img.bytesPerLine(); - for (int i=0; i < height; ++i) { - memcpy(q, p, bytesPerLine); - q += width; - p -= width; + srcx += ix; } + dest = (quint32 *)(((uchar *) dest) + dbpl); + srcy += iy; } } else { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - for (int i=0; i < height; ++i) { - const uint *end = p + width; - while (p < end) { - *q = (*p << 8) | ((*p >> 24) & 0xFF); - p++; - q++; + const int width = img.width(); + const int height = img.height(); + const uint *p = (const uint*) img.scanLine(img.height() - 1); + uint *q = (uint*) dst.scanLine(0); + + if (texture_format == GL_BGRA) { + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + // mirror + swizzle + for (int i=0; i < height; ++i) { + const uint *end = p + width; + while (p < end) { + *q = ((*p << 24) & 0xff000000) + | ((*p >> 24) & 0x000000ff) + | ((*p << 8) & 0x00ff0000) + | ((*p >> 8) & 0x0000ff00); + p++; + q++; + } + p -= 2 * width; + } + } else { + const uint bytesPerLine = img.bytesPerLine(); + for (int i=0; i < height; ++i) { + memcpy(q, p, bytesPerLine); + q += width; + p -= width; } - p -= 2 * width; } } else { - for (int i=0; i < height; ++i) { - const uint *end = p + width; - while (p < end) { - *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); - p++; - q++; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + for (int i=0; i < height; ++i) { + const uint *end = p + width; + while (p < end) { + *q = (*p << 8) | ((*p >> 24) & 0xff); + p++; + q++; + } + p -= 2 * width; + } + } else { + for (int i=0; i < height; ++i) { + const uint *end = p + width; + while (p < end) { + *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); + p++; + q++; + } + p -= 2 * width; } - p -= 2 * width; } } } @@ -1765,7 +1839,7 @@ GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint use_pbo = qt_resolve_buffer_extensions(ctx); if (use_pbo && pbo == 0) - glGenBuffersARB(1, &pbo); + glGenBuffers(1, &pbo); } // the GL_BGRA format is only present in GL version >= 1.2 @@ -1778,19 +1852,18 @@ GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint } // Scale the pixmap if needed. GL textures needs to have the - // dimensions 2^n+2(border) x 2^m+2(border). + // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL + // 2.0 or use the GL_TEXTURE_RECTANGLE texture target int tx_w = qt_next_power_of_two(image.width()); int tx_h = qt_next_power_of_two(image.height()); + bool scale = false; - // Note: the clean param is only true when a texture is bound - // from the QOpenGLPaintEngine - in that case we have to force - // a premultiplied texture format QImage img = image; if (( !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) && !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0) ) && (target == GL_TEXTURE_2D && (tx_w != image.width() || tx_h != image.height()))) { - img = image.scaled(tx_w, tx_h); + scale = true; } GLuint tx_id; @@ -1817,28 +1890,35 @@ GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint uchar *ptr = 0; if (use_pbo) { - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbo); - glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, img.width() * img.height() * 4, 0, GL_STREAM_DRAW_ARB); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo); + glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, img.width() * img.height() * 4, 0, GL_STREAM_DRAW_ARB); ptr = reinterpret_cast<uchar *>(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB)); } - if (ptr) { - QImage::Format target_format = img.format(); - if (clean || img.format() != QImage::Format_ARGB32) - target_format = QImage::Format_ARGB32_Premultiplied; + QImage::Format target_format = img.format(); + // Note: the clean param is only true when a texture is bound + // from the QOpenGLPaintEngine - in that case we have to force + // a premultiplied texture format + if (clean || img.format() != QImage::Format_ARGB32) + target_format = QImage::Format_ARGB32_Premultiplied; + if (img.format() != target_format) + img = img.convertToFormat(target_format); + if (ptr) { QImage buffer(ptr, img.width(), img.height(), target_format); - convertToGLFormatHelper(buffer, img.convertToFormat(target_format), texture_format); + convertToGLFormatHelper(buffer, img, texture_format); glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); - glTexImage2D(target, 0, format, img.width(), img.height(), 0, texture_format, GL_UNSIGNED_BYTE, 0); + glTexImage2D(target, 0, format, img.width(), img.height(), 0, texture_format, + GL_UNSIGNED_BYTE, 0); } else { - QImage tx = convertToGLFormat(img, clean, texture_format); + QImage tx(scale ? QSize(tx_w, tx_h) : img.size(), target_format); + convertToGLFormatHelper(tx, img, texture_format); glTexImage2D(target, 0, format, tx.width(), tx.height(), 0, texture_format, GL_UNSIGNED_BYTE, tx.bits()); } if (use_pbo) - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); // this assumes the size of a texture is always smaller than the max cache size int cost = img.width()*img.height()*4/1024; @@ -1890,11 +1970,13 @@ GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint /*! \internal */ GLuint QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean) { -#if !defined(QT_OPENGL_ES_2) - if (target == qt_gl_preferredTextureTarget() && pixmap.pixmapData()->classId() == QPixmapData::OpenGLClass) { - const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pixmap.pixmapData()); + Q_Q(QGLContext); + QPixmapData *pd = pixmap.pixmapData(); +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + if (target == GL_TEXTURE_2D && pd->classId() == QPixmapData::OpenGLClass) { + const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pd); - if (data->isValidContext(QGLContext::currentContext())) + if (data->isValidContext(q)) return data->bind(); } #endif @@ -2046,12 +2128,34 @@ void QGLContext::deleteTexture(QMacCompatGLuint id) } #endif -// qpaintengine_opengl.cpp -#if !defined(QT_OPENGL_ES_2) -extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); -#else -void qt_add_rect_to_array(const QRectF &r, q_vertexType *array) {}; -#endif +void qt_add_rect_to_array(const QRectF &r, q_vertexType *array) +{ + qreal left = r.left(); + qreal right = r.right(); + qreal top = r.top(); + qreal bottom = r.bottom(); + + array[0] = f2vt(left); + array[1] = f2vt(top); + array[2] = f2vt(right); + array[3] = f2vt(top); + array[4] = f2vt(right); + array[5] = f2vt(bottom); + array[6] = f2vt(left); + array[7] = f2vt(bottom); +} + +void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array) +{ + array[0] = f2vt(x1); + array[1] = f2vt(y1); + array[2] = f2vt(x2); + array[3] = f2vt(y1); + array[4] = f2vt(x2); + array[5] = f2vt(y2); + array[6] = f2vt(x1); + array[7] = f2vt(y2); +} static void qDrawTextureRect(const QRectF &target, GLint textureWidth, GLint textureHeight, GLenum textureTarget) { @@ -3238,7 +3342,7 @@ bool QGLWidget::event(QEvent *e) glFinish(); doneCurrent(); } else if (e->type() == QEvent::ParentChange) { - if (d->glcx->d_func()->screen != d->xinfo.screen()) { + if (d->glcx->d_func()->screen != d->xinfo.screen() || testAttribute(Qt::WA_TranslucentBackground)) { setContext(new QGLContext(d->glcx->requestedFormat(), this)); // ### recreating the overlay isn't supported atm } @@ -4094,14 +4198,16 @@ void QGLWidget::drawTexture(const QPointF &point, QMacCompatGLuint textureId, QM } #endif -#if defined(QT_OPENGL_ES_2) -Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_engine) -#else +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) +Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_2_engine) +#endif + +#ifndef QT_OPENGL_ES_2 Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_engine) #endif #ifdef Q_WS_QWS -Q_OPENGL_EXPORT QOpenGLPaintEngine* qt_qgl_paint_engine() +Q_OPENGL_EXPORT QPaintEngine* qt_qgl_paint_engine() { #if !defined(QT_OPENGL_ES_2) return qt_gl_engine(); @@ -4119,7 +4225,16 @@ Q_OPENGL_EXPORT QOpenGLPaintEngine* qt_qgl_paint_engine() */ QPaintEngine *QGLWidget::paintEngine() const { +#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL) return qt_gl_engine(); +#elif defined(QT_OPENGL_ES_2) + return qt_gl_2_engine(); +#else + if (!qt_gl_preferGL2Engine()) + return qt_gl_engine(); + else + return qt_gl_2_engine(); +#endif } #ifdef QT3_SUPPORT @@ -4208,6 +4323,8 @@ void QGLExtensions::init_extensions() glExtensions |= FramebufferObject; glExtensions |= GenerateMipmap; #endif + if (extensions.contains(QLatin1String("EXT_framebuffer_blit"))) + glExtensions |= FramebufferBlit; QGLContext cx(QGLFormat::defaultFormat()); if (glExtensions & TextureCompression) { @@ -4257,4 +4374,226 @@ Q_OPENGL_EXPORT const QString qt_gl_library_name() } #endif +void QGLDrawable::setDevice(QPaintDevice *pdev) +{ + wasBound = false; + widget = 0; + buffer = 0; + fbo = 0; +#ifdef Q_WS_QWS + wsurf = 0; +#endif + +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + if (pdev->devType() == QInternal::Pixmap) { + QPixmapData *data = static_cast<QPixmap *>(pdev)->pixmapData(); + Q_ASSERT(data->classId() == QPixmapData::OpenGLClass); + pixmapData = static_cast<QGLPixmapData *>(data); + + fbo = pixmapData->fbo(); + } +#else + Q_ASSERT(pdev->devType() != QInternal::Pixmap); +#endif + + if (pdev->devType() == QInternal::Widget) + widget = static_cast<QGLWidget *>(pdev); + else if (pdev->devType() == QInternal::Pbuffer) + buffer = static_cast<QGLPixelBuffer *>(pdev); + else if (pdev->devType() == QInternal::FramebufferObject) + fbo = static_cast<QGLFramebufferObject *>(pdev); +#ifdef Q_WS_QWS + else if (pdev->devType() == QInternal::UnknownDevice) + wsurf = static_cast<QWSGLPaintDevice*>(pdev)->windowSurface(); +#elif !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + else if (pdev->devType() == QInternal::UnknownDevice) + wsurf = static_cast<QGLWindowSurface *>(pdev); +#endif +} + +void QGLDrawable::swapBuffers() +{ + if (widget) { + if (widget->autoBufferSwap()) + widget->swapBuffers(); +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + } else if (pixmapData) { + pixmapData->swapBuffers(); +#endif + } else { + glFlush(); + } +} + +void QGLDrawable::makeCurrent() +{ + previous_fbo = 0; + if (!pixmapData && !fbo) { + QGLContext *ctx = context(); + previous_fbo = ctx->d_ptr->current_fbo; + ctx->d_ptr->current_fbo = 0; + if (previous_fbo) + glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + } + + if (widget) + widget->makeCurrent(); +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + else if (pixmapData) + pixmapData->makeCurrent(); +#endif + else if (buffer) + buffer->makeCurrent(); +#if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)) + else if (wsurf) + wsurf->context()->makeCurrent(); +#endif + else if (fbo) { + wasBound = fbo->isBound(); + if (!wasBound) + fbo->bind(); + } +} + +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) +QGLPixmapData *QGLDrawable::copyOnBegin() const +{ + if (!pixmapData || pixmapData->isUninitialized()) + return 0; + return pixmapData; +} +#endif + +void QGLDrawable::doneCurrent() +{ +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + if (pixmapData) { + pixmapData->doneCurrent(); + return; + } +#endif + + if (previous_fbo) { + QGLContext *ctx = context(); + ctx->d_ptr->current_fbo = previous_fbo; + glBindFramebuffer(GL_FRAMEBUFFER_EXT, previous_fbo); + } + + if (fbo && !wasBound) + fbo->release(); +} + +QSize QGLDrawable::size() const +{ + if (widget) { + return QSize(widget->d_func()->glcx->device()->width(), + widget->d_func()->glcx->device()->height()); +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + } else if (pixmapData) { + return pixmapData->size(); +#endif + } else if (buffer) { + return buffer->size(); + } else if (fbo) { + return fbo->size(); + } +#ifdef Q_WS_QWS + else if (wsurf) + return wsurf->window()->frameSize(); +#elif !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + else if (wsurf) + return QSize(wsurf->width(), wsurf->height()); +#endif + return QSize(); +} + +QGLFormat QGLDrawable::format() const +{ + if (widget) + return widget->format(); + else if (buffer) + return buffer->format(); +#if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)) + else if (wsurf) + return wsurf->context()->format(); +#endif + else if (fbo && QGLContext::currentContext()) { + QGLFormat fmt = QGLContext::currentContext()->format(); + fmt.setStencil(fbo->attachment() == QGLFramebufferObject::CombinedDepthStencil); + fmt.setDepth(fbo->attachment() != QGLFramebufferObject::NoAttachment); + return fmt; + } + + return QGLFormat(); +} + +GLuint QGLDrawable::bindTexture(const QImage &image, GLenum target, GLint format) +{ + if (widget) + return widget->d_func()->glcx->d_func()->bindTexture(image, target, format, true); + else if (buffer) + return buffer->d_func()->qctx->d_func()->bindTexture(image, target, format, true); + else if (fbo && QGLContext::currentContext()) + return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(image, target, format, true); +#if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)) + else if (wsurf) + return wsurf->context()->d_func()->bindTexture(image, target, format, true); +#endif + return 0; +} + +GLuint QGLDrawable::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) +{ + if (widget) + return widget->d_func()->glcx->d_func()->bindTexture(pixmap, target, format, true); + else if (buffer) + return buffer->d_func()->qctx->d_func()->bindTexture(pixmap, target, format, true); + else if (fbo && QGLContext::currentContext()) + return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(pixmap, target, format, true); +#if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)) + else if (wsurf) + return wsurf->context()->d_func()->bindTexture(pixmap, target, format, true); +#endif + return 0; +} + +QColor QGLDrawable::backgroundColor() const +{ + if (widget) + return widget->palette().brush(widget->backgroundRole()).color(); + else if (pixmapData) + return pixmapData->fillColor(); + return QApplication::palette().brush(QPalette::Background).color(); +} + +bool QGLDrawable::hasTransparentBackground() const +{ + return widget && widget->testAttribute(Qt::WA_TranslucentBackground); +} + +QGLContext *QGLDrawable::context() const +{ + if (widget) + return widget->d_func()->glcx; + else if (buffer) + return buffer->d_func()->qctx; + else if (fbo) + return const_cast<QGLContext *>(QGLContext::currentContext()); +#if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)) + else if (wsurf) + return wsurf->context(); +#endif + return 0; +} + +bool QGLDrawable::autoFillBackground() const +{ + if (widget) + return widget->autoFillBackground(); + else if (pixmapData) + return pixmapData->needsFill(); + else + return false; +} + QT_END_NAMESPACE diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index 01b1d6f..19d779a 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -371,9 +371,9 @@ private: friend class QMacGLWindowChangeEvent; friend QGLContextPrivate *qt_phonon_get_dptr(const QGLContext *); #endif -#ifdef Q_WS_WIN friend class QGLFramebufferObject; friend class QGLFramebufferObjectPrivate; +#ifdef Q_WS_WIN friend bool qt_resolve_GLSL_functions(QGLContext *ctx); friend bool qt_createGLSLProgram(QGLContext *ctx, GLuint &program, const char *shader_src, GLuint &shader); #endif diff --git a/src/opengl/qgl_egl.cpp b/src/opengl/qgl_egl.cpp index 98c5710..287c537 100644 --- a/src/opengl/qgl_egl.cpp +++ b/src/opengl/qgl_egl.cpp @@ -57,14 +57,14 @@ void qt_egl_set_format(QEglProperties& props, int deviceType, const QGLFormat& f // Set the pixel format to that contained in the QGLFormat // if the system hasn't already chosen a fixed format to // match the pixmap, widget, etc. - if (props.value(EGL_RED_SIZE) == EGL_DONT_CARE || f.redBufferSize() != -1) + if (props.value(EGL_RED_SIZE) == 0 || f.redBufferSize() != -1) props.setValue(EGL_RED_SIZE, f.redBufferSize() == -1 ? 1 : f.redBufferSize()); - if (props.value(EGL_GREEN_SIZE) == EGL_DONT_CARE || f.greenBufferSize() != -1) + if (props.value(EGL_GREEN_SIZE) == 0 || f.greenBufferSize() != -1) props.setValue(EGL_GREEN_SIZE, f.greenBufferSize() == -1 ? 1 : f.greenBufferSize()); - if (props.value(EGL_BLUE_SIZE) == EGL_DONT_CARE || f.blueBufferSize() != -1) + if (props.value(EGL_BLUE_SIZE) == 0 || f.blueBufferSize() != -1) props.setValue(EGL_BLUE_SIZE, f.blueBufferSize() == -1 ? 1 : f.blueBufferSize()); if (f.alpha()) { - if (props.value(EGL_ALPHA_SIZE) == EGL_DONT_CARE || f.alphaBufferSize() != -1) + if (props.value(EGL_ALPHA_SIZE) == 0 || f.alphaBufferSize() != -1) props.setValue(EGL_ALPHA_SIZE, f.alphaBufferSize() == -1 ? 1 : f.alphaBufferSize()); } diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 16aaa96..b1a63b5 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -61,6 +61,10 @@ #include "QtCore/qhash.h" #include "private/qwidget_p.h" +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) +#include "private/qpixmapdata_gl_p.h" +#endif + #ifndef QT_OPENGL_ES_1_CL #define q_vertexType float #define q_vertexTypeEnum GL_FLOAT @@ -239,6 +243,7 @@ public: QGLFormat glFormat; QGLFormat reqFormat; GLuint pbo; + GLuint fbo; uint valid : 1; uint sharing : 1; @@ -255,6 +260,9 @@ public: QGLExtensionFuncs extensionFuncs; GLint max_texture_size; + GLuint current_fbo; + QPaintEngine *active_engine; + #ifdef Q_WS_WIN static inline QGLExtensionFuncs& qt_get_extension_funcs(const QGLContext *ctx) { return ctx->d_ptr->extensionFuncs; } #endif @@ -268,7 +276,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: @@ -281,6 +289,54 @@ Q_SIGNALS: void aboutToDestroyContext(const QGLContext *context); }; +class QGLPixelBuffer; +class QGLFramebufferObject; +class QWSGLWindowSurface; +class QGLWindowSurface; +class QGLDrawable { +public: + QGLDrawable() : widget(0), buffer(0), fbo(0) +#if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)) + , wsurf(0) +#endif +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + , pixmapData(0) +#endif + {} + void setDevice(QPaintDevice *pdev); + void swapBuffers(); + void makeCurrent(); + void doneCurrent(); + QSize size() const; + QGLFormat format() const; + GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D, GLint format = GL_RGBA); + GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D, GLint format = GL_RGBA); + QColor backgroundColor() const; + QGLContext *context() const; + bool autoFillBackground() const; + bool hasTransparentBackground() const; + +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + QGLPixmapData *copyOnBegin() const; +#endif + +private: + bool wasBound; + QGLWidget *widget; + QGLPixelBuffer *buffer; + QGLFramebufferObject *fbo; +#ifdef Q_WS_QWS + QWSGLWindowSurface *wsurf; +#elif !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + QGLWindowSurface *wsurf; +#endif + +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) + QGLPixmapData *pixmapData; +#endif + int previous_fbo; +}; + // GL extension definitions class QGLExtensions { public: @@ -296,7 +352,8 @@ public: StencilWrap = 0x00000100, PackedDepthStencil = 0x00000200, NVFloatBuffer = 0x00000400, - PixelBufferObject = 0x00000800 + PixelBufferObject = 0x00000800, + FramebufferBlit = 0x00001000 }; Q_DECLARE_FLAGS(Extensions, Extension) @@ -377,6 +434,16 @@ extern QOpenGLPaintEngine* qt_qgl_paint_engine(); extern EGLDisplay qt_qgl_egl_display(); #endif +inline bool qt_gl_preferGL2Engine() +{ +#if defined(QT_OPENGL_ES_2) + return true; +#else + return (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) + && !qgetenv("QT_GL_USE_OPENGL2ENGINE").isEmpty(); +#endif +} + inline GLenum qt_gl_preferredTextureFormat() { return QSysInfo::ByteOrder == QSysInfo::BigEndian ? GL_RGBA : GL_BGRA; @@ -384,12 +451,16 @@ inline GLenum qt_gl_preferredTextureFormat() inline GLenum qt_gl_preferredTextureTarget() { +#if defined(QT_OPENGL_ES_2) + return GL_TEXTURE_2D; +#else return (QGLExtensions::glExtensions & QGLExtensions::TextureRectangle) + && !qt_gl_preferGL2Engine() ? GL_TEXTURE_RECTANGLE_NV : GL_TEXTURE_2D; +#endif } - QT_END_NAMESPACE #endif // QGL_P_H diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp index 7ed7a23..528cc3b 100644 --- a/src/opengl/qgl_x11.cpp +++ b/src/opengl/qgl_x11.cpp @@ -515,11 +515,22 @@ void *QGLContext::chooseVisual() void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth) { Q_D(QGLContext); - int spec[40]; + int spec[45]; int i = 0; spec[i++] = GLX_LEVEL; spec[i++] = f.plane(); const QX11Info *xinfo = qt_x11Info(d->paintDevice); + bool useFBConfig = false; + +#if defined(GLX_VERSION_1_3) && !defined(QT_NO_XRENDER) + QWidget* widget = 0; + if (d->paintDevice->devType() == QInternal::Widget) + widget = static_cast<QWidget*>(d->paintDevice); + + // Only use glXChooseFBConfig for widgets if we're trying to get an ARGB visual + if (widget && widget->testAttribute(Qt::WA_TranslucentBackground) && X11->use_xrender) + useFBConfig = true; +#endif #if defined(GLX_VERSION_1_1) && defined(GLX_EXT_visual_info) static bool useTranspExt = false; @@ -547,28 +558,41 @@ void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth) useTranspExtChecked = true; } - if (f.plane() && useTranspExt) { + if (f.plane() && useTranspExt && !useFBConfig) { // Required to avoid non-transparent overlay visual(!) on some systems spec[i++] = GLX_TRANSPARENT_TYPE_EXT; spec[i++] = f.rgba() ? GLX_TRANSPARENT_RGB_EXT : GLX_TRANSPARENT_INDEX_EXT; } #endif +#if defined(GLX_VERSION_1_3) + // GLX_RENDER_TYPE is only in glx >=1.3 + if (useFBConfig) { + spec[i++] = GLX_RENDER_TYPE; + spec[i++] = f.rgba() ? GLX_RGBA_BIT : GLX_COLOR_INDEX_BIT; + } +#endif + if (f.doubleBuffer()) spec[i++] = GLX_DOUBLEBUFFER; + if (useFBConfig) + spec[i++] = True; if (f.depth()) { spec[i++] = GLX_DEPTH_SIZE; spec[i++] = f.depthBufferSize() == -1 ? 1 : f.depthBufferSize(); } if (f.stereo()) { spec[i++] = GLX_STEREO; + if (useFBConfig) + spec[i++] = True; } if (f.stencil()) { spec[i++] = GLX_STENCIL_SIZE; spec[i++] = f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize(); } if (f.rgba()) { - spec[i++] = GLX_RGBA; + if (!useFBConfig) + spec[i++] = GLX_RGBA; spec[i++] = GLX_RED_SIZE; spec[i++] = f.redBufferSize() == -1 ? 1 : f.redBufferSize(); spec[i++] = GLX_GREEN_SIZE; @@ -603,8 +627,86 @@ void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth) spec[i++] = f.samples() == -1 ? 4 : f.samples(); } +#if defined(GLX_VERSION_1_3) + if (useFBConfig) { + spec[i++] = GLX_DRAWABLE_TYPE; + switch(d->paintDevice->devType()) { + case QInternal::Pixmap: + spec[i++] = GLX_PIXMAP_BIT; + break; + case QInternal::Pbuffer: + spec[i++] = GLX_PBUFFER_BIT; + break; + default: + qWarning("QGLContext: Unknown paint device type %d", d->paintDevice->devType()); + // Fall-through & assume it's a window + case QInternal::Widget: + spec[i++] = GLX_WINDOW_BIT; + break; + }; + } +#endif + spec[i] = XNone; - return glXChooseVisual(xinfo->display(), xinfo->screen(), spec); + + + XVisualInfo* chosenVisualInfo = 0; + +#if defined(GLX_VERSION_1_3) + while (useFBConfig) { + GLXFBConfig *configs; + int configCount = 0; + configs = glXChooseFBConfig(xinfo->display(), xinfo->screen(), spec, &configCount); + + if (!configs) + break; // fallback to trying glXChooseVisual + + for (i = 0; i < configCount; ++i) { + XVisualInfo* vi; + vi = glXGetVisualFromFBConfig(xinfo->display(), configs[i]); + if (!vi) + continue; + +#if !defined(QT_NO_XRENDER) + QWidget* w = 0; + if (d->paintDevice->devType() == QInternal::Widget) + w = static_cast<QWidget*>(d->paintDevice); + + if (w && w->testAttribute(Qt::WA_TranslucentBackground) && f.alpha()) { + // Attempt to find a config who's visual has a proper alpha channel + XRenderPictFormat *pictFormat; + pictFormat = XRenderFindVisualFormat(xinfo->display(), vi->visual); + + if (pictFormat && (pictFormat->type == PictTypeDirect) && pictFormat->direct.alphaMask) { + // The pict format for the visual matching the FBConfig indicates ARGB + if (chosenVisualInfo) + XFree(chosenVisualInfo); + chosenVisualInfo = vi; + break; + } + } else +#endif //QT_NO_XRENDER + if (chosenVisualInfo) { + // If we've got a visual we can use and we're not trying to find one with a + // real alpha channel, we might as well just use the one we've got + break; + } + + if (!chosenVisualInfo) + chosenVisualInfo = vi; // Have something to fall back to + else + XFree(vi); + } + + XFree(configs); + break; + } +#endif // defined(GLX_VERSION_1_3) + + if (!chosenVisualInfo) + chosenVisualInfo = glXChooseVisual(xinfo->display(), xinfo->screen(), spec); + + return chosenVisualInfo; } @@ -1173,6 +1275,12 @@ void QGLWidget::setContext(QGLContext *context, d_func()->xinfo = parentWidget()->d_func()->xinfo; } + // If the application has set WA_TranslucentBackground and not explicitly set + // the alpha buffer size to zero, modify the format so it have an alpha channel + QGLFormat& fmt = d->glcx->d_func()->glFormat; + if (testAttribute(Qt::WA_TranslucentBackground) && fmt.alphaBufferSize() == -1) + fmt.setAlphaBufferSize(1); + bool createFailed = false; if (!d->glcx->isValid()) { if (!d->glcx->create(shareContext ? shareContext : oldcx)) diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index 480a2dc..514e763 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -114,13 +114,6 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval()); #endif - // Create the EGL surface to draw into. - if (!d->eglContext->createSurface(device())) { - delete d->eglContext; - d->eglContext = 0; - return false; - } - return true; } @@ -250,13 +243,34 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, return; } + + // Create the EGL Context (which will also choose the config for us) if (d->glcx) d->glcx->doneCurrent(); QGLContext* oldcx = d->glcx; d->glcx = context; + bool createFailed = false; + if (!d->glcx->isValid()) { + if (!d->glcx->create(shareContext ? shareContext : oldcx)) + createFailed = true; + } + if (createFailed) { + if (deleteOldContext) + delete oldcx; + return; + } + + if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) { + if (deleteOldContext) + delete oldcx; + return; + } + + + + // Make sure native winIds are avaliable if (parentWidget()) { - // force creation of delay-created widgets parentWidget()->winId(); if (parentWidget()->x11Info().screen() != x11Info().screen()) d_func()->xinfo = parentWidget()->d_func()->xinfo; @@ -267,16 +281,97 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, hide(); XVisualInfo vi; + memset(&vi, 0, sizeof(XVisualInfo)); + + // Check to see if EGL is suggesting an appropriate visual id: + EGLint nativeVisualId; + QEglContext* qeglCtx = d->glcx->d_func()->eglContext; + qeglCtx->configAttrib(EGL_NATIVE_VISUAL_ID, &nativeVisualId); + vi.visualid = nativeVisualId; + + if (vi.visualid) { + // EGL has suggested a visual id, so get the rest of the visual info for that id: + XVisualInfo *chosenVisualInfo; + int matchingCount = 0; + chosenVisualInfo = XGetVisualInfo(x11Info().display(), VisualIDMask, &vi, &matchingCount); + if (chosenVisualInfo) { + qDebug("Using X Visual ID (%d) provided by EGL", (int)vi.visualid); + vi = *chosenVisualInfo; + XFree(chosenVisualInfo); + } + else { + qWarning("Warning: EGL suggested using X visual ID %d for config %d, but this seems to be invalid!", + nativeVisualId, (int)qeglCtx->config()); + vi.visualid = 0; + } + } - int err = XMatchVisualInfo(x11Info().display(), x11Info().screen(), x11Info().depth(), TrueColor, &vi); - if (err == 0) { - qWarning("Error: Couldn't get a matching X visual for format"); - return; + // If EGL does not know the visual ID, so try to select an appropriate one ourselves, first + // using XRender if we're supposed to have an alpha, then falling back to XGetVisualInfo + + bool useArgb = context->format().alpha() && !context->deviceIsPixmap(); +#if !defined(QT_NO_XRENDER) + if (vi.visualid == 0 && useArgb) { + // Try to use XRender to find an ARGB visual we can use + vi.screen = x11Info().screen(); + vi.depth = 32; + vi.c_class = TrueColor; + XVisualInfo *matchingVisuals; + int matchingCount = 0; + matchingVisuals = XGetVisualInfo(x11Info().display(), + VisualScreenMask|VisualDepthMask|VisualClassMask, + &vi, &matchingCount); + + for (int i = 0; i < matchingCount; ++i) { + XRenderPictFormat *format; + format = XRenderFindVisualFormat(x11Info().display(), matchingVisuals[i].visual); + if (format->type == PictTypeDirect && format->direct.alphaMask) { + vi = matchingVisuals[i]; + qDebug("Using X Visual ID (%d) for ARGB visual as provided by XRender", (int)vi.visualid); + break; + } + } + XFree(matchingVisuals); } +#endif + + if (vi.visualid == 0) { + EGLint depth; + qeglCtx->configAttrib(EGL_BUFFER_SIZE, &depth); + int err; + err = XMatchVisualInfo(x11Info().display(), x11Info().screen(), depth, TrueColor, &vi); + if (err == 0) { + qWarning("Warning: Can't find an X visual which matches the EGL config(%d)'s depth (%d)!", + (int)qeglCtx->config(), depth); + depth = x11Info().depth(); + err = XMatchVisualInfo(x11Info().display(), x11Info().screen(), depth, TrueColor, &vi); + if (err == 0) { + qWarning("Error: Couldn't get any matching X visual!"); + return; + } else + qWarning(" - Falling back to X11 suggested depth (%d)", depth); + } else + qDebug("Using X Visual ID (%d) for EGL provided depth (%d)", (int)vi.visualid, depth); + + // Don't try to use ARGB now unless the visual is 32-bit - even then it might stil fail :-( + if (useArgb) + useArgb = vi.depth == 32; + } + +// qDebug("Visual Info:"); +// qDebug(" bits_per_rgb=%d", vi.bits_per_rgb); +// qDebug(" red_mask=0x%x", vi.red_mask); +// qDebug(" green_mask=0x%x", vi.green_mask); +// qDebug(" blue_mask=0x%x", vi.blue_mask); +// qDebug(" colormap_size=%d", vi.colormap_size); +// qDebug(" c_class=%d", vi.c_class); +// qDebug(" depth=%d", vi.depth); +// qDebug(" screen=%d", vi.screen); +// qDebug(" visualid=%d", vi.visualid); XSetWindowAttributes a; - Window p = RootWindow(X11->display, vi.screen); + Window p = RootWindow(x11Info().display(), x11Info().screen()); if (parentWidget()) p = parentWidget()->winId(); @@ -284,9 +379,13 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, a.background_pixel = colmap.pixel(palette().color(backgroundRole())); a.border_pixel = colmap.pixel(Qt::black); - Window w = XCreateWindow(X11->display, p, x(), y(), width(), height(), - 0, vi.depth, InputOutput, vi.visual, - CWBackPixel|CWBorderPixel, &a); + unsigned int valueMask = CWBackPixel|CWBorderPixel; + if(useArgb) { + a.colormap = XCreateColormap(x11Info().display(), p, vi.visual, AllocNone); + valueMask |= CWColormap; + } + Window w = XCreateWindow(x11Info().display(), p, x(), y(), width(), height(), + 0, vi.depth, InputOutput, vi.visual, valueMask, &a); if (deleteOldContext) delete oldcx; @@ -294,27 +393,18 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, create(w); // Create with the ID of the window we've just created - d->eglSurfaceWindowId = w; // Remember the window id we created the surface for - - if (visible) - show(); - bool createFailed = false; - if (!d->glcx->isValid()) { - if (!d->glcx->create(shareContext ? shareContext : oldcx)) - createFailed = true; - } - if (createFailed) { - if (deleteOldContext) - delete oldcx; + // Create the EGL surface to draw into. + if (!d->glcx->d_func()->eglContext->createSurface(this)) { + delete d->glcx->d_func()->eglContext; + d->glcx->d_func()->eglContext = 0; return; } - if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) { - if (deleteOldContext) - delete oldcx; - return; - } + d->eglSurfaceWindowId = w; // Remember the window id we created the surface for + + if (visible) + show(); d->glcx->setWindowCreated(true); } diff --git a/src/opengl/qglextensions.cpp b/src/opengl/qglextensions.cpp index 8357cf9..3c198fb 100644 --- a/src/opengl/qglextensions.cpp +++ b/src/opengl/qglextensions.cpp @@ -45,9 +45,15 @@ QT_BEGIN_NAMESPACE bool qt_resolve_framebufferobject_extensions(QGLContext *ctx) { -#if !defined(QT_OPENGL_ES_2) - if (glIsRenderbufferEXT != 0) +#if defined(QT_OPENGL_ES_2) + static bool have_resolved = false; + if (have_resolved) + return true; + have_resolved = true; +#else + if (glIsRenderbuffer != 0) return true; +#endif if (ctx == 0) { qWarning("QGLFramebufferObject: Unable to resolve framebuffer object extensions -" @@ -55,32 +61,37 @@ bool qt_resolve_framebufferobject_extensions(QGLContext *ctx) return false; } - glIsRenderbufferEXT = (_glIsRenderbufferEXT) ctx->getProcAddress(QLatin1String("glIsRenderbufferEXT")); - glBindRenderbufferEXT = (_glBindRenderbufferEXT) ctx->getProcAddress(QLatin1String("glBindRenderbufferEXT")); - glDeleteRenderbuffersEXT = (_glDeleteRenderbuffersEXT) ctx->getProcAddress(QLatin1String("glDeleteRenderbuffersEXT")); - glGenRenderbuffersEXT = (_glGenRenderbuffersEXT) ctx->getProcAddress(QLatin1String("glGenRenderbuffersEXT")); - glRenderbufferStorageEXT = (_glRenderbufferStorageEXT) ctx->getProcAddress(QLatin1String("glRenderbufferStorageEXT")); - glGetRenderbufferParameterivEXT = - (_glGetRenderbufferParameterivEXT) ctx->getProcAddress(QLatin1String("glGetRenderbufferParameterivEXT")); - glIsFramebufferEXT = (_glIsFramebufferEXT) ctx->getProcAddress(QLatin1String("glIsFramebufferEXT")); - glBindFramebufferEXT = (_glBindFramebufferEXT) ctx->getProcAddress(QLatin1String("glBindFramebufferEXT")); - glDeleteFramebuffersEXT = (_glDeleteFramebuffersEXT) ctx->getProcAddress(QLatin1String("glDeleteFramebuffersEXT")); - glGenFramebuffersEXT = (_glGenFramebuffersEXT) ctx->getProcAddress(QLatin1String("glGenFramebuffersEXT")); - glCheckFramebufferStatusEXT = (_glCheckFramebufferStatusEXT) ctx->getProcAddress(QLatin1String("glCheckFramebufferStatusEXT")); - glFramebufferTexture1DEXT = (_glFramebufferTexture1DEXT) ctx->getProcAddress(QLatin1String("glFramebufferTexture1DEXT")); - glFramebufferTexture2DEXT = (_glFramebufferTexture2DEXT) ctx->getProcAddress(QLatin1String("glFramebufferTexture2DEXT")); - glFramebufferTexture3DEXT = (_glFramebufferTexture3DEXT) ctx->getProcAddress(QLatin1String("glFramebufferTexture3DEXT")); - glFramebufferRenderbufferEXT = (_glFramebufferRenderbufferEXT) ctx->getProcAddress(QLatin1String("glFramebufferRenderbufferEXT")); - glGetFramebufferAttachmentParameterivEXT = - (_glGetFramebufferAttachmentParameterivEXT) ctx->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameterivEXT")); - glGenerateMipmapEXT = (_glGenerateMipmapEXT) ctx->getProcAddress(QLatin1String("glGenerateMipmapEXT")); - return glIsRenderbufferEXT; + + glBlitFramebufferEXT = (_glBlitFramebufferEXT) ctx->getProcAddress(QLatin1String("glBlitFramebufferEXT")); + glRenderbufferStorageMultisampleEXT = + (_glRenderbufferStorageMultisampleEXT) ctx->getProcAddress(QLatin1String("glRenderbufferStorageMultisampleEXT")); + +#if !defined(QT_OPENGL_ES_2) + glIsRenderbuffer = (_glIsRenderbuffer) ctx->getProcAddress(QLatin1String("glIsRenderbufferEXT")); + glBindRenderbuffer = (_glBindRenderbuffer) ctx->getProcAddress(QLatin1String("glBindRenderbufferEXT")); + glDeleteRenderbuffers = (_glDeleteRenderbuffers) ctx->getProcAddress(QLatin1String("glDeleteRenderbuffersEXT")); + glGenRenderbuffers = (_glGenRenderbuffers) ctx->getProcAddress(QLatin1String("glGenRenderbuffersEXT")); + glRenderbufferStorage = (_glRenderbufferStorage) ctx->getProcAddress(QLatin1String("glRenderbufferStorageEXT")); + glGetRenderbufferParameteriv = + (_glGetRenderbufferParameteriv) ctx->getProcAddress(QLatin1String("glGetRenderbufferParameterivEXT")); + glIsFramebuffer = (_glIsFramebuffer) ctx->getProcAddress(QLatin1String("glIsFramebufferEXT")); + glBindFramebuffer = (_glBindFramebuffer) ctx->getProcAddress(QLatin1String("glBindFramebufferEXT")); + glDeleteFramebuffers = (_glDeleteFramebuffers) ctx->getProcAddress(QLatin1String("glDeleteFramebuffersEXT")); + glGenFramebuffers = (_glGenFramebuffers) ctx->getProcAddress(QLatin1String("glGenFramebuffersEXT")); + glCheckFramebufferStatus = (_glCheckFramebufferStatus) ctx->getProcAddress(QLatin1String("glCheckFramebufferStatusEXT")); + glFramebufferTexture2D = (_glFramebufferTexture2D) ctx->getProcAddress(QLatin1String("glFramebufferTexture2DEXT")); + glFramebufferRenderbuffer = (_glFramebufferRenderbuffer) ctx->getProcAddress(QLatin1String("glFramebufferRenderbufferEXT")); + glGetFramebufferAttachmentParameteriv = + (_glGetFramebufferAttachmentParameteriv) ctx->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameterivEXT")); + glGenerateMipmap = (_glGenerateMipmap) ctx->getProcAddress(QLatin1String("glGenerateMipmapEXT")); + + return glIsRenderbuffer; #else - Q_UNUSED(ctx); return true; #endif } +#if !defined(QT_OPENGL_ES_2) bool qt_resolve_version_1_3_functions(QGLContext *ctx) { if (glMultiTexCoord4f != 0) @@ -89,14 +100,12 @@ bool qt_resolve_version_1_3_functions(QGLContext *ctx) QGLContext cx(QGLFormat::defaultFormat()); glMultiTexCoord4f = (_glMultiTexCoord4f) ctx->getProcAddress(QLatin1String("glMultiTexCoord4f")); -#if defined(QT_OPENGL_ES_2) - return glMultiTexCoord4f; -#else glActiveTexture = (_glActiveTexture) ctx->getProcAddress(QLatin1String("glActiveTexture")); return glMultiTexCoord4f && glActiveTexture; -#endif } +#endif +#if !defined(QT_OPENGL_ES_2) bool qt_resolve_stencil_face_extension(QGLContext *ctx) { if (glActiveStencilFaceEXT != 0) @@ -107,7 +116,10 @@ bool qt_resolve_stencil_face_extension(QGLContext *ctx) return glActiveStencilFaceEXT; } +#endif + +#if !defined(QT_OPENGL_ES_2) bool qt_resolve_frag_program_extensions(QGLContext *ctx) { if (glProgramStringARB != 0) @@ -126,60 +138,215 @@ bool qt_resolve_frag_program_extensions(QGLContext *ctx) && glGenProgramsARB && glProgramLocalParameter4fvARB; } +#endif + bool qt_resolve_buffer_extensions(QGLContext *ctx) { - if (glBindBufferARB && glDeleteBuffersARB && glGenBuffersARB && glBufferDataARB - && glMapBufferARB && glUnmapBufferARB) + if (glMapBufferARB && glUnmapBufferARB +#if !defined(QT_OPENGL_ES_2) + && glBindBuffer && glDeleteBuffers && glGenBuffers && glBufferData +#endif + ) return true; - glBindBufferARB = (_glBindBufferARB) ctx->getProcAddress(QLatin1String("glBindBufferARB")); - glDeleteBuffersARB = (_glDeleteBuffersARB) ctx->getProcAddress(QLatin1String("glDeleteBuffersARB")); - glGenBuffersARB = (_glGenBuffersARB) ctx->getProcAddress(QLatin1String("glGenBuffersARB")); - glBufferDataARB = (_glBufferDataARB) ctx->getProcAddress(QLatin1String("glBufferDataARB")); +#if !defined(QT_OPENGL_ES_2) + glBindBuffer = (_glBindBuffer) ctx->getProcAddress(QLatin1String("glBindBufferARB")); + glDeleteBuffers = (_glDeleteBuffers) ctx->getProcAddress(QLatin1String("glDeleteBuffersARB")); + glGenBuffers = (_glGenBuffers) ctx->getProcAddress(QLatin1String("glGenBuffersARB")); + glBufferData = (_glBufferData) ctx->getProcAddress(QLatin1String("glBufferDataARB")); +#endif glMapBufferARB = (_glMapBufferARB) ctx->getProcAddress(QLatin1String("glMapBufferARB")); glUnmapBufferARB = (_glUnmapBufferARB) ctx->getProcAddress(QLatin1String("glUnmapBufferARB")); - return glBindBufferARB - && glDeleteBuffersARB - && glGenBuffersARB - && glBufferDataARB - && glMapBufferARB - && glUnmapBufferARB; + return glMapBufferARB + && glUnmapBufferARB +#if !defined(QT_OPENGL_ES_2) + && glBindBuffer + && glDeleteBuffers + && glGenBuffers + && glBufferData +#endif + ; } bool qt_resolve_glsl_extensions(QGLContext *ctx) { +#if defined(QT_OPENGL_ES_2) + // The GLSL shader functions are always present in OpenGL/ES 2.0. + // The only exceptions are glGetProgramBinaryOES and glProgramBinaryOES. + if (!QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glslResolved) { + glGetProgramBinaryOES = (_glGetProgramBinaryOES) ctx->getProcAddress(QLatin1String("glGetProgramBinaryOES")); + glProgramBinaryOES = (_glProgramBinaryOES) ctx->getProcAddress(QLatin1String("glProgramBinaryOES")); + QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glslResolved = true; + } + return true; +#else if (glCreateShader) return true; glCreateShader = (_glCreateShader) ctx->getProcAddress(QLatin1String("glCreateShader")); - glShaderSource = (_glShaderSource) ctx->getProcAddress(QLatin1String("glShaderSource")); - glCompileShader = (_glCompileShader) ctx->getProcAddress(QLatin1String("glCompileShader")); - glDeleteShader = (_glDeleteShader) ctx->getProcAddress(QLatin1String("glDeleteShader")); - - glCreateProgram = (_glCreateProgram) ctx->getProcAddress(QLatin1String("glCreateProgram")); - glAttachShader = (_glAttachShader) ctx->getProcAddress(QLatin1String("glAttachShader")); - glDetachShader = (_glDetachShader) ctx->getProcAddress(QLatin1String("glDetachShader")); - glLinkProgram = (_glLinkProgram) ctx->getProcAddress(QLatin1String("glLinkProgram")); - glUseProgram = (_glUseProgram) ctx->getProcAddress(QLatin1String("glUseProgram")); - glDeleteProgram = (_glDeleteProgram) ctx->getProcAddress(QLatin1String("glDeleteProgram")); - - glGetShaderInfoLog = (_glGetShaderInfoLog) ctx->getProcAddress(QLatin1String("glGetShaderInfoLog")); - glGetShaderiv = (_glGetShaderiv) ctx->getProcAddress(QLatin1String("glGetShaderiv")); - glGetProgramiv = (_glGetProgramiv) ctx->getProcAddress(QLatin1String("glGetProgramiv")); - - glGetUniformLocation = (_glGetUniformLocation) ctx->getProcAddress(QLatin1String("glGetUniformLocation")); - glUniform4fv = (_glUniform4fv) ctx->getProcAddress(QLatin1String("glUniform4fv")); - glUniform3fv = (_glUniform3fv) ctx->getProcAddress(QLatin1String("glUniform3fv")); - glUniform2fv = (_glUniform2fv) ctx->getProcAddress(QLatin1String("glUniform2fv")); - glUniform1fv = (_glUniform1fv) ctx->getProcAddress(QLatin1String("glUniform1fv")); - glUniform1i = (_glUniform1i) ctx->getProcAddress(QLatin1String("glUniform1i")); - - return glCreateShader && glShaderSource && glCompileShader && glDeleteProgram && - glCreateProgram && glAttachShader && glDetachShader && glLinkProgram && glUseProgram && - glDeleteProgram && glGetShaderInfoLog && glGetShaderiv && glGetProgramiv && glGetUniformLocation && - glUniform1i && glUniform1fv && glUniform2fv && glUniform3fv && glUniform4fv; + if (glCreateShader) { + glShaderSource = (_glShaderSource) ctx->getProcAddress(QLatin1String("glShaderSource")); + glShaderBinary = (_glShaderBinary) ctx->getProcAddress(QLatin1String("glShaderBinary")); + glCompileShader = (_glCompileShader) ctx->getProcAddress(QLatin1String("glCompileShader")); + glDeleteShader = (_glDeleteShader) ctx->getProcAddress(QLatin1String("glDeleteShader")); + glIsShader = (_glIsShader) ctx->getProcAddress(QLatin1String("glIsShader")); + + glCreateProgram = (_glCreateProgram) ctx->getProcAddress(QLatin1String("glCreateProgram")); + glAttachShader = (_glAttachShader) ctx->getProcAddress(QLatin1String("glAttachShader")); + glDetachShader = (_glDetachShader) ctx->getProcAddress(QLatin1String("glDetachShader")); + glLinkProgram = (_glLinkProgram) ctx->getProcAddress(QLatin1String("glLinkProgram")); + glUseProgram = (_glUseProgram) ctx->getProcAddress(QLatin1String("glUseProgram")); + glDeleteProgram = (_glDeleteProgram) ctx->getProcAddress(QLatin1String("glDeleteProgram")); + glIsProgram = (_glIsProgram) ctx->getProcAddress(QLatin1String("glIsProgram")); + + glGetShaderInfoLog = (_glGetShaderInfoLog) ctx->getProcAddress(QLatin1String("glGetShaderInfoLog")); + glGetShaderiv = (_glGetShaderiv) ctx->getProcAddress(QLatin1String("glGetShaderiv")); + glGetShaderSource = (_glGetShaderSource) ctx->getProcAddress(QLatin1String("glGetShaderSource")); + glGetProgramiv = (_glGetProgramiv) ctx->getProcAddress(QLatin1String("glGetProgramiv")); + glGetProgramInfoLog = (_glGetProgramInfoLog) ctx->getProcAddress(QLatin1String("glGetProgramInfoLog")); + + glGetUniformLocation = (_glGetUniformLocation) ctx->getProcAddress(QLatin1String("glGetUniformLocation")); + glUniform4fv = (_glUniform4fv) ctx->getProcAddress(QLatin1String("glUniform4fv")); + glUniform3fv = (_glUniform3fv) ctx->getProcAddress(QLatin1String("glUniform3fv")); + glUniform2fv = (_glUniform2fv) ctx->getProcAddress(QLatin1String("glUniform2fv")); + glUniform1fv = (_glUniform1fv) ctx->getProcAddress(QLatin1String("glUniform1fv")); + glUniform1i = (_glUniform1i) ctx->getProcAddress(QLatin1String("glUniform1i")); + glUniform1iv = (_glUniform1iv) ctx->getProcAddress(QLatin1String("glUniform1iv")); + glUniformMatrix2fv = (_glUniformMatrix2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2fv")); + glUniformMatrix3fv = (_glUniformMatrix3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3fv")); + glUniformMatrix4fv = (_glUniformMatrix4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4fv")); + glUniformMatrix2x3fv = (_glUniformMatrix2x3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2x3fv")); + glUniformMatrix2x4fv = (_glUniformMatrix2x4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2x4fv")); + glUniformMatrix3x2fv = (_glUniformMatrix3x2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3x2fv")); + glUniformMatrix3x4fv = (_glUniformMatrix3x4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3x4fv")); + glUniformMatrix4x2fv = (_glUniformMatrix4x2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4x2fv")); + glUniformMatrix4x3fv = (_glUniformMatrix4x3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4x3fv")); + + glBindAttribLocation = (_glBindAttribLocation) ctx->getProcAddress(QLatin1String("glBindAttribLocation")); + glGetAttribLocation = (_glGetAttribLocation) ctx->getProcAddress(QLatin1String("glGetAttribLocation")); + glVertexAttrib1fv = (_glVertexAttrib1fv) ctx->getProcAddress(QLatin1String("glVertexAttrib1fv")); + glVertexAttrib2fv = (_glVertexAttrib2fv) ctx->getProcAddress(QLatin1String("glVertexAttrib2fv")); + glVertexAttrib3fv = (_glVertexAttrib3fv) ctx->getProcAddress(QLatin1String("glVertexAttrib3fv")); + glVertexAttrib4fv = (_glVertexAttrib4fv) ctx->getProcAddress(QLatin1String("glVertexAttrib4fv")); + glVertexAttribPointer = (_glVertexAttribPointer) ctx->getProcAddress(QLatin1String("glVertexAttribPointer")); + glDisableVertexAttribArray = (_glDisableVertexAttribArray) ctx->getProcAddress(QLatin1String("glDisableVertexAttribArray")); + glEnableVertexAttribArray = (_glEnableVertexAttribArray) ctx->getProcAddress(QLatin1String("glEnableVertexAttribArray")); + + } else { + // We may not have the standard shader functions, but we might + // have the older ARB functions instead. + glCreateShader = (_glCreateShader) ctx->getProcAddress(QLatin1String("glCreateShaderObjectARB")); + glShaderSource = (_glShaderSource) ctx->getProcAddress(QLatin1String("glShaderSourceARB")); + glShaderBinary = (_glShaderBinary) ctx->getProcAddress(QLatin1String("glShaderBinaryARB")); + glCompileShader = (_glCompileShader) ctx->getProcAddress(QLatin1String("glCompileShaderARB")); + glDeleteShader = (_glDeleteShader) ctx->getProcAddress(QLatin1String("glDeleteObjectARB")); + glIsShader = 0; + + glCreateProgram = (_glCreateProgram) ctx->getProcAddress(QLatin1String("glCreateProgramObjectARB")); + glAttachShader = (_glAttachShader) ctx->getProcAddress(QLatin1String("glAttachObjectARB")); + glDetachShader = (_glDetachShader) ctx->getProcAddress(QLatin1String("glDetachObjectARB")); + glLinkProgram = (_glLinkProgram) ctx->getProcAddress(QLatin1String("glLinkProgramARB")); + glUseProgram = (_glUseProgram) ctx->getProcAddress(QLatin1String("glUseProgramObjectARB")); + glDeleteProgram = (_glDeleteProgram) ctx->getProcAddress(QLatin1String("glDeleteObjectARB")); + glIsProgram = 0; + + glGetShaderInfoLog = (_glGetShaderInfoLog) ctx->getProcAddress(QLatin1String("glGetInfoLogARB")); + glGetShaderiv = (_glGetShaderiv) ctx->getProcAddress(QLatin1String("glGetObjectParameterivARB")); + glGetShaderSource = (_glGetShaderSource) ctx->getProcAddress(QLatin1String("glGetShaderSourceARB")); + glGetProgramiv = (_glGetProgramiv) ctx->getProcAddress(QLatin1String("glGetObjectParameterivARB")); + glGetProgramInfoLog = (_glGetProgramInfoLog) ctx->getProcAddress(QLatin1String("glGetInfoLogARB")); + + glGetUniformLocation = (_glGetUniformLocation) ctx->getProcAddress(QLatin1String("glGetUniformLocationARB")); + glUniform4fv = (_glUniform4fv) ctx->getProcAddress(QLatin1String("glUniform4fvARB")); + glUniform3fv = (_glUniform3fv) ctx->getProcAddress(QLatin1String("glUniform3fvARB")); + glUniform2fv = (_glUniform2fv) ctx->getProcAddress(QLatin1String("glUniform2fvARB")); + glUniform1fv = (_glUniform1fv) ctx->getProcAddress(QLatin1String("glUniform1fvARB")); + glUniform1i = (_glUniform1i) ctx->getProcAddress(QLatin1String("glUniform1iARB")); + glUniform1iv = (_glUniform1iv) ctx->getProcAddress(QLatin1String("glUniform1ivARB")); + glUniformMatrix2fv = (_glUniformMatrix2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2fvARB")); + glUniformMatrix3fv = (_glUniformMatrix3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3fvARB")); + glUniformMatrix4fv = (_glUniformMatrix4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4fvARB")); + glUniformMatrix2x3fv = (_glUniformMatrix2x3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2x3fvARB")); + glUniformMatrix2x4fv = (_glUniformMatrix2x4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix2x4fvARB")); + glUniformMatrix3x2fv = (_glUniformMatrix3x2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3x2fvARB")); + glUniformMatrix3x4fv = (_glUniformMatrix3x4fv) ctx->getProcAddress(QLatin1String("glUniformMatrix3x4fvARB")); + glUniformMatrix4x2fv = (_glUniformMatrix4x2fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4x2fvARB")); + glUniformMatrix4x3fv = (_glUniformMatrix4x3fv) ctx->getProcAddress(QLatin1String("glUniformMatrix4x3fvARB")); + + glBindAttribLocation = (_glBindAttribLocation) ctx->getProcAddress(QLatin1String("glBindAttribLocationARB")); + glGetAttribLocation = (_glGetAttribLocation) ctx->getProcAddress(QLatin1String("glGetAttribLocationARB")); + glVertexAttrib1fv = (_glVertexAttrib1fv) ctx->getProcAddress(QLatin1String("glVertexAttrib1fvARB")); + glVertexAttrib2fv = (_glVertexAttrib2fv) ctx->getProcAddress(QLatin1String("glVertexAttrib2fvARB")); + glVertexAttrib3fv = (_glVertexAttrib3fv) ctx->getProcAddress(QLatin1String("glVertexAttrib3fvARB")); + glVertexAttrib4fv = (_glVertexAttrib4fv) ctx->getProcAddress(QLatin1String("glVertexAttrib4fvARB")); + glVertexAttribPointer = (_glVertexAttribPointer) ctx->getProcAddress(QLatin1String("glVertexAttribPointerARB")); + glDisableVertexAttribArray = (_glDisableVertexAttribArray) ctx->getProcAddress(QLatin1String("glDisableVertexAttribArrayARB")); + glEnableVertexAttribArray = (_glEnableVertexAttribArray) ctx->getProcAddress(QLatin1String("glEnableVertexAttribArrayARB")); + } + + // Note: glShaderBinary(), glIsShader(), glIsProgram(), and + // glUniformMatrixNxMfv() are optional, but all other functions + // are required. + + return glCreateShader && + glShaderSource && + glCompileShader && + glDeleteProgram && + glCreateProgram && + glAttachShader && + glDetachShader && + glLinkProgram && + glUseProgram && + glDeleteProgram && + glGetShaderInfoLog && + glGetShaderiv && + glGetShaderSource && + glGetProgramiv && + glGetProgramInfoLog && + glGetUniformLocation && + glUniform1fv && + glUniform2fv && + glUniform3fv && + glUniform4fv && + glUniform1i && + glUniform1iv && + glUniformMatrix2fv && + glUniformMatrix3fv && + glUniformMatrix4fv && + glBindAttribLocation && + glGetAttribLocation && + glVertexAttrib1fv && + glVertexAttrib2fv && + glVertexAttrib3fv && + glVertexAttrib4fv && + glVertexAttribPointer && + glDisableVertexAttribArray && + glEnableVertexAttribArray; +#endif } +#if !defined(QT_OPENGL_ES_2) +bool qt_resolve_version_2_0_functions(QGLContext *ctx) +{ + bool gl2supported = true; + if (!qt_resolve_glsl_extensions(ctx)) + gl2supported = false; + + if (!qt_resolve_version_1_3_functions(ctx)) + gl2supported = false; + + if (glStencilOpSeparate) + return gl2supported; + + glStencilOpSeparate = (_glStencilOpSeparate) ctx->getProcAddress(QLatin1String("glStencilOpSeparate")); + if (!glStencilOpSeparate) + gl2supported = false; + + return gl2supported; +} +#endif + + QT_END_NAMESPACE diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h index a0517f5..d6edfb6 100644 --- a/src/opengl/qglextensions_p.h +++ b/src/opengl/qglextensions_p.h @@ -74,11 +74,15 @@ typedef ptrdiff_t GLsizeiptrARB; #endif +#ifndef GL_VERSION_2_0 +typedef char GLchar; +#endif + // ARB_pixel_buffer_object -typedef void (APIENTRY *_glBindBufferARB) (GLenum, GLuint); -typedef void (APIENTRY *_glDeleteBuffersARB) (GLsizei, const GLuint *); -typedef void (APIENTRY *_glGenBuffersARB) (GLsizei, GLuint *); -typedef void (APIENTRY *_glBufferDataARB) (GLenum, GLsizeiptrARB, const GLvoid *, GLenum); +typedef void (APIENTRY *_glBindBuffer) (GLenum, GLuint); +typedef void (APIENTRY *_glDeleteBuffers) (GLsizei, const GLuint *); +typedef void (APIENTRY *_glGenBuffers) (GLsizei, GLuint *); +typedef void (APIENTRY *_glBufferData) (GLenum, GLsizeiptrARB, const GLvoid *, GLenum); typedef GLvoid* (APIENTRY *_glMapBufferARB) (GLenum, GLenum); typedef GLboolean (APIENTRY *_glUnmapBufferARB) (GLenum); @@ -92,8 +96,10 @@ typedef void (APIENTRY *_glProgramLocalParameter4fvARB) (GLenum, GLuint, const G // GLSL typedef GLuint (APIENTRY *_glCreateShader) (GLenum); typedef void (APIENTRY *_glShaderSource) (GLuint, GLsizei, const char **, const GLint *); +typedef void (APIENTRY *_glShaderBinary) (GLint, const GLuint*, GLenum, const void*, GLint); typedef void (APIENTRY *_glCompileShader) (GLuint); typedef void (APIENTRY *_glDeleteShader) (GLuint); +typedef GLboolean (APIENTRY *_glIsShader) (GLuint); typedef GLuint (APIENTRY *_glCreateProgram) (); typedef void (APIENTRY *_glAttachShader) (GLuint, GLuint); @@ -101,62 +107,101 @@ typedef void (APIENTRY *_glDetachShader) (GLuint, GLuint); typedef void (APIENTRY *_glLinkProgram) (GLuint); typedef void (APIENTRY *_glUseProgram) (GLuint); typedef void (APIENTRY *_glDeleteProgram) (GLuint); +typedef GLboolean (APIENTRY *_glIsProgram) (GLuint); typedef void (APIENTRY *_glGetShaderInfoLog) (GLuint, GLsizei, GLsizei *, char *); typedef void (APIENTRY *_glGetShaderiv) (GLuint, GLenum, GLint *); +typedef void (APIENTRY *_glGetShaderSource) (GLuint, GLsizei, GLsizei *, char *); typedef void (APIENTRY *_glGetProgramiv) (GLuint, GLenum, GLint *); +typedef void (APIENTRY *_glGetProgramInfoLog) (GLuint, GLsizei, GLsizei *, char *); typedef GLuint (APIENTRY *_glGetUniformLocation) (GLuint, const char*); -typedef void (APIENTRY *_glUniform4fv) (GLint, GLsizei, GLfloat *); -typedef void (APIENTRY *_glUniform3fv) (GLint, GLsizei, GLfloat *); -typedef void (APIENTRY *_glUniform2fv) (GLint, GLsizei, GLfloat *); -typedef void (APIENTRY *_glUniform1fv) (GLint, GLsizei, GLfloat *); +typedef void (APIENTRY *_glUniform4fv) (GLint, GLsizei, const GLfloat *); +typedef void (APIENTRY *_glUniform3fv) (GLint, GLsizei, const GLfloat *); +typedef void (APIENTRY *_glUniform2fv) (GLint, GLsizei, const GLfloat *); +typedef void (APIENTRY *_glUniform1fv) (GLint, GLsizei, const GLfloat *); typedef void (APIENTRY *_glUniform1i) (GLint, GLint); +typedef void (APIENTRY *_glUniform1iv) (GLint, GLsizei, const GLint *); +typedef void (APIENTRY *_glUniformMatrix2fv) (GLint, GLsizei, GLboolean, const GLfloat *); +typedef void (APIENTRY *_glUniformMatrix3fv) (GLint, GLsizei, GLboolean, const GLfloat *); +typedef void (APIENTRY *_glUniformMatrix4fv) (GLint, GLsizei, GLboolean, const GLfloat *); +typedef void (APIENTRY *_glUniformMatrix2x3fv) (GLint, GLsizei, GLboolean, const GLfloat *); +typedef void (APIENTRY *_glUniformMatrix2x4fv) (GLint, GLsizei, GLboolean, const GLfloat *); +typedef void (APIENTRY *_glUniformMatrix3x2fv) (GLint, GLsizei, GLboolean, const GLfloat *); +typedef void (APIENTRY *_glUniformMatrix3x4fv) (GLint, GLsizei, GLboolean, const GLfloat *); +typedef void (APIENTRY *_glUniformMatrix4x2fv) (GLint, GLsizei, GLboolean, const GLfloat *); +typedef void (APIENTRY *_glUniformMatrix4x3fv) (GLint, GLsizei, GLboolean, const GLfloat *); + +typedef void (APIENTRY *_glBindAttribLocation) (GLuint, GLuint, const char *); +typedef GLint (APIENTRY *_glGetAttribLocation) (GLuint, const char *); +typedef void (APIENTRY *_glVertexAttrib1fv) (GLuint, const GLfloat *); +typedef void (APIENTRY *_glVertexAttrib2fv) (GLuint, const GLfloat *); +typedef void (APIENTRY *_glVertexAttrib3fv) (GLuint, const GLfloat *); +typedef void (APIENTRY *_glVertexAttrib4fv) (GLuint, const GLfloat *); +typedef void (APIENTRY *_glVertexAttribPointer) (GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid *); +typedef void (APIENTRY *_glDisableVertexAttribArray) (GLuint); +typedef void (APIENTRY *_glEnableVertexAttribArray) (GLuint); + +typedef void (APIENTRY *_glGetProgramBinaryOES) (GLuint, GLsizei, GLsizei *, GLenum *, void *); +typedef void (APIENTRY *_glProgramBinaryOES) (GLuint, GLenum, const void *, GLint); -typedef void (APIENTRY *_glActiveStencilFaceEXT) (GLenum ); typedef void (APIENTRY *_glMultiTexCoord4f) (GLenum, GLfloat, GLfloat, GLfloat, GLfloat); +typedef void (APIENTRY *_glActiveStencilFaceEXT) (GLenum ); + +// Needed for GL2 engine: +typedef void (APIENTRY *_glStencilOpSeparate) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); typedef void (APIENTRY *_glActiveTexture) (GLenum); + // EXT_GL_framebuffer_object -typedef GLboolean (APIENTRY *_glIsRenderbufferEXT) (GLuint renderbuffer); -typedef void (APIENTRY *_glBindRenderbufferEXT) (GLenum target, GLuint renderbuffer); -typedef void (APIENTRY *_glDeleteRenderbuffersEXT) (GLsizei n, const GLuint *renderbuffers); -typedef void (APIENTRY *_glGenRenderbuffersEXT) (GLsizei n, GLuint *renderbuffers); -typedef void (APIENTRY *_glRenderbufferStorageEXT) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (APIENTRY *_glGetRenderbufferParameterivEXT) (GLenum target, GLenum pname, GLint *params); -typedef GLboolean (APIENTRY *_glIsFramebufferEXT) (GLuint framebuffer); -typedef void (APIENTRY *_glBindFramebufferEXT) (GLenum target, GLuint framebuffer); -typedef void (APIENTRY *_glDeleteFramebuffersEXT) (GLsizei n, const GLuint *framebuffers); -typedef void (APIENTRY *_glGenFramebuffersEXT) (GLsizei n, GLuint *framebuffers); -typedef GLenum (APIENTRY *_glCheckFramebufferStatusEXT) (GLenum target); -typedef void (APIENTRY *_glFramebufferTexture1DEXT) (GLenum target, GLenum attachment, GLenum textarget, - GLuint texture, GLint level); -typedef void (APIENTRY *_glFramebufferTexture2DEXT) (GLenum target, GLenum attachment, GLenum textarget, +typedef GLboolean (APIENTRY *_glIsRenderbuffer) (GLuint renderbuffer); +typedef void (APIENTRY *_glBindRenderbuffer) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRY *_glDeleteRenderbuffers) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRY *_glGenRenderbuffers) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRY *_glRenderbufferStorage) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRY *_glGetRenderbufferParameteriv) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRY *_glIsFramebuffer) (GLuint framebuffer); +typedef void (APIENTRY *_glBindFramebuffer) (GLenum target, GLuint framebuffer); +typedef void (APIENTRY *_glDeleteFramebuffers) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRY *_glGenFramebuffers) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRY *_glCheckFramebufferStatus) (GLenum target); +typedef void (APIENTRY *_glFramebufferTexture2D) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (APIENTRY *_glFramebufferTexture3DEXT) (GLenum target, GLenum attachment, GLenum textarget, - GLuint texture, GLint level, GLint zoffset); -typedef void (APIENTRY *_glFramebufferRenderbufferEXT) (GLenum target, GLenum attachment, GLenum renderbuffertarget, +typedef void (APIENTRY *_glFramebufferRenderbuffer) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -typedef void (APIENTRY *_glGetFramebufferAttachmentParameterivEXT) (GLenum target, GLenum attachment, GLenum pname, +typedef void (APIENTRY *_glGetFramebufferAttachmentParameteriv) (GLenum target, GLenum attachment, GLenum pname, GLint *params); -typedef void (APIENTRY *_glGenerateMipmapEXT) (GLenum target); +typedef void (APIENTRY *_glGenerateMipmap) (GLenum target); + +// EXT_GL_framebuffer_blit +typedef void (APIENTRY *_glBlitFramebufferEXT) (int srcX0, int srcY0, int srcX1, int srcY1, + int dstX0, int dstY0, int dstX1, int dstY1, + GLbitfield mask, GLenum filter); + +// EXT_GL_framebuffer_multisample +typedef void (APIENTRY *_glRenderbufferStorageMultisampleEXT) (GLenum target, GLsizei samples, + GLenum internalformat, GLsizei width, GLsizei height); QT_BEGIN_NAMESPACE struct QGLExtensionFuncs { QGLExtensionFuncs() { +#if !defined(QT_OPENGL_ES_2) qt_glProgramStringARB = 0; qt_glBindProgramARB = 0; qt_glDeleteProgramsARB = 0; qt_glGenProgramsARB = 0; qt_glProgramLocalParameter4fvARB = 0; + // GLSL qt_glCreateShader = 0; qt_glShaderSource = 0; + qt_glShaderBinary = 0; qt_glCompileShader = 0; qt_glDeleteShader = 0; + qt_glIsShader = 0; qt_glCreateProgram = 0; qt_glAttachShader = 0; @@ -164,10 +209,13 @@ struct QGLExtensionFuncs qt_glLinkProgram = 0; qt_glUseProgram = 0; qt_glDeleteProgram = 0; + qt_glIsProgram = 0; qt_glGetShaderInfoLog = 0; qt_glGetShaderiv = 0; + qt_glGetShaderSource = 0; qt_glGetProgramiv = 0; + qt_glGetProgramInfoLog = 0; qt_glGetUniformLocation = 0; qt_glUniform4fv = 0; @@ -175,40 +223,74 @@ struct QGLExtensionFuncs qt_glUniform2fv = 0; qt_glUniform1fv = 0; qt_glUniform1i = 0; + qt_glUniform1iv = 0; + qt_glUniformMatrix2fv = 0; + qt_glUniformMatrix3fv = 0; + qt_glUniformMatrix4fv = 0; + qt_glUniformMatrix2x3fv = 0; + qt_glUniformMatrix2x4fv = 0; + qt_glUniformMatrix3x2fv = 0; + qt_glUniformMatrix3x4fv = 0; + qt_glUniformMatrix4x2fv = 0; + qt_glUniformMatrix4x3fv = 0; + + qt_glBindAttribLocation = 0; + qt_glGetAttribLocation = 0; + qt_glVertexAttrib1fv = 0; + qt_glVertexAttrib2fv = 0; + qt_glVertexAttrib3fv = 0; + qt_glVertexAttrib4fv = 0; + qt_glVertexAttribPointer = 0; + qt_glDisableVertexAttribArray = 0; + qt_glEnableVertexAttribArray = 0; + + // Extras for GL2 engine: + qt_glActiveTexture = 0; + qt_glStencilOpSeparate = 0; qt_glActiveStencilFaceEXT = 0; - qt_glMultiTexCoord4f = 0; - qt_glActiveTexture = 0; +#else + qt_glslResolved = false; + + qt_glGetProgramBinaryOES = 0; + qt_glProgramBinaryOES = 0; +#endif + // FBOs #if !defined(QT_OPENGL_ES_2) - qt_glIsRenderbufferEXT = 0; - qt_glBindRenderbufferEXT = 0; - qt_glDeleteRenderbuffersEXT = 0; - qt_glGenRenderbuffersEXT = 0; - qt_glRenderbufferStorageEXT = 0; - qt_glGetRenderbufferParameterivEXT = 0; - qt_glIsFramebufferEXT = 0; - qt_glBindFramebufferEXT = 0; - qt_glDeleteFramebuffersEXT = 0; - qt_glGenFramebuffersEXT = 0; - qt_glCheckFramebufferStatusEXT = 0; - qt_glFramebufferTexture1DEXT = 0; - qt_glFramebufferTexture2DEXT = 0; - qt_glFramebufferTexture3DEXT = 0; - qt_glFramebufferRenderbufferEXT = 0; - qt_glGetFramebufferAttachmentParameterivEXT = 0; - qt_glGenerateMipmapEXT = 0; -#endif - - qt_glBindBufferARB = 0; - qt_glDeleteBuffersARB = 0; - qt_glGenBuffersARB = 0; - qt_glBufferDataARB = 0; + qt_glIsRenderbuffer = 0; + qt_glBindRenderbuffer = 0; + qt_glDeleteRenderbuffers = 0; + qt_glGenRenderbuffers = 0; + qt_glRenderbufferStorage = 0; + qt_glGetRenderbufferParameteriv = 0; + qt_glIsFramebuffer = 0; + qt_glBindFramebuffer = 0; + qt_glDeleteFramebuffers = 0; + qt_glGenFramebuffers = 0; + qt_glCheckFramebufferStatus = 0; + qt_glFramebufferTexture2D = 0; + qt_glFramebufferRenderbuffer = 0; + qt_glGetFramebufferAttachmentParameteriv = 0; + qt_glGenerateMipmap = 0; +#endif + qt_glBlitFramebufferEXT = 0; + qt_glRenderbufferStorageMultisampleEXT = 0; + + // Buffer objects: +#if !defined(QT_OPENGL_ES_2) + qt_glBindBuffer = 0; + qt_glDeleteBuffers = 0; + qt_glGenBuffers = 0; + qt_glBufferData = 0; +#endif qt_glMapBufferARB = 0; qt_glUnmapBufferARB = 0; } + +#if !defined(QT_OPENGL_ES_2) _glProgramStringARB qt_glProgramStringARB; _glBindProgramARB qt_glBindProgramARB; _glDeleteProgramsARB qt_glDeleteProgramsARB; @@ -218,8 +300,10 @@ struct QGLExtensionFuncs // GLSL definitions _glCreateShader qt_glCreateShader; _glShaderSource qt_glShaderSource; + _glShaderBinary qt_glShaderBinary; _glCompileShader qt_glCompileShader; _glDeleteShader qt_glDeleteShader; + _glIsShader qt_glIsShader; _glCreateProgram qt_glCreateProgram; _glAttachShader qt_glAttachShader; @@ -227,10 +311,13 @@ struct QGLExtensionFuncs _glLinkProgram qt_glLinkProgram; _glUseProgram qt_glUseProgram; _glDeleteProgram qt_glDeleteProgram; + _glIsProgram qt_glIsProgram; _glGetShaderInfoLog qt_glGetShaderInfoLog; _glGetShaderiv qt_glGetShaderiv; + _glGetShaderSource qt_glGetShaderSource; _glGetProgramiv qt_glGetProgramiv; + _glGetProgramInfoLog qt_glGetProgramInfoLog; _glGetUniformLocation qt_glGetUniformLocation; _glUniform4fv qt_glUniform4fv; @@ -238,38 +325,74 @@ struct QGLExtensionFuncs _glUniform2fv qt_glUniform2fv; _glUniform1fv qt_glUniform1fv; _glUniform1i qt_glUniform1i; + _glUniform1iv qt_glUniform1iv; + _glUniformMatrix2fv qt_glUniformMatrix2fv; + _glUniformMatrix3fv qt_glUniformMatrix3fv; + _glUniformMatrix4fv qt_glUniformMatrix4fv; + _glUniformMatrix2x3fv qt_glUniformMatrix2x3fv; + _glUniformMatrix2x4fv qt_glUniformMatrix2x4fv; + _glUniformMatrix3x2fv qt_glUniformMatrix3x2fv; + _glUniformMatrix3x4fv qt_glUniformMatrix3x4fv; + _glUniformMatrix4x2fv qt_glUniformMatrix4x2fv; + _glUniformMatrix4x3fv qt_glUniformMatrix4x3fv; + + _glBindAttribLocation qt_glBindAttribLocation; + _glGetAttribLocation qt_glGetAttribLocation; + _glVertexAttrib1fv qt_glVertexAttrib1fv; + _glVertexAttrib2fv qt_glVertexAttrib2fv; + _glVertexAttrib3fv qt_glVertexAttrib3fv; + _glVertexAttrib4fv qt_glVertexAttrib4fv; + _glVertexAttribPointer qt_glVertexAttribPointer; + _glDisableVertexAttribArray qt_glDisableVertexAttribArray; + _glEnableVertexAttribArray qt_glEnableVertexAttribArray; - _glActiveStencilFaceEXT qt_glActiveStencilFaceEXT; +#else + bool qt_glslResolved; + + _glGetProgramBinaryOES qt_glGetProgramBinaryOES; + _glProgramBinaryOES qt_glProgramBinaryOES; +#endif + _glActiveStencilFaceEXT qt_glActiveStencilFaceEXT; _glMultiTexCoord4f qt_glMultiTexCoord4f; + +#if !defined(QT_OPENGL_ES_2) + // Extras needed for GL2 engine: _glActiveTexture qt_glActiveTexture; + _glStencilOpSeparate qt_glStencilOpSeparate; +#endif + // FBOs #if !defined(QT_OPENGL_ES_2) - _glIsRenderbufferEXT qt_glIsRenderbufferEXT; - _glBindRenderbufferEXT qt_glBindRenderbufferEXT; - _glDeleteRenderbuffersEXT qt_glDeleteRenderbuffersEXT; - _glGenRenderbuffersEXT qt_glGenRenderbuffersEXT; - _glRenderbufferStorageEXT qt_glRenderbufferStorageEXT; - _glGetRenderbufferParameterivEXT qt_glGetRenderbufferParameterivEXT; - _glIsFramebufferEXT qt_glIsFramebufferEXT; - _glBindFramebufferEXT qt_glBindFramebufferEXT; - _glDeleteFramebuffersEXT qt_glDeleteFramebuffersEXT; - _glGenFramebuffersEXT qt_glGenFramebuffersEXT; - _glCheckFramebufferStatusEXT qt_glCheckFramebufferStatusEXT; - _glFramebufferTexture1DEXT qt_glFramebufferTexture1DEXT; - _glFramebufferTexture2DEXT qt_glFramebufferTexture2DEXT; - _glFramebufferTexture3DEXT qt_glFramebufferTexture3DEXT; - _glFramebufferRenderbufferEXT qt_glFramebufferRenderbufferEXT; - _glGetFramebufferAttachmentParameterivEXT qt_glGetFramebufferAttachmentParameterivEXT; - _glGenerateMipmapEXT qt_glGenerateMipmapEXT; -#endif - - _glBindBufferARB qt_glBindBufferARB; - _glDeleteBuffersARB qt_glDeleteBuffersARB; - _glGenBuffersARB qt_glGenBuffersARB; - _glBufferDataARB qt_glBufferDataARB; + _glIsRenderbuffer qt_glIsRenderbuffer; + _glBindRenderbuffer qt_glBindRenderbuffer; + _glDeleteRenderbuffers qt_glDeleteRenderbuffers; + _glGenRenderbuffers qt_glGenRenderbuffers; + _glRenderbufferStorage qt_glRenderbufferStorage; + _glGetRenderbufferParameteriv qt_glGetRenderbufferParameteriv; + _glIsFramebuffer qt_glIsFramebuffer; + _glBindFramebuffer qt_glBindFramebuffer; + _glDeleteFramebuffers qt_glDeleteFramebuffers; + _glGenFramebuffers qt_glGenFramebuffers; + _glCheckFramebufferStatus qt_glCheckFramebufferStatus; + _glFramebufferTexture2D qt_glFramebufferTexture2D; + _glFramebufferRenderbuffer qt_glFramebufferRenderbuffer; + _glGetFramebufferAttachmentParameteriv qt_glGetFramebufferAttachmentParameteriv; + _glGenerateMipmap qt_glGenerateMipmap; +#endif + _glBlitFramebufferEXT qt_glBlitFramebufferEXT; + _glRenderbufferStorageMultisampleEXT qt_glRenderbufferStorageMultisampleEXT; + + // Buffer objects +#if !defined(QT_OPENGL_ES_2) + _glBindBuffer qt_glBindBuffer; + _glDeleteBuffers qt_glDeleteBuffers; + _glGenBuffers qt_glGenBuffers; + _glBufferData qt_glBufferData; +#endif _glMapBufferARB qt_glMapBufferARB; _glUnmapBufferARB qt_glUnmapBufferARB; + }; @@ -397,6 +520,28 @@ struct QGLExtensionFuncs #define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 #endif +// GL_EXT_framebuffer_blit +#ifndef GL_READ_FRAMEBUFFER_EXT +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#endif + +// GL_EXT_framebuffer_multisample +#ifndef GL_RENDERBUFFER_SAMPLES_EXT +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#endif + +#ifndef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#endif + +#ifndef GL_MAX_SAMPLES_EXT +#define GL_MAX_SAMPLES_EXT 0x8D5 +#endif + +#ifndef GL_DRAW_FRAMEBUFFER_EXT +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#endif + #ifndef GL_EXT_packed_depth_stencil #define GL_DEPTH_STENCIL_EXT 0x84F9 #define GL_UNSIGNED_INT_24_8_EXT 0x84FA @@ -416,6 +561,42 @@ struct QGLExtensionFuncs #define GL_UNPACK_IMAGE_HEIGHT 0x806E #endif +#ifndef GL_VERSION_1_4 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#endif + +#ifndef GL_VERSION_2_0 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#endif + + +#if !defined(QT_OPENGL_ES_2) #define glProgramStringARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glProgramStringARB #define glBindProgramARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindProgramARB #define glDeleteProgramsARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteProgramsARB @@ -426,63 +607,52 @@ struct QGLExtensionFuncs #define glMultiTexCoord4f QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glMultiTexCoord4f -#if !defined(QT_OPENGL_ES_2) #define glActiveTexture QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glActiveTexture -#endif - -#if !defined(QT_OPENGL_ES_2) - -#define glIsRenderbufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glIsRenderbufferEXT -#define glBindRenderbufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindRenderbufferEXT -#define glDeleteRenderbuffersEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteRenderbuffersEXT -#define glGenRenderbuffersEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenRenderbuffersEXT -#define glRenderbufferStorageEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glRenderbufferStorageEXT -#define glGetRenderbufferParameterivEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetRenderbufferParameterivEXT -#define glIsFramebufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glIsFramebufferEXT -#define glBindFramebufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindFramebufferEXT -#define glDeleteFramebuffersEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteFramebuffersEXT -#define glGenFramebuffersEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenFramebuffersEXT -#define glCheckFramebufferStatusEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glCheckFramebufferStatusEXT -#define glFramebufferTexture1DEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferTexture1DEXT -#define glFramebufferTexture2DEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferTexture2DEXT -#define glFramebufferTexture3DEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferTexture3DEXT -#define glFramebufferRenderbufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferRenderbufferEXT -#define glGetFramebufferAttachmentParameterivEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetFramebufferAttachmentParameterivEXT -#define glGenerateMipmapEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenerateMipmapEXT - -#else // QT_OPENGL_ES_2 +#endif // !defined(QT_OPENGL_ES_2) -#define glIsRenderbufferEXT glIsRenderbuffer -#define glBindRenderbufferEXT glBindRenderbuffer -#define glDeleteRenderbuffersEXT glDeleteRenderbuffers -#define glGenRenderbuffersEXT glGenRenderbuffers -#define glRenderbufferStorageEXT glRenderbufferStorage -#define glGetRenderbufferParameterivEXT glGetRenderbufferParameteriv -#define glIsFramebufferEXT glIsFramebuffer -#define glBindFramebufferEXT glBindFramebuffer -#define glDeleteFramebuffersEXT glDeleteFramebuffers -#define glGenFramebuffersEXT glGenFramebuffers -#define glCheckFramebufferStatusEXT glCheckFramebufferStatus -#define glFramebufferTexture1DEXT glFramebufferTexture1D -#define glFramebufferTexture2DEXT glFramebufferTexture2D -#define glFramebufferTexture3DEXT glFramebufferTexture3D -#define glFramebufferRenderbufferEXT glFramebufferRenderbuffer -#define glGetFramebufferAttachmentParameterivEXT glGetFramebufferAttachmentParameteriv -#define glGenerateMipmapEXT glGenerateMipmap +// FBOs +#if !defined(QT_OPENGL_ES_2) +#define glIsRenderbuffer QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glIsRenderbuffer +#define glBindRenderbuffer QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindRenderbuffer +#define glDeleteRenderbuffers QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteRenderbuffers +#define glGenRenderbuffers QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenRenderbuffers +#define glRenderbufferStorage QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glRenderbufferStorage +#define glGetRenderbufferParameteriv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetRenderbufferParameteriv +#define glIsFramebuffer QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glIsFramebuffer +#define glBindFramebuffer QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindFramebuffer +#define glDeleteFramebuffers QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteFramebuffers +#define glGenFramebuffers QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenFramebuffers +#define glCheckFramebufferStatus QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glCheckFramebufferStatus +#define glFramebufferTexture2D QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferTexture2D +#define glFramebufferRenderbuffer QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glFramebufferRenderbuffer +#define glGetFramebufferAttachmentParameteriv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetFramebufferAttachmentParameteriv +#define glGenerateMipmap QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenerateMipmap #endif // QT_OPENGL_ES_2 +#define glBlitFramebufferEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBlitFramebufferEXT +#define glRenderbufferStorageMultisampleEXT QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glRenderbufferStorageMultisampleEXT + -#define glBindBufferARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindBufferARB -#define glDeleteBuffersARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteBuffersARB -#define glGenBuffersARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenBuffersARB -#define glBufferDataARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBufferDataARB +// Buffer objects +#if !defined(QT_OPENGL_ES_2) +#define glBindBuffer QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindBuffer +#define glDeleteBuffers QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteBuffers +#define glGenBuffers QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGenBuffers +#define glBufferData QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBufferData +#endif #define glMapBufferARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glMapBufferARB #define glUnmapBufferARB QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUnmapBufferARB + +// GLSL +#if !defined(QT_OPENGL_ES_2) + #define glCreateShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glCreateShader #define glShaderSource QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glShaderSource +#define glShaderBinary QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glShaderBinary #define glCompileShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glCompileShader #define glDeleteShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteShader +#define glIsShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glIsShader #define glCreateProgram QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glCreateProgram #define glAttachShader QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glAttachShader @@ -490,10 +660,13 @@ struct QGLExtensionFuncs #define glLinkProgram QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glLinkProgram #define glUseProgram QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUseProgram #define glDeleteProgram QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDeleteProgram +#define glIsProgram QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glIsProgram #define glGetShaderInfoLog QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetShaderInfoLog #define glGetShaderiv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetShaderiv +#define glGetShaderSource QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetShaderSource #define glGetProgramiv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetProgramiv +#define glGetProgramInfoLog QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetProgramInfoLog #define glGetUniformLocation QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetUniformLocation #define glUniform4fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform4fv @@ -501,11 +674,48 @@ struct QGLExtensionFuncs #define glUniform2fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform2fv #define glUniform1fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform1fv #define glUniform1i QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform1i +#define glUniform1iv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniform1iv +#define glUniformMatrix2fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniformMatrix2fv +#define glUniformMatrix3fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniformMatrix3fv +#define glUniformMatrix4fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniformMatrix4fv +#define glUniformMatrix2x3fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniformMatrix2x3fv +#define glUniformMatrix2x4fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniformMatrix2x4fv +#define glUniformMatrix3x2fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniformMatrix3x2fv +#define glUniformMatrix3x4fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniformMatrix3x4fv +#define glUniformMatrix4x2fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniformMatrix4x2fv +#define glUniformMatrix4x3fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glUniformMatrix4x3fv + +#define glBindAttribLocation QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glBindAttribLocation +#define glGetAttribLocation QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetAttribLocation +#define glVertexAttrib1fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glVertexAttrib1fv +#define glVertexAttrib2fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glVertexAttrib2fv +#define glVertexAttrib3fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glVertexAttrib3fv +#define glVertexAttrib4fv QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glVertexAttrib4fv +#define glVertexAttribPointer QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glVertexAttribPointer +#define glDisableVertexAttribArray QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glDisableVertexAttribArray +#define glEnableVertexAttribArray QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glEnableVertexAttribArray + +#else // QT_OPENGL_ES_2 + +#define glGetProgramBinaryOES QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glGetProgramBinaryOES +#define glProgramBinaryOES QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glProgramBinaryOES + +#endif // QT_OPENGL_ES_2 + + +#if !defined(QT_OPENGL_ES_2) +#define glStencilOpSeparate QGLContextPrivate::qt_get_extension_funcs(ctx).qt_glStencilOpSeparate +#endif + +#if defined(QT_OPENGL_ES_2) +#define glClearDepth glClearDepthf +#endif extern bool qt_resolve_framebufferobject_extensions(QGLContext *ctx); bool qt_resolve_buffer_extensions(QGLContext *ctx); bool qt_resolve_version_1_3_functions(QGLContext *ctx); +bool qt_resolve_version_2_0_functions(QGLContext *ctx); bool qt_resolve_stencil_face_extension(QGLContext *ctx); bool qt_resolve_frag_program_extensions(QGLContext *ctx); diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp index c362b7e..3e7ca0a 100644 --- a/src/opengl/qglframebufferobject.cpp +++ b/src/opengl/qglframebufferobject.cpp @@ -43,7 +43,12 @@ #include <qdebug.h> #include <private/qgl_p.h> +#include <private/qpaintengineex_opengl2_p.h> + +#ifndef QT_OPENGL_ES_2 #include <private/qpaintengine_opengl_p.h> +#endif + #include <qglframebufferobject.h> #include <qlibrary.h> #include <qimage.h> @@ -67,29 +72,242 @@ extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool); } \ } +class QGLFramebufferObjectFormatPrivate +{ +public: + int samples; + QGLFramebufferObject::Attachment attachment; + GLenum target; + GLenum internal_format; +}; + +/*! + \class QGLFramebufferObjectFormat + \brief The QGLFramebufferObjectFormat class specifies the format of an OpenGL + framebuffer object. + + \since 4.6 + + \ingroup multimedia + + A framebuffer object has several characteristics: + \list + \i \link setSamples() Number of samples per pixels.\endlink + \i \link setAttachment() Depth and/or stencil attachments.\endlink + \i \link setTextureTarget() Texture target.\endlink + \i \link setInternalFormat() Internal format.\endlink + \endlist + + Note that the desired attachments or number of samples per pixels might not + be supported by the hardware driver. Call QGLFramebufferObject::format() + after creating a QGLFramebufferObject to find the exact format that was + used to create the frame buffer object. + + \sa QGLFramebufferObject +*/ + +/*! + \since 4.6 + + Creates a QGLFramebufferObjectFormat object with properties specifying + the format of an OpenGL framebuffer object. + + A multisample framebuffer object is specified by setting \a samples + to a value different from zero. If the desired amount of samples per pixel is + not supported by the hardware then the maximum number of samples per pixel + will be used. Note that multisample framebuffer objects can not be bound as + textures. Also, the \c{GL_EXT_framebuffer_multisample} extension is required + to create a framebuffer with more than one sample per pixel. + + For multisample framebuffer objects a color render buffer is created, + otherwise a texture with the texture target \a target is created. + The color render buffer or texture will have the internal format + \a internalFormat, and will be bound to the \c GL_COLOR_ATTACHMENT0 + attachment in the framebuffer object. + + The \a attachment parameter describes the depth/stencil buffer + configuration. + + \sa samples(), attachment(), target(), internalFormat() +*/ + +QGLFramebufferObjectFormat::QGLFramebufferObjectFormat(int samples, + QGLFramebufferObject::Attachment attachment, + GLenum target, + GLenum internalFormat) +{ + d = new QGLFramebufferObjectFormatPrivate; + d->samples = samples; + d->attachment = attachment; + d->target = target; + d->internal_format = internalFormat; +} + +/*! + \since 4.6 + + Constructs a copy of \a other. +*/ + +QGLFramebufferObjectFormat::QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other) +{ + d = new QGLFramebufferObjectFormatPrivate; + *d = *other.d; +} + +/*! + \since 4.6 + + Assigns \a other to this object. +*/ + +QGLFramebufferObjectFormat &QGLFramebufferObjectFormat::operator=(const QGLFramebufferObjectFormat &other) +{ + *d = *other.d; + return *this; +} + +/*! + \since 4.6 + + Destroys the QGLFramebufferObjectFormat. +*/ +QGLFramebufferObjectFormat::~QGLFramebufferObjectFormat() +{ + delete d; +} + +/*! + \since 4.6 + + Sets the number of samples per pixel for a multisample framebuffer object + to \a samples. + A sample count of 0 represents a regular non-multisample framebuffer object. + + \sa samples() +*/ +void QGLFramebufferObjectFormat::setSamples(int samples) +{ + d->samples = samples; +} + +/*! + \since 4.6 + + Returns the number of samples per pixel if a framebuffer object + is a multisample framebuffer object. Otherwise, returns 0. + + \sa setSamples() +*/ +int QGLFramebufferObjectFormat::samples() const +{ + return d->samples; +} + +/*! + \since 4.6 + + Sets the attachments a framebuffer object should have to \a attachment. + + \sa attachment() +*/ +void QGLFramebufferObjectFormat::setAttachment(QGLFramebufferObject::Attachment attachment) +{ + d->attachment = attachment; +} + +/*! + \since 4.6 + + Returns the status of the depth and stencil buffers attached to + a framebuffer object. + + \sa setAttachment() +*/ +QGLFramebufferObject::Attachment QGLFramebufferObjectFormat::attachment() const +{ + return d->attachment; +} + +/*! + \since 4.6 + + Sets the texture target of the texture attached to a framebuffer object to + \a target. Ignored for multisample framebuffer objects. + + \sa textureTarget(), samples() +*/ +void QGLFramebufferObjectFormat::setTextureTarget(GLenum target) +{ + d->target = target; +} + +/*! + \since 4.6 + + Returns the texture target of the texture attached to a framebuffer object. + Ignored for multisample framebuffer objects. + + \sa setTextureTarget(), samples() +*/ +GLenum QGLFramebufferObjectFormat::textureTarget() const +{ + return d->target; +} + +/*! + \since 4.6 + + Sets the internal format of a framebuffer object's texture or multisample + framebuffer object's color buffer to \a internalFormat. + + \sa internalFormat() +*/ +void QGLFramebufferObjectFormat::setInternalFormat(GLenum internalFormat) +{ + d->internal_format = internalFormat; +} + +/*! + \since 4.6 + + Returns the internal format of a framebuffer object's texture or + multisample framebuffer object's color buffer. + + \sa setInternalFormat() +*/ +GLenum QGLFramebufferObjectFormat::internalFormat() const +{ + return d->internal_format; +} + 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, - GLenum internal_format, GLenum texture_target); + GLenum internal_format, GLenum texture_target, GLint samples = 0); bool checkFramebufferStatus() const; GLuint texture; GLuint fbo; GLuint depth_stencil_buffer; + GLuint color_buffer; GLenum target; QSize size; + QGLFramebufferObjectFormat format; + int samples; uint valid : 1; uint bound : 1; QGLFramebufferObject::Attachment fbo_attachment; QGLContext *ctx; // for Windows extension ptrs + GLuint previous_fbo; }; bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const { - GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); switch(status) { case GL_NO_ERROR: case GL_FRAMEBUFFER_COMPLETE_EXT: @@ -121,6 +339,9 @@ bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: qDebug("QGLFramebufferObject: Framebuffer incomplete, missing read buffer."); break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: + qDebug("QGLFramebufferObject: Framebuffer incomplete, attachments must have same number of samples per pixel."); + break; default: qDebug() <<"QGLFramebufferObject: An undefined error has occurred: "<< status; break; @@ -129,7 +350,7 @@ bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const } void QGLFramebufferObjectPrivate::init(const QSize &sz, QGLFramebufferObject::Attachment attachment, - GLenum texture_target, GLenum internal_format) + GLenum texture_target, GLenum internal_format, GLint samples) { ctx = const_cast<QGLContext *>(QGLContext::currentContext()); bool ext_detected = (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject); @@ -141,81 +362,137 @@ void QGLFramebufferObjectPrivate::init(const QSize &sz, QGLFramebufferObject::At // texture dimensions while (glGetError() != GL_NO_ERROR) {} // reset error state - glGenFramebuffersEXT(1, &fbo); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo); QT_CHECK_GLERROR(); // init texture - glGenTextures(1, &texture); - glBindTexture(target, texture); - glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (samples == 0) { + glGenTextures(1, &texture); + glBindTexture(target, texture); + glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); #ifndef QT_OPENGL_ES - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #else - glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #endif - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - target, texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + target, texture, 0); - QT_CHECK_GLERROR(); - valid = checkFramebufferStatus(); + QT_CHECK_GLERROR(); + valid = checkFramebufferStatus(); + + color_buffer = 0; + samples = 0; + } else { + GLint maxSamples; + glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples); + + samples = qBound(1, int(samples), int(maxSamples)); + + glGenRenderbuffers(1, &color_buffer); + glBindRenderbuffer(GL_RENDERBUFFER_EXT, color_buffer); + if (glRenderbufferStorageMultisampleEXT) { + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, + internal_format, size.width(), size.height()); + } else { + samples = 0; + glRenderbufferStorage(GL_RENDERBUFFER_EXT, internal_format, + size.width(), size.height()); + } + + glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_RENDERBUFFER_EXT, color_buffer); + + QT_CHECK_GLERROR(); + valid = checkFramebufferStatus(); + + if (valid) + glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_SAMPLES_EXT, &samples); + } if (attachment == QGLFramebufferObject::CombinedDepthStencil && (QGLExtensions::glExtensions & QGLExtensions::PackedDepthStencil)) { // depth and stencil buffer needs another extension - glGenRenderbuffersEXT(1, &depth_stencil_buffer); - Q_ASSERT(!glIsRenderbufferEXT(depth_stencil_buffer)); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_buffer); - Q_ASSERT(glIsRenderbufferEXT(depth_stencil_buffer)); - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, size.width(), size.height()); + glGenRenderbuffers(1, &depth_stencil_buffer); + Q_ASSERT(!glIsRenderbuffer(depth_stencil_buffer)); + glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_stencil_buffer); + Q_ASSERT(glIsRenderbuffer(depth_stencil_buffer)); + if (samples != 0 && glRenderbufferStorageMultisampleEXT) + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, + GL_DEPTH24_STENCIL8_EXT, size.width(), size.height()); + else + glRenderbufferStorage(GL_RENDERBUFFER_EXT, + GL_DEPTH24_STENCIL8_EXT, size.width(), size.height()); + GLint i = 0; - glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i); + glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_buffer); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_buffer); fbo_attachment = QGLFramebufferObject::CombinedDepthStencil; + valid = checkFramebufferStatus(); if (!valid) - glDeleteRenderbuffersEXT(1, &depth_stencil_buffer); + glDeleteRenderbuffers(1, &depth_stencil_buffer); } else if (attachment == QGLFramebufferObject::Depth || attachment == QGLFramebufferObject::CombinedDepthStencil) { - glGenRenderbuffersEXT(1, &depth_stencil_buffer); - Q_ASSERT(!glIsRenderbufferEXT(depth_stencil_buffer)); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_buffer); - Q_ASSERT(glIsRenderbufferEXT(depth_stencil_buffer)); + glGenRenderbuffers(1, &depth_stencil_buffer); + Q_ASSERT(!glIsRenderbuffer(depth_stencil_buffer)); + glBindRenderbuffer(GL_RENDERBUFFER_EXT, depth_stencil_buffer); + Q_ASSERT(glIsRenderbuffer(depth_stencil_buffer)); + if (samples != 0 && glRenderbufferStorageMultisampleEXT) { +#ifdef QT_OPENGL_ES +#define GL_DEPTH_COMPONENT16 0x81A5 + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, + GL_DEPTH_COMPONENT16, size.width(), size.height()); +#else + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, + GL_DEPTH_COMPONENT, size.width(), size.height()); +#endif + } else { #ifdef QT_OPENGL_ES #define GL_DEPTH_COMPONENT16 0x81A5 - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, size.width(), size.height()); + glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, size.width(), size.height()); #else - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height()); + glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, size.width(), size.height()); #endif + } GLint i = 0; - glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i); + glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_buffer); fbo_attachment = QGLFramebufferObject::Depth; valid = checkFramebufferStatus(); if (!valid) - glDeleteRenderbuffersEXT(1, &depth_stencil_buffer); + glDeleteRenderbuffers(1, &depth_stencil_buffer); } else { fbo_attachment = QGLFramebufferObject::NoAttachment; } - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); if (!valid) { - glDeleteTextures(1, &texture); - glDeleteFramebuffersEXT(1, &fbo); + if (color_buffer) + glDeleteRenderbuffers(1, &color_buffer); + else + glDeleteTextures(1, &texture); + glDeleteFramebuffers(1, &fbo); } QT_CHECK_GLERROR(); + + format.setTextureTarget(target); + format.setSamples(int(samples)); + format.setAttachment(fbo_attachment); + format.setInternalFormat(internal_format); } /*! @@ -263,13 +540,18 @@ void QGLFramebufferObjectPrivate::init(const QSize &sz, QGLFramebufferObject::At framebuffer objects more portable. \endlist - Note that primitives drawn to a QGLFramebufferObject with QPainter - will only be antialiased if the QPainter::HighQualityAntialiasing - render hint is set. This is because there is currently no support - for the \c{GL_EXT_framebuffer_multisample} extension, which is - required to do multisample based antialiasing. Also note that the - QPainter::HighQualityAntialiasing render hint requires the - \c{GL_ARB_fragment_program} extension to work in OpenGL. + Note that you need to create a QGLFramebufferObject with more than one + sample per pixel for primitives to be antialiased when drawing using a + QPainter, unless if the QPainter::HighQualityAntialiasing render hint is + set. The QPainter::HighQualityAntialiasing render hint will enable + antialiasing as long as the \c{GL_ARB_fragment_program} extension is + present. To create a multisample framebuffer object you should use one of + the constructors that take a QGLFramebufferObject parameter, and set the + QGLFramebufferObject::samples() property to a non-zero value. + + If you want to use a framebuffer object with multisampling enabled + as a texture, you first need to copy from it to a regular framebuffer + object using QGLContext::blitFramebuffer(). \sa {Framebuffer Object Example} */ @@ -357,6 +639,32 @@ QGLFramebufferObject::QGLFramebufferObject(int width, int height, GLenum target) d->init(QSize(width, height), NoAttachment, target, DEFAULT_FORMAT); } +/*! \overload + + Constructs an OpenGL framebuffer object of the given \a size based on the + supplied \a format. +*/ + +QGLFramebufferObject::QGLFramebufferObject(const QSize &size, const QGLFramebufferObjectFormat &format) + : d_ptr(new QGLFramebufferObjectPrivate) +{ + Q_D(QGLFramebufferObject); + d->init(size, format.attachment(), format.textureTarget(), format.internalFormat(), format.samples()); +} + +/*! \overload + + Constructs an OpenGL framebuffer object of the given \a width and \a height + based on the supplied \a format. +*/ + +QGLFramebufferObject::QGLFramebufferObject(int width, int height, const QGLFramebufferObjectFormat &format) + : d_ptr(new QGLFramebufferObjectPrivate) +{ + Q_D(QGLFramebufferObject); + d->init(QSize(width, height), format.attachment(), format.textureTarget(), format.internalFormat(), format.samples()); +} + #ifdef Q_MAC_COMPAT_GL_FUNCTIONS /*! \internal */ QGLFramebufferObject::QGLFramebufferObject(int width, int height, QMacCompatGLenum target) @@ -444,9 +752,11 @@ QGLFramebufferObject::~QGLFramebufferObject() || qgl_share_reg()->checkSharing(d->ctx, QGLContext::currentContext()))) { glDeleteTextures(1, &d->texture); + if (d->color_buffer) + glDeleteRenderbuffers(1, &d->color_buffer); if (d->depth_stencil_buffer) - glDeleteRenderbuffersEXT(1, &d->depth_stencil_buffer); - glDeleteFramebuffersEXT(1, &d->fbo); + glDeleteRenderbuffers(1, &d->depth_stencil_buffer); + glDeleteFramebuffers(1, &d->fbo); } delete d_ptr; } @@ -473,6 +783,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() { @@ -480,8 +799,16 @@ bool QGLFramebufferObject::bind() return false; Q_D(QGLFramebufferObject); QGL_FUNC_CONTEXT; - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, d->fbo); + glBindFramebuffer(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(). + if (context->d_ptr->current_fbo != d->fbo) { + d->previous_fbo = context->d_ptr->current_fbo; + context->d_ptr->current_fbo = d->fbo; + } + } return d->valid; } @@ -491,6 +818,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() { @@ -498,10 +831,19 @@ bool QGLFramebufferObject::release() return false; Q_D(QGLFramebufferObject); QGL_FUNC_CONTEXT; - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - d->valid = d->checkFramebufferStatus(); d->bound = false; - return d->valid; + + const QGLContext *context = QGLContext::currentContext(); + if (context) { + // Restore the previous setting for stacked framebuffer objects. + if (d->previous_fbo != context->d_ptr->current_fbo) { + context->d_ptr->current_fbo = d->previous_fbo; + glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->previous_fbo); + } + d->previous_fbo = 0; + } + + return true; } /*! @@ -510,6 +852,9 @@ bool QGLFramebufferObject::release() Returns the texture id for the texture attached as the default rendering target in this framebuffer object. This texture id can be bound as a normal texture in your own GL code. + + If a multisample framebuffer object is used then the value returned + from this function will be invalid. */ GLuint QGLFramebufferObject::texture() const { @@ -530,6 +875,15 @@ QSize QGLFramebufferObject::size() const } /*! + Returns the format of this framebuffer object. +*/ +const QGLFramebufferObjectFormat &QGLFramebufferObject::format() const +{ + Q_D(const QGLFramebufferObject); + return d->format; +} + +/*! \fn QImage QGLFramebufferObject::toImage() const Returns the contents of this framebuffer object as a QImage. @@ -547,17 +901,27 @@ QImage QGLFramebufferObject::toImage() const return image; } -#if !defined(QT_OPENGL_ES_2) -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_paintengine) +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) +Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_buffer_2_engine) +#endif + +#ifndef QT_OPENGL_ES_2 +Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_engine) #endif /*! \reimp */ QPaintEngine *QGLFramebufferObject::paintEngine() const { -#if !defined(QT_OPENGL_ES_2) - return qt_buffer_paintengine(); +#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL) + return qt_buffer_engine(); +#elif defined(QT_OPENGL_ES_2) + return qt_buffer_2_engine(); #else - return 0; + Q_D(const QGLFramebufferObject); + if (d->ctx->d_func()->internal_context || qt_gl_preferGL2Engine()) + return qt_buffer_2_engine(); + else + return qt_buffer_engine(); #endif } @@ -569,7 +933,8 @@ QPaintEngine *QGLFramebufferObject::paintEngine() const */ bool QGLFramebufferObject::hasOpenGLFramebufferObjects() { - QGLWidget dmy; // needed to detect and init the QGLExtensions object + if (!QGLContext::currentContext()) + QGLWidget dmy; // needed to detect and init the QGLExtensions object return (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject); } @@ -624,15 +989,16 @@ void QGLFramebufferObject::drawTexture(const QPointF &point, QMacCompatGLuint te } #endif -extern int qt_defaultDpi(); +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); /*! \reimp */ int QGLFramebufferObject::metric(PaintDeviceMetric metric) const { Q_D(const QGLFramebufferObject); - float dpmx = qt_defaultDpi()*100./2.54; - float dpmy = qt_defaultDpi()*100./2.54; + float dpmx = qt_defaultDpiX()*100./2.54; + float dpmy = qt_defaultDpiY()*100./2.54; int w = d->size.width(); int h = d->size.height(); switch (metric) { @@ -655,16 +1021,16 @@ int QGLFramebufferObject::metric(PaintDeviceMetric metric) const return 32;//d->depth; case PdmDpiX: - return (int)(dpmx * 0.0254); + return qRound(dpmx * 0.0254); case PdmDpiY: - return (int)(dpmy * 0.0254); + return qRound(dpmy * 0.0254); case PdmPhysicalDpiX: - return (int)(dpmx * 0.0254); + return qRound(dpmx * 0.0254); case PdmPhysicalDpiY: - return (int)(dpmy * 0.0254); + return qRound(dpmy * 0.0254); default: qWarning("QGLFramebufferObject::metric(), Unhandled metric type: %d.\n", metric); @@ -720,4 +1086,85 @@ bool QGLFramebufferObject::isBound() const return d->bound; } +/*! + \fn bool QGLFramebufferObject::hasOpenGLFramebufferBlit() + + \since 4.6 + + Returns true if the OpenGL \c{GL_EXT_framebuffer_blit} extension + is present on this system; otherwise returns false. +*/ +bool QGLFramebufferObject::hasOpenGLFramebufferBlit() +{ + if (!QGLContext::currentContext()) + QGLWidget dmy; // needed to detect and init the QGLExtensions object + return (QGLExtensions::glExtensions & QGLExtensions::FramebufferBlit); +} + +/*! + \since 4.6 + + Blits from the \a sourceRect rectangle in the \a source framebuffer + object to the \a targetRect rectangle in the \a target framebuffer object. + + If \a source or \a target is 0, the default framebuffer will be used + instead of a framebuffer object as source or target respectively. + + The \a buffers parameter should be a mask consisting of any combination of + COLOR_BUFFER_BIT, DEPTH_BUFFER_BIT, and STENCIL_BUFFER_BIT. Any buffer type + that is not present both in the source and target buffers is ignored. + + The \a sourceRect and \a targetRect rectangles may have different sizes; + in this case \a buffers should not contain DEPTH_BUFFER_BIT or + STENCIL_BUFFER_BIT. The \a filter parameter should be set to GL_LINEAR or + GL_NEAREST, and specifies whether linear or nearest interpolation should + be used when scaling is performed. + + If \a source equals \a target a copy is performed within the same buffer. + Results are undefined if the source and target rectangles overlap and + have different sizes. The sizes must also be the same if any of the + framebuffer objects are multisample framebuffers. + + Note that the scissor test will restrict the blit area if enabled. + + This function will have no effect unless hasOpenGLFramebufferBlit() returns + true. +*/ +void QGLFramebufferObject::blitFramebuffer(QGLFramebufferObject *target, const QRect &targetRect, + QGLFramebufferObject *source, const QRect &sourceRect, + GLbitfield buffers, + GLenum filter) +{ + if (!(QGLExtensions::glExtensions & QGLExtensions::FramebufferBlit)) + return; + + const QGLContext *ctx = QGLContext::currentContext(); + if (!ctx) + return; + + const int height = ctx->device()->height(); + + const int sh = source ? source->height() : height; + const int th = target ? target->height() : height; + + const int sx0 = sourceRect.left(); + const int sx1 = sourceRect.left() + sourceRect.width(); + const int sy0 = sh - (sourceRect.top() + sourceRect.height()); + const int sy1 = sh - sourceRect.top(); + + const int tx0 = targetRect.left(); + const int tx1 = targetRect.left() + targetRect.width(); + const int ty0 = th - (targetRect.top() + targetRect.height()); + const int ty1 = th - targetRect.top(); + + glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, source ? source->handle() : 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, target ? target->handle() : 0); + + glBlitFramebufferEXT(sx0, sy0, sx1, sy1, + tx0, ty0, tx1, ty1, + buffers, filter); + + glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); +} + QT_END_NAMESPACE diff --git a/src/opengl/qglframebufferobject.h b/src/opengl/qglframebufferobject.h index a9e1b2f..0a2a9d2 100644 --- a/src/opengl/qglframebufferobject.h +++ b/src/opengl/qglframebufferobject.h @@ -52,6 +52,7 @@ QT_BEGIN_NAMESPACE QT_MODULE(OpenGL) class QGLFramebufferObjectPrivate; +class QGLFramebufferObjectFormat; class Q_OPENGL_EXPORT QGLFramebufferObject : public QPaintDevice { @@ -77,6 +78,9 @@ public: GLenum target = GL_TEXTURE_2D, GLenum internal_format = GL_RGBA); #endif + QGLFramebufferObject(const QSize &size, const QGLFramebufferObjectFormat &format); + QGLFramebufferObject(int width, int height, const QGLFramebufferObjectFormat &format); + #ifdef Q_MAC_COMPAT_GL_FUNCTIONS QGLFramebufferObject(const QSize &size, QMacCompatGLenum target = GL_TEXTURE_2D); QGLFramebufferObject(int width, int height, QMacCompatGLenum target = GL_TEXTURE_2D); @@ -89,10 +93,13 @@ public: virtual ~QGLFramebufferObject(); + const QGLFramebufferObjectFormat &format() const; + bool isValid() const; bool isBound() const; bool bind(); bool release(); + GLuint texture() const; QSize size() const; QImage toImage() const; @@ -110,6 +117,12 @@ public: void drawTexture(const QPointF &point, QMacCompatGLuint textureId, QMacCompatGLenum textureTarget = GL_TEXTURE_2D); #endif + static bool hasOpenGLFramebufferBlit(); + static void blitFramebuffer(QGLFramebufferObject *target, const QRect &targetRect, + QGLFramebufferObject *source, const QRect &sourceRect, + GLbitfield buffers = GL_COLOR_BUFFER_BIT, + GLenum filter = GL_NEAREST); + protected: int metric(PaintDeviceMetric metric) const; int devType() const { return QInternal::FramebufferObject; } @@ -120,6 +133,42 @@ private: friend class QGLDrawable; }; +class QGLFramebufferObjectFormatPrivate; +class Q_OPENGL_EXPORT QGLFramebufferObjectFormat +{ +public: +#if !defined(QT_OPENGL_ES) || defined(Q_QDOC) + QGLFramebufferObjectFormat(int samples = 0, + QGLFramebufferObject::Attachment attachment = QGLFramebufferObject::NoAttachment, + GLenum target = GL_TEXTURE_2D, + GLenum internalFormat = GL_RGBA8); +#else + QGLFramebufferObjectFormat(int samples = 0, + QGLFramebufferObject::Attachment attachment = QGLFramebufferObject::NoAttachment, + GLenum target = GL_TEXTURE_2D, + GLenum internalFormat = GL_RGBA); +#endif + + QGLFramebufferObjectFormat(const QGLFramebufferObjectFormat &other); + QGLFramebufferObjectFormat &operator=(const QGLFramebufferObjectFormat &other); + ~QGLFramebufferObjectFormat(); + + void setSamples(int samples); + int samples() const; + + void setAttachment(QGLFramebufferObject::Attachment attachment); + QGLFramebufferObject::Attachment attachment() const; + + void setTextureTarget(GLenum target); + GLenum textureTarget() const; + + void setInternalFormat(GLenum internalFormat); + GLenum internalFormat() const; + +private: + QGLFramebufferObjectFormatPrivate *d; +}; + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/opengl/qglpixelbuffer.cpp b/src/opengl/qglpixelbuffer.cpp index 5f74f26..efc58f2 100644 --- a/src/opengl/qglpixelbuffer.cpp +++ b/src/opengl/qglpixelbuffer.cpp @@ -81,7 +81,9 @@ #include <private/qglpixelbuffer_p.h> #include <qimage.h> -#if !defined(QT_OPENGL_ES_2) +#include <private/qpaintengineex_opengl2_p.h> + +#ifndef QT_OPENGL_ES_2 #include <private/qpaintengine_opengl_p.h> #endif @@ -363,29 +365,39 @@ bool QGLPixelBuffer::isValid() const return !d->invalid; } -#if !defined(QT_OPENGL_ES_2) -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_paintengine) +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) +Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_buffer_2_engine) +#endif + +#ifndef QT_OPENGL_ES_2 +Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_buffer_engine) #endif /*! \reimp */ QPaintEngine *QGLPixelBuffer::paintEngine() const { -#if !defined(QT_OPENGL_ES_2) - return qt_buffer_paintengine(); +#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL) + return qt_buffer_engine(); +#elif defined(QT_OPENGL_ES_2) + return qt_buffer_2_engine(); #else - return 0; + if (d_ptr->qctx->d_func()->internal_context || qt_gl_preferGL2Engine()) + return qt_buffer_2_engine(); + else + return qt_buffer_engine(); #endif } -extern int qt_defaultDpi(); +extern int qt_defaultDpiX(); +extern int qt_defaultDpiY(); /*! \reimp */ int QGLPixelBuffer::metric(PaintDeviceMetric metric) const { Q_D(const QGLPixelBuffer); - float dpmx = qt_defaultDpi()*100./2.54; - float dpmy = qt_defaultDpi()*100./2.54; + float dpmx = qt_defaultDpiX()*100./2.54; + float dpmy = qt_defaultDpiY()*100./2.54; int w = d->req_size.width(); int h = d->req_size.height(); switch (metric) { @@ -408,16 +420,16 @@ int QGLPixelBuffer::metric(PaintDeviceMetric metric) const return 32;//d->depth; case PdmDpiX: - return (int)(dpmx * 0.0254); + return qRound(dpmx * 0.0254); case PdmDpiY: - return (int)(dpmy * 0.0254); + return qRound(dpmy * 0.0254); case PdmPhysicalDpiX: - return (int)(dpmx * 0.0254); + return qRound(dpmx * 0.0254); case PdmPhysicalDpiY: - return (int)(dpmy * 0.0254); + return qRound(dpmy * 0.0254); default: qWarning("QGLPixelBuffer::metric(), Unhandled metric type: %d\n", metric); diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index ff23948..eb3298b 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -45,27 +45,12 @@ #include "qpaintengine_opengl_p.h" #include "qglpixelbuffer.h" -#include "qglextensions_p.h" +#include "qglshaderprogram.h" #include "qgl_p.h" #include "private/qapplication_p.h" -#ifndef GL_FRAGMENT_SHADER -#define GL_FRAGMENT_SHADER 0x8B30 -#endif - -#ifndef GL_COMPILE_STATUS -#define GL_COMPILE_STATUS 0x8B81 -#endif - -#ifndef GL_LINK_STATUS -#define GL_LINK_STATUS 0x8B82 -#endif - - - - QT_BEGIN_NAMESPACE @@ -79,117 +64,6 @@ void QGLPixmapFilterBase::drawImpl(QPainter *painter, const QPointF &pos, const processGL(painter, pos, src, source); } -QGLSLProgram::QGLSLProgram(const QString &src) - : ctx(QGLContext::currentContext()) -{ - if (!qt_resolve_glsl_extensions(const_cast<QGLContext *>(ctx))) { - qWarning("Failed to resolve GLSL functions"); - m_valid = false; - return; - } - - m_shader = glCreateShader(GL_FRAGMENT_SHADER); - - const QByteArray src_ba = src.toAscii(); - const char *src_cstr = src_ba.constData(); - - glShaderSource(m_shader, 1, &src_cstr, 0); - glCompileShader(m_shader); - glGetShaderiv(m_shader, GL_COMPILE_STATUS, &m_valid); - if (!m_valid) { - char data[4096]; - GLsizei len; - glGetShaderInfoLog(m_shader, 4096, &len, data); - qWarning("Failed to compile GLSL shader:\n%s\nCODE:\n%s\n", data, src_cstr); - return; - } - - m_program = glCreateProgram(); - glAttachShader(m_program, m_shader); - glLinkProgram(m_program); - glGetProgramiv(m_program, GL_LINK_STATUS, &m_valid); - if (!m_valid) { - char data[4096]; - GLsizei len; - glGetShaderInfoLog(m_shader, 4096, &len, data); - qWarning("Failed to link GLSL program:\n%s\nCODE:\n%s\n", data, src_cstr); - return; - } -} - -QGLSLProgram::~QGLSLProgram() -{ - glDeleteProgram(m_program); - glDeleteShader(m_shader); -} - -bool QGLSLProgram::success() const -{ - return m_valid; -} - -void QGLSLProgram::enable() -{ - glUseProgram(m_program); -} - -void QGLSLProgram::disable() -{ - glUseProgram(0); -} - -typedef GLuint (APIENTRY *_glGetUniformLocation) (GLuint, const char*); -typedef void (APIENTRY *_glUniform4fv) (GLint, GLsizei, GLfloat *); -typedef void (APIENTRY *_glUniform3fv) (GLint, GLsizei, GLfloat *); -typedef void (APIENTRY *_glUniform2fv) (GLint, GLsizei, GLfloat *); -typedef void (APIENTRY *_glUniform1fv) (GLint, GLsizei, GLfloat *); -typedef void (APIENTRY *_glUniform1i) (GLint, GLint); - -int QGLSLProgram::getUniformLocation(const QString &name) -{ - return glGetUniformLocation(m_program, name.toAscii().constData()); -} - -void QGLSLProgram::setUniform(int uniform, int value) -{ - enable(); - glUniform1i(uniform, value); -} - -void QGLSLProgram::setUniform(int uniform, qreal value) -{ - enable(); - GLfloat v[] = { value }; - glUniform1fv(uniform, 1, v); -} - -void QGLSLProgram::setUniform(int uniform, qreal v1, qreal v2) -{ - enable(); - GLfloat v[] = { v1, v2 }; - glUniform2fv(uniform, 1, v); -} - -void QGLSLProgram::setUniform(int uniform, qreal v1, qreal v2, qreal v3) -{ - enable(); - GLfloat v[] = { v1, v2, v3 }; - glUniform3fv(uniform, 1, v); -} - -void QGLSLProgram::setUniform(int uniform, qreal v1, qreal v2, qreal v3, qreal v4) -{ - enable(); - GLfloat v[] = { v1, v2, v3, v4 }; - glUniform4fv(uniform, 1, v); -} - -void QGLSLProgram::setUniform(int uniform, int count, float *v) -{ - enable(); - glUniform1fv(uniform, count, v); -} - class QGLPixmapColorizeFilter: public QGLPixmapFilter<QPixmapColorizeFilter> { public: @@ -199,8 +73,8 @@ protected: bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &pixmap, const QRectF &srcRect) const; private: - mutable QGLSLProgram m_program; - uint m_colorUniform; + mutable QGLShaderProgram m_program; + int m_colorUniform; }; class QGLPixmapConvolutionFilter: public QGLPixmapFilter<QPixmapConvolutionFilter> @@ -213,11 +87,11 @@ protected: bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; private: - QString generateConvolutionShader() const; + QByteArray generateConvolutionShader() const; - mutable QGLSLProgram *m_program; - mutable uint m_scaleUniform; - mutable uint m_matrixUniform; + mutable QGLShaderProgram *m_program; + mutable int m_scaleUniform; + mutable int m_matrixUniform; mutable int m_kernelWidth; mutable int m_kernelHeight; @@ -242,10 +116,8 @@ QPixmapFilter *QGLContextPrivate::createPixmapFilter(int type) const return 0; } -#if !defined(QT_OPENGL_ES_2) extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); extern void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array); -#endif static void qgl_drawTexture(const QRectF &rect, int tx_width, int tx_height, const QRectF & src) { @@ -294,10 +166,12 @@ static const char *qt_gl_colorize_filter = "}"; QGLPixmapColorizeFilter::QGLPixmapColorizeFilter() - : m_program(QLatin1String(qt_gl_colorize_filter)) { - m_program.setUniform(m_program.getUniformLocation(QLatin1String("texture")), 0); // GL_TEXTURE_0 - m_colorUniform = m_program.getUniformLocation(QLatin1String("color")); + m_program.addShader(QGLShader::FragmentShader, qt_gl_colorize_filter); + m_program.link(); + m_program.enable(); + m_program.setUniformValue(m_program.uniformLocation("texture"), GLint(0)); // GL_TEXTURE_0 + m_colorUniform = m_program.uniformLocation("color"); } bool QGLPixmapColorizeFilter::processGL(QPainter *, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const @@ -305,10 +179,10 @@ bool QGLPixmapColorizeFilter::processGL(QPainter *, const QPointF &pos, const QP bindTexture(src); QColor col = color(); - m_program.setUniform(m_colorUniform, col.redF(), col.greenF(), col.blueF()); + m_program.enable(); + m_program.setUniformValue(m_colorUniform, col.redF(), col.greenF(), col.blueF()); QRectF target = (srcRect.isNull() ? QRectF(src.rect()) : srcRect).translated(pos); - m_program.enable(); qgl_drawTexture(target, src.width(), src.height(), srcRect); m_program.disable(); @@ -316,7 +190,7 @@ bool QGLPixmapColorizeFilter::processGL(QPainter *, const QPointF &pos, const QP } // generates convolution filter code for arbitrary sized kernel -QString QGLPixmapConvolutionFilter::generateConvolutionShader() const { +QByteArray QGLPixmapConvolutionFilter::generateConvolutionShader() const { QByteArray code; code.append("uniform sampler2D texture;\n"); code.append("uniform vec2 inv_texture_size;\n"); @@ -352,7 +226,7 @@ QString QGLPixmapConvolutionFilter::generateConvolutionShader() const { code.append(" }\n"); code.append(" gl_FragColor = sum;\n"); code.append("}"); - return QLatin1String(code); + return code; } QGLPixmapConvolutionFilter::QGLPixmapConvolutionFilter() @@ -384,10 +258,12 @@ bool QGLPixmapConvolutionFilter::processGL(QPainter *, const QPointF &pos, const m_kernelWidth = columns(); m_kernelHeight = rows(); - QString code = generateConvolutionShader(); - m_program = new QGLSLProgram(code); - m_scaleUniform = m_program->getUniformLocation(QLatin1String("inv_texture_size")); - m_matrixUniform = m_program->getUniformLocation(QLatin1String("matrix")); + QByteArray code = generateConvolutionShader(); + m_program = new QGLShaderProgram(); + m_program->addShader(QGLShader::FragmentShader, code); + m_program->link(); + m_scaleUniform = m_program->uniformLocation("inv_texture_size"); + m_matrixUniform = m_program->uniformLocation("matrix"); } const qreal *kernel = convolutionKernel(); @@ -397,10 +273,10 @@ bool QGLPixmapConvolutionFilter::processGL(QPainter *, const QPointF &pos, const const qreal iw = 1.0 / src.width(); const qreal ih = 1.0 / src.height(); - m_program->setUniform(m_scaleUniform, iw, ih); - m_program->setUniform(m_matrixUniform, m_kernelWidth * m_kernelHeight, conv); - m_program->enable(); + m_program->setUniformValue(m_scaleUniform, iw, ih); + m_program->setUniformValueArray(m_matrixUniform, conv, m_kernelWidth * m_kernelHeight, 1); + qgl_drawTexture(target, src.width(), src.height(), boundingRectFor(srcRect)); m_program->disable(); return true; diff --git a/src/opengl/qglpixmapfilter_p.h b/src/opengl/qglpixmapfilter_p.h index dc2eea6..d6742fc 100644 --- a/src/opengl/qglpixmapfilter_p.h +++ b/src/opengl/qglpixmapfilter_p.h @@ -87,35 +87,6 @@ public: } }; -class Q_OPENGL_EXPORT QGLSLProgram -{ -public: - QGLSLProgram(const QString &src); - ~QGLSLProgram(); - - bool success() const; - - void enable(); - void disable(); - - int getUniformLocation(const QString &name); - - void setUniform(int uniform, int value); - void setUniform(int uniform, qreal value); - void setUniform(int uniform, qreal v1, qreal v2); - void setUniform(int uniform, qreal v1, qreal v2, qreal v3); - void setUniform(int uniform, qreal v1, qreal v2, qreal v3, qreal v4); - void setUniform(int uniform, int count, float *v); - -private: - GLuint m_shader; - GLuint m_program; - - GLint m_valid; - - const QGLContext *ctx; -}; - QT_END_NAMESPACE QT_END_HEADER diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp new file mode 100644 index 0000000..d74b930 --- /dev/null +++ b/src/opengl/qglshaderprogram.cpp @@ -0,0 +1,2992 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 "qglshaderprogram.h" +#include "qglextensions_p.h" +#include "qgl_p.h" +#include <QtCore/qdebug.h> +#include <QtCore/qfile.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +#if !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1) + +/*! + \class QGLShaderProgram + \brief The QGLShaderProgram class allows OpenGL shader programs to be linked and used. + \since 4.6 + + \section1 Introduction + + This class supports shader programs written in the OpenGL Shading + Language (GLSL) and in the OpenGL/ES Shading Language (GLSL/ES). + + QGLShader and QGLShaderProgram shelter the programmer from the details of + compiling and linking vertex and fragment shaders. + + The following example creates a vertex shader program using the + supplied source \c{code}. Once compiled and linked, the shader + program is activated in the current QGLContext by calling + QGLShaderProgram::enable(): + + \code + QGLShader shader(QGLShader::VertexShader); + shader.setSourceCode(code); + + QGLShaderProgram program(context); + program.addShader(shader); + program.link(); + + program.enable(); + \endcode + + \section1 Writing portable shaders + + Shader programs can be difficult to reuse across OpenGL implementations + because of varying levels of support for standard vertex attributes and + uniform variables. In particular, GLSL/ES lacks all of the + standard variables that are present on desktop OpenGL systems: + \c{gl_Vertex}, \c{gl_Normal}, \c{gl_Color}, and so on. Desktop OpenGL + lacks the variable qualifiers \c{highp}, \c{mediump}, and \c{lowp}. + + The QGLShaderProgram class makes the process of writing portable shaders + easier by prefixing all shader programs with the following lines on + desktop OpenGL: + + \code + #define highp + #define mediump + #define lowp + \endcode + + This makes it possible to run most GLSL/ES shader programs + on desktop systems. The programmer should restrict themselves + to just features that are present in GLSL/ES, and avoid + standard variable names that only work on the desktop. + + \section1 Simple shader example + + \code + program.addShader(QGLShader::VertexShader, + "attribute highp vec4 vertex;\n" + "attribute mediump mat4 matrix;\n" + "void main(void)\n" + "{\n" + " gl_Position = matrix * vertex;\n" + "}"); + program.addShader(QGLShader::FragmentShader, + "uniform mediump vec4 color;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = color;\n" + "}"); + program.link(); + program.enable(); + + int vertexLocation = program.attributeLocation("vertex"); + int matrixLocation = program.attributeLocation("matrix"); + int colorLocation = program.uniformLocation("color"); + \endcode + + With the above shader program active, we can draw a green triangle + as follows: + + \code + static GLfloat const triangleVertices[] = { + 60.0f, 10.0f, 0.0f, + 110.0f, 110.0f, 0.0f, + 10.0f, 110.0f, 0.0f + }; + + QColor color(0, 255, 0, 255); + + QMatrix4x4 pmvMatrix; + pmvMatrix.ortho(rect()); + + program.setAttributeArray(vertexLocation, triangleVertices, 3); + program.setUniformValue(matrixLocation, pmvMatrix); + program.setUniformValue(colorLocation, color); + + glDrawArrays(GL_TRIANGLES, 0, 3); + \endcode + + \section1 Partial shaders + + Desktop GLSL can attach an arbitrary number of vertex and fragment + shaders to a shader program. Embedded GLSL/ES on the other hand + supports only a single shader of each type on a shader program. + + Multiple shaders of the same type can be useful when large libraries + of shaders are needed. Common functions can be factored out into + library shaders that can be reused in multiple shader programs. + + To support this use of shaders, the application programmer can + create shaders with the QGLShader::PartialVertexShader and + QGLShader::PartialFragmentShader types. These types direct + QGLShader and QGLShaderProgram to delay shader compilation until + link time. + + When link() is called, the sources for the partial shaders are + concatenated, and a single vertex or fragment shader is compiled + and linked into the shader program. + + It is more efficient to use the QGLShader::VertexShader and + QGLShader::FragmentShader when there is only one shader of that + type in the program. + + \sa QGLShader +*/ + +/*! + \class QGLShader + \brief The QGLShader class allows OpenGL shaders to be compiled. + \since 4.6 + + This class supports shaders written in the OpenGL Shading Language (GLSL) + and in the OpenGL/ES Shading Language (GLSL/ES). + + QGLShader and QGLShaderProgram shelter the programmer from the details of + compiling and linking vertex and fragment shaders. + + \sa QGLShaderProgram +*/ + +/*! + \enum QGLShader::ShaderType + This enum specifies the type of QGLShader that is being created. + + \value VertexShader Vertex shader written in the OpenGL Shading Language (GLSL). + \value FragmentShader Fragment shader written in the OpenGL Shading Language (GLSL). + \value PartialVertexShader Partial vertex shader that will be concatenated with all other partial vertex shaders at link time. + \value PartialFragmentShader Partial fragment shader that will be concatenated with all other partial fragment shaders at link time. +*/ + +#ifndef GL_FRAGMENT_SHADER +#define GL_FRAGMENT_SHADER 0x8B30 +#endif +#ifndef GL_VERTEX_SHADER +#define GL_VERTEX_SHADER 0x8B31 +#endif +#ifndef GL_COMPILE_STATUS +#define GL_COMPILE_STATUS 0x8B81 +#endif +#ifndef GL_LINK_STATUS +#define GL_LINK_STATUS 0x8B82 +#endif +#ifndef GL_INFO_LOG_LENGTH +#define GL_INFO_LOG_LENGTH 0x8B84 +#endif +#ifndef GL_ACTIVE_UNIFORMS +#define GL_ACTIVE_UNIFORMS 0x8B86 +#endif +#ifndef GL_ACTIVE_UNIFORM_MAX_LENGTH +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#endif +#ifndef GL_ACTIVE_ATTRIBUTES +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#endif +#ifndef GL_ACTIVE_ATTRIBUTE_MAX_LENGTH +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#endif +#ifndef GL_CURRENT_VERTEX_ATTRIB +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#endif +#ifndef GL_SHADER_SOURCE_LENGTH +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#endif +#ifndef GL_SHADER_BINARY_FORMATS +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#endif +#ifndef GL_NUM_SHADER_BINARY_FORMATS +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#endif + +class QGLShaderPrivate +{ +public: + QGLShaderPrivate(QGLShader::ShaderType type, const QGLContext *ctx) + { + context = ctx; + shader = 0; + shaderType = type; + compiled = false; + isPartial = (type == QGLShader::PartialVertexShader || + type == QGLShader::PartialFragmentShader); + hasPartialSource = false; + } + + const QGLContext *context; + GLuint shader; + QGLShader::ShaderType shaderType; + bool compiled; + bool isPartial; + bool hasPartialSource; + QString log; + QByteArray partialSource; + + bool create(); + bool compile(); +}; + +#define ctx context + +bool QGLShaderPrivate::create() +{ + if (isPartial) + return true; + if (!context) + context = QGLContext::currentContext(); + if (!context) + return false; + if (qt_resolve_glsl_extensions(const_cast<QGLContext *>(context))) { + if (shaderType == QGLShader::VertexShader) + shader = glCreateShader(GL_VERTEX_SHADER); + else + shader = glCreateShader(GL_FRAGMENT_SHADER); + if (!shader) { + qWarning() << "QGLShader: could not create shader"; + return false; + } + return true; + } else { + return false; + } +} + +bool QGLShaderPrivate::compile() +{ + // Partial shaders are compiled during QGLShaderProgram::link(). + if (isPartial && hasPartialSource) { + compiled = true; + return true; + } + if (!shader) + return false; + glCompileShader(shader); + GLint value = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &value); + compiled = (value != 0); + value = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &value); + if (!compiled && value > 1) { + char *logbuf = new char [value]; + GLint len; + glGetShaderInfoLog(shader, value, &len, logbuf); + log = QString::fromLatin1(logbuf); + qWarning() << "QGLShader::compile:" << log; + delete [] logbuf; + } + return compiled; +} + +#undef ctx +#define ctx d->context + +/*! + Constructs a new QGLShader object of the specified \a type + and attaches it to \a parent. If shader programs are not supported, + then isValid() will return false. + + This constructor is normally followed by a call to setSourceCode() + or setSourceCodeFile(). + + The shader will be associated with the current QGLContext. + + \sa setSourceCode(), setSourceCodeFile(), isValid() +*/ +QGLShader::QGLShader(QGLShader::ShaderType type, QObject *parent) + : QObject(parent) +{ + d = new QGLShaderPrivate(type, QGLContext::currentContext()); + d->create(); +} + +/*! + Constructs a new QGLShader object from the source code in \a fileName + and attaches it to \a parent. If the filename ends in \c{.fsh}, + it is assumed to be a fragment shader, otherwise it is assumed to + be a vertex shader (normally the extension is \c{.vsh} for vertex shaders). + If the shader could not be loaded, then isValid() will return false. + + The shader will be associated with the current QGLContext. + + \sa isValid() +*/ +QGLShader::QGLShader(const QString& fileName, QObject *parent) + : QObject(parent) +{ + if (fileName.endsWith(QLatin1String(".fsh"), Qt::CaseInsensitive)) + d = new QGLShaderPrivate(QGLShader::FragmentShader, QGLContext::currentContext()); + else + d = new QGLShaderPrivate(QGLShader::VertexShader, QGLContext::currentContext()); + if (d->create() && !setSourceCodeFile(fileName)) { + if (d->shader) + glDeleteShader(d->shader); + d->shader = 0; + } +} + +/*! + Constructs a new QGLShader object of the specified \a type from the + source code in \a fileName and attaches it to \a parent. + If the shader could not be loaded, then isValid() will return false. + + The shader will be associated with the current QGLContext. + + \sa isValid() +*/ +QGLShader::QGLShader + (const QString& fileName, QGLShader::ShaderType type, QObject *parent) + : QObject(parent) +{ + d = new QGLShaderPrivate(type, QGLContext::currentContext()); + if (d->create() && !setSourceCodeFile(fileName)) { + if (d->shader) + glDeleteShader(d->shader); + d->shader = 0; + } +} + +/*! + Constructs a new QGLShader object of the specified \a type + and attaches it to \a parent. If shader programs are not supported, + then isValid() will return false. + + This constructor is normally followed by a call to setSourceCode() + or setSourceCodeFile(). + + The shader will be associated with \a context. + + \sa setSourceCode(), setSourceCodeFile(), isValid() +*/ +QGLShader::QGLShader(QGLShader::ShaderType type, const QGLContext *context, QObject *parent) + : QObject(parent) +{ + d = new QGLShaderPrivate(type, context); + d->create(); +} + +/*! + Constructs a new QGLShader object from the source code in \a fileName + and attaches it to \a parent. If the filename ends in \c{.fsh}, + it is assumed to be a fragment shader, otherwise it is assumed to + be a vertex shader (normally the extension is \c{.vsh} for vertex shaders). + If the shader could not be loaded, then isValid() will return false. + + The shader will be associated with \a context. + + \sa isValid() +*/ +QGLShader::QGLShader(const QString& fileName, const QGLContext *context, QObject *parent) + : QObject(parent) +{ + if (fileName.endsWith(QLatin1String(".fsh"), Qt::CaseInsensitive)) + d = new QGLShaderPrivate(QGLShader::FragmentShader, context); + else + d = new QGLShaderPrivate(QGLShader::VertexShader, context); + if (d->create() && !setSourceCodeFile(fileName)) { + if (d->shader) + glDeleteShader(d->shader); + d->shader = 0; + } +} + +/*! + Constructs a new QGLShader object of the specified \a type from the + source code in \a fileName and attaches it to \a parent. + If the shader could not be loaded, then isValid() will return false. + + The shader will be associated with \a context. + + \sa isValid() +*/ +QGLShader::QGLShader + (const QString& fileName, QGLShader::ShaderType type, const QGLContext *context, QObject *parent) + : QObject(parent) +{ + d = new QGLShaderPrivate(type, context); + if (d->create() && !setSourceCodeFile(fileName)) { + if (d->shader) + glDeleteShader(d->shader); + d->shader = 0; + } +} + +/*! + Deletes this shader. If the shader has been attached to a + QGLShaderProgram object, then the actual shader will stay around + until the QGLShaderProgram is destroyed. +*/ +QGLShader::~QGLShader() +{ + if (d->shader) + glDeleteShader(d->shader); + delete d; +} + +/*! + Returns true if this shader is valid. Shaders become invalid + when they are destroyed and no longer attached to a QGLShaderProgram. +*/ +bool QGLShader::isValid() const +{ + if (d->isPartial && d->hasPartialSource) + return true; + if (!d->shader) + return false; +#if defined(QT_OPENGL_ES_2) + return glIsShader(d->shader); +#else + // glIsShader() may not exist on some systems. + return (!glIsShader || glIsShader(d->shader)); +#endif +} + +/*! + Returns the type of this shader. +*/ +QGLShader::ShaderType QGLShader::shaderType() const +{ + return d->shaderType; +} + +// The precision qualifiers are useful on OpenGL/ES systems, +// but usually not present on desktop systems. Define the +// keywords to empty strings on desktop systems. +#ifndef QT_OPENGL_ES +#define QGL_DEFINE_QUALIFIERS 1 +static const char qualifierDefines[] = + "#define lowp\n" + "#define mediump\n" + "#define highp\n"; +#endif + +/*! + Sets the \a source code for this shader and compiles it. + Returns true if the source was successfully compiled, false otherwise. + + If shaderType() is PartialVertexShader or PartialFragmentShader, + then this function will always return true, even if the source code + is invalid. Partial shaders are compiled when QGLShaderProgram::link() + is called. +*/ +bool QGLShader::setSourceCode(const char *source) +{ + if (d->isPartial) { + d->partialSource = QByteArray(source); + d->hasPartialSource = true; + return d->compile(); + } else if (d->shader) { + QVarLengthArray<const char *> src; +#ifdef QGL_DEFINE_QUALIFIERS + src.append(qualifierDefines); +#endif + src.append(source); + glShaderSource(d->shader, src.size(), src.data(), 0); + return d->compile(); + } else { + return false; + } +} + +/*! + \overload + + Sets the \a source code for this shader and compiles it. + Returns true if the source was successfully compiled, false otherwise. + + If shaderType() is PartialVertexShader or PartialFragmentShader, + then this function will always return true, even if the source code + is invalid. Partial shaders are compiled when QGLShaderProgram::link() + is called. +*/ +bool QGLShader::setSourceCode(const QByteArray& source) +{ + return setSourceCode(source.constData()); +} + +/*! + \overload + + Sets the \a source code for this shader and compiles it. + Returns true if the source was successfully compiled, false otherwise. + + If shaderType() is PartialVertexShader or PartialFragmentShader, + then this function will always return true, even if the source code + is invalid. Partial shaders are compiled when QGLShaderProgram::link() + is called. +*/ +bool QGLShader::setSourceCode(const QString& source) +{ + return setSourceCode(source.toLatin1().constData()); +} + +/*! + Sets the source code for this shader to the contents of \a fileName + and compiles it. Returns true if the file could be opened and the + source compiled, false otherwise. + + If shaderType() is PartialVertexShader or PartialFragmentShader, + then this function will always return true, even if the source code + is invalid. Partial shaders are compiled when QGLShaderProgram::link() + is called. +*/ +bool QGLShader::setSourceCodeFile(const QString& fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) { + qWarning() << "QGLShader: Unable to open file" << fileName; + return false; + } + + QByteArray contents = file.readAll(); + return setSourceCode(contents.constData()); +} + +/*! + Sets the binary code for this shader to the \a length bytes from + the array \a binary. The \a format specifies how the binary data + should be interpreted by the OpenGL engine. Returns true if the + binary was set on the shader; false otherwise. + + This function cannot be used with PartialVertexShader or + PartialFragmentShader. + + \sa shaderBinaryFormats() +*/ +bool QGLShader::setBinaryCode(GLenum format, const void *binary, int length) +{ +#if !defined(QT_OPENGL_ES_2) + if (!glShaderBinary) + return false; +#endif + if (d->isPartial || !d->shader) + return false; + glGetError(); // Clear error state. + glShaderBinary(1, &(d->shader), format, binary, length); + return (glGetError() == GL_NO_ERROR); +} + +/*! + Sets the binary code for this shader to the \a length bytes from + the array \a binary. The \a format specifies how the binary data + should be interpreted by the OpenGL engine. Returns true if the + binary was set on the shader; false otherwise. + + The \a otherShader will also have binary code set on it. This is + for the case where \a binary contains both vertex and fragment + shader code. + + This function cannot be used with PartialVertexShader or + PartialFragmentShader. + + \sa shaderBinaryFormats() +*/ +bool QGLShader::setBinaryCode + (QGLShader& otherShader, GLenum format, const void *binary, int length) +{ +#if !defined(QT_OPENGL_ES_2) + if (!glShaderBinary) + return false; +#endif + if (d->isPartial || !d->shader) + return false; + if (otherShader.d->isPartial || !otherShader.d->shader) + return false; + glGetError(); // Clear error state. + GLuint shaders[2]; + shaders[0] = d->shader; + shaders[1] = otherShader.d->shader; + glShaderBinary(2, shaders, format, binary, length); + return (glGetError() == GL_NO_ERROR); +} + +/*! + Returns a list of all binary formats that are supported by + setBinaryCode() on this system. + + \sa setBinaryCode() +*/ +QList<GLenum> QGLShader::shaderBinaryFormats() +{ + GLint num; + QList<GLenum> list; + glGetError(); // Clear error state. + glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &num); + if (glGetError() != GL_NO_ERROR || num <= 0) + return list; + QVarLengthArray<GLint> formats(num); + glGetIntegerv(GL_SHADER_BINARY_FORMATS, formats.data()); + for (GLint i = 0; i < num; ++i) + list += (GLenum)(formats[i]); + return list; +} + +/*! + Returns the source code for this shader. + + \sa setSourceCode() +*/ +QByteArray QGLShader::sourceCode() const +{ + if (d->isPartial) + return d->partialSource; + if (!d->shader) + return QByteArray(); + GLint size = 0; + glGetShaderiv(d->shader, GL_SHADER_SOURCE_LENGTH, &size); + if (size <= 0) + return QByteArray(); + GLint len = 0; + char *source = new char [size]; + glGetShaderSource(d->shader, size, &len, source); + QByteArray src(source); + delete [] source; + return src; +} + +/*! + Returns true if this shader has been compiled; false otherwise. + + \sa setSourceCode() +*/ +bool QGLShader::isCompiled() const +{ + return d->compiled; +} + +/*! + Returns the errors and warnings that occurred during the last compile. + + \sa setSourceCode() +*/ +QString QGLShader::log() const +{ + return d->log; +} + +/*! + Returns the OpenGL identifier associated with this shader. + + If shaderType() is PartialVertexShader or PartialFragmentShader, + this function will always return zero. Partial shaders are + created and compiled when QGLShaderProgram::link() is called. + + \sa QGLShaderProgram::programId() +*/ +GLuint QGLShader::shaderId() const +{ + return d->shader; +} + +#undef ctx +#define ctx context + +class QGLShaderProgramPrivate +{ +public: + QGLShaderProgramPrivate(const QGLContext *ctx) + { + context = ctx; + program = 0; + linked = false; + inited = false; + hasPartialShaders = false; + vertexShader = 0; + fragmentShader = 0; + } + ~QGLShaderProgramPrivate() + { + if (program) + glDeleteProgram(program); + } + + const QGLContext *context; + GLuint program; + bool linked; + bool inited; + bool hasPartialShaders; + QString log; + QList<QGLShader *> shaders; + QList<QGLShader *> anonShaders; + QGLShader *vertexShader; + QGLShader *fragmentShader; +}; + +#undef ctx +#define ctx d->context + +/*! + Constructs a new shader program and attaches it to \a parent. + The program will be invalid until addShader() is called. + + The shader program will be associated with the current QGLContext. + + \sa isValid(), addShader() +*/ +QGLShaderProgram::QGLShaderProgram(QObject *parent) + : QObject(parent) +{ + d = new QGLShaderProgramPrivate(QGLContext::currentContext()); +} + +/*! + Constructs a new shader program and attaches it to \a parent. + The program will be invalid until addShader() is called. + + The shader program will be associated with \a context. + + \sa isValid(), addShader() +*/ +QGLShaderProgram::QGLShaderProgram(const QGLContext *context, QObject *parent) + : QObject(parent) +{ + d = new QGLShaderProgramPrivate(context); +} + +/*! + Deletes this shader program. +*/ +QGLShaderProgram::~QGLShaderProgram() +{ + delete d; +} + +bool QGLShaderProgram::init() +{ + if (d->program || d->inited) + return true; + d->inited = true; + if (!d->context) + d->context = QGLContext::currentContext(); + if (!d->context) + return false; + if (qt_resolve_glsl_extensions(const_cast<QGLContext *>(d->context))) { + d->program = glCreateProgram(); + if (!(d->program)) { + qWarning() << "QGLShaderProgram: could not create shader program"; + return false; + } + return true; + } else { + qWarning() << "QGLShaderProgram: shader programs are not supported"; + return false; + } +} + +/*! + Returns true if this shader program object is valid, false otherwise. +*/ +bool QGLShaderProgram::isValid() const +{ + if (!d->program) + return false; +#if defined(QT_OPENGL_ES_2) + return glIsProgram(d->program); +#else + // glIsProgram() may not exist on some systems. + return (!glIsProgram || glIsProgram(d->program)); +#endif +} + +/*! + Adds a compiled \a shader to this shader program. Returns true + if the shader could be added, or false otherwise. + + Ownership of the \a shader object remains with the caller. + It will not be deleted when this QGLShaderProgram instance + is deleted. This allows the caller to add the same shader + to multiple shader programs. + + \sa removeShader(), link(), removeAllShaders() +*/ +bool QGLShaderProgram::addShader(QGLShader *shader) +{ + if (!init()) + return false; + if (d->shaders.contains(shader)) + return true; // Already added to this shader program. + if (d->program && shader) { + if (!shader->d->compiled) + return false; + if (!shader->d->isPartial) { + if (!shader->d->shader) + return false; + glAttachShader(d->program, shader->d->shader); + } else { + d->hasPartialShaders = true; + } + d->linked = false; // Program needs to be relinked. + d->shaders.append(shader); + return true; + } else { + return false; + } +} + +/*! + Compiles \a source as a shader of the specified \a type and + adds it to this shader program. Returns true if compilation + was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QGLShader first. + + \sa removeShader(), link(), log(), removeAllShaders() +*/ +bool QGLShaderProgram::addShader(QGLShader::ShaderType type, const char *source) +{ + if (!init()) + return false; + QGLShader *shader = new QGLShader(type, this); + if (!shader->setSourceCode(source)) { + d->log = shader->log(); + delete shader; + return false; + } + d->anonShaders.append(shader); + return addShader(shader); +} + +/*! + \overload + + Compiles \a source as a shader of the specified \a type and + adds it to this shader program. Returns true if compilation + was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QGLShader first. + + \sa removeShader(), link(), log(), removeAllShaders() +*/ +bool QGLShaderProgram::addShader(QGLShader::ShaderType type, const QByteArray& source) +{ + return addShader(type, source.constData()); +} + +/*! + \overload + + Compiles \a source as a shader of the specified \a type and + adds it to this shader program. Returns true if compilation + was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QGLShader first. + + \sa removeShader(), link(), log(), removeAllShaders() +*/ +bool QGLShaderProgram::addShader(QGLShader::ShaderType type, const QString& source) +{ + return addShader(type, source.toLatin1().constData()); +} + +/*! + Removes \a shader from this shader program. The object is not deleted. + + \sa addShader(), link(), removeAllShaders() +*/ +void QGLShaderProgram::removeShader(QGLShader *shader) +{ + if (d->program && shader && shader->d->shader) { + glDetachShader(d->program, shader->d->shader); + d->linked = false; // Program needs to be relinked. + } + d->shaders.removeAll(shader); + d->anonShaders.removeAll(shader); +} + +/*! + Returns a list of all shaders that have been added to this shader + program using addShader(). + + \sa addShader(), removeShader() +*/ +QList<QGLShader *> QGLShaderProgram::shaders() const +{ + return d->shaders; +} + +/*! + Removes all of the shaders that were added to this program previously. + The QGLShader objects for the shaders will not be deleted if they + were constructed externally. QGLShader objects that are constructed + internally by QGLShaderProgram will be deleted. + + \sa addShader(), removeShader() +*/ +void QGLShaderProgram::removeAllShaders() +{ + foreach (QGLShader *shader, d->shaders) { + if (d->program && shader && shader->d->shader) + glDetachShader(d->program, shader->d->shader); + } + foreach (QGLShader *shader, d->anonShaders) { + // Delete shader objects that were created anonymously. + delete shader; + } + d->shaders.clear(); + d->anonShaders.clear(); + d->linked = false; // Program needs to be relinked. +} + +#if defined(QT_OPENGL_ES_2) + +#ifndef GL_PROGRAM_BINARY_LENGTH_OES +#define GL_PROGRAM_BINARY_LENGTH_OES 0x8741 +#endif +#ifndef GL_NUM_PROGRAM_BINARY_FORMATS_OES +#define GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE +#endif +#ifndef GL_PROGRAM_BINARY_FORMATS_OES +#define GL_PROGRAM_BINARY_FORMATS_OES 0x87FF +#endif + +#endif + +/*! + Returns the program binary associated with this shader program. + The numeric identifier of the program binary format is returned + in \a format. The \c OES_get_program_binary extension will need + to be supported by the system for binary retrieval to succeed. + + Returns an empty QByteArray if the program binary cannot be + retrieved on this system, or the shader program has not yet + been linked. + + The returned binary can be supplied to setProgramBinary() on the + same machine at some future point to reload the program. It contains + the compiled code of all of the shaders that were attached to the + program at the time programBinary() is called. + + \sa setProgramBinary(), programBinaryFormats() +*/ +QByteArray QGLShaderProgram::programBinary(int *format) const +{ +#if defined(QT_OPENGL_ES_2) + if (!isLinked()) + return QByteArray(); + + // Get the length of the binary data, bailing out if there is none. + GLint length = 0; + glGetProgramiv(d->program, GL_PROGRAM_BINARY_LENGTH_OES, &length); + if (length <= 0) + return QByteArray(); + + // Retrieve the binary data. + QByteArray binary(length, 0); + GLenum binaryFormat; + glGetProgramBinaryOES(d->program, length, 0, &binaryFormat, binary.data()); + if (format) + *format = (int)binaryFormat; + return binary; +#else + Q_UNUSED(format); + return QByteArray(); +#endif +} + +/*! + Sets the \a binary for this shader program according to \a format. + Returns true if the binary was set, or false if the binary format + is not supported or this system does not support program binaries. + The program will be linked if the load succeeds. + + \sa programBinary(), programBinaryFormats(), isLinked() +*/ +bool QGLShaderProgram::setProgramBinary(int format, const QByteArray& binary) +{ +#if defined(QT_OPENGL_ES_2) + // Load the binary and check that it was linked correctly. + glProgramBinaryOES(d->program, (GLenum)format, + binary.constData(), binary.size()); + GLint value = 0; + glGetProgramiv(d->program, GL_LINK_STATUS, &value); + d->linked = (value != 0); + value = 0; + glGetProgramiv(d->program, GL_INFO_LOG_LENGTH, &value); + d->log = QString(); + if (value > 1) { + char *logbuf = new char [value]; + GLint len; + glGetProgramInfoLog(d->program, value, &len, logbuf); + d->log = QString::fromLatin1(logbuf); + qWarning() << "QGLShaderProgram::setProgramBinary:" << d->log; + delete [] logbuf; + } + return d->linked; +#else + Q_UNUSED(format); + Q_UNUSED(binary); + return false; +#endif +} + +/*! + Returns the list of program binary formats that are accepted by + this system for use with setProgramBinary(). + + \sa programBinary(), setProgramBinary() +*/ +QList<int> QGLShaderProgram::programBinaryFormats() +{ +#if defined(QT_OPENGL_ES_2) + GLint count = 0; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &count); + if (count <= 0) + return QList<int>(); + QVector<int> list; + list.resize(count); + glGetIntegerv(GL_PROGRAM_BINARY_FORMATS_OES, list.data()); + return list.toList(); +#else + return QList<int>(); +#endif +} + +/*! + Links together the shaders that were added to this program with + addShader(). Returns true if the link was successful or + false otherwise. If the link failed, the error messages can + be retrieved with log(). + + Subclasses can override this function to initialize attributes + and uniform variables for use in specific shader programs. + + If the shader program was already linked, calling this + function again will force it to be re-linked. + + \sa addShader(), log() +*/ +bool QGLShaderProgram::link() +{ + if (!d->program) + return false; + if (d->hasPartialShaders) { + // Compile the partial vertex and fragment shaders. + QByteArray vertexSource; + QByteArray fragmentSource; + foreach (QGLShader *shader, d->shaders) { + if (shader->shaderType() == QGLShader::PartialVertexShader) + vertexSource += shader->sourceCode(); + else if (shader->shaderType() == QGLShader::PartialFragmentShader) + fragmentSource += shader->sourceCode(); + } + if (vertexSource.isEmpty()) { + if (d->vertexShader) { + glDetachShader(d->program, d->vertexShader->d->shader); + delete d->vertexShader; + d->vertexShader = 0; + } + } else { + if (!d->vertexShader) { + d->vertexShader = + new QGLShader(QGLShader::VertexShader, this); + } + if (!d->vertexShader->setSourceCode(vertexSource)) { + d->log = d->vertexShader->log(); + return false; + } + glAttachShader(d->program, d->vertexShader->d->shader); + } + if (fragmentSource.isEmpty()) { + if (d->fragmentShader) { + glDetachShader(d->program, d->fragmentShader->d->shader); + delete d->fragmentShader; + d->fragmentShader = 0; + } + } else { + if (!d->fragmentShader) { + d->fragmentShader = + new QGLShader(QGLShader::FragmentShader, this); + } + if (!d->fragmentShader->setSourceCode(fragmentSource)) { + d->log = d->fragmentShader->log(); + return false; + } + glAttachShader(d->program, d->fragmentShader->d->shader); + } + } + glLinkProgram(d->program); + GLint value = 0; + glGetProgramiv(d->program, GL_LINK_STATUS, &value); + d->linked = (value != 0); + value = 0; + glGetProgramiv(d->program, GL_INFO_LOG_LENGTH, &value); + d->log = QString(); + if (value > 1) { + char *logbuf = new char [value]; + GLint len; + glGetProgramInfoLog(d->program, value, &len, logbuf); + d->log = QString::fromLatin1(logbuf); + qWarning() << "QGLShaderProgram::link:" << d->log; + delete [] logbuf; + } + return d->linked; +} + +/*! + Returns true if this shader program has been linked; false otherwise. + + \sa link() +*/ +bool QGLShaderProgram::isLinked() const +{ + return d->linked; +} + +/*! + Returns the errors and warnings that occurred during the last link() + or addShader() with explicitly specified source code. + + \sa link() +*/ +QString QGLShaderProgram::log() const +{ + return d->log; +} + +/*! + Enable use of this shader program in the currently active QGLContext. + Returns true if the program was successfully enabled; false + otherwise. If the shader program has not yet been linked, + or it needs to be re-linked, this function will call link(). + + \sa link(), disable() +*/ +bool QGLShaderProgram::enable() +{ + if (!d->program) + return false; + if (!d->linked && !link()) + return false; + glUseProgram(d->program); + return true; +} + +#undef ctx +#define ctx QGLContext::currentContext() + +/*! + Disables the active shader program in the current QGLContext. + This is equivalent to calling \c{glUseProgram(0)}. + + \sa enable() +*/ +void QGLShaderProgram::disable() +{ +#if defined(QT_OPENGL_ES_2) + glUseProgram(0); +#else + if (glUseProgram) + glUseProgram(0); +#endif +} + +#undef ctx +#define ctx d->context + +/*! + Returns the OpenGL identifier associated with this shader program. + + \sa QGLShader::shaderId() +*/ +GLuint QGLShaderProgram::programId() const +{ + return d->program; +} + +/*! + Binds the attribute \a name to the specified \a location. This + function can be called before or after the program has been linked. + Any attributes that have not been explicitly bound when the program + is linked will be assigned locations automatically. + + \sa attributeLocation() +*/ +void QGLShaderProgram::bindAttributeLocation(const char *name, int location) +{ + glBindAttribLocation(d->program, location, name); +} + +/*! + \overload + + Binds the attribute \a name to the specified \a location. This + function can be called before or after the program has been linked. + Any attributes that have not been explicitly bound when the program + is linked will be assigned locations automatically. + + \sa attributeLocation() +*/ +void QGLShaderProgram::bindAttributeLocation(const QByteArray& name, int location) +{ + glBindAttribLocation(d->program, location, name.constData()); +} + +/*! + \overload + + Binds the attribute \a name to the specified \a location. This + function can be called before or after the program has been linked. + Any attributes that have not been explicitly bound when the program + is linked will be assigned locations automatically. + + \sa attributeLocation() +*/ +void QGLShaderProgram::bindAttributeLocation(const QString& name, int location) +{ + glBindAttribLocation(d->program, location, name.toLatin1().constData()); +} + +/*! + Returns the location of the attribute \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + attribute for this shader program. + + \sa uniformLocation(), bindAttributeLocation() +*/ +int QGLShaderProgram::attributeLocation(const char *name) const +{ + if (d->linked) { + return glGetAttribLocation(d->program, name); + } else { + qWarning() << "QGLShaderProgram::attributeLocation(" << name + << "): shader program is not linked"; + return -1; + } +} + +/*! + \overload + + Returns the location of the attribute \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + attribute for this shader program. + + \sa uniformLocation(), bindAttributeLocation() +*/ +int QGLShaderProgram::attributeLocation(const QByteArray& name) const +{ + return attributeLocation(name.constData()); +} + +/*! + \overload + + Returns the location of the attribute \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + attribute for this shader program. + + \sa uniformLocation(), bindAttributeLocation() +*/ +int QGLShaderProgram::attributeLocation(const QString& name) const +{ + return attributeLocation(name.toLatin1().constData()); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(int location, GLfloat value) +{ + if (location != -1) + glVertexAttrib1fv(location, &value); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(const char *name, GLfloat value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to + the 2D vector (\a x, \a y). + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(int location, GLfloat x, GLfloat y) +{ + if (location != -1) { + GLfloat values[2] = {x, y}; + glVertexAttrib2fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to + the 2D vector (\a x, \a y). + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(const char *name, GLfloat x, GLfloat y) +{ + setAttributeValue(attributeLocation(name), x, y); +} + +/*! + Sets the attribute at \a location in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue + (int location, GLfloat x, GLfloat y, GLfloat z) +{ + if (location != -1) { + GLfloat values[3] = {x, y, z}; + glVertexAttrib3fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue + (const char *name, GLfloat x, GLfloat y, GLfloat z) +{ + setAttributeValue(attributeLocation(name), x, y, z); +} + +/*! + Sets the attribute at \a location in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue + (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + if (location != -1) { + GLfloat values[4] = {x, y, z, w}; + glVertexAttrib4fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue + (const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + setAttributeValue(attributeLocation(name), x, y, z, w); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(int location, const QVector2D& value) +{ + if (location != -1) + glVertexAttrib2fv(location, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(const char *name, const QVector2D& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(int location, const QVector3D& value) +{ + if (location != -1) + glVertexAttrib3fv(location, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(const char *name, const QVector3D& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(int location, const QVector4D& value) +{ + if (location != -1) + glVertexAttrib4fv(location, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(const char *name, const QVector4D& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(int location, const QColor& value) +{ + if (location != -1) { + GLfloat values[4] = {value.redF(), value.greenF(), value.blueF(), value.alphaF()}; + glVertexAttrib4fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue(const char *name, const QColor& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to the + contents of \a values, which contains \a columns elements, each + consisting of \a rows elements. The \a rows value should be + 1, 2, 3, or 4. This function is typically used to set matrix + values and column vectors. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue + (int location, const GLfloat *values, int columns, int rows) +{ + if (rows < 1 || rows > 4) { + qWarning() << "QGLShaderProgram::setAttributeValue: rows" << rows << "not supported"; + return; + } + if (location != -1) { + while (columns-- > 0) { + if (rows == 1) + glVertexAttrib1fv(location, values); + else if (rows == 2) + glVertexAttrib2fv(location, values); + else if (rows == 3) + glVertexAttrib3fv(location, values); + else + glVertexAttrib4fv(location, values); + values += rows; + ++location; + } + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to the + contents of \a values, which contains \a columns elements, each + consisting of \a rows elements. The \a rows value should be + 1, 2, 3, or 4. This function is typically used to set matrix + values and column vectors. + + \sa setUniformValue() +*/ +void QGLShaderProgram::setAttributeValue + (const char *name, const GLfloat *values, int columns, int rows) +{ + setAttributeValue(attributeLocation(name), values, columns, rows); +} + +/*! + Sets an array of vertex \a values on the attribute at \a location + in this shader program. The \a size indicates the number of + components per vertex (1, 2, 3, or 4), and the \a stride indicates + the number of bytes between vertices. A default \a stride value + of zero indicates that the vertices are densely packed in \a values. + + \sa setAttributeValue(), setUniformValue(), disableAttributeArray() +*/ +void QGLShaderProgram::setAttributeArray + (int location, const GLfloat *values, int size, int stride) +{ + if (location != -1) { + glVertexAttribPointer(location, size, GL_FLOAT, GL_FALSE, + stride, values); + glEnableVertexAttribArray(location); + } +} + +/*! + Sets an array of 2D vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + \sa setAttributeValue(), setUniformValue(), disableAttributeArray() +*/ +void QGLShaderProgram::setAttributeArray + (int location, const QVector2D *values, int stride) +{ + if (location != -1) { + glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, + stride, values); + glEnableVertexAttribArray(location); + } +} + +/*! + Sets an array of 3D vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + \sa setAttributeValue(), setUniformValue(), disableAttributeArray() +*/ +void QGLShaderProgram::setAttributeArray + (int location, const QVector3D *values, int stride) +{ + if (location != -1) { + glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, + stride, values); + glEnableVertexAttribArray(location); + } +} + +/*! + Sets an array of 4D vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + \sa setAttributeValue(), setUniformValue(), disableAttributeArray() +*/ +void QGLShaderProgram::setAttributeArray + (int location, const QVector4D *values, int stride) +{ + if (location != -1) { + glVertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, + stride, values); + glEnableVertexAttribArray(location); + } +} + +/*! + \overload + + Sets an array of vertex \a values on the attribute called \a name + in this shader program. The \a size indicates the number of + components per vertex (1, 2, 3, or 4), and the \a stride indicates + the number of bytes between vertices. A default \a stride value + of zero indicates that the vertices are densely packed in \a values. + + \sa setAttributeValue(), setUniformValue(), disableAttributeArray() +*/ +void QGLShaderProgram::setAttributeArray + (const char *name, const GLfloat *values, int size, int stride) +{ + setAttributeArray(attributeLocation(name), values, size, stride); +} + +/*! + \overload + + Sets an array of 2D vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + \sa setAttributeValue(), setUniformValue(), disableAttributeArray() +*/ +void QGLShaderProgram::setAttributeArray + (const char *name, const QVector2D *values, int stride) +{ + setAttributeArray(attributeLocation(name), values, stride); +} + +/*! + \overload + + Sets an array of 3D vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + \sa setAttributeValue(), setUniformValue(), disableAttributeArray() +*/ +void QGLShaderProgram::setAttributeArray + (const char *name, const QVector3D *values, int stride) +{ + setAttributeArray(attributeLocation(name), values, stride); +} + +/*! + \overload + + Sets an array of 4D vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + \sa setAttributeValue(), setUniformValue(), disableAttributeArray() +*/ +void QGLShaderProgram::setAttributeArray + (const char *name, const QVector4D *values, int stride) +{ + setAttributeArray(attributeLocation(name), values, stride); +} + +/*! + Disables the vertex array at \a location in this shader program + that was enabled by a previous call to setAttributeArray(). + + \sa setAttributeArray(), setAttributeValue(), setUniformValue() +*/ +void QGLShaderProgram::disableAttributeArray(int location) +{ + if (location != -1) + glDisableVertexAttribArray(location); +} + +/*! + \overload + + Disables the vertex array called \a name in this shader program + that was enabled by a previous call to setAttributeArray(). + + \sa setAttributeArray(), setAttributeValue(), setUniformValue() +*/ +void QGLShaderProgram::disableAttributeArray(const char *name) +{ + disableAttributeArray(attributeLocation(name)); +} + +/*! + Returns the location of the uniform variable \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + uniform variable for this shader program. + + \sa attributeLocation() +*/ +int QGLShaderProgram::uniformLocation(const char *name) const +{ + if (d->linked) { + return glGetUniformLocation(d->program, name); + } else { + qWarning() << "QGLShaderProgram::uniformLocation(" << name + << "): shader program is not linked"; + return -1; + } +} + +/*! + \overload + + Returns the location of the uniform variable \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + uniform variable for this shader program. + + \sa attributeLocation() +*/ +int QGLShaderProgram::uniformLocation(const QByteArray& name) const +{ + return uniformLocation(name.constData()); +} + +/*! + \overload + + Returns the location of the uniform variable \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + uniform variable for this shader program. + + \sa attributeLocation() +*/ +int QGLShaderProgram::uniformLocation(const QString& name) const +{ + return uniformLocation(name.toLatin1().constData()); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, GLfloat value) +{ + if (location != -1) + glUniform1fv(location, 1, &value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, GLfloat value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, GLint value) +{ + if (location != -1) + glUniform1i(location, value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, GLint value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + This function should be used when setting sampler values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, GLuint value) +{ + if (location != -1) + glUniform1i(location, value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. This function should be used when setting sampler values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, GLuint value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to + the 2D vector (\a x, \a y). + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, GLfloat x, GLfloat y) +{ + if (location != -1) { + GLfloat values[2] = {x, y}; + glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the 2D vector (\a x, \a y). + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, GLfloat x, GLfloat y) +{ + setUniformValue(uniformLocation(name), x, y); +} + +/*! + Sets the uniform variable at \a location in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue + (int location, GLfloat x, GLfloat y, GLfloat z) +{ + if (location != -1) { + GLfloat values[3] = {x, y, z}; + glUniform3fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue + (const char *name, GLfloat x, GLfloat y, GLfloat z) +{ + setUniformValue(uniformLocation(name), x, y, z); +} + +/*! + Sets the uniform variable at \a location in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue + (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + if (location != -1) { + GLfloat values[4] = {x, y, z, w}; + glUniform4fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue + (const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + setUniformValue(uniformLocation(name), x, y, z, w); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QVector2D& value) +{ + if (location != -1) + glUniform2fv(location, 1, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QVector2D& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QVector3D& value) +{ + if (location != -1) + glUniform3fv(location, 1, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QVector3D& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QVector4D& value) +{ + if (location != -1) + glUniform4fv(location, 1, reinterpret_cast<const GLfloat *>(&value)); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QVector4D& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to + the red, green, blue, and alpha components of \a color. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QColor& color) +{ + if (location != -1) { + GLfloat values[4] = {color.redF(), color.greenF(), color.blueF(), color.alphaF()}; + glUniform4fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the red, green, blue, and alpha components of \a color. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QColor& color) +{ + setUniformValue(uniformLocation(name), color); +} + +/*! + Sets the uniform variable at \a location in the current context to + the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QPoint& point) +{ + if (location != -1) { + GLfloat values[4] = {point.x(), point.y()}; + glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QPoint& point) +{ + setUniformValue(uniformLocation(name), point); +} + +/*! + Sets the uniform variable at \a location in the current context to + the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QPointF& point) +{ + if (location != -1) { + GLfloat values[4] = {point.x(), point.y()}; + glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QPointF& point) +{ + setUniformValue(uniformLocation(name), point); +} + +/*! + Sets the uniform variable at \a location in the current context to + the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QSize& size) +{ + if (location != -1) { + GLfloat values[4] = {size.width(), size.width()}; + glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QSize& size) +{ + setUniformValue(uniformLocation(name), size); +} + +/*! + Sets the uniform variable at \a location in the current context to + the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QSizeF& size) +{ + if (location != -1) { + GLfloat values[4] = {size.width(), size.height()}; + glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QSizeF& size) +{ + setUniformValue(uniformLocation(name), size); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 2x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QMatrix2x2& value) +{ + if (location != -1) + glUniformMatrix2fv(location, 1, GL_FALSE, value.data()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QMatrix2x2& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 2x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QMatrix2x3& value) +{ +#if !defined(QT_OPENGL_ES_2) + if (location != -1) { + if (glUniformMatrix2x3fv) { + // OpenGL 2.1+: pass the matrix directly. + glUniformMatrix2x3fv(location, 1, GL_FALSE, value.data()); + } else { + // OpenGL 2.0: pass the matrix columns as a vector. + glUniform3fv(location, 2, value.data()); + } + } +#else + if (location != -1) + glUniform3fv(location, 2, value.data()); +#endif +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QMatrix2x3& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 2x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QMatrix2x4& value) +{ +#if !defined(QT_OPENGL_ES_2) + if (location != -1) { + if (glUniformMatrix2x4fv) { + // OpenGL 2.1+: pass the matrix directly. + glUniformMatrix2x4fv(location, 1, GL_FALSE, value.data()); + } else { + // OpenGL 2.0: pass the matrix columns as a vector. + glUniform4fv(location, 2, value.data()); + } + } +#else + if (location != -1) + glUniform4fv(location, 2, value.data()); +#endif +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QMatrix2x4& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 3x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QMatrix3x2& value) +{ +#if !defined(QT_OPENGL_ES_2) + if (location != -1) { + if (glUniformMatrix3x2fv) { + // OpenGL 2.1+: pass the matrix directly. + glUniformMatrix3x2fv(location, 1, GL_FALSE, value.data()); + } else { + // OpenGL 2.0: pass the matrix columns as a vector. + glUniform2fv(location, 3, value.data()); + } + } +#else + if (location != -1) + glUniform2fv(location, 3, value.data()); +#endif +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QMatrix3x2& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 3x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QMatrix3x3& value) +{ + if (location != -1) + glUniformMatrix3fv(location, 1, GL_FALSE, value.data()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QMatrix3x3& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 3x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QMatrix3x4& value) +{ +#if !defined(QT_OPENGL_ES_2) + if (location != -1) { + if (glUniformMatrix3x4fv) { + // OpenGL 2.1+: pass the matrix directly. + glUniformMatrix3x4fv(location, 1, GL_FALSE, value.data()); + } else { + // OpenGL 2.0: pass the matrix columns as a vector. + glUniform4fv(location, 3, value.data()); + } + } +#else + if (location != -1) + glUniform4fv(location, 3, value.data()); +#endif +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QMatrix3x4& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 4x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QMatrix4x2& value) +{ +#if !defined(QT_OPENGL_ES_2) + if (location != -1) { + if (glUniformMatrix4x2fv) { + // OpenGL 2.1+: pass the matrix directly. + glUniformMatrix4x2fv(location, 1, GL_FALSE, value.data()); + } else { + // OpenGL 2.0: pass the matrix columns as a vector. + glUniform2fv(location, 4, value.data()); + } + } +#else + if (location != -1) + glUniform2fv(location, 4, value.data()); +#endif +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QMatrix4x2& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 4x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QMatrix4x3& value) +{ +#if !defined(QT_OPENGL_ES_2) + if (location != -1) { + if (glUniformMatrix4x3fv) { + // OpenGL 2.1+: pass the matrix directly. + glUniformMatrix4x3fv(location, 1, GL_FALSE, value.data()); + } else { + // OpenGL 2.0: pass the matrix columns as a vector. + glUniform3fv(location, 4, value.data()); + } + } +#else + if (location != -1) + glUniform3fv(location, 4, value.data()); +#endif +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QMatrix4x3& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 4x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const QMatrix4x4& value) +{ + if (location != -1) + glUniformMatrix4fv(location, 1, GL_FALSE, value.data()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const QMatrix4x4& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + \overload + + Sets the uniform variable at \a location in the current context + to a 4x4 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(int location, const GLfloat value[4][4]) +{ + if (location != -1) + glUniformMatrix4fv(location, 1, GL_FALSE, value[0]); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x4 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValue(const char *name, const GLfloat value[4][4]) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to a + 3x3 transformation matrix \a value that is specified as a QTransform value. + + To set a QTransform value as a 4x4 matrix in a shader, use + \c{setUniformValue(location, QMatrix4x4(value))}. +*/ +void QGLShaderProgram::setUniformValue(int location, const QTransform& value) +{ + if (location != -1) { + GLfloat mat[3][3] = { + {value.m11(), value.m12(), value.m13()}, + {value.m21(), value.m22(), value.m23()}, + {value.m31(), value.m32(), value.m33()} + }; + glUniformMatrix3fv(location, 1, GL_FALSE, mat[0]); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to a + 3x3 transformation matrix \a value that is specified as a QTransform value. + + To set a QTransform value as a 4x4 matrix in a shader, use + \c{setUniformValue(name, QMatrix4x4(value))}. +*/ +void QGLShaderProgram::setUniformValue + (const char *name, const QTransform& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const GLint *values, int count) +{ + if (location != -1) + glUniform1iv(location, count, values); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray + (const char *name, const GLint *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count elements of \a values. This overload + should be used when setting an array of sampler values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const GLuint *values, int count) +{ + if (location != -1) + glUniform1iv(location, count, reinterpret_cast<const GLint *>(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count elements of \a values. This overload + should be used when setting an array of sampler values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray + (const char *name, const GLuint *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count elements of \a values. Each element + has \a size components. The \a size must be 1, 2, 3, or 4. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const GLfloat *values, int count, int size) +{ + if (location != -1) { + if (size == 1) + glUniform1fv(location, count, values); + else if (size == 2) + glUniform2fv(location, count, values); + else if (size == 3) + glUniform3fv(location, count, values); + else if (size == 4) + glUniform4fv(location, count, values); + else + qWarning() << "QGLShaderProgram::setUniformValue: size" << size << "not supported"; + } +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count elements of \a values. Each element + has \a size components. The \a size must be 1, 2, 3, or 4. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray + (const char *name, const GLfloat *values, int count, int size) +{ + setUniformValueArray(uniformLocation(name), values, count, size); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QVector2D *values, int count) +{ + if (location != -1) + glUniform2fv(location, count, reinterpret_cast<const GLfloat *>(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QVector2D *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QVector3D *values, int count) +{ + if (location != -1) + glUniform3fv(location, count, reinterpret_cast<const GLfloat *>(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QVector3D *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QVector4D *values, int count) +{ + if (location != -1) + glUniform4fv(location, count, reinterpret_cast<const GLfloat *>(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QVector4D *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +// We may have to repack matrix arrays if the matrix types +// contain additional flag bits. Especially QMatrix4x4. +#define setUniformMatrixArray(func,location,values,count,type,cols,rows) \ + if (location == -1 || count <= 0) \ + return; \ + if (count == 1 || sizeof(type) == cols * rows * sizeof(GLfloat)) { \ + func(location, count, GL_FALSE, values->constData()); \ + } else { \ + QVarLengthArray<GLfloat> temp(cols * rows * count); \ + for (int index = 0; index < count; ++index) { \ + qMemCopy(temp.data() + cols * rows * index, \ + values[index].constData(), cols * rows * sizeof(GLfloat)); \ + } \ + func(location, count, GL_FALSE, temp.constData()); \ + } +#if !defined(QT_OPENGL_ES_2) +#define setUniformGenericMatrixArray(func,colfunc,location,values,count,type,cols,rows) \ + if (location == -1 || count <= 0) \ + return; \ + if (count == 1 || sizeof(type) == cols * rows * sizeof(GLfloat)) { \ + if (func) \ + func(location, count, GL_FALSE, values->constData()); \ + else \ + colfunc(location, cols * count, values->constData()); \ + } else { \ + QVarLengthArray<GLfloat> temp(cols * rows * count); \ + for (int index = 0; index < count; ++index) { \ + qMemCopy(temp.data() + cols * rows * index, \ + values[index].constData(), cols * rows * sizeof(GLfloat)); \ + } \ + if (func) \ + func(location, count, GL_FALSE, temp.constData()); \ + else \ + colfunc(location, count * cols, temp.constData()); \ + } +#else +#define setUniformGenericMatrixArray(func,colfunc,location,values,count,type,cols,rows) \ + if (location == -1 || count <= 0) \ + return; \ + if (count == 1 || sizeof(type) == cols * rows * sizeof(GLfloat)) { \ + colfunc(location, cols * count, values->constData()); \ + } else { \ + QVarLengthArray<GLfloat> temp(cols * rows * count); \ + for (int index = 0; index < count; ++index) { \ + qMemCopy(temp.data() + cols * rows * index, \ + values[index].constData(), cols * rows * sizeof(GLfloat)); \ + } \ + colfunc(location, count * cols, temp.constData()); \ + } +#endif + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QMatrix2x2 *values, int count) +{ + setUniformMatrixArray + (glUniformMatrix2fv, location, values, count, QMatrix2x2, 2, 2); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x2 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QMatrix2x3 *values, int count) +{ + setUniformGenericMatrixArray + (glUniformMatrix2x3fv, glUniform3fv, location, values, count, + QMatrix2x3, 2, 3); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x3 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QMatrix2x4 *values, int count) +{ + setUniformGenericMatrixArray + (glUniformMatrix2x4fv, glUniform4fv, location, values, count, + QMatrix2x4, 2, 4); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x4 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QMatrix3x2 *values, int count) +{ + setUniformGenericMatrixArray + (glUniformMatrix3x2fv, glUniform2fv, location, values, count, + QMatrix3x2, 3, 2); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x2 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QMatrix3x3 *values, int count) +{ + setUniformMatrixArray + (glUniformMatrix3fv, location, values, count, QMatrix3x3, 3, 3); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x3 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QMatrix3x4 *values, int count) +{ + setUniformGenericMatrixArray + (glUniformMatrix3x4fv, glUniform4fv, location, values, count, + QMatrix3x4, 3, 4); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x4 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QMatrix4x2 *values, int count) +{ + setUniformGenericMatrixArray + (glUniformMatrix4x2fv, glUniform2fv, location, values, count, + QMatrix4x2, 4, 2); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x2 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QMatrix4x3 *values, int count) +{ + setUniformGenericMatrixArray + (glUniformMatrix4x3fv, glUniform3fv, location, values, count, + QMatrix4x3, 4, 3); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x3 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(int location, const QMatrix4x4 *values, int count) +{ + setUniformMatrixArray + (glUniformMatrix4fv, location, values, count, QMatrix4x4, 4, 4); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x4 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Returns true if shader programs written in the OpenGL Shading + Language (GLSL) are supported on this system; false otherwise. + + The \a context is used to resolve the GLSL extensions. + If \a context is null, then QGLContext::currentContext() is used. +*/ +bool QGLShaderProgram::hasShaderPrograms(const QGLContext *context) +{ +#if !defined(QT_OPENGL_ES_2) + if (!context) + context = QGLContext::currentContext(); + if (!context) + return false; + return qt_resolve_glsl_extensions(const_cast<QGLContext *>(context)); +#else + Q_UNUSED(context); + return true; +#endif +} + +#endif + +QT_END_NAMESPACE diff --git a/src/opengl/qglshaderprogram.h b/src/opengl/qglshaderprogram.h new file mode 100644 index 0000000..b69d28e --- /dev/null +++ b/src/opengl/qglshaderprogram.h @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the 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 QGLSHADERPROGRAM_H +#define QGLSHADERPROGRAM_H + +#include <QtOpenGL/qgl.h> +#include <QtGui/qvector2d.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qmatrix4x4.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(OpenGL) + +#if !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1) + +class QGLShaderProgram; +class QGLShaderPrivate; + +class Q_OPENGL_EXPORT QGLShader : public QObject +{ + Q_OBJECT +public: + enum ShaderType + { + VertexShader, + FragmentShader, + PartialVertexShader, + PartialFragmentShader + }; + + explicit QGLShader(QGLShader::ShaderType type, QObject *parent = 0); + explicit QGLShader(const QString& fileName, QObject *parent = 0); + QGLShader(const QString& fileName, QGLShader::ShaderType type, QObject *parent = 0); + QGLShader(QGLShader::ShaderType type, const QGLContext *context, QObject *parent = 0); + QGLShader(const QString& fileName, const QGLContext *context, QObject *parent = 0); + QGLShader(const QString& fileName, QGLShader::ShaderType type, const QGLContext *context, QObject *parent = 0); + virtual ~QGLShader(); + + bool isValid() const; + + QGLShader::ShaderType shaderType() const; + + bool setSourceCode(const char *source); + bool setSourceCode(const QByteArray& source); + bool setSourceCode(const QString& source); + bool setSourceCodeFile(const QString& fileName); + + bool setBinaryCode(GLenum format, const void *binary, int length); + bool setBinaryCode(QGLShader& otherShader, GLenum format, const void *binary, int length); + + static QList<GLenum> shaderBinaryFormats(); + + QByteArray sourceCode() const; + + bool isCompiled() const; + QString log() const; + + GLuint shaderId() const; + +private: + QGLShaderPrivate *d; + + friend class QGLShaderProgram; + + Q_DISABLE_COPY(QGLShader); +}; + +class QGLShaderProgramPrivate; + +class Q_OPENGL_EXPORT QGLShaderProgram : public QObject +{ + Q_OBJECT +public: + explicit QGLShaderProgram(QObject *parent = 0); + explicit QGLShaderProgram(const QGLContext *context, QObject *parent = 0); + virtual ~QGLShaderProgram(); + + bool isValid() const; + + bool addShader(QGLShader *shader); + void removeShader(QGLShader *shader); + QList<QGLShader *> shaders() const; + + bool addShader(QGLShader::ShaderType type, const char *source); + bool addShader(QGLShader::ShaderType type, const QByteArray& source); + bool addShader(QGLShader::ShaderType type, const QString& source); + + void removeAllShaders(); + + QByteArray programBinary(int *format) const; + bool setProgramBinary(int format, const QByteArray& binary); + static QList<int> programBinaryFormats(); + + virtual bool link(); + bool isLinked() const; + QString log() const; + + bool enable(); + static void disable(); + + GLuint programId() const; + + void bindAttributeLocation(const char *name, int location); + void bindAttributeLocation(const QByteArray& name, int location); + void bindAttributeLocation(const QString& name, int location); + + int attributeLocation(const char *name) const; + int attributeLocation(const QByteArray& name) const; + int attributeLocation(const QString& name) const; + + void setAttributeValue(int location, GLfloat value); + void setAttributeValue(int location, GLfloat x, GLfloat y); + void setAttributeValue(int location, GLfloat x, GLfloat y, GLfloat z); + void setAttributeValue(int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setAttributeValue(int location, const QVector2D& value); + void setAttributeValue(int location, const QVector3D& value); + void setAttributeValue(int location, const QVector4D& value); + void setAttributeValue(int location, const QColor& value); + void setAttributeValue(int location, const GLfloat *values, int columns, int rows); + + void setAttributeValue(const char *name, GLfloat value); + void setAttributeValue(const char *name, GLfloat x, GLfloat y); + void setAttributeValue(const char *name, GLfloat x, GLfloat y, GLfloat z); + void setAttributeValue(const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setAttributeValue(const char *name, const QVector2D& value); + void setAttributeValue(const char *name, const QVector3D& value); + void setAttributeValue(const char *name, const QVector4D& value); + void setAttributeValue(const char *name, const QColor& value); + void setAttributeValue(const char *name, const GLfloat *values, int columns, int rows); + + void setAttributeArray + (int location, const GLfloat *values, int size, int stride = 0); + void setAttributeArray + (int location, const QVector2D *values, int stride = 0); + void setAttributeArray + (int location, const QVector3D *values, int stride = 0); + void setAttributeArray + (int location, const QVector4D *values, int stride = 0); + void setAttributeArray + (const char *name, const GLfloat *values, int size, int stride = 0); + void setAttributeArray + (const char *name, const QVector2D *values, int stride = 0); + void setAttributeArray + (const char *name, const QVector3D *values, int stride = 0); + void setAttributeArray + (const char *name, const QVector4D *values, int stride = 0); + void disableAttributeArray(int location); + void disableAttributeArray(const char *name); + + int uniformLocation(const char *name) const; + int uniformLocation(const QByteArray& name) const; + int uniformLocation(const QString& name) const; + + void setUniformValue(int location, GLfloat value); + void setUniformValue(int location, GLint value); + void setUniformValue(int location, GLuint value); + void setUniformValue(int location, GLfloat x, GLfloat y); + void setUniformValue(int location, GLfloat x, GLfloat y, GLfloat z); + void setUniformValue(int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setUniformValue(int location, const QVector2D& value); + void setUniformValue(int location, const QVector3D& value); + void setUniformValue(int location, const QVector4D& value); + void setUniformValue(int location, const QColor& color); + void setUniformValue(int location, const QPoint& point); + void setUniformValue(int location, const QPointF& point); + void setUniformValue(int location, const QSize& size); + void setUniformValue(int location, const QSizeF& size); + void setUniformValue(int location, const QMatrix2x2& value); + void setUniformValue(int location, const QMatrix2x3& value); + void setUniformValue(int location, const QMatrix2x4& value); + void setUniformValue(int location, const QMatrix3x2& value); + void setUniformValue(int location, const QMatrix3x3& value); + void setUniformValue(int location, const QMatrix3x4& value); + void setUniformValue(int location, const QMatrix4x2& value); + void setUniformValue(int location, const QMatrix4x3& value); + void setUniformValue(int location, const QMatrix4x4& value); + void setUniformValue(int location, const GLfloat value[4][4]); + void setUniformValue(int location, const QTransform& value); + + void setUniformValue(const char *name, GLfloat value); + void setUniformValue(const char *name, GLint value); + void setUniformValue(const char *name, GLuint value); + void setUniformValue(const char *name, GLfloat x, GLfloat y); + void setUniformValue(const char *name, GLfloat x, GLfloat y, GLfloat z); + void setUniformValue(const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setUniformValue(const char *name, const QVector2D& value); + void setUniformValue(const char *name, const QVector3D& value); + void setUniformValue(const char *name, const QVector4D& value); + void setUniformValue(const char *name, const QColor& color); + void setUniformValue(const char *name, const QPoint& point); + void setUniformValue(const char *name, const QPointF& point); + void setUniformValue(const char *name, const QSize& size); + void setUniformValue(const char *name, const QSizeF& size); + void setUniformValue(const char *name, const QMatrix2x2& value); + void setUniformValue(const char *name, const QMatrix2x3& value); + void setUniformValue(const char *name, const QMatrix2x4& value); + void setUniformValue(const char *name, const QMatrix3x2& value); + void setUniformValue(const char *name, const QMatrix3x3& value); + void setUniformValue(const char *name, const QMatrix3x4& value); + void setUniformValue(const char *name, const QMatrix4x2& value); + void setUniformValue(const char *name, const QMatrix4x3& value); + void setUniformValue(const char *name, const QMatrix4x4& value); + void setUniformValue(const char *name, const GLfloat value[4][4]); + void setUniformValue(const char *name, const QTransform& value); + + void setUniformValueArray(int location, const GLfloat *values, int count, int size); + void setUniformValueArray(int location, const GLint *values, int count); + void setUniformValueArray(int location, const GLuint *values, int count); + void setUniformValueArray(int location, const QVector2D *values, int count); + void setUniformValueArray(int location, const QVector3D *values, int count); + void setUniformValueArray(int location, const QVector4D *values, int count); + void setUniformValueArray(int location, const QMatrix2x2 *values, int count); + void setUniformValueArray(int location, const QMatrix2x3 *values, int count); + void setUniformValueArray(int location, const QMatrix2x4 *values, int count); + void setUniformValueArray(int location, const QMatrix3x2 *values, int count); + void setUniformValueArray(int location, const QMatrix3x3 *values, int count); + void setUniformValueArray(int location, const QMatrix3x4 *values, int count); + void setUniformValueArray(int location, const QMatrix4x2 *values, int count); + void setUniformValueArray(int location, const QMatrix4x3 *values, int count); + void setUniformValueArray(int location, const QMatrix4x4 *values, int count); + + void setUniformValueArray(const char *name, const GLfloat *values, int count, int size); + void setUniformValueArray(const char *name, const GLint *values, int count); + void setUniformValueArray(const char *name, const GLuint *values, int count); + void setUniformValueArray(const char *name, const QVector2D *values, int count); + void setUniformValueArray(const char *name, const QVector3D *values, int count); + void setUniformValueArray(const char *name, const QVector4D *values, int count); + void setUniformValueArray(const char *name, const QMatrix2x2 *values, int count); + void setUniformValueArray(const char *name, const QMatrix2x3 *values, int count); + void setUniformValueArray(const char *name, const QMatrix2x4 *values, int count); + void setUniformValueArray(const char *name, const QMatrix3x2 *values, int count); + void setUniformValueArray(const char *name, const QMatrix3x3 *values, int count); + void setUniformValueArray(const char *name, const QMatrix3x4 *values, int count); + void setUniformValueArray(const char *name, const QMatrix4x2 *values, int count); + void setUniformValueArray(const char *name, const QMatrix4x3 *values, int count); + void setUniformValueArray(const char *name, const QMatrix4x4 *values, int count); + + static bool hasShaderPrograms(const QGLContext *context = 0); + +private: + QGLShaderProgramPrivate *d; + + Q_DISABLE_COPY(QGLShaderProgram); + + bool init(); +}; + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/opengl/qpaintengine_opengl.cpp b/src/opengl/qpaintengine_opengl.cpp index 5a212f5..e3d8a1d 100644 --- a/src/opengl/qpaintengine_opengl.cpp +++ b/src/opengl/qpaintengine_opengl.cpp @@ -39,7 +39,6 @@ ** ****************************************************************************/ -#include <private/qtextengine_p.h> #include <qdebug.h> #include <private/qfontengine_p.h> #include <qmath.h> @@ -57,13 +56,11 @@ #include "qpen.h" #include "qvarlengtharray.h" #include <private/qpainter_p.h> -#include <qglpixelbuffer.h> #include <private/qglpixelbuffer_p.h> #include <private/qbezier_p.h> #include <qglframebufferobject.h> #include "private/qtessellator_p.h" -#include "private/qwindowsurface_gl_p.h" #include "util/fragmentprograms_p.h" @@ -125,35 +122,6 @@ struct QT_PointF { qreal y; }; -void qt_add_rect_to_array(const QRectF &r, q_vertexType *array) -{ - qreal left = r.left(); - qreal right = r.right(); - qreal top = r.top(); - qreal bottom = r.bottom(); - - array[0] = f2vt(left); - array[1] = f2vt(top); - array[2] = f2vt(right); - array[3] = f2vt(top); - array[4] = f2vt(right); - array[5] = f2vt(bottom); - array[6] = f2vt(left); - array[7] = f2vt(bottom); -} - -void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array) -{ - array[0] = f2vt(x1); - array[1] = f2vt(y1); - array[2] = f2vt(x2); - array[3] = f2vt(y1); - array[4] = f2vt(x2); - array[5] = f2vt(y2); - array[6] = f2vt(x1); - array[7] = f2vt(y2); -} - struct QGLTrapezoid { QGLTrapezoid() @@ -190,180 +158,6 @@ const QGLTrapezoid QGLTrapezoid::translated(const QPointF &delta) const return trap; } -class QGLDrawable { -public: - QGLDrawable() : widget(0), buffer(0), fbo(0) - , wsurf(0) - {} - inline void setDevice(QPaintDevice *pdev); - inline void swapBuffers(); - inline void makeCurrent(); - inline void doneCurrent(); - inline QSize size() const; - inline QGLFormat format() const; - inline GLuint bindTexture(const QImage &image, GLenum target = GL_TEXTURE_2D, GLint format = GL_RGBA); - inline GLuint bindTexture(const QPixmap &pixmap, GLenum target = GL_TEXTURE_2D, GLint format = GL_RGBA); - inline QColor backgroundColor() const; - inline QGLContext *context() const; - inline bool autoFillBackground() const; - -private: - bool wasBound; - QGLWidget *widget; - QGLPixelBuffer *buffer; - QGLFramebufferObject *fbo; -#ifdef Q_WS_QWS - QWSGLWindowSurface *wsurf; -#else - QGLWindowSurface *wsurf; -#endif -}; - -void QGLDrawable::setDevice(QPaintDevice *pdev) -{ - wasBound = false; - widget = 0; - buffer = 0; - fbo = 0; -#ifdef Q_WS_QWS - wsurf = 0; -#endif - if (pdev->devType() == QInternal::Widget) - widget = static_cast<QGLWidget *>(pdev); - else if (pdev->devType() == QInternal::Pbuffer) - buffer = static_cast<QGLPixelBuffer *>(pdev); - else if (pdev->devType() == QInternal::FramebufferObject) - fbo = static_cast<QGLFramebufferObject *>(pdev); - else if (pdev->devType() == QInternal::UnknownDevice) -#ifdef Q_WS_QWS - wsurf = static_cast<QWSGLPaintDevice*>(pdev)->windowSurface(); -#else - wsurf = static_cast<QGLWindowSurface *>(pdev); -#endif -} - -inline void QGLDrawable::swapBuffers() -{ - if (widget) { - if (widget->autoBufferSwap()) - widget->swapBuffers(); - } else { - glFlush(); - } -} - -inline void QGLDrawable::makeCurrent() -{ - if (widget) - widget->makeCurrent(); - else if (buffer) - buffer->makeCurrent(); - else if (wsurf) - wsurf->context()->makeCurrent(); - else if (fbo) { - wasBound = fbo->isBound(); - if (!wasBound) - fbo->bind(); - } -} - -inline void QGLDrawable::doneCurrent() -{ - if (fbo && !wasBound) - fbo->release(); -} - -inline QSize QGLDrawable::size() const -{ - if (widget) { - return QSize(widget->d_func()->glcx->device()->width(), - widget->d_func()->glcx->device()->height()); - } else if (buffer) { - return buffer->size(); - } else if (fbo) { - return fbo->size(); - } else if (wsurf) { -#ifdef Q_WS_QWS - return wsurf->window()->frameSize(); -#else - return QSize(wsurf->width(), wsurf->height()); -#endif - } - return QSize(); -} - -inline QGLFormat QGLDrawable::format() const -{ - if (widget) - return widget->format(); - else if (buffer) - return buffer->format(); - else if (wsurf) - return wsurf->context()->format(); - else if (fbo && QGLContext::currentContext()) { - QGLFormat fmt = QGLContext::currentContext()->format(); - fmt.setStencil(fbo->attachment() == QGLFramebufferObject::CombinedDepthStencil); - fmt.setDepth(fbo->attachment() != QGLFramebufferObject::NoAttachment); - return fmt; - } - - return QGLFormat(); -} - -inline GLuint QGLDrawable::bindTexture(const QImage &image, GLenum target, GLint format) -{ - if (widget) - return widget->d_func()->glcx->d_func()->bindTexture(image, target, format, true); - else if (buffer) - return buffer->d_func()->qctx->d_func()->bindTexture(image, target, format, true); - else if (fbo && QGLContext::currentContext()) - return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(image, target, format, true); - else if (wsurf) - return wsurf->context()->d_func()->bindTexture(image, target, format, true); - return 0; -} - -inline GLuint QGLDrawable::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) -{ - if (widget) - return widget->d_func()->glcx->d_func()->bindTexture(pixmap, target, format, true); - else if (buffer) - return buffer->d_func()->qctx->d_func()->bindTexture(pixmap, target, format, true); - else if (fbo && QGLContext::currentContext()) - return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(pixmap, target, format, true); - else if (wsurf) - return wsurf->context()->d_func()->bindTexture(pixmap, target, format, true); - return 0; -} - -inline QColor QGLDrawable::backgroundColor() const -{ - if (widget) - return widget->palette().brush(widget->backgroundRole()).color(); - return QApplication::palette().brush(QPalette::Background).color(); -} - -inline QGLContext *QGLDrawable::context() const -{ - if (widget) - return widget->d_func()->glcx; - else if (buffer) - return buffer->d_func()->qctx; - else if (fbo) - return const_cast<QGLContext *>(QGLContext::currentContext()); - else if (wsurf) - return wsurf->context(); - return 0; -} - -inline bool QGLDrawable::autoFillBackground() const -{ - if (widget) - return widget->autoFillBackground(); - else - return false; -} - class QOpenGLImmediateModeTessellator; class QGLMaskGenerator; @@ -1536,9 +1330,15 @@ bool QOpenGLPaintEngine::begin(QPaintDevice *pdev) d->offscreen.begin(); - const QColor &c = d->drawable.backgroundColor(); - glClearColor(c.redF(), c.greenF(), c.blueF(), 1.0); if (d->drawable.context()->d_func()->clear_on_painter_begin && d->drawable.autoFillBackground()) { + + if (d->drawable.hasTransparentBackground()) + glClearColor(0.0, 0.0, 0.0, 0.0); + else { + const QColor &c = d->drawable.backgroundColor(); + glClearColor(c.redF(), c.greenF(), c.blueF(), 1.0); + } + GLbitfield clearBits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; #ifndef QT_OPENGL_ES clearBits |= GL_ACCUM_BUFFER_BIT; @@ -1924,15 +1724,15 @@ static void drawTrapezoid(const QGLTrapezoid &trap, const qreal offscreenHeight, qreal leftB = trap.bottomLeftX + (trap.topLeftX - trap.bottomLeftX) * reciprocal; qreal rightB = trap.bottomRightX + (trap.topRightX - trap.bottomRightX) * reciprocal; - const bool topZero = qFuzzyCompare(topDist + 1, 1); + const bool topZero = qFuzzyIsNull(topDist); reciprocal = topZero ? 1.0 / bottomDist : 1.0 / topDist; qreal leftA = topZero ? (trap.bottomLeftX - leftB) * reciprocal : (trap.topLeftX - leftB) * reciprocal; qreal rightA = topZero ? (trap.bottomRightX - rightB) * reciprocal : (trap.topRightX - rightB) * reciprocal; - qreal invLeftA = qFuzzyCompare(leftA + 1, 1) ? 0.0 : 1.0 / leftA; - qreal invRightA = qFuzzyCompare(rightA + 1, 1) ? 0.0 : 1.0 / rightA; + qreal invLeftA = qFuzzyIsNull(leftA) ? 0.0 : 1.0 / leftA; + qreal invRightA = qFuzzyIsNull(rightA) ? 0.0 : 1.0 / rightA; // fragment program needs the negative of invRightA as it mirrors the line glTexCoord4f(topDist, bottomDist, invLeftA, -invRightA); @@ -2233,7 +2033,7 @@ void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule) // Enable color writes. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glStencilMask(0); + glStencilMask(stencilMask); setGradientOps(cbrush, QRectF(QPointF(min_x, min_y), QSizeF(max_x - min_x, max_y - min_y))); @@ -2245,12 +2045,14 @@ void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule) // Enable stencil func. glStencilFunc(GL_NOTEQUAL, 0, stencilMask); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); composite(rect); } else { DEBUG_ONCE qDebug() << "QOpenGLPaintEnginePrivate: Drawing polygon using stencil method (no fragment programs)"; // Enable stencil func. glStencilFunc(GL_NOTEQUAL, 0, stencilMask); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); #ifndef QT_OPENGL_ES glBegin(GL_QUADS); glVertex2f(min_x, min_y); @@ -2261,24 +2063,6 @@ void QOpenGLPaintEnginePrivate::fillVertexArray(Qt::FillRule fillRule) #endif } - glStencilMask(~0); - glStencilFunc(GL_ALWAYS, 0, 0); - glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); - - // clear all stencil values to 0 - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - -#ifndef QT_OPENGL_ES - glBegin(GL_QUADS); - glVertex2f(min_x, min_y); - glVertex2f(max_x, min_y); - glVertex2f(max_x, max_y); - glVertex2f(min_x, max_y); - glEnd(); -#endif - - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - // Disable stencil writes. glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilMask(0); @@ -2460,11 +2244,12 @@ void QOpenGLPaintEnginePrivate::updateDepthClip() return; } -#ifndef QT_OPENGL_ES - glClearDepth(0.0f); -#else +#if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_2) || defined(QT_OPENGL_ES_1_CL) glClearDepthf(0.0f); +#else + glClearDepth(0.0f); #endif + glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glClear(GL_DEPTH_BUFFER_BIT); @@ -3300,6 +3085,9 @@ QGLTrapezoidMaskGenerator::QGLTrapezoidMaskGenerator(const QPainterPath &path, c { } +extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); +extern void qt_add_texcoords_to_array(qreal x1, qreal y1, qreal x2, qreal y2, q_vertexType *array); + void QGLTrapezoidMaskGenerator::drawMask(const QRect &rect) { #ifdef QT_OPENGL_ES @@ -3445,8 +3233,7 @@ QVector<QGLTrapezoid> QGLRectMaskGenerator::generateTrapezoids() // manhattan distance (no rotation) qreal width = qAbs(delta.x()) + qAbs(delta.y()); - Q_ASSERT(qFuzzyCompare(delta.x() + 1, static_cast<qreal>(1)) - || qFuzzyCompare(delta.y() + 1, static_cast<qreal>(1))); + Q_ASSERT(qFuzzyIsNull(delta.x()) || qFuzzyIsNull(delta.y())); tessellator.tessellateRect(first, last, width); } else { @@ -5783,9 +5570,11 @@ void QOpenGLPaintEnginePrivate::ensureDrawableTexture() QPixmapFilter *QOpenGLPaintEngine::createPixmapFilter(int type) const { +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) if (QGLContext::currentContext()) return QGLContext::currentContext()->d_func()->createPixmapFilter(type); else +#endif return 0; } diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index 5d668cd..85bcda5 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qpixmap.h" +#include "qglframebufferobject.h" #include <private/qpaintengine_raster_p.h> @@ -48,6 +49,8 @@ #include <private/qgl_p.h> #include <private/qdrawhelper_p.h> +#include <private/qpaintengineex_opengl2_p.h> + QT_BEGIN_NAMESPACE extern QGLWidget* qt_gl_share_widget(); @@ -57,12 +60,14 @@ class QGLShareContextScope public: QGLShareContextScope(const QGLContext *ctx) : m_oldContext(0) - , m_ctx(const_cast<QGLContext *>(ctx)) { - const QGLContext *currentContext = QGLContext::currentContext(); + QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); if (currentContext != ctx && !qgl_share_reg()->checkSharing(ctx, currentContext)) { - m_oldContext = const_cast<QGLContext *>(currentContext); + m_oldContext = currentContext; + m_ctx = const_cast<QGLContext *>(ctx); m_ctx->makeCurrent(); + } else { + m_ctx = currentContext; } } @@ -87,58 +92,29 @@ private: QGLContext *m_ctx; }; -void qt_gl_convertFromGLImage(QImage *img) -{ - const int w = img->width(); - const int h = img->height(); - - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - uint *p = (uint*)img->bits(); - uint *end = p + w*h; - - while (p < end) { - uint a = *p << 24; - *p = (*p >> 8) | a; - p++; - } - - *img = img->mirrored(); - } else { - // mirror image - uint *data = (uint *)img->bits(); - - const int mid = h/2; - - for (int y = 0; y < mid; ++y) { - uint *p = data + y * w; - uint *end = p + w; - uint *q = data + (h - y - 1) * w; - - while (p < end) - qSwap(*p++, *q++); - } - } -} - - static int qt_gl_pixmap_serial = 0; QGLPixmapData::QGLPixmapData(PixelType type) : QPixmapData(type, OpenGLClass) , m_width(0) , m_height(0) - , m_texture(0) + , m_renderFbo(0) + , m_textureId(0) + , m_engine(0) + , m_ctx(0) , m_dirty(false) + , m_hasFillColor(false) { setSerialNumber(++qt_gl_pixmap_serial); } QGLPixmapData::~QGLPixmapData() { - if (m_texture && qt_gl_share_widget()) { - QGLShareContextScope ctx(qt_gl_share_widget()->context()); - glDeleteTextures(1, &m_texture); - } + QGLWidget *shareWidget = qt_gl_share_widget(); + if (!shareWidget) + return; + QGLShareContextScope ctx(shareWidget->context()); + glDeleteTextures(1, &m_textureId); } bool QGLPixmapData::isValid() const @@ -148,6 +124,9 @@ bool QGLPixmapData::isValid() const bool QGLPixmapData::isValidContext(const QGLContext *ctx) const { + if (ctx == m_ctx) + return true; + const QGLContext *share_ctx = qt_gl_share_widget()->context(); return ctx == share_ctx || qgl_share_reg()->checkSharing(ctx, share_ctx); } @@ -160,6 +139,12 @@ void QGLPixmapData::resize(int width, int height) m_width = width; m_height = height; + if (m_textureId) { + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + glDeleteTextures(1, &m_textureId); + m_textureId = 0; + } + m_source = QImage(); m_dirty = isValid(); setSerialNumber(++qt_gl_pixmap_serial); @@ -173,28 +158,37 @@ void QGLPixmapData::ensureCreated() const m_dirty = false; QGLShareContextScope ctx(qt_gl_share_widget()->context()); + m_ctx = ctx; const GLenum format = qt_gl_preferredTextureFormat(); - const GLenum target = qt_gl_preferredTextureTarget(); - - if (!m_texture) - glGenTextures(1, &m_texture); - - glBindTexture(target, m_texture); + const GLenum target = GL_TEXTURE_2D; + + if (!m_textureId) { + glGenTextures(1, &m_textureId); + glBindTexture(target, m_textureId); + glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } - if (m_source.isNull()) { - glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0, format, GL_UNSIGNED_BYTE, 0); - } else { + if (!m_source.isNull()) { const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, format); - glBindTexture(target, m_texture); - glTexImage2D(target, 0, GL_RGBA, m_width, m_height, 0, format, - GL_UNSIGNED_BYTE, tx.bits()); + glBindTexture(target, m_textureId); + glTexSubImage2D(target, 0, 0, 0, m_width, m_height, format, + GL_UNSIGNED_BYTE, tx.bits()); - m_source = QImage(); + if (useFramebufferObjects()) + m_source = QImage(); } } +QGLFramebufferObject *QGLPixmapData::fbo() const +{ + return m_renderFbo; +} + void QGLPixmapData::fromImage(const QImage &image, Qt::ImageConversionFlags) { @@ -203,6 +197,32 @@ void QGLPixmapData::fromImage(const QImage &image, resize(image.width(), image.height()); m_source = image; m_dirty = true; + m_hasFillColor = false; + + if (m_textureId) { + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + glDeleteTextures(1, &m_textureId); + m_textureId = 0; + } +} + +bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect) +{ + Q_UNUSED(dx); + Q_UNUSED(dy); + Q_UNUSED(rect); + return false; +} + +void QGLPixmapData::copy(const QPixmapData *data, const QRect &rect) +{ + if (data->classId() != QPixmapData::OpenGLClass) { + QPixmapData::copy(data, rect); + return; + } + + // can be optimized to do a framebuffer blit or similar ... + QPixmapData::copy(data, rect); } void QGLPixmapData::fill(const QColor &color) @@ -210,7 +230,11 @@ void QGLPixmapData::fill(const QColor &color) if (!isValid()) return; - if (!m_source.isNull()) { + if (useFramebufferObjects()) { + m_source = QImage(); + m_hasFillColor = true; + m_fillColor = color; + } else if (!m_source.isNull()) { m_source.fill(PREMUL(color.rgba())); } else { // ## TODO: improve performance here @@ -231,29 +255,114 @@ QImage QGLPixmapData::toImage() const if (!isValid()) return QImage(); - if (!m_source.isNull()) + if (m_renderFbo) { + copyBackFromRenderFbo(true); + } else if (!m_source.isNull()) { return m_source; - else if (m_dirty) - return QImage(m_width, m_height, QImage::Format_ARGB32_Premultiplied); + } else if (m_dirty || m_hasFillColor) { + QImage img(m_width, m_height, QImage::Format_ARGB32_Premultiplied); + if (m_hasFillColor) + img.fill(PREMUL(m_fillColor.rgba())); + return img; + } else { + ensureCreated(); + } + + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha); + glBindTexture(GL_TEXTURE_2D, m_textureId); + return qt_gl_read_texture(QSize(m_width, m_height), true, true); +} + +struct TextureBuffer +{ + QGLFramebufferObject *fbo; + QGL2PaintEngineEx *engine; +}; + +static QVector<TextureBuffer> textureBufferStack; +static int currentTextureBuffer = 0; + +void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const +{ + if (!isValid()) + return; + + m_hasFillColor = false; + + const QGLContext *share_ctx = qt_gl_share_widget()->context(); + QGLShareContextScope ctx(share_ctx); ensureCreated(); - QGLShareContextScope ctx(qt_gl_share_widget()->context()); - QImage img(m_width, m_height, QImage::Format_ARGB32_Premultiplied); + if (!ctx->d_ptr->fbo) + glGenFramebuffers(1, &ctx->d_ptr->fbo); + + glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, m_textureId, 0); + + const int x0 = 0; + const int x1 = m_width; + const int y0 = 0; + const int y1 = m_height; + + glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, m_renderFbo->handle()); + + glDisable(GL_SCISSOR_TEST); + + glBlitFramebufferEXT(x0, y0, x1, y1, + x0, y0, x1, y1, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); + + if (keepCurrentFboBound) + glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); +} + +void QGLPixmapData::swapBuffers() +{ + if (!isValid()) + return; + + copyBackFromRenderFbo(false); + m_renderFbo->release(); + + --currentTextureBuffer; + + m_renderFbo = 0; + m_engine = 0; +} - GLenum format = qt_gl_preferredTextureFormat(); - GLenum target = qt_gl_preferredTextureTarget(); +void QGLPixmapData::makeCurrent() +{ + if (isValid() && m_renderFbo) + m_renderFbo->bind(); +} - glBindTexture(target, m_texture); -#ifndef QT_OPENGL_ES - glGetTexImage(target, 0, format, GL_UNSIGNED_BYTE, img.bits()); -#else - // XXX - cannot download textures this way on OpenGL/ES. -#endif +void QGLPixmapData::doneCurrent() +{ + if (isValid() && m_renderFbo) + m_renderFbo->release(); +} - qt_gl_convertFromGLImage(&img); +static TextureBuffer createTextureBuffer(const QSize &size, QGL2PaintEngineEx *engine = 0) +{ + TextureBuffer buffer; + QGLFramebufferObjectFormat fmt; + fmt.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + fmt.setSamples(4); + + buffer.fbo = new QGLFramebufferObject(size, fmt); + buffer.engine = engine ? engine : new QGL2PaintEngineEx; - return img; + return buffer; +} + +bool QGLPixmapData::useFramebufferObjects() +{ + return QGLFramebufferObject::hasOpenGLFramebufferObjects() + && QGLFramebufferObject::hasOpenGLFramebufferBlit(); } QPaintEngine* QGLPixmapData::paintEngine() const @@ -261,23 +370,72 @@ QPaintEngine* QGLPixmapData::paintEngine() const if (!isValid()) return 0; - m_source = toImage(); - m_dirty = true; + if (m_engine) + return m_engine; + else if (!useFramebufferObjects()) { + m_dirty = true; + if (m_source.size() != size()) + m_source = QImage(size(), QImage::Format_ARGB32_Premultiplied); + if (m_hasFillColor) { + m_source.fill(PREMUL(m_fillColor.rgba())); + m_hasFillColor = false; + } + return m_source.paintEngine(); + } + + extern QGLWidget* qt_gl_share_widget(); - return m_source.paintEngine(); + if (!QGLContext::currentContext()) + qt_gl_share_widget()->makeCurrent(); + QGLShareContextScope ctx(qt_gl_share_widget()->context()); + + if (textureBufferStack.size() <= currentTextureBuffer) { + textureBufferStack << createTextureBuffer(size()); + } else { + QSize sz = textureBufferStack.at(currentTextureBuffer).fbo->size(); + if (sz.width() < m_width || sz.height() < m_height) { + if (sz.width() < m_width) + sz.setWidth(qMax(m_width, qRound(sz.width() * 1.5))); + if (sz.height() < m_height) + sz.setHeight(qMax(m_height, qRound(sz.height() * 1.5))); + delete textureBufferStack.at(currentTextureBuffer).fbo; + textureBufferStack[currentTextureBuffer] = + createTextureBuffer(sz, textureBufferStack.at(currentTextureBuffer).engine); + qDebug() << "Creating new pixmap texture buffer:" << sz; + } + } + + m_renderFbo = textureBufferStack.at(currentTextureBuffer).fbo; + m_engine = textureBufferStack.at(currentTextureBuffer).engine; + + ++currentTextureBuffer; + + return m_engine; } -GLuint QGLPixmapData::bind() const +GLuint QGLPixmapData::bind(bool copyBack) const { - ensureCreated(); - glBindTexture(qt_gl_preferredTextureTarget(), m_texture); - return m_texture; + if (m_renderFbo && copyBack) { + copyBackFromRenderFbo(true); + } else { + if (m_hasFillColor) { + m_dirty = true; + m_source = QImage(m_width, m_height, QImage::Format_ARGB32_Premultiplied); + m_source.fill(PREMUL(m_fillColor.rgba())); + m_hasFillColor = false; + } + ensureCreated(); + } + + GLuint id = m_textureId; + glBindTexture(GL_TEXTURE_2D, id); + return id; } GLuint QGLPixmapData::textureId() const { ensureCreated(); - return m_texture; + return m_textureId; } extern int qt_defaultDpiX(); diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h index 63703fd..1b6b7ae 100644 --- a/src/opengl/qpixmapdata_gl_p.h +++ b/src/opengl/qpixmapdata_gl_p.h @@ -60,6 +60,7 @@ QT_BEGIN_NAMESPACE class QPaintEngine; +class QGLFramebufferObject; class QGLPixmapData : public QPixmapData { @@ -72,19 +73,37 @@ 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); bool hasAlphaChannel() const; QImage toImage() const; QPaintEngine* paintEngine() const; - GLuint bind() const; + GLuint bind(bool copyBack = true) const; GLuint textureId() const; bool isValidContext(const QGLContext *ctx) const; void ensureCreated() const; + bool isUninitialized() const { return m_dirty && m_source.isNull(); } + + bool needsFill() const { return m_hasFillColor; } + QColor fillColor() const { return m_fillColor; } + + QSize size() const { return QSize(m_width, m_height); } + int width() const { return m_width; } + int height() const { return m_height; } + + QGLFramebufferObject *fbo() const; + + void makeCurrent(); + void doneCurrent(); + void swapBuffers(); + protected: int metric(QPaintDevice::PaintDeviceMetric metric) const; @@ -92,12 +111,26 @@ private: QGLPixmapData(const QGLPixmapData &other); QGLPixmapData &operator=(const QGLPixmapData &other); + void copyBackFromRenderFbo(bool keepCurrentFboBound) const; + + static bool useFramebufferObjects(); + int m_width; int m_height; - mutable GLuint m_texture; - mutable bool m_dirty; + mutable QGLFramebufferObject *m_renderFbo; + mutable GLuint m_textureId; + mutable QPaintEngine *m_engine; + mutable QGLContext *m_ctx; mutable QImage m_source; + + // the texture is not in sync with the source image + mutable bool m_dirty; + + // fill has been called and no painting has been done, so the pixmap is + // represented by a single fill color + mutable QColor m_fillColor; + mutable bool m_hasFillColor; }; QT_END_NAMESPACE diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp index b41adf9..a3422b5 100644 --- a/src/opengl/qwindowsurface_gl.cpp +++ b/src/opengl/qwindowsurface_gl.cpp @@ -69,7 +69,8 @@ #include <private/qglpixelbuffer_p.h> #include <private/qgraphicssystem_gl_p.h> -#include <private/qpaintengine_opengl_p.h> + +#include <private/qpaintengineex_opengl2_p.h> #ifndef GLX_ARB_multisample #define GLX_SAMPLE_BUFFERS_ARB 100000 @@ -100,17 +101,19 @@ QGLGraphicsSystem::QGLGraphicsSystem() int i = 0; int spec[16]; spec[i++] = GLX_RGBA; -#if 0 spec[i++] = GLX_DOUBLEBUFFER; - spec[i++] = GLX_DEPTH_SIZE; - spec[i++] = 8; - spec[i++] = GLX_STENCIL_SIZE; - spec[i++] = 8; - spec[i++] = GLX_SAMPLE_BUFFERS_ARB; - spec[i++] = 1; - spec[i++] = GLX_SAMPLES_ARB; - spec[i++] = 4; -#endif + + if (!qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull()) { + spec[i++] = GLX_DEPTH_SIZE; + spec[i++] = 8; + spec[i++] = GLX_STENCIL_SIZE; + spec[i++] = 8; + spec[i++] = GLX_SAMPLE_BUFFERS_ARB; + spec[i++] = 1; + spec[i++] = GLX_SAMPLES_ARB; + spec[i++] = 4; + } + spec[i++] = XNone; XVisualInfo *vi = glXChooseVisual(X11->display, X11->defaultScreen, spec); @@ -187,9 +190,10 @@ public: } void cleanup() { - delete widget; - widget = 0; + QGLWidget *w = widget; cleanedUp = true; + widget = 0; + delete w; } static bool cleanedUp; @@ -227,6 +231,7 @@ struct QGLWindowSurfacePrivate int tried_fbo : 1; int tried_pb : 1; + int destructive_swap_buffers : 1; QGLContext *ctx; @@ -250,6 +255,7 @@ QGLWindowSurface::QGLWindowSurface(QWidget *window) d_ptr->ctx = 0; d_ptr->tried_fbo = false; d_ptr->tried_pb = false; + d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull(); } QGLWindowSurface::~QGLWindowSurface() @@ -287,18 +293,12 @@ void QGLWindowSurface::hijackWindow(QWidget *widget) qDebug() << "hijackWindow() context created for" << widget << d_ptr->contexts.size(); } -#if !defined(QT_OPENGL_ES_2) -Q_GLOBAL_STATIC(QOpenGLPaintEngine, qt_gl_window_surface_paintengine) -#endif +Q_GLOBAL_STATIC(QGL2PaintEngineEx, qt_gl_window_surface_2_engine) /*! \reimp */ QPaintEngine *QGLWindowSurface::paintEngine() const { -#if !defined(QT_OPENGL_ES_2) - return qt_gl_window_surface_paintengine(); -#else - return 0; -#endif + return qt_gl_window_surface_2_engine(); } int QGLWindowSurface::metric(PaintDeviceMetric m) const @@ -313,6 +313,8 @@ QGLContext *QGLWindowSurface::context() const QPaintDevice *QGLWindowSurface::paintDevice() { + updateGeometry(); + if (d_ptr->pb) return d_ptr->pb; @@ -328,6 +330,7 @@ static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, void QGLWindowSurface::beginPaint(const QRegion &) { + updateGeometry(); } void QGLWindowSurface::endPaint(const QRegion &rgn) @@ -343,6 +346,9 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint & QWidget *parent = widget->internalWinId() ? widget : widget->nativeParentWidget(); Q_ASSERT(parent); + if (!geometry().isValid()) + return; + hijackWindow(parent); QRect br = rgn.boundingRect().translated(offset); @@ -350,52 +356,56 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint & QPoint wOffset = qt_qwidget_data(parent)->wrect.topLeft(); QRect rect = br.translated(-offset - wOffset); - const GLenum target = qt_gl_preferredTextureTarget(); + const GLenum target = GL_TEXTURE_2D; if (context()) { context()->makeCurrent(); if (context()->format().doubleBuffer()) { - glBindTexture(target, d_ptr->tex_id); +#if !defined(QT_OPENGL_ES_2) + if (d_ptr->destructive_swap_buffers) { + glBindTexture(target, d_ptr->tex_id); - QVector<QRect> rects = d_ptr->paintedRegion.rects(); - for (int i = 0; i < rects.size(); ++i) { - QRect br = rects.at(i); - if (br.isEmpty()) - continue; + QVector<QRect> rects = d_ptr->paintedRegion.rects(); + for (int i = 0; i < rects.size(); ++i) { + QRect br = rects.at(i); + if (br.isEmpty()) + continue; - const uint bottom = window()->height() - (br.y() + br.height()); - glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height()); - } + const uint bottom = window()->height() - (br.y() + br.height()); + glCopyTexSubImage2D(target, 0, br.x(), bottom, br.x(), bottom, br.width(), br.height()); + } - glBindTexture(target, 0); + glBindTexture(target, 0); - QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion; + QRegion dirtyRegion = QRegion(window()->rect()) - d_ptr->paintedRegion; - if (!dirtyRegion.isEmpty()) { - context()->makeCurrent(); + if (!dirtyRegion.isEmpty()) { + context()->makeCurrent(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); #ifndef QT_OPENGL_ES - glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999); + glOrtho(0, window()->width(), window()->height(), 0, -999999, 999999); #else - glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999); + glOrthof(0, window()->width(), window()->height(), 0, -999999, 999999); #endif - glViewport(0, 0, window()->width(), window()->height()); + glViewport(0, 0, window()->width(), window()->height()); - QVector<QRect> rects = dirtyRegion.rects(); - glColor4f(1, 1, 1, 1); - for (int i = 0; i < rects.size(); ++i) { - QRect rect = rects.at(i); - if (rect.isEmpty()) - continue; + QVector<QRect> rects = dirtyRegion.rects(); + glColor4f(1, 1, 1, 1); + for (int i = 0; i < rects.size(); ++i) { + QRect rect = rects.at(i); + if (rect.isEmpty()) + continue; - drawTexture(rect, d_ptr->tex_id, window()->size(), rect); + drawTexture(rect, d_ptr->tex_id, window()->size(), rect); + } } +#endif } d_ptr->paintedRegion = QRegion(); @@ -421,48 +431,75 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint & } QSize size = widget->rect().size(); - if (ctx->format().doubleBuffer()) { + if (d_ptr->destructive_swap_buffers && ctx->format().doubleBuffer()) { rect = parent->rect(); br = rect.translated(wOffset); size = parent->size(); } - ctx->makeCurrent(); + glDisable(GL_SCISSOR_TEST); + + if (d_ptr->fbo && QGLExtensions::glExtensions & QGLExtensions::FramebufferBlit) { + const int h = d_ptr->fbo->height(); + + const int x0 = rect.left(); + const int x1 = rect.left() + rect.width(); + const int y0 = h - (rect.top() + rect.height()); + const int y1 = h - rect.top(); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, 0); + + glBlitFramebufferEXT(x0, y0, x1, y1, + x0, y0, x1, y1, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, d_ptr->fbo->handle()); + } +#if !defined(QT_OPENGL_ES_2) + else { + glDisable(GL_DEPTH_TEST); + + if (d_ptr->fbo) { + d_ptr->fbo->release(); + } else { + ctx->makeCurrent(); #ifdef Q_WS_MAC - ctx->updatePaintDevice(); + ctx->updatePaintDevice(); #endif - if (d_ptr->fbo) - d_ptr->fbo->release(); + } - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); #ifndef QT_OPENGL_ES - glOrtho(0, size.width(), size.height(), 0, -999999, 999999); + glOrtho(0, size.width(), size.height(), 0, -999999, 999999); #else - glOrthof(0, size.width(), size.height(), 0, -999999, 999999); + glOrthof(0, size.width(), size.height(), 0, -999999, 999999); #endif - glViewport(0, 0, size.width(), size.height()); + glViewport(0, 0, size.width(), size.height()); - glColor4f(1, 1, 1, 1); - drawTexture(rect, texture, window()->size(), br); + glColor4f(1, 1, 1, 1); + drawTexture(rect, texture, window()->size(), br); + + if (d_ptr->fbo) + d_ptr->fbo->bind(); + } +#endif if (ctx->format().doubleBuffer()) ctx->swapBuffers(); else glFlush(); - - if (d_ptr->fbo) - d_ptr->fbo->bind(); } -void QGLWindowSurface::setGeometry(const QRect &rect) +void QGLWindowSurface::updateGeometry() { - QWindowSurface::setGeometry(rect); + QRect rect = QWindowSurface::geometry(); - const GLenum target = qt_gl_preferredTextureTarget(); + const GLenum target = GL_TEXTURE_2D; if (rect.width() <= 0 || rect.height() <= 0) return; @@ -473,13 +510,51 @@ void QGLWindowSurface::setGeometry(const QRect &rect) d_ptr->size = rect.size(); if (d_ptr->ctx) { - glBindTexture(target, d_ptr->tex_id); - glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glBindTexture(target, 0); + if (d_ptr->destructive_swap_buffers) { + glBindTexture(target, d_ptr->tex_id); + glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + glBindTexture(target, 0); + } return; } - if (d_ptr->pb || !d_ptr->tried_pb) { + if (d_ptr->destructive_swap_buffers + && (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject) +#ifdef QT_OPENGL_ES_2 + && (QGLExtensions::glExtensions & QGLExtensions::FramebufferBlit) +#endif + && (d_ptr->fbo || !d_ptr->tried_fbo)) + { + d_ptr->tried_fbo = true; + hijackWindow(window()); + QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext); + ctx->d_ptr->internal_context = true; + ctx->makeCurrent(); + delete d_ptr->fbo; + + QGLFramebufferObjectFormat format; + format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + format.setInternalFormat(GL_RGBA); + format.setTextureTarget(target); + + if (QGLExtensions::glExtensions & QGLExtensions::FramebufferBlit) + format.setSamples(8); + + d_ptr->fbo = new QGLFramebufferObject(rect.size(), format); + d_ptr->fbo->bind(); + if (d_ptr->fbo->isValid()) { + qDebug() << "Created Window Surface FBO" << rect.size() + << "with samples" << d_ptr->fbo->format().samples(); + return; + } else { + qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back"; + delete d_ptr->fbo; + d_ptr->fbo = 0; + } + } + +#if !defined(QT_OPENGL_ES_2) + if (d_ptr->destructive_swap_buffers && (d_ptr->pb || !d_ptr->tried_pb)) { d_ptr->tried_pb = true; if (d_ptr->pb) { @@ -494,7 +569,7 @@ void QGLWindowSurface::setGeometry(const QRect &rect) qt_gl_share_widget()); if (d_ptr->pb->isValid()) { - qDebug() << "PB Sample buffers:" << d_ptr->pb->format().sampleBuffers(); + qDebug() << "Created Window Surface Pixelbuffer, Sample buffers:" << d_ptr->pb->format().sampleBuffers(); d_ptr->pb->makeCurrent(); glGenTextures(1, &d_ptr->pb_tex_id); @@ -521,44 +596,32 @@ void QGLWindowSurface::setGeometry(const QRect &rect) d_ptr->pb = 0; } } - - if ((QGLExtensions::glExtensions & QGLExtensions::FramebufferObject) && (d_ptr->fbo || !d_ptr->tried_fbo)) { - d_ptr->tried_fbo = true; - hijackWindow(window()); - QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext); - ctx->d_ptr->internal_context = true; - ctx->makeCurrent(); - delete d_ptr->fbo; - d_ptr->fbo = new QGLFramebufferObject(rect.size(), QGLFramebufferObject::CombinedDepthStencil, - GLenum(target), GLenum(GL_RGBA)); - - d_ptr->fbo->bind(); - if (d_ptr->fbo->isValid()) { - return; - } else { - qDebug() << "QGLWindowSurface: Failed to create valid FBO, falling back"; - delete d_ptr->fbo; - d_ptr->fbo = 0; - } - } +#endif // !defined(QT_OPENGL_ES_2) hijackWindow(window()); QGLContext *ctx = reinterpret_cast<QGLContext *>(window()->d_func()->extraData()->glContext); ctx->makeCurrent(); - glGenTextures(1, &d_ptr->tex_id); - glBindTexture(target, d_ptr->tex_id); - glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + if (d_ptr->destructive_swap_buffers) { + glGenTextures(1, &d_ptr->tex_id); + glBindTexture(target, d_ptr->tex_id); + glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glBindTexture(target, 0); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindTexture(target, 0); + } qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;; d_ptr->ctx = ctx; d_ptr->ctx->d_ptr->internal_context = true; } +void QGLWindowSurface::setGeometry(const QRect &rect) +{ + QWindowSurface::setGeometry(rect); +} + bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy) { // this code randomly fails currently for unknown reasons @@ -582,7 +645,7 @@ bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy) return true; #endif - const GLenum target = qt_gl_preferredTextureTarget(); + const GLenum target = GL_TEXTURE_2D; glBindTexture(target, d_ptr->tex_id); glCopyTexImage2D(target, 0, GL_RGBA, br.x(), d_ptr->pb->height() - (br.y() + br.height()), br.width(), br.height(), 0); @@ -595,7 +658,7 @@ bool QGLWindowSurface::scroll(const QRegion &area, int dx, int dy) static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, const QRectF &br) { - const GLenum target = qt_gl_preferredTextureTarget(); + const GLenum target = GL_TEXTURE_2D; QRectF src = br.isEmpty() ? QRectF(QPointF(), texSize) : QRectF(QPointF(br.x(), texSize.height() - br.bottom()), br.size()); @@ -623,6 +686,7 @@ static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, extern void qt_add_rect_to_array(const QRectF &r, q_vertexType *array); // qpaintengine_opengl.cpp qt_add_rect_to_array(rect, vertexArray); +#if !defined(QT_OPENGL_ES_2) glVertexPointer(2, q_vertexTypeEnum, 0, vertexArray); glTexCoordPointer(2, q_vertexTypeEnum, 0, texCoordArray); @@ -634,6 +698,7 @@ static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); +#endif glDisable(target); glBindTexture(target, 0); diff --git a/src/opengl/qwindowsurface_gl_p.h b/src/opengl/qwindowsurface_gl_p.h index 0194378..d47e3e3 100644 --- a/src/opengl/qwindowsurface_gl_p.h +++ b/src/opengl/qwindowsurface_gl_p.h @@ -74,6 +74,7 @@ public: QPaintDevice *paintDevice(); void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); void setGeometry(const QRect &rect); + void updateGeometry(); bool scroll(const QRegion &area, int dx, int dy); void beginPaint(const QRegion ®ion); diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp index b0ffbf1..65efdbc 100644 --- a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp @@ -104,7 +104,7 @@ bool PvrEglScreen::connect(const QString &displaySpec) break; case PVR2D_ARGB8888: d = 32; - setPixelFormat(QImage::Format_ARGB32); + setPixelFormat(QImage::Format_ARGB32_Premultiplied); break; default: pvrQwsDisplayClose(); diff --git a/src/plugins/imageformats/ico/qicohandler.cpp b/src/plugins/imageformats/ico/qicohandler.cpp index 299190b..da5ae15 100644 --- a/src/plugins/imageformats/ico/qicohandler.cpp +++ b/src/plugins/imageformats/ico/qicohandler.cpp @@ -57,38 +57,38 @@ // in an ICO file. typedef struct { - quint8 bWidth; // Width of the image - quint8 bHeight; // Height of the image (times 2) - quint8 bColorCount; // Number of colors in image (0 if >=8bpp) [ not ture ] - quint8 bReserved; // Reserved - quint16 wPlanes; // Color Planes - quint16 wBitCount; // Bits per pixel - quint32 dwBytesInRes; // how many bytes in this resource? - quint32 dwImageOffset; // where in the file is this image + quint8 bWidth; // Width of the image + quint8 bHeight; // Height of the image (times 2) + quint8 bColorCount; // Number of colors in image (0 if >=8bpp) [ not ture ] + quint8 bReserved; // Reserved + quint16 wPlanes; // Color Planes + quint16 wBitCount; // Bits per pixel + quint32 dwBytesInRes; // how many bytes in this resource? + quint32 dwImageOffset; // where in the file is this image } ICONDIRENTRY, *LPICONDIRENTRY; #define ICONDIRENTRY_SIZE 16 typedef struct { - quint16 idReserved; // Reserved - quint16 idType; // resource type (1 for icons) - quint16 idCount; // how many images? - ICONDIRENTRY idEntries[1]; // the entries for each image + quint16 idReserved; // Reserved + quint16 idType; // resource type (1 for icons) + quint16 idCount; // how many images? + ICONDIRENTRY idEntries[1]; // the entries for each image } ICONDIR, *LPICONDIR; #define ICONDIR_SIZE 6 // Exclude the idEntries field -typedef struct { // BMP information header - quint32 biSize; // size of this struct - quint32 biWidth; // pixmap width - quint32 biHeight; // pixmap height - quint16 biPlanes; // should be 1 - quint16 biBitCount; // number of bits per pixel - quint32 biCompression; // compression method - quint32 biSizeImage; // size of image - quint32 biXPelsPerMeter; // horizontal resolution - quint32 biYPelsPerMeter; // vertical resolution - quint32 biClrUsed; // number of colors used - quint32 biClrImportant; // number of important colors +typedef struct { // BMP information header + quint32 biSize; // size of this struct + quint32 biWidth; // pixmap width + quint32 biHeight; // pixmap height (specifies the combined height of the XOR and AND masks) + quint16 biPlanes; // should be 1 + quint16 biBitCount; // number of bits per pixel + quint32 biCompression; // compression method + quint32 biSizeImage; // size of image + quint32 biXPelsPerMeter; // horizontal resolution + quint32 biYPelsPerMeter; // vertical resolution + quint32 biClrUsed; // number of colors used + quint32 biClrImportant; // number of important colors } BMP_INFOHDR ,*LPBMP_INFOHDR; #define BMP_INFOHDR_SIZE 40 @@ -108,7 +108,7 @@ private: bool readHeader(); bool readIconEntry(int index, ICONDIRENTRY * iconEntry); - bool readBMPHeader(ICONDIRENTRY & iconEntry, BMP_INFOHDR * header); + bool readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header); void findColorInfo(QImage & image); void readColorTable(QImage & image); @@ -254,7 +254,7 @@ ICOReader::ICOReader(QIODevice * iodevice) int ICOReader::count() { if (readHeader()) - return iconDir.idCount; + return iconDir.idCount; return 0; } @@ -268,17 +268,17 @@ bool ICOReader::canRead(QIODevice *iodev) if (readIconDir(iodev, &ikonDir)) { qint64 readBytes = ICONDIR_SIZE; if (readIconDirEntry(iodev, &ikonDir.idEntries[0])) { - readBytes += ICONDIRENTRY_SIZE; - // ICO format does not have a magic identifier, so we read 6 different values, which will hopefully be enough to identify the file. - if ( ikonDir.idReserved == 0 - && ikonDir.idType == 1 - && ikonDir.idEntries[0].bReserved == 0 - && ikonDir.idEntries[0].wPlanes <= 1 - && ikonDir.idEntries[0].wBitCount <= 32 // Bits per pixel - && ikonDir.idEntries[0].dwBytesInRes >= 40 // Must be over 40, since sizeof (infoheader) == 40 - ) { - isProbablyICO = true; - } + readBytes += ICONDIRENTRY_SIZE; + // ICO format does not have a magic identifier, so we read 6 different values, which will hopefully be enough to identify the file. + if ( ikonDir.idReserved == 0 + && ikonDir.idType == 1 + && ikonDir.idEntries[0].bReserved == 0 + && ikonDir.idEntries[0].wPlanes <= 1 + && ikonDir.idEntries[0].wBitCount <= 32 // Bits per pixel + && ikonDir.idEntries[0].dwBytesInRes >= 40 // Must be over 40, since sizeof (infoheader) == 40 + ) { + isProbablyICO = true; + } if (iodev->isSequential()) { // Our structs might be padded due to alignment, so we need to fetch each member before we ungetChar() ! @@ -323,8 +323,7 @@ bool ICOReader::canRead(QIODevice *iodev) iodev->ungetChar((tmp >> 8) & 0xff); iodev->ungetChar(tmp & 0xff); } - - } + } if (!iodev->isSequential()) iodev->seek(oldPos); } @@ -334,21 +333,21 @@ bool ICOReader::canRead(QIODevice *iodev) bool ICOReader::readHeader() { if (iod && !headerRead) { - startpos = iod->pos(); - if (readIconDir(iod, &iconDir)) { - if (iconDir.idReserved == 0 || iconDir.idType == 1) - headerRead = true; - } + startpos = iod->pos(); + if (readIconDir(iod, &iconDir)) { + if (iconDir.idReserved == 0 || iconDir.idType == 1) + headerRead = true; + } } return headerRead; } -bool ICOReader::readIconEntry(int index, ICONDIRENTRY * iconEntry) +bool ICOReader::readIconEntry(int index, ICONDIRENTRY *iconEntry) { if (iod) { - if (iod->seek(startpos + ICONDIR_SIZE + (index * ICONDIRENTRY_SIZE))) { - return readIconDirEntry(iod, iconEntry); + if (iod->seek(startpos + ICONDIR_SIZE + (index * ICONDIRENTRY_SIZE))) { + return readIconDirEntry(iod, iconEntry); } } return false; @@ -356,49 +355,24 @@ bool ICOReader::readIconEntry(int index, ICONDIRENTRY * iconEntry) -bool ICOReader::readBMPHeader(ICONDIRENTRY & iconEntry, BMP_INFOHDR * header) +bool ICOReader::readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header) { - memset(&icoAttrib, 0, sizeof(IcoAttrib)); if (iod) { - if (iod->seek(startpos + iconEntry.dwImageOffset)) { - if (readBMPInfoHeader(iod, header)) { - - icoAttrib.nbits = header->biBitCount ? header->biBitCount : iconEntry.wBitCount; - icoAttrib.h = header->biHeight / 2; // this height is always double the iconEntry height (for the mask) - icoAttrib.w = header->biWidth; - - switch (icoAttrib.nbits) { - case 32: - case 24: - case 16: - icoAttrib.depth = 32; - break; - case 8: - case 4: - icoAttrib.depth = 8; - break; - default: - icoAttrib.depth = 1; - } - - if ( icoAttrib.depth == 32 ) // there's no colormap - icoAttrib.ncolors = 0; - else // # colors used - icoAttrib.ncolors = header->biClrUsed ? header->biClrUsed : 1 << icoAttrib.nbits; - //qDebug() << "Bits:" << icoAttrib.nbits << "Depth:" << icoAttrib.depth << "Ncols:" << icoAttrib.ncolors; - return TRUE; - } - } + if (iod->seek(startpos + imageOffset)) { + if (readBMPInfoHeader(iod, header)) { + return TRUE; + } + } } return FALSE; } void ICOReader::findColorInfo(QImage & image) { - if (icoAttrib.ncolors > 0) { // set color table - readColorTable(image); + if (icoAttrib.ncolors > 0) { // set color table + readColorTable(image); } else if (icoAttrib.nbits == 16) { // don't support RGB values for 15/16 bpp - image = QImage(); + image = QImage(); } } @@ -406,29 +380,29 @@ void ICOReader::readColorTable(QImage & image) { if (iod) { image.setNumColors(icoAttrib.ncolors); - uchar rgb[4]; - for (int i=0; i<icoAttrib.ncolors; i++) { - if (iod->read((char*)rgb, 4) != 4) { - image = QImage(); - break; - } - image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0])); - } + uchar rgb[4]; + for (int i=0; i<icoAttrib.ncolors; i++) { + if (iod->read((char*)rgb, 4) != 4) { + image = QImage(); + break; + } + image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0])); + } } else { - image = QImage(); + image = QImage(); } } void ICOReader::readBMP(QImage & image) { - if (icoAttrib.nbits == 1) { // 1 bit BMP image - read1BitBMP(image); - } else if (icoAttrib.nbits == 4) { // 4 bit BMP image - read4BitBMP(image); + if (icoAttrib.nbits == 1) { // 1 bit BMP image + read1BitBMP(image); + } else if (icoAttrib.nbits == 4) { // 4 bit BMP image + read4BitBMP(image); } else if (icoAttrib.nbits == 8) { - read8BitBMP(image); + read8BitBMP(image); } else if (icoAttrib.nbits == 16 || icoAttrib.nbits == 24 || icoAttrib.nbits == 32 ) { // 16,24,32 bit BMP image - read16_24_32BMP(image); + read16_24_32BMP(image); } } @@ -442,17 +416,17 @@ void ICOReader::read1BitBMP(QImage & image) { if (iod) { - int h = image.height(); - int bpl = image.bytesPerLine(); + int h = image.height(); + int bpl = image.bytesPerLine(); - while (--h >= 0) { - if (iod->read((char*)image.scanLine(h),bpl) != bpl) { - image = QImage(); - break; - } - } + while (--h >= 0) { + if (iod->read((char*)image.scanLine(h),bpl) != bpl) { + image = QImage(); + break; + } + } } else { - image = QImage(); + image = QImage(); } } @@ -460,30 +434,30 @@ void ICOReader::read4BitBMP(QImage & image) { if (iod) { - int h = icoAttrib.h; - int buflen = ((icoAttrib.w+7)/8)*4; - uchar *buf = new uchar[buflen]; - Q_CHECK_PTR(buf); - - while (--h >= 0) { - if (iod->read((char*)buf,buflen) != buflen) { - image = QImage(); - break; - } - register uchar *p = image.scanLine(h); - uchar *b = buf; - for (int i=0; i<icoAttrib.w/2; i++) { // convert nibbles to bytes - *p++ = *b >> 4; - *p++ = *b++ & 0x0f; - } - if (icoAttrib.w & 1) // the last nibble - *p = *b >> 4; - } - - delete [] buf; + int h = icoAttrib.h; + int buflen = ((icoAttrib.w+7)/8)*4; + uchar *buf = new uchar[buflen]; + Q_CHECK_PTR(buf); + + while (--h >= 0) { + if (iod->read((char*)buf,buflen) != buflen) { + image = QImage(); + break; + } + register uchar *p = image.scanLine(h); + uchar *b = buf; + for (int i=0; i<icoAttrib.w/2; i++) { // convert nibbles to bytes + *p++ = *b >> 4; + *p++ = *b++ & 0x0f; + } + if (icoAttrib.w & 1) // the last nibble + *p = *b >> 4; + } + + delete [] buf; } else { - image = QImage(); + image = QImage(); } } @@ -491,52 +465,51 @@ void ICOReader::read8BitBMP(QImage & image) { if (iod) { - int h = icoAttrib.h; - int bpl = image.bytesPerLine(); + int h = icoAttrib.h; + int bpl = image.bytesPerLine(); - while (--h >= 0) { - if (iod->read((char *)image.scanLine(h), bpl) != bpl) { - image = QImage(); - break; - } - } + while (--h >= 0) { + if (iod->read((char *)image.scanLine(h), bpl) != bpl) { + image = QImage(); + break; + } + } } else { - image = QImage(); + image = QImage(); } } void ICOReader::read16_24_32BMP(QImage & image) { if (iod) { - - int h = icoAttrib.h; - register QRgb *p; - QRgb *end; - uchar *buf = new uchar[image.bytesPerLine()]; - int bpl = ((icoAttrib.w*icoAttrib.nbits+31)/32)*4; - uchar *b; - - while (--h >= 0) { - p = (QRgb *)image.scanLine(h); - end = p + icoAttrib.w; - if (iod->read((char *)buf, bpl) != bpl) { - image = QImage(); - break; - } - b = buf; - while (p < end) { + int h = icoAttrib.h; + register QRgb *p; + QRgb *end; + uchar *buf = new uchar[image.bytesPerLine()]; + int bpl = ((icoAttrib.w*icoAttrib.nbits+31)/32)*4; + uchar *b; + + while (--h >= 0) { + p = (QRgb *)image.scanLine(h); + end = p + icoAttrib.w; + if (iod->read((char *)buf, bpl) != bpl) { + image = QImage(); + break; + } + b = buf; + while (p < end) { if (icoAttrib.nbits == 24) *p++ = qRgb(*(b+2), *(b+1), *b); else if (icoAttrib.nbits == 32) *p++ = qRgba(*(b+2), *(b+1), *b, *(b+3)); - b += icoAttrib.nbits/8; - } - } + b += icoAttrib.nbits/8; + } + } - delete[] buf; + delete[] buf; } else { - image = QImage(); + image = QImage(); } } @@ -550,7 +523,28 @@ QImage ICOReader::iconAt(int index) if (readIconEntry(index, &iconEntry)) { BMP_INFOHDR header; - if (readBMPHeader(iconEntry, &header)) { + if (readBMPHeader(iconEntry.dwImageOffset, &header)) { + icoAttrib.nbits = header.biBitCount ? header.biBitCount : iconEntry.wBitCount; + + switch (icoAttrib.nbits) { + case 32: + case 24: + case 16: + icoAttrib.depth = 32; + break; + case 8: + case 4: + icoAttrib.depth = 8; + break; + default: + icoAttrib.depth = 1; + } + if (icoAttrib.depth == 32) // there's no colormap + icoAttrib.ncolors = 0; + else // # colors used + icoAttrib.ncolors = header.biClrUsed ? header.biClrUsed : 1 << icoAttrib.nbits; + icoAttrib.w = iconEntry.bWidth; + icoAttrib.h = iconEntry.bHeight; QImage::Format format = QImage::Format_ARGB32; if (icoAttrib.nbits == 24) @@ -605,8 +599,8 @@ QList<QImage> ICOReader::read(QIODevice * device) QList<QImage> images; ICOReader reader(device); - for (int i=0; i<reader.count(); i++) - images += reader.iconAt(i); + for (int i = 0; i < reader.count(); i++) + images += reader.iconAt(i); return images; } @@ -659,8 +653,8 @@ bool ICOReader::write(QIODevice * device, const QList<QImage> & images) } maskImage = maskImage.convertToFormat(QImage::Format_Mono); - int nbits = 32; - int bpl_bmp = ((image.width()*nbits+31)/32)*4; + int nbits = 32; + int bpl_bmp = ((image.width()*nbits+31)/32)*4; entries[i].bColorCount = 0; entries[i].bReserved = 0; @@ -670,7 +664,7 @@ bool ICOReader::write(QIODevice * device, const QList<QImage> & images) entries[i].dwBytesInRes = BMP_INFOHDR_SIZE + (bpl_bmp * image.height()) + (maskImage.bytesPerLine() * maskImage.height()); entries[i].wPlanes = 1; - if (i==0) + if (i == 0) entries[i].dwImageOffset = origOffset + ICONDIR_SIZE + (id.idCount * ICONDIRENTRY_SIZE); else @@ -695,7 +689,7 @@ bool ICOReader::write(QIODevice * device, const QList<QImage> & images) uchar *b; memset( buf, 0, bpl_bmp ); int y; - for (y=image.height()-1; y>=0; y--) { // write the image bits + for (y = image.height() - 1; y >= 0; y--) { // write the image bits // 32 bits QRgb *p = (QRgb *)image.scanLine(y); QRgb *end = p + image.width(); @@ -717,13 +711,10 @@ bool ICOReader::write(QIODevice * device, const QList<QImage> & images) maskImage.invertPixels(); // seems as though it needs this // NOTE! !! The mask is only flipped vertically - not horizontally !! - for (y=maskImage.height()-1; y>=0; y--) + for (y = maskImage.height() - 1; y >= 0; y--) buffer.write((char*)maskImage.scanLine(y), maskImage.bytesPerLine()); - } - - if (writeIconDir(device, id)) { int i; bool bOK = true; @@ -731,7 +722,7 @@ bool ICOReader::write(QIODevice * device, const QList<QImage> & images) bOK = writeIconDirEntry(device, entries[i]); } if (bOK) { - for (i=0; i<id.idCount && bOK; i++) { + for (i = 0; i < id.idCount && bOK; i++) { bOK = writeBMPInfoHeader(device, bmpHeaders[i]); bOK &= (device->write(imageData[i]) == (int) imageData[i].size()); } diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp index 518e6d1..77dfeb3 100644 --- a/src/plugins/imageformats/tiff/qtiffhandler.cpp +++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp @@ -168,7 +168,7 @@ bool QTiffHandler::read(QImage *image) break; default: // do nothing as defaults have already - // been set within the QImage class + // been set within the QImage class break; } for (uint32 y=0; y<height; ++y) @@ -218,6 +218,24 @@ bool QTiffHandler::write(const QImage &image) return false; } + // set the resolution + bool resolutionSet = false; + const int dotPerMeterX = image.dotsPerMeterX(); + const int dotPerMeterY = image.dotsPerMeterY(); + if ((dotPerMeterX % 100) == 0 + && (dotPerMeterY % 100) == 0) { + resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER) + && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, dotPerMeterX/100.0) + && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, dotPerMeterY/100.0); + } else { + resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH) + && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, static_cast<float>(image.logicalDpiX())) + && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, static_cast<float>(image.logicalDpiY())); + } + if (!resolutionSet) { + TIFFClose(tiff); + return false; + } // try to do the ARGB32 conversion in chunks no greater than 16 MB int chunks = (width * height * 4 / (1024 * 1024 * 16)) + 1; int chunkHeight = qMax(height / chunks, 1); 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/linuxiskbddriverplugin.cpp b/src/plugins/kbddrivers/linuxis/linuxiskbddriverplugin.cpp deleted file mode 100644 index 79cd298..0000000 --- a/src/plugins/kbddrivers/linuxis/linuxiskbddriverplugin.cpp +++ /dev/null @@ -1,87 +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 "linuxiskbddriverplugin.h" -#include "linuxiskbdhandler.h" - -#include <qdebug.h> -#if 1 -#define qLog(x) qDebug() -#else -#define qLog(x) while (0) qDebug() -#endif - -LinuxInputSubsystemKbdDriverPlugin::LinuxInputSubsystemKbdDriverPlugin( QObject *parent ) - : QKbdDriverPlugin( parent ) -{ -} - -LinuxInputSubsystemKbdDriverPlugin::~LinuxInputSubsystemKbdDriverPlugin() -{ -} - -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; -} - -QWSKeyboardHandler* LinuxInputSubsystemKbdDriverPlugin::create( const QString &driver) -{ - if( driver.toLower() == "linuxis" || driver.toLower() == "linuxiskbdhandler" ) { - qLog(Input) << "Before call LinuxInputSubsystemKbdHandler()"; - return new LinuxInputSubsystemKbdHandler(); - } - return 0; -} - -QStringList LinuxInputSubsystemKbdDriverPlugin::keys() const -{ - return QStringList() << "linuxis" << "linuxiskbdhandler"; -} - -Q_EXPORT_PLUGIN2(qwslinuxiskbdhandler, LinuxInputSubsystemKbdDriverPlugin) 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 f7c8a95..0ab04e6 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.2 + VERSION=4.6.0 } else { VERSION=$${QT_MAJOR_VERSION}.$${QT_MINOR_VERSION}.$${QT_PATCH_VERSION} } @@ -67,11 +67,6 @@ mac:!static:contains(QT_CONFIG, qt_framework) { mac { CONFIG += explicitlib - true { #we want to use O2 on Qt itself (Os was used to fix other failures in older GCC) - QMAKE_CFLAGS_RELEASE ~= s,-Os,-O2, - QMAKE_CXXFLAGS_RELEASE ~= s,-Os,-O2, - QMAKE_OBJECTIVE_CFLAGS_RELEASE ~= s,-Os,-O2, - } macx-g++ { QMAKE_CFLAGS += -fconstant-cfstrings QMAKE_CXXFLAGS += -fconstant-cfstrings diff --git a/src/qt3support/dialogs/q3filedialog.cpp b/src/qt3support/dialogs/q3filedialog.cpp index 1ec0cfc..b9f8196 100644 --- a/src/qt3support/dialogs/q3filedialog.cpp +++ b/src/qt3support/dialogs/q3filedialog.cpp @@ -4611,11 +4611,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/qt3support/other/q3dragobject.cpp b/src/qt3support/other/q3dragobject.cpp index fb57220..50b75a4 100644 --- a/src/qt3support/other/q3dragobject.cpp +++ b/src/qt3support/other/q3dragobject.cpp @@ -208,11 +208,6 @@ void Q3DragObject::setPixmap(QPixmap pm, const QPoint& hotspot) Q_D(Q3DragObject); d->pixmap = pm; d->hot = hotspot; -#if 0 - QDragManager *manager = QDragManager::self(); - if (manager && manager->object == d->data) - manager->updatePixmap(); -#endif } /*! diff --git a/src/script/qscriptengine_p.cpp b/src/script/qscriptengine_p.cpp index a2e58de..84a420d 100644 --- a/src/script/qscriptengine_p.cpp +++ b/src/script/qscriptengine_p.cpp @@ -1662,6 +1662,11 @@ bool QScriptEnginePrivate::convert(const QScriptValueImpl &value, return false; } +QScriptEngine::DemarshalFunction QScriptEnginePrivate::demarshalFunction(int type) const +{ + return m_customTypes.value(type).demarshal; +} + QScriptValuePrivate *QScriptEnginePrivate::registerValue(const QScriptValueImpl &value) { if (value.isString()) { diff --git a/src/script/qscriptenginefwd_p.h b/src/script/qscriptenginefwd_p.h index 2ea66c5..855317c 100644 --- a/src/script/qscriptenginefwd_p.h +++ b/src/script/qscriptenginefwd_p.h @@ -350,6 +350,7 @@ public: QScriptValueImpl create(int type, const void *ptr); static bool convert(const QScriptValueImpl &value, int type, void *ptr, QScriptEnginePrivate *eng); + QScriptEngine::DemarshalFunction demarshalFunction(int type) const; QScriptValueImpl arrayFromStringList(const QStringList &lst); static QStringList stringListFromArray(const QScriptValueImpl &arr); diff --git a/src/script/qscriptextqobject.cpp b/src/script/qscriptextqobject.cpp index d18c3da..4522807 100644 --- a/src/script/qscriptextqobject.cpp +++ b/src/script/qscriptextqobject.cpp @@ -425,7 +425,7 @@ static void callQtMethod(QScriptContextPrivate *context, QMetaMethod::MethodType matchDistance += 10; } } - } else if (actual.isNumber()) { + } else if (actual.isNumber() || actual.isString()) { // see if it's an enum value QMetaEnum m; if (argType.isMetaEnum()) { @@ -436,11 +436,21 @@ static void callQtMethod(QScriptContextPrivate *context, QMetaMethod::MethodType m = meta->enumerator(mi); } if (m.isValid()) { - int ival = actual.toInt32(); - if (m.valueToKey(ival) != 0) { - qVariantSetValue(v, ival); - converted = true; - matchDistance += 10; + if (actual.isNumber()) { + int ival = actual.toInt32(); + if (m.valueToKey(ival) != 0) { + qVariantSetValue(v, ival); + converted = true; + matchDistance += 10; + } + } else { + QString sval = actual.toString(); + int ival = m.keyToValue(sval.toLatin1()); + if (ival != -1) { + qVariantSetValue(v, ival); + converted = true; + matchDistance += 10; + } } } } @@ -1809,7 +1819,16 @@ void QScript::QtPropertyFunction::execute(QScriptContextPrivate *context) } } else { // set - QVariant v = variantFromValue(eng_p, prop.userType(), context->argument(0)); + QScriptValueImpl arg = context->argument(0); + QVariant v; + if (prop.isEnumType() && arg.isString() + && !eng_p->demarshalFunction(prop.userType())) { + // give QMetaProperty::write() a chance to convert from + // string to enum value + v = arg.toString(); + } else { + v = variantFromValue(eng_p, prop.userType(), arg); + } QScriptable *scriptable = scriptableFromQObject(qobject); QScriptEngine *oldEngine = 0; diff --git a/src/svg/qgraphicssvgitem.cpp b/src/svg/qgraphicssvgitem.cpp index e17df03..a14636e 100644 --- a/src/svg/qgraphicssvgitem.cpp +++ b/src/svg/qgraphicssvgitem.cpp @@ -186,7 +186,7 @@ static void qt_graphicsItem_highlightSelected( QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option) { const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1)); - if (qFuzzyCompare(qMax(murect.width(), murect.height()) + 1, 1)) + if (qFuzzyIsNull(qMax(murect.width(), murect.height()))) return; const QRectF mbrect = painter->transform().mapRect(item->boundingRect()); diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp index fd4c875..2d351ff 100644 --- a/src/svg/qsvggenerator.cpp +++ b/src/svg/qsvggenerator.cpp @@ -971,7 +971,7 @@ void QSvgPaintEngine::updateState(const QPaintEngineState &state) } if (flags & QPaintEngine::DirtyOpacity) { - if (!qFuzzyCompare(state.opacity(), 1)) + if (!qFuzzyIsNull(state.opacity() - 1)) stream() << "opacity=\""<<state.opacity()<<"\" "; } diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp index 9ff9c26..1cd1f8b 100644 --- a/src/svg/qsvggraphics.cpp +++ b/src/svg/qsvggraphics.cpp @@ -94,7 +94,7 @@ QSvgCircle::QSvgCircle(QSvgNode *parent, const QRectF &rect) QRectF QSvgCircle::bounds() const { qreal sw = strokeWidth(); - if (qFuzzyCompare(sw + 1, 1)) + if (qFuzzyIsNull(sw)) return m_bounds; else { QPainterPath path; @@ -129,7 +129,7 @@ QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect) QRectF QSvgEllipse::bounds() const { qreal sw = strokeWidth(); - if (qFuzzyCompare(sw + 1, 1)) + if (qFuzzyIsNull(sw)) return m_bounds; else { QPainterPath path; @@ -190,7 +190,7 @@ void QSvgPath::draw(QPainter *p, QSvgExtraStates &states) QRectF QSvgPath::bounds() const { qreal sw = strokeWidth(); - if (qFuzzyCompare(sw + 1, 1)) + if (qFuzzyIsNull(sw)) return m_cachedBounds; else { return boundsOnStroke(m_path, sw); @@ -206,7 +206,7 @@ QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly) QRectF QSvgPolygon::bounds() const { qreal sw = strokeWidth(); - if (qFuzzyCompare(sw + 1, 1)) + if (qFuzzyIsNull(sw)) return m_poly.boundingRect(); else { QPainterPath path; @@ -249,7 +249,7 @@ QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry) QRectF QSvgRect::bounds() const { qreal sw = strokeWidth(); - if (qFuzzyCompare(sw + 1, 1)) + if (qFuzzyIsNull(sw)) return m_rect; else { QPainterPath path; @@ -596,7 +596,7 @@ QRectF QSvgUse::transformedBounds(const QTransform &transform) const QRectF QSvgPolyline::bounds() const { qreal sw = strokeWidth(); - if (qFuzzyCompare(sw + 1, 1)) + if (qFuzzyIsNull(sw)) return m_poly.boundingRect(); else { QPainterPath path; @@ -608,7 +608,7 @@ QRectF QSvgPolyline::bounds() const QRectF QSvgArc::bounds() const { qreal sw = strokeWidth(); - if (qFuzzyCompare(sw + 1, 1)) + if (qFuzzyIsNull(sw)) return m_cachedBounds; else { return boundsOnStroke(cubic, sw); @@ -623,7 +623,7 @@ QRectF QSvgImage::bounds() const QRectF QSvgLine::bounds() const { qreal sw = strokeWidth(); - if (qFuzzyCompare(sw + 1, 1)) { + if (qFuzzyIsNull(sw)) { qreal minX = qMin(m_bounds.x1(), m_bounds.x2()); qreal minY = qMin(m_bounds.y1(), m_bounds.y2()); qreal maxX = qMax(m_bounds.x1(), m_bounds.x2()); diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index 6a897e8..c5026b8 100644 --- a/src/svg/qsvghandler.cpp +++ b/src/svg/qsvghandler.cpp @@ -2550,6 +2550,9 @@ static QSvgNode *createImageNode(QSvgNode *parent, return 0; } + if (image.format() == QImage::Format_ARGB32) + image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + QSvgNode *img = new QSvgImage(parent, image, QRect(int(nx), 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/qbenchmark.cpp b/src/testlib/qbenchmark.cpp index 7687fec..894296d 100644 --- a/src/testlib/qbenchmark.cpp +++ b/src/testlib/qbenchmark.cpp @@ -1,4 +1,3 @@ - /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). 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..f22b3d2 --- /dev/null +++ b/src/testlib/qtestbasicstreamer.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 "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 QTestElement *, 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, element->attributes()); + + formatAfterAttributes(element, buf); + outputString(buf); + + if(hasChildren) + outputElements(element->childElements(), true); + + formatEnd(element, buf); + outputString(buf); + + element = element->previousElement(); + } +} + +void QTestBasicStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const +{ + char buf[1024]; + while(attribute){ + formatAttributes(element, 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..527b1d4 --- /dev/null +++ b/src/testlib/qtestbasicstreamer.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 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 QTestElement *element = 0, const QTestElementAttribute *attribute = 0, char *formatted = 0) const; + virtual void outputElements(QTestElement *element, bool isChildElement = false) const; + virtual void outputElementAttributes(const QTestElement *element, QTestElementAttribute *attribute) const; + + private: + const QTestLogger *testLogger; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index b5200dc..041f2db 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -54,6 +54,7 @@ #include <QtCore/qdir.h> #include <QtCore/qprocess.h> #include <QtCore/qdebug.h> +#include <QtCore/qlibraryinfo.h> #include "QtTest/private/qtestlog_p.h" #include "QtTest/private/qtesttable_p.h" @@ -818,8 +819,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" @@ -844,9 +847,8 @@ static void qParseArgs(int argc, char *argv[]) " -iterations n : Sets the number of accumulation iterations.\n" " -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. -// " -chart : Runs the chart generator after the test. No output is printed to the console\n" +#if !defined(QT_NO_PROCESS) || !defined(QT_NO_SETTINGS) + " -chart : Create chart based on the benchmark result.\n" #endif "\n" " -help : This help\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) { @@ -957,7 +963,7 @@ static void qParseArgs(int argc, char *argv[]) } else if (strcmp(argv[i], "-vb") == 0) { QBenchmarkGlobalData::current->verboseOutput = true; -#ifndef QT_NO_PROCESS +#if !defined(QT_NO_PROCESS) || !defined(QT_NO_SETTINGS) } else if (strcmp(argv[i], "-chart") == 0) { QBenchmarkGlobalData::current->createChart = true; QTestLog::setLogMode(QTestLog::XML); @@ -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 @@ -1457,26 +1463,21 @@ int QTest::qExec(QObject *testObject, int argc, char **argv) #endif -#ifndef QT_NO_PROCESS +#if !defined(QT_NO_PROCESS) || !defined(QT_NO_SETTINGS) if (QBenchmarkGlobalData::current->createChart) { - -#define XSTR(s) STR(s) -#define STR(s) #s + QString chartLocation = QLibraryInfo::location(QLibraryInfo::BinariesPath); #ifdef Q_OS_WIN - const char * path = XSTR(QBENCHLIB_BASE) "/tools/generatereport/generatereport.exe"; + chartLocation += QLatin1String("/../tools/qtestlib/chart/release/chart.exe"); #else - const char * path = XSTR(QBENCHLIB_BASE) "/tools/generatereport/generatereport"; + chartLocation += QLatin1String("/../tools/qtestlib/chart/chart"); #endif -#undef XSTR -#undef STR - - if (QFile::exists(QLatin1String(path))) { + if (QFile::exists(chartLocation)) { QProcess p; p.setProcessChannelMode(QProcess::ForwardedChannels); - p.start(QLatin1String(path), QStringList() << QLatin1String("results.xml")); + p.start(chartLocation, QStringList() << QLatin1String("results.xml")); p.waitForFinished(-1); } else { - qWarning("Could not find %s, please make sure it is compiled.", path); + qDebug() << QLatin1String("Could not find the chart tool in ") + chartLocation + QLatin1String(", please make sure it is compiled."); } } #endif diff --git a/src/testlib/qtestcoreelement.h b/src/testlib/qtestcoreelement.h new file mode 100644 index 0000000..4cf8fcb --- /dev/null +++ b/src/testlib/qtestcoreelement.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 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", + "system-err" + }; + + 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..686e157 --- /dev/null +++ b/src/testlib/qtestcorelist.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 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..a417360 --- /dev/null +++ b/src/testlib/qtestelement.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 "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..c1932da --- /dev/null +++ b/src/testlib/qtestelement.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 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..540389b --- /dev/null +++ b/src/testlib/qtestelementattribute.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 "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..261f3f7 --- /dev/null +++ b/src/testlib/qtestelementattribute.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 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, + LET_SystemError = 7 + }; +} + +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..50741de --- /dev/null +++ b/src/testlib/qtestfilelogger.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 "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..892657d --- /dev/null +++ b/src/testlib/qtestfilelogger.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 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..75fec40 --- /dev/null +++ b/src/testlib/qtestlightxmlstreamer.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 "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..382a14a --- /dev/null +++ b/src/testlib/qtestlightxmlstreamer.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 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..46af232 --- /dev/null +++ b/src/testlib/qtestlogger.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 "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), errorLogElement(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; + delete errorLogElement; + errorLogElement = new QTestElement(QTest::LET_SystemError); + 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(); + } + + currentLogElement->addLogElement(errorLogElement); + + 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: + ++failureCounter; + typeBuf = "xpass"; + break; + case QAbstractTestLogger::Pass: + ++passCounter; + typeBuf = "pass"; + break; + case QAbstractTestLogger::XFail: + ++passCounter; + typeBuf = "xfail"; + break; + case QAbstractTestLogger::Fail: + ++failureCounter; + typeBuf = "fail"; + break; + default: + typeBuf = "??????"; + break; + } + + if (type == QAbstractTestLogger::Fail || type == QAbstractTestLogger::XPass + || ((format != TLF_XunitXml) && (type == QAbstractTestLogger::XFail))) { + QTestElement *failureElement = new QTestElement(QTest::LET_Failure); + failureElement->addAttribute(QTest::AI_Result, typeBuf); + if(file) + failureElement->addAttribute(QTest::AI_File, file); + else + failureElement->addAttribute(QTest::AI_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, "xfail")) { + overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail); + } + else if (!strcmp(oldResult, "xpass")) { + 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); + + /* + Since XFAIL does not add a failure to the testlog in xunitxml, add a message, so we still + have some information about the expected failure. + */ + if (format == TLF_XunitXml && type == QAbstractTestLogger::XFail) { + QTestLogger::addMessage(QAbstractTestLogger::Info, description, file, line); + } +} + +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; + + // Also add the message to the system error log (i.e. stderr), if one exists + if (errorLogElement) { + QTestElement *systemErrorElement = new QTestElement(QTest::LET_Error); + systemErrorElement->addAttribute(QTest::AI_Description, message); + errorLogElement->addLogElement(systemErrorElement); + } +} + +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..9807fd2 --- /dev/null +++ b/src/testlib/qtestlogger_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 QTESTLOGGER_P_H +#define QTESTLOGGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <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; + QTestElement *errorLogElement; + 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..5d57bab --- /dev/null +++ b/src/testlib/qtestxmlstreamer.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 "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]]></DataTag>\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..58544a4 --- /dev/null +++ b/src/testlib/qtestxmlstreamer.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 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..a34791c --- /dev/null +++ b/src/testlib/qtestxunitstreamer.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 "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)); + + // Errors are written as CDATA within system-err, comments elsewhere + if (element->elementType() == QTest::LET_Error) { + if (element->parentElement()->elementType() == QTest::LET_SystemError) { + QTest::qt_snprintf(formatted, 1024, "<![CDATA["); + } + else { + QTest::qt_snprintf(formatted, 1024, "%s<!--", indent); + } + return; + } + + 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 QTestElement* element, const QTestElementAttribute *attribute, char *formatted) const +{ + if(!attribute || !formatted ) + return; + + QTest::AttributeIndex attrindex = attribute->index(); + + // For errors within system-err, we only want to output `message' + if (element && element->elementType() == QTest::LET_Error + && element->parentElement()->elementType() == QTest::LET_SystemError) { + + if (attrindex != QTest::AI_Description) return; + + QXmlTestLogger::xmlCdata(formatted, attribute->value(), 1024); + return; + } + + 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; + + // Errors are written as CDATA within system-err, comments elsewhere + if (element->elementType() == QTest::LET_Error) { + if (element->parentElement()->elementType() == QTest::LET_SystemError) { + QTest::qt_snprintf(formatted, 1024, "]]>\n"); + } + else { + QTest::qt_snprintf(formatted, 1024, " -->\n"); + } + 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, 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..b4b82f0 --- /dev/null +++ b/src/testlib/qtestxunitstreamer.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtTest 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 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 QTestElement *element = 0, 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..0606ced 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,156 @@ 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('&', "&"); + + // not strictly necessary, but allows handling of comments without + // having to explicitly look for `--' + 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/src/xml/dom/qdom.cpp b/src/xml/dom/qdom.cpp index 550abc9..dc6ff92 100644 --- a/src/xml/dom/qdom.cpp +++ b/src/xml/dom/qdom.cpp @@ -2694,7 +2694,7 @@ void QDomNode::save(QTextStream& str, int indent) const If the document contains invalid XML characters or characters that cannot be encoded in the given encoding, the result and behavior is undefined. - \since 4.2 + \since 4.2 */ void QDomNode::save(QTextStream& str, int indent, EncodingPolicy encodingPolicy) const { @@ -4597,9 +4597,9 @@ void QDomElementPrivate::save(QTextStream& s, int depth, int indent) const /* Write out attributes. */ if (!m_attr->map.isEmpty()) { - s << ' '; QHash<QString, QDomNodePrivate *>::const_iterator it = m_attr->map.constBegin(); for (; it != m_attr->map.constEnd(); ++it) { + s << ' '; if (it.value()->namespaceURI.isNull()) { s << it.value()->name << "=\"" << encodeText(it.value()->value, s, true, true) << '\"'; } else { @@ -4622,7 +4622,6 @@ void QDomElementPrivate::save(QTextStream& s, int depth, int indent) const outputtedPrefixes.insert(it.value()->prefix); } } - s << ' '; } } |