diff options
Diffstat (limited to 'src/declarative')
266 files changed, 72009 insertions, 0 deletions
diff --git a/src/declarative/3rdparty/3rdparty.pri b/src/declarative/3rdparty/3rdparty.pri new file mode 100644 index 0000000..5350fcf --- /dev/null +++ b/src/declarative/3rdparty/3rdparty.pri @@ -0,0 +1,2 @@ +HEADERS += \ + 3rdparty/qlistmodelinterface.h\ diff --git a/src/declarative/3rdparty/easing.cpp b/src/declarative/3rdparty/easing.cpp new file mode 100644 index 0000000..50e9e51 --- /dev/null +++ b/src/declarative/3rdparty/easing.cpp @@ -0,0 +1,726 @@ +/* +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. +*/ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 (M_PI / 2) +#endif + + +/** + * Easing equation function for a simple linear tweening, with no easing. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeNone(float t, float b, float c, float d) +{ + return c*t/d + b; +} + +/** + * Easing equation function for a quadratic (t^2) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInQuad(float t, float b, float c, float d) +{ + float t_adj = (float)t / (float)d; + return c*t_adj*t_adj + b; +} + +/** +* Easing equation function for a quadratic (t^2) easing out: decelerating to zero velocity. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeOutQuad(float t, float b, float c, float d) +{ + float t_adj = (float)t / (float)d; + return -c *t_adj*(t_adj-2) + b; +} + +/** +* Easing equation function for a quadratic (t^2) easing in/out: acceleration until halfway, then deceleration. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeInOutQuad(float t, float b, float c, float d) +{ + float t_adj = 2.0f * (float)t / (float)d; + if (t_adj < 1) { + return c/2*t_adj*t_adj + b; + } else { + --t_adj; + return -c/2 * ((t_adj)*(t_adj-2) - 1) + b; + } +} + +/** +* Easing equation function for a quadratic (t^2) easing out/in: deceleration until halfway, then acceleration. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeOutInQuad(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutQuad (t*2, b, c/2, d); + return easeInQuad((t*2)-d, b+c/2, c/2, d); +} + +/** +* Easing equation function for a cubic (t^3) easing in: accelerating from zero velocity. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeInCubic(float t, float b, float c, float d) +{ + float t_adj = (float)t / (float)d; + return c*t_adj*t_adj*t_adj + b; +} + +/** +* Easing equation function for a cubic (t^3) easing out: decelerating from zero velocity. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeOutCubic(float t, float b, float c, float d) +{ + float t_adj = (float)t / (float)(d) - 1.0f; + return c*(t_adj*t_adj*t_adj + 1) + b; +} + +/** +* Easing equation function for a cubic (t^3) easing in/out: acceleration until halfway, then deceleration. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeInOutCubic(float t, float b, float c, float d) +{ + float t_adj = 2.0f * (float)t / (float)(d); + if(t_adj < 1) return c/2*t_adj*t_adj*t_adj + b; + else { + t_adj -= 2; + return c/2*(t_adj*t_adj*t_adj + 2) + b; + } +} + +/** +* Easing equation function for a cubic (t^3) easing out/in: deceleration until halfway, then acceleration. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeOutInCubic(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutCubic (t*2, b, c/2, d); + return easeInCubic((t*2)-d, b+c/2, c/2, d); +} + +/** +* Easing equation function for a quartic (t^4) easing in: accelerating from zero velocity. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeInQuart(float t, float b, float c, float d) +{ + float t_adj = (float)t / (float)d; + return c*t_adj*t_adj*t_adj*t_adj + b; +} + +/** +* Easing equation function for a quartic (t^4) easing out: decelerating from zero velocity. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeOutQuart(float t, float b, float c, float d) +{ + float t_adj = (float)t / (float)d - 1.0f; + return -c * (t_adj*t_adj*t_adj*t_adj - 1) + b; +} + +/** +* Easing equation function for a quartic (t^4) easing in/out: acceleration until halfway, then deceleration. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeInOutQuart(float t, float b, float c, float d) +{ + float t_adj = 2.0f * (float)t / (float)d; + if (t_adj < 1) return c/2*t_adj*t_adj*t_adj*t_adj + b; + else { + t_adj -= 2.0f; + return -c/2 * (t_adj*t_adj*t_adj*t_adj - 2) + b; + } +} + +/** +* Easing equation function for a quartic (t^4) easing out/in: deceleration until halfway, then acceleration. +* +* @param t Current time (in frames or seconds). +* @param b Starting value. +* @param c Change needed in value. +* @param d Expected easing duration (in frames or seconds). +* @return The correct value. +*/ +static float easeOutInQuart(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutQuart (t*2, b, c/2, d); + return easeInQuart((t*2)-d, b+c/2, c/2, d); +} + +/** + * Easing equation function for a quintic (t^5) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInQuint(float t, float b, float c, float d) +{ + float t_adj = (float)t/(float)d; + return c*t_adj*t_adj*t_adj*t_adj*t_adj + b; +} + +/** + * Easing equation function for a quintic (t^5) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutQuint(float t, float b, float c, float d) +{ + float t_adj = (float)t/(float)d - 1.0f; + return c*(t_adj*t_adj*t_adj*t_adj*t_adj + 1) + b; +} + +/** + * Easing equation function for a quintic (t^5) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInOutQuint(float t, float b, float c, float d) +{ + float t_adj = 2.0f * (float)t/(float)d; + if (t_adj < 1) return c/2*t_adj*t_adj*t_adj*t_adj*t_adj + b; + else { + t_adj -= 2.0f; + return c/2*(t_adj*t_adj*t_adj*t_adj*t_adj + 2) + b; + } +} + +/** + * Easing equation function for a quintic (t^5) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutInQuint(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutQuint (t*2, b, c/2, d); + return easeInQuint((t*2)-d, b+c/2, c/2, d); +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInSine(float t, float b, float c, float d) +{ + float t_adj = (float)t/(float)d; + return -c * ::cos(t_adj * M_PI_2) + c + b; +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutSine(float t, float b, float c, float d) +{ + float t_adj = (float)t/(float)d; + return c * ::sin(t_adj * M_PI_2) + b; +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInOutSine(float t, float b, float c, float d) +{ + float t_adj = (float)t/(float)d; + return -c/2 * (::cos(M_PI*t_adj) - 1) + b; +} + +/** + * Easing equation function for a sinusoidal (sin(t)) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutInSine(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutSine (t*2, b, c/2, d); + return easeInSine((t*2)-d, b+c/2, c/2, d); +} + +/** + * Easing equation function for an exponential (2^t) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInExpo(float t, float b, float c, float d) +{ + return (t==0) ? b : c * ::pow(2, 10 * ((float)t/(float)d - 1)) + b - c * 0.001; +} + +/** + * Easing equation function for an exponential (2^t) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutExpo(float t, float b, float c, float d) +{ + return (t==d) ? b+c : c * 1.001 * (-::pow(2, -10 * (float)t/(float)d) + 1) + b; +} + +/** + * Easing equation function for an exponential (2^t) easing in/out: acceleration until halfway, then deceleration. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInOutExpo(float t, float b, float c, float d) +{ + if (t==0) return b; + if (t==d) return b+c; + float t_adj = 2.0f * (float)t/(float)d; + if (t_adj < 1) return c/2 * ::pow(2, 10 * (t_adj - 1)) + b - c * 0.0005; + return c/2 * 1.0005 * (-::pow(2, -10 * (t_adj - 1)) + 2) + b; +} + +/** + * Easing equation function for an exponential (2^t) easing out/in: deceleration until halfway, then acceleration. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutInExpo(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutExpo (t*2, b, c/2, d); + return easeInExpo((t*2)-d, b+c/2, c/2, d); +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing in: accelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInCirc(float t, float b, float c, float d) +{ + float t_adj = (float)t / (float)d; + return -c * (::sqrt(1 - t_adj*t_adj) - 1) + b; +} + +/** + * Easing equation function for a circular (sqrt(1-t^2)) easing out: decelerating from zero velocity. + * + * @param t Current time (in frames or seconds). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutCirc(float t, float b, float c, float d) +{ + float t_adj = (float)t / (float)d - 1.0f; + return c * ::sqrt(1 - t_adj * t_adj) + b; +} + +/** + * 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). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInOutCirc(float t, float b, float c, float d) +{ + float t_adj = 2.0f * (float)t / (float)d; + if (t_adj < 1) return -c/2 * (::sqrt(1 - t_adj*t_adj) - 1) + b; + else { + t_adj -= 2.0f; + return c/2 * (::sqrt(1 - t_adj*t_adj) + 1) + b; + } +} + +/** + * 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). + * @param b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutInCirc(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutCirc (t*2, b, c/2, d); + return easeInCirc((t*2)-d, b+c/2, c/2, d); +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static float easeInElastic(float t, float b, float c, float d) +{ + if (t==0) return b; + float t_adj = (float)t / (float)d; + if (t_adj==1) return b+c; + + qreal p = d * 0.3f; + qreal a = c; + qreal s = p / 4.0f; + + t_adj -= 1.0f; + return -(a*::pow(2,10*t_adj) * ::sin( (t_adj*d-s)*(2*M_PI)/p )) + b; +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static float easeOutElastic(float t, float b, float c, float d) +{ + if (t==0) return b; + float t_adj = (float)t / (float)d; + if (t_adj==1) return b+c; + + qreal p = d * 0.3f; + qreal a = c; + qreal s = p / 4; + + return (a*::pow(2,-10*t_adj) * ::sin( (t_adj*d-s)*(2*M_PI)/p ) + c + b); +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static float easeInOutElastic(float t, float b, float c, float d) +{ + if (t==0) return b; + float t_adj = 2.0f * (float)t / (float)d; + if (t_adj==2) return b+c; + + // XXX + qreal p = d * 0.3f * 1.5f; + qreal a = c; + qreal s = p / 4; + + if (t_adj < 1) return -.5*(a*::pow(2,10*(t_adj-1)) * ::sin( ((t_adj-1)*d-s)*(2*M_PI)/p )) + b; + return a*::pow(2,-10*(t_adj-1)) * ::sin( ((t_adj-1)*d-s)*(2*M_PI)/p )*.5 + c + b; +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @param a Amplitude. + * @param p Period. + * @return The correct value. + */ +static float easeOutInElastic(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutElastic (t*2, b, c/2, d); + return easeInElastic((t*2)-d, b+c/2, c/2, d); +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (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 float easeInBack(float t, float b, float c, float d) +{ + // XXX + qreal s = 1.70158; + + float t_adj = (float)t / (float)d; + return c*(t_adj)*t_adj*((s+1)*t_adj - s) + b; +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (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 float easeOutBack(float t, float b, float c, float d) +{ + // XXX + qreal s = 1.70158; + + float t_adj = (float)t / (float)d - 1.0f; + return c*(t_adj*t_adj*((s+1)*t_adj + s) + 1) + b; +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (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 float easeInOutBack(float t, float b, float c, float d) +{ + // XXX + qreal s = 1.70158; + + float t_adj = 2.0f * (float)t / (float)d; + if (t_adj < 1) { + s *= 1.525f; + return c/2*(t_adj*t_adj*((s+1)*t_adj - s)) + b; + } else { + t_adj -= 2; + s *= 1.525f; + return c/2*(t_adj*t_adj*((s+1)*t_adj + s) + 2) + b; + } +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (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 float easeOutInBack(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutBack (t*2, b, c/2, d); + return easeInBack((t*2)-d, b+c/2, c/2, d); +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutBounce(float t, float b, float c, float d) +{ + float t_adj = (float)t / (float)d; + if ((t_adj) < (1/2.75)) { + return c*(7.5625*t_adj*t_adj) + b; + } else if (t_adj < (2/2.75)) { + t_adj -= (1.5f/2.75f); + return c*(7.5625*t_adj*t_adj + .75) + b; + } else if (t_adj < (2.5/2.75)) { + t_adj -= (2.25f/2.75f); + return c*(7.5625*t_adj*t_adj + .9375) + b; + } else { + t_adj -= (2.65f/2.75f); + return c*(7.5625*t_adj*t_adj + .984375) + b; + } +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInBounce(float t, float b, float c, float d) +{ + return c - easeOutBounce (d-t, 0, c, d) + b; +} + + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeInOutBounce(float t, float b, float c, float d) +{ + if (t < d/2) return easeInBounce (t*2, 0, c, d) * .5 + b; + else return easeOutBounce (t*2-d, 0, c, d) * .5 + c*.5 + b; +} + +/** + * 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 b Starting value. + * @param c Change needed in value. + * @param d Expected easing duration (in frames or seconds). + * @return The correct value. + */ +static float easeOutInBounce(float t, float b, float c, float d) +{ + if (t < d/2) return easeOutBounce (t*2, b, c/2, d); + return easeInBounce((t*2)-d, b+c/2, c/2, d); +} + diff --git a/src/declarative/3rdparty/legal.qdoc b/src/declarative/3rdparty/legal.qdoc new file mode 100644 index 0000000..bd0a9b2 --- /dev/null +++ b/src/declarative/3rdparty/legal.qdoc @@ -0,0 +1,35 @@ +/*! +\page legal-easing.html +\title easing +\ingroup qtopia3rdparty +Easing Equations by Robert Penner. +\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/declarative/3rdparty/qlistmodelinterface.h b/src/declarative/3rdparty/qlistmodelinterface.h new file mode 100644 index 0000000..446b89b --- /dev/null +++ b/src/declarative/3rdparty/qlistmodelinterface.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QLISTMODELINTERFACE_H +#define QLISTMODELINTERFACE_H + +#include <QHash> +#include <QVariant> +#include <qfxglobal.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +/*! + \class QListModelInterface + \brief The QListModelInterface class can be subclassed to provide C++ models to QFx Views + + This class is comprised primarily of pure virtual functions which you need to implement in a subclass. You can then use the subclass as a model for a QFx view, such as a QFxListView. +*/ +class Q_DECLARATIVE_EXPORT QListModelInterface : public QObject +{ + Q_OBJECT +public: + QListModelInterface(QObject *parent = 0) : QObject(parent) {} + virtual ~QListModelInterface() {} + + // ### move these into the Qt namespace + enum Roles { + TextRole = Qt::DisplayRole, + IconRole = Qt::DecorationRole + }; + + /*! + Returns the number of data entries in the model. + */ + virtual int count() const = 0; + /*! + Returns the data at the given \a index for the specifed \a roles. + */ + virtual QHash<int,QVariant> data(int index, const QList<int> &roles = (QList<int>())) const = 0; + /*! + Sets the data at the given \a index.\a values is a mapping of QVariant values to roles. + */ + virtual bool setData(int index, const QHash<int,QVariant> &values) { Q_UNUSED(index); Q_UNUSED(values); return false; } + /*! + This convinience function can be used to set the data for one specific role. It internally uses the other setData and does not need to be reimplemented. + */ + inline bool setData(int index, const QVariant &value, int role) + { + QHash<int,QVariant> values; + values.insert(role, value); + return setData(index, values); + } + + /*! + Returns which roles the list provides data for. + */ + virtual QList<int> roles() const = 0; + /*! + Returns a string description of the specified \a role. + */ + virtual QString toString(int role) const = 0; + + //void bind(int index, int role, QObject *object, const char *propertyName, bool readOnly = true); + +Q_SIGNALS: + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int from, int to, int count); + void itemsChanged(int index, int count, const QList<int> &roles); + +protected: + QListModelInterface(QObjectPrivate &dd, QObject *parent) : QObject(dd, parent) {} +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif //QTREEMODELINTERFACE_H diff --git a/src/declarative/canvas/canvas.pri b/src/declarative/canvas/canvas.pri new file mode 100644 index 0000000..8abfd99 --- /dev/null +++ b/src/declarative/canvas/canvas.pri @@ -0,0 +1,19 @@ +SOURCES += \ + canvas/qsimplecanvas.cpp \ + canvas/qsimplecanvasitem.cpp \ + canvas/qsimplecanvasfilter.cpp \ + canvas/qsimplecanvasserver.cpp + +HEADERS += \ + canvas/qsimplecanvas.h \ + canvas/qsimplecanvasitem.h \ + canvas/qsimplecanvasfilter.h \ + canvas/qsimplecanvas_p.h \ + canvas/qsimplecanvasitem_p.h \ + canvas/qsimplecanvasfilter_p.h \ + canvas/qsimplecanvasserver_p.h + +contains(QT_CONFIG, opengles2): SOURCES += canvas/qsimplecanvas_opengl.cpp +else:contains(QT_CONFIG, opengles1): SOURCES += canvas/qsimplecanvas_opengl1.cpp +else:SOURCES += canvas/qsimplecanvas_software.cpp +SOURCES += canvas/qsimplecanvas_graphicsview.cpp diff --git a/src/declarative/canvas/qsimplecanvas.cpp b/src/declarative/canvas/qsimplecanvas.cpp new file mode 100644 index 0000000..5eb6c60 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvas.cpp @@ -0,0 +1,981 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qwidget.h" +#include "qmutex.h" +#include "qdebug.h" +#include "qcoreapplication.h" +#include "qsimplecanvasitem.h" +#include "qsimplecanvasitem_p.h" +#include "qsimplecanvas_p.h" +#include "qtimer.h" +#include "qdatetime.h" +#include "qgraphicssceneevent.h" +#if defined(QFX_RENDER_OPENGL) +#include <glheaders.h> +#endif +#include "qboxlayout.h" +#include "qsimplecanvasserver_p.h" +#include "qsimplecanvas.h" + + +QT_BEGIN_NAMESPACE +DEFINE_BOOL_CONFIG_OPTION(fullUpdate, GFX_CANVAS_FULL_UPDATE); +DEFINE_BOOL_CONFIG_OPTION(continuousUpdate, GFX_CANVAS_CONTINUOUS_UPDATE); +DEFINE_BOOL_CONFIG_OPTION(useGraphicsView, QFX_USE_GRAPHICSVIEW); + +template<class T, int s = 60> +class CircularList +{ +public: + CircularList() + : _first(0), _size(0) {} + + void append(const T &t) + { + int entry = (_first + _size) % s; + _array[entry] = t; + if(_size == s) + _first = (_first + 1) % s; + else + _size++; + } + + int size() const + { + return _size; + } + + T &operator[](int idx) + { + Q_ASSERT(idx < _size); + int entry = (_first + idx) % s; + return _array[entry]; + } + + void clear() + { + _first = 0; + _size = 0; + } +private: + int _first; + int _size; + T _array[s]; +}; + + +class QSimpleCanvasRootLayer : public QSimpleCanvasLayer +{ +public: + QSimpleCanvasRootLayer(QSimpleCanvas *); + virtual void addDirty(QSimpleCanvasItem *); + virtual void remDirty(QSimpleCanvasItem *); + +private: + friend class QSimpleCanvasItem; + QSimpleCanvas *_canvas; +}; + +void QSimpleCanvasRootLayer::addDirty(QSimpleCanvasItem *i) +{ + _canvas->addDirty(i); +} + +void QSimpleCanvasRootLayer::remDirty(QSimpleCanvasItem *i) +{ + _canvas->remDirty(i); +} + +void QSimpleCanvasPrivate::clearFocusPanel(QSimpleCanvasItem *panel) +{ + if(q->activeFocusPanel() == panel) { + focusPanels.pop(); + + switchToFocusPanel(q->activeFocusPanel(), panel, Qt::OtherFocusReason); + panel->activePanelOutEvent(); + } else { + for(int ii = 0; ii < focusPanels.count(); ++ii) + if(focusPanels.at(ii) == panel) { + focusPanels.remove(ii); + break; + } + } +} + +void QSimpleCanvasPrivate::switchToFocusPanel(QSimpleCanvasItem *panel, QSimpleCanvasItem *wasPanel, Qt::FocusReason focusReason) +{ + if(panel) + panel->activePanelInEvent(); + + QSimpleCanvasItem *wasFocusRoot = focusPanelData.value(wasPanel); + if(wasFocusRoot) + clearActiveFocusItem(wasFocusRoot, focusReason); + + QSimpleCanvasItem *newFocusRoot = focusPanelData.value(panel); + if(newFocusRoot) + setFocusItem(newFocusRoot, focusReason); +} + +void QSimpleCanvasPrivate::setActiveFocusPanel(QSimpleCanvasItem *panel, Qt::FocusReason focusReason) +{ + if(q->activeFocusPanel() == panel) + return; + + if(panel) { + for(int ii = 0; ii < focusPanels.count(); ++ii) + if(focusPanels.at(ii) == panel) { + focusPanels.remove(ii); + break; + } + } + QSimpleCanvasItem *old = q->activeFocusPanel(); + if(panel) + focusPanels << panel; + switchToFocusPanel(panel, old, focusReason); + if(old) + old->activePanelOutEvent(); +} + +void QSimpleCanvasPrivate::clearActiveFocusItem(QSimpleCanvasItem *item, + Qt::FocusReason focusReason) +{ + if (!item || !item->d_func()) + return; + + if(!item->d_func()->hasActiveFocus) + return; + + item->d_func()->hasActiveFocus = false; + if(item->options() & QSimpleCanvasItem::IsFocusRealm) { + QSimpleCanvasItem *newItem = focusPanelData.value(item); + if(newItem) { + clearActiveFocusItem(newItem, focusReason); + } else { + focusItem = 0; + QFocusEvent event(QEvent::FocusOut, focusReason); + item->focusOutEvent(&event); + } + } else { + focusItem = 0; + QFocusEvent event(QEvent::FocusOut, focusReason); + item->focusOutEvent(&event); + } + + if(item->options() & QSimpleCanvasItem::AcceptsInputMethods){ + if(q->testAttribute(Qt::WA_InputMethodEnabled)) + q->setAttribute(Qt::WA_InputMethodEnabled,false); + } + item->activeFocusChanged(true); +} + +void QSimpleCanvasPrivate::setActiveFocusItem(QSimpleCanvasItem *item, + Qt::FocusReason focusReason) +{ + while(true) { + item->d_func()->setActiveFocus(true); + item->activeFocusChanged(true); + if(item->options() & QSimpleCanvasItem::IsFocusRealm) { + QSimpleCanvasItem *newItem = focusPanelData.value(item); + if(newItem) + item = newItem; + else + break; + } else { + break; + } + } + + if(item->options() & QSimpleCanvasItem::AcceptsInputMethods){ + if(!q->testAttribute(Qt::WA_InputMethodEnabled)) + q->setAttribute(Qt::WA_InputMethodEnabled,true); + } + focusItem = item; + QFocusEvent event(QEvent::FocusIn, focusReason); + focusItem->focusInEvent(&event); +} + +void QSimpleCanvasPrivate::clearFocusItem(QSimpleCanvasItem *item) +{ + // XXX +#if 0 + while(item->focusProxy()) + item = item->focusProxy(); +#endif + + QSimpleCanvasItem *scope = 0; + QSimpleCanvasItem *citem = item; + while(citem && !scope) { + if(citem->options() & QSimpleCanvasItem::IsFocusPanel) + scope = citem; + else if(citem != item && citem->options() & QSimpleCanvasItem::IsFocusRealm) + scope = citem; + citem = citem->parent(); + } + Q_ASSERT(scope); // At the very least we'll find the canvas root + + bool isActive = false; + + if(scope->options() & QSimpleCanvasItem::IsFocusPanel) + isActive = (scope == q->activeFocusPanel()); + else if(scope->options() & QSimpleCanvasItem::IsFocusRealm) + isActive = scope->hasActiveFocus(); + + if(isActive) clearActiveFocusItem(item, Qt::OtherFocusReason); + item->d_func()->setFocus(false); + item->focusChanged(false); + + focusPanelData.insert(scope, 0); + + if(lastFocusItem == item) + lastFocusItem = 0; + if(focusItem == item) + focusItem = 0; + + if(scope->options() & QSimpleCanvasItem::IsFocusRealm && scope->hasActiveFocus()) { + setActiveFocusItem(scope, Qt::OtherFocusReason); + } else { + QSimpleCanvasItem *item = QSimpleCanvasItem::findNextFocus(scope); + if (item) + item->setFocus(true); + } +} + +void QSimpleCanvasPrivate::setFocusItem(QSimpleCanvasItem *item, + Qt::FocusReason focusReason, + bool overwrite) +{ + Q_ASSERT(item); + + // XXX +#if 0 + while(item->focusProxy()) + item = item->focusProxy(); +#endif + + if(item == focusItem) + return; + + QSimpleCanvasItem *scope = 0; + QSimpleCanvasItem *citem = item; + while(citem && !scope) { + if(citem->options() & QSimpleCanvasItem::IsFocusPanel) + scope = citem; + else if(citem != item && citem->options() & QSimpleCanvasItem::IsFocusRealm) + scope = citem; + citem = citem->parent(); + } + Q_ASSERT(scope); // At the very least we'll find the canvas root + + if(!overwrite && focusPanelData.contains(scope)) { + item->d_func()->setFocus(false); + item->focusChanged(false); + return; + } + + QSimpleCanvasItem *oldFocus = focusPanelData.value(scope); + bool isActive = false; + + if(scope->options() & QSimpleCanvasItem::IsFocusPanel) + isActive = (scope == q->activeFocusPanel()); + else if(scope->options() & QSimpleCanvasItem::IsFocusRealm) + isActive = scope->hasActiveFocus(); + + if(oldFocus) { + if(isActive) clearActiveFocusItem(oldFocus, focusReason); + oldFocus->d_func()->setFocus(false); + oldFocus->focusChanged(false); + } + + focusPanelData.insert(scope, item); + + if(isActive) + lastFocusItem = item; + + if (item) { + item->d_func()->setFocus(true); + item->focusChanged(true); + if(isActive) + setActiveFocusItem(item, focusReason); + } +} + + +bool QSimpleCanvas::eventFilter(QObject *obj, QEvent *event) +{ + Q_UNUSED(obj); + switch (event->type()) { + case QEvent::GraphicsSceneMouseMove: { + QGraphicsSceneMouseEvent *me = (QGraphicsSceneMouseEvent*)event; + if (!me->buttons()) + break; + } + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseRelease: { + //same logic as filter() function + for(int ii = 0; ii < d->mouseFilters.count(); ++ii) { + if(d->mouseFilters.at(ii)->mouseFilter((QGraphicsSceneMouseEvent*)event)) + return true; + } + break; + } + default: + break; + } + return false; +} + +void QSimpleCanvasPrivate::installMouseFilter(QSimpleCanvasItem *item) +{ + mouseFilters << item; +} + +void QSimpleCanvasPrivate::removeMouseFilter(QSimpleCanvasItem *item) +{ + mouseFilters.removeAll(item); +} + +bool QSimpleCanvasPrivate::filter(QMouseEvent *e) +{ + if(mouseFilters.isEmpty()) + return false; + + QGraphicsSceneMouseEvent *me = mouseEventToSceneMouseEvent(e, e->pos()); + for(int ii = 0; ii < mouseFilters.count(); ++ii) { + if(mouseFilters.at(ii)->mouseFilter(me)) { + delete me; + return true; + } + } + delete me; + return false; +} + +QGraphicsSceneMouseEvent *QSimpleCanvasPrivate::mouseEventToSceneMouseEvent(QMouseEvent *e, const QPoint &item) +{ + QEvent::Type t; + switch(e->type()) { + default: + case QEvent::MouseButtonPress: + t = QEvent::GraphicsSceneMousePress; + break; + case QEvent::MouseButtonRelease: + t = QEvent::GraphicsSceneMouseRelease; + break; + case QEvent::MouseMove: + t = QEvent::GraphicsSceneMouseMove; + break; + case QEvent::MouseButtonDblClick: + t = QGraphicsSceneEvent::GraphicsSceneMouseDoubleClick; + break; + } + + QGraphicsSceneMouseEvent *me = new QGraphicsSceneMouseEvent(t); + me->setButton(e->button()); + me->setButtons(e->buttons()); + me->setPos(item); + me->setScreenPos(e->pos()); + me->setScenePos(e->pos()); + return me; +} + +bool QSimpleCanvasPrivate::deliverMousePress(QSimpleCanvasItem *base, QMouseEvent *e) +{ + if(base->clipType()) { + QRectF br = base->boundingRect(); + QPointF pos = base->mapFromScene(e->pos()); + if(!br.contains(pos.toPoint())) + return false; + } + + const QList<QSimpleCanvasItem *> &children = base->d_func()->children; + for(int ii = children.count() - 1; ii >= 0; --ii) { + if(children.at(ii)->visible() != 0.) + if(deliverMousePress(children.at(ii), e)) + return true; + } + + if(base->acceptedMouseButtons() & e->button()) { + QRectF br = base->boundingRect(); + QPoint pos = base->mapFromScene(e->pos()).toPoint(); + + if(br.contains(pos)) { + QGraphicsSceneMouseEvent *me = mouseEventToSceneMouseEvent(e, pos); + if (me->type() == QEvent::GraphicsSceneMousePress) + base->mousePressEvent(me); + else + base->mouseDoubleClickEvent(me); + bool isAccepted = me->isAccepted(); + delete me; + if(isAccepted) { + lastMouseItem = base; + return true; + } + } + } + return false; +} + +QSimpleCanvasRootLayer::QSimpleCanvasRootLayer(QSimpleCanvas *c) +: _canvas(c) +{ + QSimpleCanvasItem::d_func()->canvas = c; + setOptions(IsFocusPanel); + update(); +} + + +struct QSimpleCanvasTiming +{ + QSimpleCanvasTiming() + : time(-1), paintTime(-1), timeBetweenFrames(-1) {} + QSimpleCanvasTiming(const QRegion &_r, int _time, int _paintTime, + int _timeBetweenFrames) + : region(_r), time(_time), paintTime(_paintTime), + timeBetweenFrames(_timeBetweenFrames) {} + QSimpleCanvasTiming(const QSimpleCanvasTiming &other) + : region(other.region), time(other.time), paintTime(other.paintTime), + timeBetweenFrames(other.timeBetweenFrames) {} + QSimpleCanvasTiming &operator=(const QSimpleCanvasTiming &other) { + region = other.region; time = other.time; paintTime = other.paintTime; + timeBetweenFrames = other.timeBetweenFrames; return *this; + } + QRegion region; + int time; + int paintTime; + int timeBetweenFrames; +}; + +// XXX +static CircularList<QSimpleCanvasTiming> gfxCanvasTiming; + +void QSimpleCanvasGraphicsView::paintEvent(QPaintEvent *pe) +{ + QRegion r = pe->region(); + int tbf = canvas->frameTimer.restart(); + + canvas->lrpTimer.start(); + QGraphicsView::paintEvent(pe); + canvas->lrpTime = canvas->lrpTimer.elapsed(); + + int frametimer = canvas->frameTimer.elapsed(); + gfxCanvasTiming.append(QSimpleCanvasTiming(r, frametimer, canvas->lrpTime, tbf)); + canvas->lrpTime = 0; + if(canvas->canvasServer) + canvas->canvasServer->addTiming(canvas->lrpTime, frametimer, tbf); +} + +void QSimpleCanvasGraphicsView::focusInEvent(QFocusEvent *) +{ +} + +/*! + \class QSimpleCanvas + \brief The QSimpleCanvas class implements the canvas used by Qt Declarative + */ + +QSimpleCanvas::QSimpleCanvas(CanvasMode mode, QWidget *parent) +: QWidget(parent), d(new QSimpleCanvasPrivate(this)) +{ + d->init(mode); +} + +QSimpleCanvas::QSimpleCanvas(QWidget *parent) +: QWidget(parent), d(new QSimpleCanvasPrivate(this)) +{ + d->init(useGraphicsView()?GraphicsView:SimpleCanvas); +} + +void QSimpleCanvasPrivate::init(QSimpleCanvas::CanvasMode mode) +{ + this->mode = mode; + + if(mode == QSimpleCanvas::SimpleCanvas) + qWarning("QSimpleCanvas: Using simple canvas"); + else + qWarning("QSimpleCanvas: Using GraphicsView canvas"); + + if(fullUpdate()) + qWarning("QSimpleCanvas: Full update enabled"); + if(continuousUpdate()) + qWarning("QSimpleCanvas: Continuous update enabled"); + + QByteArray env = qgetenv("GFX_CANVAS_SERVER_PORT"); + if(!env.isEmpty()){ + int port = env.toInt(); + if(port >= 1024) + canvasServer = new QSimpleCanvasServer(port, q); + } + + root = new QSimpleCanvasRootLayer(q); + root->setActiveFocusPanel(true); + q->setFocusPolicy(Qt::StrongFocus); + + if(mode == QSimpleCanvas::GraphicsView) { + view = new QSimpleCanvasGraphicsView(this); + QHBoxLayout *layout = new QHBoxLayout(q); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + q->setLayout(layout); + layout->addWidget(view); + view->setOptimizationFlags(QGraphicsView::DontSavePainterState); + view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view->setFrameStyle(0); + static_cast<QSimpleCanvasItemPrivate*>(root->d_ptr)->convertToGraphicsItem(); + view->scene()->addItem(static_cast<QSimpleCanvasItemPrivate*>(root->d_ptr)->graphicsItem); + + // These seem to give the best performance + view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + view->scene()->setItemIndexMethod(QGraphicsScene::NoIndex); + view->viewport()->setFocusPolicy(Qt::NoFocus); + } + +#if defined(QFX_RENDER_OPENGL) && defined(Q_WS_X11) + QTimer *t = new QTimer(q); + t->setInterval(200); + QObject::connect(t, SIGNAL(timeout()), &egl, SLOT(updateGL())); + t->start(); +#endif +} + +QSimpleCanvas::~QSimpleCanvas() +{ + delete d->root; + delete d; +} + +void QSimpleCanvasPrivate::paint(QPainter &p) +{ +#if defined(QFX_RENDER_QPAINTER) + if(!isSetup) + root->d_func()->setupPainting(0, q->rect()); + + lrpTimer.start(); + + root->d_func()->paint(p); + + lrpTime = lrpTimer.elapsed(); +#else + Q_UNUSED(p); +#endif +} + +QSimpleCanvas::CanvasMode QSimpleCanvas::canvasMode() const +{ + return d->mode; +} + +QSimpleCanvasItem *QSimpleCanvas::root() +{ + return d->root; +} + +void QSimpleCanvas::keyPressEvent(QKeyEvent *event) +{ + if (d->focusItem) + d->focusItem->keyPressEvent(event); + QWidget::keyPressEvent(event); +} + +void QSimpleCanvas::keyReleaseEvent(QKeyEvent *event) +{ + if (d->focusItem) + d->focusItem->keyReleaseEvent(event); + QWidget::keyReleaseEvent(event); +} + +void QSimpleCanvas::inputMethodEvent(QInputMethodEvent *event) +{ + if(d->focusItem) + d->focusItem->inputMethodEvent(event); + else + QWidget::inputMethodEvent(event); +} + +QVariant QSimpleCanvas::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if(d->focusItem) + return d->focusItem->inputMethodQuery(query); + return QWidget::inputMethodQuery(query); +} + +void QSimpleCanvas::mousePressEvent(QMouseEvent *e) +{ + if(d->isSimpleCanvas() && + (d->filter(e) || d->deliverMousePress(d->root, e))) { + e->accept(); + } else { + QWidget::mousePressEvent(e); + } +} + +void QSimpleCanvas::mouseDoubleClickEvent(QMouseEvent *e) +{ + if(d->isSimpleCanvas() && + (d->filter(e) || d->deliverMousePress(d->root, e))) { + e->accept(); + } else { + QWidget::mouseDoubleClickEvent(e); + } +} + +void QSimpleCanvas::mouseMoveEvent(QMouseEvent *e) +{ + if(d->isSimpleCanvas() && d->filter(e)) { + e->accept(); + } else if(d->isSimpleCanvas() && d->lastMouseItem) { + QPoint p = d->lastMouseItem->mapFromScene(e->pos()).toPoint(); + QGraphicsSceneMouseEvent *me = d->mouseEventToSceneMouseEvent(e, p); + d->lastMouseItem->mouseMoveEvent(me); + e->setAccepted(me->isAccepted()); + delete me; + } else { + QWidget::mouseMoveEvent(e); + } +} + +void QSimpleCanvas::mouseReleaseEvent(QMouseEvent *e) +{ + if(d->isSimpleCanvas() && d->filter(e)) { + e->accept(); + } else if(d->isSimpleCanvas() && d->lastMouseItem) { + QPoint p = d->lastMouseItem->mapFromScene(e->pos()).toPoint(); + QGraphicsSceneMouseEvent *me = d->mouseEventToSceneMouseEvent(e, p); + d->lastMouseItem->mouseReleaseEvent(me); + d->lastMouseItem->mouseUngrabEvent(); + e->setAccepted(me->isAccepted()); + delete me; + d->lastMouseItem = 0; + } else { + QWidget::mouseReleaseEvent(e); + } +} + +void QSimpleCanvas::focusInEvent(QFocusEvent *event) +{ + // XXX +#if 0 + if (d->lastFocusItem && event->reason() == Qt::ActiveWindowFocusReason) { + d->setFocusItem(d->lastFocusItem, event->reason()); + } else { + QSimpleCanvasItem *panel = activeFocusPanel(); + QSimpleCanvasItem *focusItem = 0; + if(panel->isFocusable()) + focusItem = panel; + else + focusItem = QSimpleCanvasItem::findNextFocus(panel); + + if (focusItem) + d->setFocusItem(focusItem, event->reason()); + else + QWidget::focusNextPrevChild(true); + } +#endif + QWidget::focusInEvent(event); +} + +void QSimpleCanvas::focusOutEvent(QFocusEvent *event) +{ + // XXX +#if 0 + if (event->reason() == Qt::ActiveWindowFocusReason) { + d->lastFocusItem = activeFocusPanel(); + d->setActiveFocusPanel(0, Qt::ActiveWindowFocusReason); + } +#endif + QWidget::focusOutEvent(event); +} + +bool QSimpleCanvas::focusNextPrevChild(bool) +{ + // XXX +#if 0 + if (d->focusItem) { + QSimpleCanvasItem *item = next ? QSimpleCanvasItem::findNextFocus(d->focusItem) : QSimpleCanvasItem::findPrevFocus(d->focusItem); + if (item) { + d->setFocusItem(item, + next ? Qt::TabFocusReason : Qt::BacktabFocusReason); + return true; + } + } + + QSimpleCanvasItem *panel = activeFocusPanel(); + QSimpleCanvasItem *item = 0; + if(panel->isFocusable()) + item = panel; + else + item = next ? QSimpleCanvasItem::findNextFocus(panel) : QSimpleCanvasItem::findPrevFocus(panel); + if (item && item != d->focusItem) { + d->setFocusItem(item, + next ? Qt::TabFocusReason : Qt::BacktabFocusReason); + return true; + } + + if (d->focusItem) + d->setActiveFocusPanel(0, next ? Qt::TabFocusReason : Qt::BacktabFocusReason); + +#endif + return false; +} + + +void QSimpleCanvas::showEvent(QShowEvent *e) +{ +#if defined(QFX_RENDER_OPENGL) + d->egl.resize(width(), height()); +#endif + if(d->isGraphicsView()) + d->view->setSceneRect(rect()); + + QWidget::showEvent(e); +} + +void QSimpleCanvas::resizeEvent(QResizeEvent *e) +{ +#if defined(QFX_RENDER_OPENGL) + d->egl.resize(width(), height()); +#endif + if(d->isGraphicsView()) + d->view->setSceneRect(rect()); + QWidget::resizeEvent(e); +} + + +void QSimpleCanvas::remDirty(QSimpleCanvasItem *c) +{ + d->dirtyItems.removeAll(c); +} + +void QSimpleCanvas::queueUpdate() +{ + if(!d->timer) { + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + d->timer = 1; + } +} + +void QSimpleCanvas::addDirty(QSimpleCanvasItem *c) +{ + queueUpdate(); + if(d->isSimpleCanvas()) { + d->oldDirty |= c->d_func()->data()->lastPaintRect; +#if defined(QFX_RENDER_OPENGL) + // Check for filters + QSimpleCanvasItem *fi = c->parent(); + while(fi) { + if(fi->d_func()->data()->dirty) { + break; + } else if(fi->filter()) { + fi->update(); + break; + } + fi = fi->parent(); + } +#endif + d->dirtyItems.append(c); + } +} + +QRect QSimpleCanvasPrivate::dirtyItemClip() const +{ + QRect rv; + if(isSimpleCanvas()) { +#if defined(QFX_RENDER_OPENGL) + QRectF r; + for(int ii = 0; ii < dirtyItems.count(); ++ii) + r |= dirtyItems.at(ii)->d_func()->data()->lastPaintRect; + rv = egl.map(r); +#else + for(int ii = 0; ii < dirtyItems.count(); ++ii) + rv |= dirtyItems.at(ii)->d_func()->data()->lastPaintRect; +#endif + } + return rv; +} + +QRegion QSimpleCanvasPrivate::resetDirty() +{ + if(isSimpleCanvas()) { +#if defined(QFX_RENDER_OPENGL) + QRect r = egl.map(oldDirty) | dirtyItemClip(); +#else + QRect r = oldDirty | dirtyItemClip(); +#endif + if (!r.isEmpty()) + r.adjust(-1,-1,2,2); //make sure we get everything (since we rounded from floats to ints) + for(int ii = 0; ii < dirtyItems.count(); ++ii) + static_cast<QSimpleCanvasItemPrivate*>(dirtyItems.at(ii)->d_ptr)->data()->dirty = false; + dirtyItems.clear(); + oldDirty = QRect(); + + if(fullUpdate()) + return QRegion(); + else + return QRegion(r); + } else { + return QRegion(); + } +} + +QSimpleCanvasItem *QSimpleCanvas::focusItem() const +{ + return d->focusItem; +} + +QSimpleCanvasItem *QSimpleCanvas::activeFocusPanel() const +{ + if(d->focusPanels.isEmpty()) + return 0; + else + return d->focusPanels.top(); +} + +bool QSimpleCanvas::event(QEvent *e) +{ + if(e->type() == QEvent::User && d->isSimpleCanvas()) { + d->timer = 0; + d->isSetup = true; +#if defined(QFX_RENDER_OPENGL1) + unsigned int zero = 0; + d->root->d_func()->setupPainting(0, rect(), &zero); +#else + d->root->d_func()->setupPainting(0, rect()); +#endif + + QRegion r = d->resetDirty(); + + int tbf = d->frameTimer.restart(); + +#if defined(QFX_RENDER_QPAINTER) + if(r.isEmpty() || fullUpdate()) + repaint(); + else + repaint(r); + emit framePainted(); +#else + QRect br = r.boundingRect(); + QRect nr(br.x(), height() - br.y() - br.height(), br.width(), br.height()); + + if(r.isEmpty() || fullUpdate()) + d->egl.updateGL(); + else + d->egl.updateGL(nr); + emit framePainted(); +#endif + d->isSetup = false; + + int frametimer = d->frameTimer.elapsed(); + gfxCanvasTiming.append(QSimpleCanvasTiming(r, frametimer, d->lrpTime, tbf)); + if(d->canvasServer) + d->canvasServer->addTiming(d->lrpTime, frametimer, tbf); + d->lrpTime = 0; + if(continuousUpdate()) + queueUpdate(); + + return true; + } else { + return QWidget::event(e); + } +} + +void QSimpleCanvas::paintEvent(QPaintEvent *) +{ +#if defined(QFX_RENDER_QPAINTER) + if(d->mode == SimpleCanvas) { + QPainter p(this); + d->paint(p); + } +#endif +} + +void QSimpleCanvas::dumpTiming() +{ + for(int ii = 0; ii < gfxCanvasTiming.size(); ++ii) { + const QSimpleCanvasTiming &t = gfxCanvasTiming[ii]; + + qreal repaintFps = 1000. / qreal(t.time); + qreal paintFps = 1000. / qreal(t.paintTime); + qreal tbfFps = 1000. / qreal(t.timeBetweenFrames); + + qWarning() << "repaint():" << t.time << "ms," << repaintFps << "fps. paint():" << t.paintTime << "ms," << paintFps << "fps. timeSinceLastFrame:" << t.timeBetweenFrames << "ms," << tbfFps << "fps."; + qWarning() << t.region; + } + gfxCanvasTiming.clear(); +} + +void QSimpleCanvas::dumpItems() +{ + int items = d->root->d_func()->dump(0); + qWarning() << "Total:" << items; +} + +void QSimpleCanvas::checkState() +{ + if(d->isSimpleCanvas()) { + QSimpleCanvasItemPrivate::FocusStateCheckRDatas r; + if(d->root->d_func()->checkFocusState(0, &r)) + qWarning() << "State OK"; + } +} + +/*! + Returns canvas as an image. Not a fast operation. +*/ +QImage QSimpleCanvas::asImage() const +{ + if(d->isSimpleCanvas()) { +#if defined(QFX_RENDER_QPAINTER) + QImage img(width(),height(),QImage::Format_RGB32); + QPainter p(&img); + const_cast<QSimpleCanvas*>(this)->d->paint(p); + return img; +#elif defined(QFX_RENDER_OPENGL) + return d->egl.grabFrameBuffer(); +#endif + } else { + QImage img(width(),height(),QImage::Format_RGB32); + QPainter p(&img); + d->view->render(&p); + return img; + } +} +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvas.h b/src/declarative/canvas/qsimplecanvas.h new file mode 100644 index 0000000..3da7251 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvas.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QSIMPLECANVAS_H +#define QSIMPLECANVAS_H + +#include <qfxglobal.h> + +#ifdef QFX_RENDER_OPENGL +#include <QtGui/qmatrix4x4.h> +#endif + +#include <QTransform> +#include <QPainter> +#include <QDebug> +#include <QWidget> +#include <QImage> +#include <QKeyEvent> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +namespace QSimpleCanvasConfig +{ + enum ImageType { Opaque, Translucent }; + +#ifdef QFX_RENDER_OPENGL + typedef QMatrix4x4 Matrix; + typedef QImage Image; + + inline Matrix transformToMatrix(const QTransform &) + { + return Matrix(); // XXX + } + inline QTransform matrixToTransform(const Matrix &) + { + return QTransform(); // XXX + } + inline bool needConvert(ImageType, const Image &) + { return false; } + inline Image convert(ImageType, const Image &i) + { return i; } + inline Image create(const QSize &s) + { return QImage(s, QImage::Format_ARGB32); } + inline const Image &toImage(const QImage &i) + { return i; } + +#elif defined(QFX_RENDER_QPAINTER) + typedef QTransform Matrix; + typedef QImage Image; + + inline Matrix transformToMatrix(const QTransform &t) + { return t; } + inline QTransform matrixToTransform(const Matrix &t) + { return t; } + inline bool needConvert(ImageType type, const Image &img) { + QImage::Format f = img.format(); + return !((type == Opaque && f == QImage::Format_RGB16) || + (type == Translucent && f == QImage::Format_ARGB32_Premultiplied)); + } + inline Image convert(ImageType type, const Image &img) { + if(type == Opaque) + return img.convertToFormat(QImage::Format_RGB16); + else + return img.convertToFormat(QImage::Format_ARGB32_Premultiplied); + } + inline Image create(const QSize &s) + { return QImage(s, QImage::Format_ARGB32_Premultiplied); } + inline const Image &toImage(const QImage &i) + { return i; } +#endif +} + +class QSimpleCanvas; +class QSimpleCanvasLayer; + +class QGraphicsSceneMouseEvent; +class GLBasicShaders; +class QSimpleCanvasItem; +class QSimpleCanvasPrivate; +class Q_DECLARATIVE_EXPORT QSimpleCanvas : public QWidget +{ +Q_OBJECT +public: + typedef QSimpleCanvasConfig::Matrix Matrix; + + enum CanvasMode { GraphicsView, SimpleCanvas }; + + QSimpleCanvas(QWidget *parent = 0); + QSimpleCanvas(CanvasMode, QWidget *parent = 0); + virtual ~QSimpleCanvas(); + + CanvasMode canvasMode() const; + + QSimpleCanvasItem *root(); + + // Debugging + void dumpTiming(); + void dumpItems(); + void checkState(); + + QSimpleCanvasItem *focusItem() const; + QSimpleCanvasItem *activeFocusPanel() const; + QImage asImage() const; + +Q_SIGNALS: + void framePainted(); + +protected: + virtual bool event(QEvent *); + virtual void paintEvent(QPaintEvent *); + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseDoubleClickEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual bool focusNextPrevChild(bool next); + virtual bool eventFilter(QObject *obj, QEvent *event); + virtual void inputMethodEvent(QInputMethodEvent *event); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + virtual void showEvent(QShowEvent *); + virtual void resizeEvent(QResizeEvent *); +private: + + friend class QSimpleCanvasRootLayer; + friend class QSimpleCanvasPrivate; + friend class QSimpleCanvasItem; + friend class QSimpleCanvasItemPrivate; + friend class QSimpleCanvasFilter; + friend class QSimpleGraphicsItem; + + void queueUpdate(); + QSimpleCanvasPrivate *d; + void addDirty(QSimpleCanvasItem *); + void remDirty(QSimpleCanvasItem *); +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/canvas/qsimplecanvas_graphicsview.cpp b/src/declarative/canvas/qsimplecanvas_graphicsview.cpp new file mode 100644 index 0000000..0f80128 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvas_graphicsview.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qsimplecanvas.h" +#include "qsimplecanvas_p.h" +#include "qsimplecanvasitem.h" +#include "qsimplecanvasitem_p.h" +#include <QGraphicsItem> +#include <QGraphicsSceneMouseEvent> + + +QT_BEGIN_NAMESPACE +static QHash<QGraphicsScene*,QSimpleCanvas*> sceneMap; + +QSimpleCanvasGraphicsView::QSimpleCanvasGraphicsView(QSimpleCanvasPrivate *parent) +: QGraphicsView(parent->q), canvas(parent) +{ + setScene(&_scene); + sceneMap[&_scene] = parent->q; + setFrameShape(QFrame::NoFrame); + viewport()->setAttribute(Qt::WA_OpaquePaintEvent); +} + +QSimpleCanvasGraphicsView::~QSimpleCanvasGraphicsView() +{ + sceneMap.remove(&_scene); +} + +QSimpleGraphicsItem::QSimpleGraphicsItem(QSimpleCanvasItem *canvasItem) +: /*scene(0),*/ owner(canvasItem) +{ + setAcceptedMouseButtons(Qt::NoButton); +} + +QSimpleGraphicsItem::~QSimpleGraphicsItem() +{ + owner->d_func()->graphicsItem = 0; +} + +void QSimpleGraphicsItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *, QWidget *) +{ + owner->paintContents(*painter); +} + +QRectF QSimpleGraphicsItem::boundingRect() const +{ + return owner->boundingRect(); +} + +void QSimpleGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + owner->mousePressEvent(event); +} + +void QSimpleGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + owner->mouseReleaseEvent(event); +} + +void QSimpleGraphicsItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + owner->mouseDoubleClickEvent(event); +} + +void QSimpleGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + owner->mouseMoveEvent(event); +} + +bool QSimpleGraphicsItem::sceneEvent(QEvent *event) +{ + bool rv = QGraphicsItem::sceneEvent(event); + if(event->type() == QEvent::UngrabMouse) + owner->mouseUngrabEvent(); + return rv; +} + +QVariant QSimpleGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if(change == ItemSceneHasChanged) { + QSimpleCanvasItemPrivate *owner_d = static_cast<QSimpleCanvasItemPrivate*>(owner->d_ptr); + QSimpleCanvas *oldCanvas = owner_d->canvas; + owner_d->canvas = sceneMap[scene()]; + if (owner_d->canvas){ + if (owner->hasFocus()) + owner->canvas()->d->setFocusItem(owner, Qt::OtherFocusReason); + if (owner->options() & QSimpleCanvasItem::IsFocusPanel) { + if (owner_d->wantsActiveFocusPanelPendingCanvas) { + owner_d->hasBeenActiveFocusPanel = true; + owner->canvas()->d->setActiveFocusPanel(owner); + owner_d->wantsActiveFocusPanelPendingCanvas = false; + } + } + } + if(owner->options() & QSimpleCanvasItem::MouseFilter) + owner_d->gvRemoveMouseFilter(); + + if (oldCanvas != owner_d->canvas) + owner->canvasChanged(); + + if(owner->options() & QSimpleCanvasItem::MouseFilter) + owner_d->gvAddMouseFilter(); + } + + return QGraphicsItem::itemChange(change, value); +} + +void QSimpleGraphicsItem::keyPressEvent(QKeyEvent *event) +{ + owner->keyPressEvent(event); + QGraphicsItem::keyPressEvent(event); +} + +void QSimpleGraphicsItem::keyReleaseEvent(QKeyEvent *event) +{ + owner->keyReleaseEvent(event); + QGraphicsItem::keyReleaseEvent(event); +} + +void QSimpleGraphicsItem::focusInEvent(QFocusEvent *) +{ + if (!owner->hasFocus()) + owner->setFocus(true); +} + +void QSimpleCanvasItemPrivate::gvRemoveMouseFilter() +{ + QGraphicsScene *scene = graphicsItem->scene(); + if(!scene) return; + + scene->removeEventFilter(q_ptr); +} + +void QSimpleCanvasItemPrivate::gvAddMouseFilter() +{ + QGraphicsScene *scene = graphicsItem->scene(); + if(!scene) return; + + scene->installEventFilter(q_ptr); +} +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvas_opengl.cpp b/src/declarative/canvas/qsimplecanvas_opengl.cpp new file mode 100644 index 0000000..f508aea --- /dev/null +++ b/src/declarative/canvas/qsimplecanvas_opengl.cpp @@ -0,0 +1,456 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qsimplecanvas.h" +#include "qsimplecanvas_p.h" +#include "qsimplecanvasitem_p.h" +#include "qsimplecanvasfilter_p.h" +#include <glsave.h> +#include <QtOpenGL/qglframebufferobject.h> +#include <gltexture.h> +#include <math.h> + + +QT_BEGIN_NAMESPACE +void CanvasEGLWidget::paintGL() +{ + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + _canvas->paintGL(); +} + +void CanvasEGLWidget::updateGL() +{ + _clip = QRect(); + QGLWidget::updateGL(); +} + +void CanvasEGLWidget::updateGL(const QRect &r) +{ + if(r.isEmpty()) + return; + + _clip = r; + QGLWidget::updateGL(); +} + +void CanvasEGLWidget::resizeGL(int w, int h) +{ + glViewport(0, 0, w, h); +} + +QRect CanvasEGLWidget::map(const QRectF &f) const +{ + return invDefaultTransform.mapRect(f).toAlignedRect(); +} + +void CanvasEGLWidget::resizeEvent(QResizeEvent *e) +{ + defaultTransform.setIdentity(); + defaultTransform.flipCoordinates(); + defaultTransform.translate(-1, -1, 0); + defaultTransform.scale(2. / width(), 2. / height(), 1. / (1024. * 1024.)); + invDefaultTransform = defaultTransform.inverted(); + _canvas->root->d_func()->data()->transformActive = defaultTransform; + _canvas->root->d_func()->data()->transformValid = true; + + QGLWidget::resizeEvent(e); +} + +void CanvasEGLWidget::initializeGL() +{ + glEnable(GL_BLEND); + glEnable(GL_STENCIL_TEST); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, + GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glStencilFunc(GL_EQUAL, 0, 0xFFFFFFFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); +} + +void QSimpleCanvasPrivate::paintGL() +{ + lrpTimer.start(); + + QSimpleCanvasItemPrivate::GLPaintParameters p; + p.sceneRect = QRect(0, 0, q->width(), q->height()); + p.clipRect = p.sceneRect; + p.stencilValue = 0; + p.opacity = 1; + p.forceParamRefresh = false; + if(!isSetup) + root->d_func()->setupPainting(0, QRect()); + root->d_func()->paint(p); + + lrpTime = lrpTimer.elapsed(); +} + +QGLFramebufferObject *QSimpleCanvasPrivate::acquire(int w, int h) +{ + if(w <= 0 || h <= 0) + return 0; + + int size = qMax(w, h); + for(int ii = 0; ii < frameBuffers.count(); ++ii) { + if(frameBuffers.at(ii)->width() >= size) { + QGLFramebufferObject *rv = frameBuffers.at(ii); + frameBuffers.removeAt(ii); + return rv; + } + } + + // Find power of two + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + + QGLFramebufferObject *fbo = new QGLFramebufferObject(size, size); + return fbo; +} + +void QSimpleCanvasPrivate::release(QGLFramebufferObject *buf) +{ + int size = qMax(buf->width(), buf->height()); + for(int ii = 0; ii < frameBuffers.count(); ++ii) { + if(frameBuffers.at(ii)->width() >= size) { + frameBuffers.insert(ii, buf); + return; + } + } + frameBuffers.append(buf); +} + +GLBasicShaders *QSimpleCanvasItemPrivate::basicShaders() const +{ + return canvas->d->basicShaders(); +} + +QSimpleCanvas::Matrix QSimpleCanvasItemPrivate::localTransform() const +{ + Q_Q(const QSimpleCanvasItem); + + QSimpleCanvas::Matrix trans; + trans.translate(q->x(), q->y()); + QPointF to = transformOrigin(); + trans.translate(to.x(), to.y()); + trans.scale(q->scale(), q->scale()); + trans.translate(-to.x(), -to.y()); + if(data()->transformUser) + trans *= *data()->transformUser; + return trans; +} + +void QSimpleCanvasItemPrivate::simplePaintChild(const GLPaintParameters ¶ms, QSimpleCanvasItem *child) +{ + Q_Q(QSimpleCanvasItem); + + GLPaintParameters childParams = params; + if(clip) + ++childParams.stencilValue; + + if(child->d_func()->data()->activeOpacity != 0) { + childParams.boundingRect = child->boundingRect(); + + if(child->filter() && child->filter()->enabled()) { + QSimpleCanvasItem::GLPainter painter(q); + painter.activeTransform = child->d_func()->data()->transformActive; + painter.activeOpacity = child->d_func()->data()->activeOpacity; + painter.sceneClipRect = params.clipRect; + child->filter()->d->doFilterGL(painter, childParams); + } else { + child->d_func()->paint(childParams); + } + } +} + +void QSimpleCanvasItemPrivate::paintChild(const GLPaintParameters ¶ms, + QSimpleCanvasItem *child) +{ + if(params.forceParamRefresh) { + QSimpleCanvas::Matrix t = child->d_func()->data()->transformActive; + qreal o = child->d_func()->data()->activeOpacity; + setupChildState(child); + simplePaintChild(params, child); + child->d_func()->data()->transformActive = t; + child->d_func()->data()->transformValid = true; + child->d_func()->data()->activeOpacity = o; + } else { + simplePaintChild(params, child); + } +} + +void QSimpleCanvasItemPrivate::setupChildState(QSimpleCanvasItem *child) +{ + qreal visible = child->visible(); + child->d_func()->data()->activeOpacity = data()->activeOpacity; + if(visible != 1) + child->d_func()->data()->activeOpacity *= visible; + + if(child->d_func()->data()->activeOpacity != 0) { + // Calculate child's transform + qreal x = child->x(); + qreal y = child->y(); + qreal scale = child->scale(); + QSimpleCanvasItem::Flip flip = child->flip(); + + QSimpleCanvas::Matrix &am = child->d_func()->data()->transformActive; + am = data()->transformActive; + if(x != 0 || y != 0) + am.translate(x, y); + if(scale != 1) { + QPointF to = child->d_func()->transformOrigin(); + if(to.x() != 0. || to.y() != 0.) + am.translate(to.x(), to.y()); + am.scale(scale, scale); + if(to.x() != 0. || to.y() != 0.) + am.translate(-to.x(), -to.y()); + } + if(child->d_func()->data()->transformUser) + am *= *child->d_func()->data()->transformUser; + if(flip) { + QRectF br = child->boundingRect(); + am.translate(br.width() / 2., br.height() / 2); + am.rotate(180, (flip & QSimpleCanvasItem::VerticalFlip)?1:0, (flip & QSimpleCanvasItem::HorizontalFlip)?1:0, 0); + am.translate(-br.width() / 2., -br.height() / 2); + } + child->d_func()->data()->transformValid = true; + } +} + +QRectF QSimpleCanvasItemPrivate::setupPainting(int version, const QRect &bounding) +{ + Q_Q(QSimpleCanvasItem); + + QRectF filteredBoundRect = q->boundingRect(); + if(filter) + filteredBoundRect = filter->itemBoundingRect(filteredBoundRect); + QRectF rv = data()->transformActive.mapRect(filteredBoundRect); + + for(int ii = 0; ii < children.count(); ++ii) { + QSimpleCanvasItem *child = children.at(ii); + setupChildState(child); + + if(child->d_func()->data()->activeOpacity != 0) + rv |= child->d_func()->setupPainting(version, bounding); + } + + data()->lastPaintRect = rv; + return rv; +} + +void QSimpleCanvasItemPrivate::paint(GLPaintParameters &oldParams, QSimpleCanvasFilter::Layer layer) +{ + if(!layer) + return; + + Q_Q(QSimpleCanvasItem); + + GLPaintParameters params = oldParams; + + qreal width = params.boundingRect.width(); + qreal height = params.boundingRect.height(); + + GLfloat vertices[] = { 0, height, + width, height, + 0, 0, + width, 0 }; + + // XXX Handle separate cliping modes + if(clip) { + if(params.stencilValue == 255) + qWarning() + << "OpenGL: Clip recursion greater than 255 not permitted."; + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + glStencilFunc(GL_EQUAL, params.stencilValue, 0xFFFFFFFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + + ConstantColorShader *shader = basicShaders()->constantColor(); + shader->enable(); + shader->setTransform(data()->transformActive); + + shader->setAttributeArray(ConstantColorShader::Vertices, vertices, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(ConstantColorShader::Vertices); + + glStencilFunc(GL_EQUAL, params.stencilValue + 1, 0xFFFFFFFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + // XXX Copied from qsimplecanvas_opengl1 scissor based clipping + QRectF r = data()->transformActive.mapRect(params.boundingRect); + r.translate(1, 1); + float xscale = 0.5 * float(params.sceneRect.width()); + float yscale = 0.5 * float(params.sceneRect.height()); + r.moveTo(r.x() * xscale, r.y() * yscale); + r.setSize(QSizeF(r.width() * xscale, r.height() * yscale)); + int sr_x = int(::floorf(r.x())); + int sr_y = int(::floorf(r.y())); + int sr_width = int(::ceilf(r.right())) - sr_x; + int sr_height = int(::ceilf(r.bottom())) - sr_y; + QRect sr(sr_x, sr_y, sr_width, sr_height); + sr.moveTo(sr.x(), params.sceneRect.height() - sr.y() - sr.height()); + sr &= params.clipRect; + params.clipRect = sr; + } + + zOrderChildren(); + + int upto = 0; + for(upto = 0; upto < children.count(); ++upto) { + QSimpleCanvasItem *c = children.at(upto); + if(c->z() < 0) { + if(layer & QSimpleCanvasFilter::ChildrenUnderItem) + paintChild(params, c); + } else { + break; + } + } + + if(layer & QSimpleCanvasFilter::Item && + q->options() & QSimpleCanvasItem::HasContents) { + QSimpleCanvasItem::GLPainter painter(q); + painter.activeTransform = data()->transformActive; + painter.activeOpacity = data()->activeOpacity; + painter.sceneClipRect = params.clipRect; + + q->paintGLContents(painter); + } + + if(layer & QSimpleCanvasFilter::ChildrenAboveItem) { + for(; upto < children.count(); ++upto) { + QSimpleCanvasItem *c = children.at(upto); + paintChild(params, c); + } + } + + if(clip) { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glStencilFunc(GL_EQUAL, params.stencilValue + 1, 0xFFFFFFFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); + + ConstantColorShader *shader = basicShaders()->constantColor(); + shader->enable(); + shader->setTransform(data()->transformActive); + + shader->setAttributeArray(ConstantColorShader::Vertices, vertices, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(ConstantColorShader::Vertices); + + glStencilFunc(GL_EQUAL, params.stencilValue, 0xFFFFFFFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } +} + +QGLShaderProgram *QSimpleCanvasItem::GLPainter::useTextureShader() +{ + if(activeOpacity == 1.) { + item->basicShaders()->singleTexture()->enable(); + item->basicShaders()->singleTexture()->setTransform(activeTransform); + return item->basicShaders()->singleTexture(); + } else { + item->basicShaders()->singleTextureOpacity()->enable(); + item->basicShaders()->singleTextureOpacity()->setTransform(activeTransform); + item->basicShaders()->singleTextureOpacity()->setOpacity(activeOpacity); + return item->basicShaders()->singleTextureOpacity(); + } + +} + +QGLShaderProgram *QSimpleCanvasItem::GLPainter::useColorShader(const QColor &color) +{ + QColor c = color; + item->basicShaders()->constantColor()->enable(); + if(activeOpacity != 1.) { + c.setAlpha(int(c.alpha() * activeOpacity)); + } + + item->basicShaders()->constantColor()->setColor(c); + item->basicShaders()->constantColor()->setTransform(activeTransform); + + return item->basicShaders()->constantColor(); +} + +void QSimpleCanvasItem::GLPainter::drawImage(const QPointF &point, + const GLTexture &texture) +{ + drawImage(QRectF(point, QSizeF(texture.width(), texture.height())), texture); +} + +void QSimpleCanvasItem::GLPainter::drawImage(const QRectF &rect, + const GLTexture &img) +{ + QGLShaderProgram *shader = useTextureShader(); + + GLfloat vertices[8]; + GLfloat texVertices[8]; + + float widthV = img.width(); + float heightV = img.height(); + + vertices[0] = rect.x(); vertices[1] = rect.y()+heightV; + vertices[2] = rect.x()+widthV; vertices[3] = rect.y()+heightV; + vertices[4] = rect.x(); vertices[5] = rect.y(); + vertices[6] = rect.x()+widthV; vertices[7] = rect.y(); + + texVertices[0] = 0; texVertices[1] = 0; + texVertices[2] = 1; texVertices[3] = 0; + texVertices[4] = 0; texVertices[5] = 1; + texVertices[6] = 1; texVertices[7] = 1; + + shader->setAttributeArray(SingleTextureShader::Vertices, vertices, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, texVertices, 2); + + glBindTexture(GL_TEXTURE_2D, img.texture()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + shader->disableAttributeArray(SingleTextureShader::Vertices); + shader->disableAttributeArray(SingleTextureShader::TextureCoords); +} + +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvas_opengl1.cpp b/src/declarative/canvas/qsimplecanvas_opengl1.cpp new file mode 100644 index 0000000..ad21c77 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvas_opengl1.cpp @@ -0,0 +1,401 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qsimplecanvas.h" +#include "qsimplecanvas_p.h" +#include "qsimplecanvasitem_p.h" +#include "qsimplecanvasfilter_p.h" +#include <gltexture.h> +#include <glsave.h> +#include <math.h> + + +QT_BEGIN_NAMESPACE +void CanvasEGLWidget::updateGL() +{ + _clip = QRect(); + QGLWidget::updateGL(); +} + +void CanvasEGLWidget::updateGL(const QRect &r) +{ + if(r.isEmpty()) + return; + + _clip = r; + QGLWidget::updateGL(); +} + +void CanvasEGLWidget::paintGL() +{ + if(!_clip.isEmpty()) { + glEnable(GL_SCISSOR_TEST); + glScissor(_clip.x(), _clip.y(), _clip.width(), _clip.height()); + } else { + glDisable(GL_SCISSOR_TEST); + } + + glDepthMask(GL_TRUE); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDepthMask(GL_FALSE); + + _canvas->paintGL(); +} + +QRect CanvasEGLWidget::map(const QRectF &f) +{ + return invDefaultTransform.mapRect(f).toAlignedRect(); +} + +void CanvasEGLWidget::resizeGL(int w, int h) +{ + glViewport(0, 0, w, h); +} + +void CanvasEGLWidget::resizeEvent(QResizeEvent *e) +{ + defaultTransform.identity(); + defaultTransform.flipCoordinates(); + defaultTransform.translate(-1, -1, 0); + defaultTransform.scale(2. / width(), 2. / height(), -2. / 65536.); + invDefaultTransform = defaultTransform.inverted(); + _canvas->root->d_func()->transformActive = defaultTransform; + QGLWidget::resizeEvent(e); +} + +void CanvasEGLWidget::initializeGL() +{ + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_GREATER); +#ifdef QT_OPENGL_ES + glClearDepthf(0); +#else + glClearDepth(0); +#endif +} + +void QSimpleCanvasPrivate::paintGL() +{ + lrpTimer.start(); + + QSimpleCanvasItemPrivate::GLPaintParameters p; + p.sceneRect = QRect(0, 0, q->width(), q->height()); + p.clipRect = p.sceneRect; + p.opacity = 1; + p.forceParamRefresh = false; + if(!isSetup) { + unsigned int zero = 0; + root->d_func()->setupPainting(0, QRect(), &zero); + } + root->d_func()->paint(p); + + lrpTime = lrpTimer.elapsed(); +} + +QSimpleCanvas::Matrix QSimpleCanvasItemPrivate::localTransform() const +{ + Q_Q(const QSimpleCanvasItem); + + QSimpleCanvas::Matrix trans; + trans.translate(q->x(), q->y()); + QPointF to = transformOrigin(); + trans.translate(to.x(), to.y()); + trans.scale(q->scale().value(), q->scale().value()); + trans.translate(-to.x(), -to.y()); + trans *= transformUser; + return trans; +} + +void QSimpleCanvasItemPrivate::simplePaintChild(const GLPaintParameters ¶ms, QSimpleCanvasItem *child) +{ + GLPaintParameters childParams = params; + + if(child->d_func()->activeOpacity != 0) { + childParams.boundingRect = child->boundingRect(); + child->d_func()->paint(childParams); + } +} + +void QSimpleCanvasItemPrivate::paintChild(const GLPaintParameters ¶ms, + QSimpleCanvasItem *child) +{ + if(params.forceParamRefresh) { + QSimpleCanvas::Matrix t = child->d_func()->transformActive; + qreal o = child->d_func()->activeOpacity; + setupChildState(child); + simplePaintChild(params, child); + child->d_func()->transformActive = t; + child->d_func()->activeOpacity = o; + } else { + simplePaintChild(params, child); + } +} + + +void QSimpleCanvasItemPrivate::setupChildState(QSimpleCanvasItem *child) +{ + qreal visible = child->visible().value(); + child->d_func()->activeOpacity = activeOpacity; + if(visible != 1) + child->d_func()->activeOpacity *= visible; + + if(child->d_func()->activeOpacity != 0) { + // Calculate child's transform + qreal x = child->x(); + qreal y = child->y(); + qreal scale = child->scale().value(); + QSimpleCanvasItem::Flip flip = child->flip(); + + QSimpleCanvas::Matrix &am = child->d_func()->transformActive; + am = transformActive; + if(x != 0 || y != 0) + am.translate(x, y); + if(scale != 1) { + QPointF to = child->d_func()->transformOrigin(); + if(to.x() != 0. || to.y() != 0.) + am.translate(to.x(), to.y()); + am.scale(scale, scale); + if(to.x() != 0. || to.y() != 0.) + am.translate(-to.x(), -to.y()); + } + if(child->d_func()->transformUserSet) + am *= child->d_func()->transformUser; + if(flip) { + QRectF br = child->boundingRect(); + am.translate(br.width() / 2., br.height() / 2); + am.rotate(180, (flip & QSimpleCanvasItem::VerticalFlip)?1:0, (flip & QSimpleCanvasItem::HorizontalFlip)?1:0, 0); + am.translate(-br.width() / 2., -br.height() / 2); + } + } +} + +QRectF QSimpleCanvasItemPrivate::setupPainting(int version, const QRect &bounding, unsigned int *zero) +{ + Q_Q(QSimpleCanvasItem); + + QRectF rv = transformActive.mapRect(q->boundingRect()); + + unsigned int oldZero = *zero; + + for(int ii = 0; ii < children.count(); ++ii) { + QSimpleCanvasItem *child = children.at(ii); + setupChildState(child); + + if(child->d_func()->activeOpacity != 0) + rv |= child->d_func()->setupPainting(version, bounding, zero); + } + + if(clip || oldZero != *zero) + (*zero)++; + transformActive.translate(0, 0, *zero); + + lastPaintRect = rv; + return rv; +} + +void QSimpleCanvasItemPrivate::paintNoClip(GLPaintParameters ¶ms, QSimpleCanvasFilter::Layer layer) +{ + Q_Q(QSimpleCanvasItem); + + zOrderChildren(); + + int upto = 0; + for(upto = 0; upto < children.count(); ++upto) { + QSimpleCanvasItem *c = children.at(upto); + if(c->zValue().value() < 0) { + if(layer & QSimpleCanvasFilter::ChildrenUnderItem) + paintChild(params, c); + } else { + break; + } + } + + if(layer & QSimpleCanvasFilter::Item && + q->options() & QSimpleCanvasItem::HasContents) { + QSimpleCanvasItem::GLPainter painter(q); + painter.activeTransform = transformActive; + painter.activeOpacity = activeOpacity; + painter.sceneClipRect = params.clipRect; + + q->paintGLContents(painter); + } + + if(layer & QSimpleCanvasFilter::ChildrenAboveItem) { + for(; upto < children.count(); ++upto) { + QSimpleCanvasItem *c = children.at(upto); + paintChild(params, c); + } + } +} + +void QSimpleCanvasItemPrivate::paint(GLPaintParameters ¶ms, QSimpleCanvasFilter::Layer layer) +{ + if(!layer) + return; + + // XXX Handle separate cliping modes + if(clip) { + + GLSaveScissor ss; + qreal width = params.boundingRect.width(); + qreal height = params.boundingRect.height(); + float margin = width + height; + + GLfloat clipvertices[] = + { + -margin, -margin, + margin, -margin, + margin, 0, + + -margin, -margin, + -margin, 0, + margin, 0, + + -margin, 0, + -margin, margin, + 0, 0, + + 0, 0, + 0, margin, + -margin, margin, + + 0, height, + 0, margin, + margin, margin, + + margin, margin, + 0, height, + margin, height, + + width, 0, + margin, 0, + margin, height, + + margin, height, + width, height, + width, 0 + }; + + QRectF r = transformActive.mapRect(params.boundingRect); + r.translate(1, 1); + float xscale = 0.5 * float(params.sceneRect.width()); + float yscale = 0.5 * float(params.sceneRect.height()); + r.moveTo(r.x() * xscale, r.y() * yscale); + r.setSize(QSizeF(r.width() * xscale, r.height() * yscale)); + + glEnable(GL_SCISSOR_TEST); + int sr_x = ::floorf(r.x()); + int sr_y = ::floorf(r.y()); + int sr_width = ::ceilf(r.right()) - sr_x; + int sr_height = ::ceilf(r.bottom()) - sr_y; + + QRect sr(sr_x, sr_y, sr_width, sr_height); + if(ss.wasEnabled()) + sr &= ss.rect(); + + glScissor(sr.x(), sr.y(), sr.width(), sr.height()); + + { + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(transformActive.data()); + glDepthMask(GL_TRUE); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, clipvertices); + glDrawArrays(GL_TRIANGLES, 0, 24); + glDisableClientState(GL_VERTEX_ARRAY); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + } + + GLPaintParameters newParams = params; + newParams.clipRect = sr; + newParams.clipRect.moveTo(sr.x(), params.sceneRect.height() - sr.y() - sr.height()); + paintNoClip(newParams, layer); + } else { + paintNoClip(params, layer); + } +} + +QGLShaderProgram *QSimpleCanvasItem::GLPainter::useTextureShader() +{ + qFatal("Cannot call QSimpleCanvasItem::GLPainter::useTextureShader() when using OpenGL ES 1.1"); + return 0; +} + +QGLShaderProgram *QSimpleCanvasItem::GLPainter::useColorShader(const QColor &color) +{ + Q_UNUSED(color); + qFatal("Cannot call QSimpleCanvasItem::GLPainter::useColorShader() when using OpenGL ES 1.1"); + return 0; +} + +GLBasicShaders *QSimpleCanvasItemPrivate::basicShaders() const +{ + qFatal("Cannot call QSimpleCanvasItem::basicShaders() when using OpenGL ES 1.1"); + return 0; +} + +QGLFramebufferObject *QSimpleCanvasPrivate::acquire(int, int) +{ + return 0; +} + +void QSimpleCanvasPrivate::release(QGLFramebufferObject *) +{ +} + +void QSimpleCanvasItem::GLPainter::drawImage(const QPointF &point, + const GLTexture &texture) +{ + drawImage(QRectF(point, QSizeF(texture.width(), texture.height())), texture); +} + +void QSimpleCanvasItem::GLPainter::drawImage(const QRectF &rect, + const GLTexture &img) +{ + qFatal("Cannot call QSimpleCanvasItem::GLPainter::drawImage() when using OpenGL ES 1.1"); +} + +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvas_p.h b/src/declarative/canvas/qsimplecanvas_p.h new file mode 100644 index 0000000..4c8b41e --- /dev/null +++ b/src/declarative/canvas/qsimplecanvas_p.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QSIMPLECANVAS_P_H +#define QSIMPLECANVAS_P_H + +#include "qsimplecanvas.h" +#include <qstack.h> +#include <qdatetime.h> + +#if defined(QFX_RENDER_OPENGL) + +#if defined(QFX_RENDER_OPENGL2) +#include "glbasicshaders.h" +#endif + +#include <QGLWidget> +QT_BEGIN_NAMESPACE + +class CanvasEGLWidget : public QGLWidget +{ +public: + CanvasEGLWidget(QSimpleCanvas *parent, QSimpleCanvasPrivate *canvas) + : + QGLWidget(parent), + _canvas(canvas) + { + } + + virtual void paintGL(); + virtual void resizeGL(int,int); + virtual void resizeEvent(QResizeEvent *e); + virtual void initializeGL(); + + void updateGL(); + void updateGL(const QRect &); + + QRect map(const QRectF &) const; +private: + QRect _clip; + QSimpleCanvasPrivate *_canvas; + QSimpleCanvas::Matrix defaultTransform; + QSimpleCanvas::Matrix invDefaultTransform; +}; +#endif + +#include <QGraphicsView> +#include <QGraphicsScene> + +struct QSimpleCanvasGraphicsView : public QGraphicsView +{ +public: + QSimpleCanvasGraphicsView(QSimpleCanvasPrivate *parent); + ~QSimpleCanvasGraphicsView(); + +protected: + virtual void paintEvent(QPaintEvent *); + virtual void focusInEvent(QFocusEvent *); + +private: + QSimpleCanvasPrivate *canvas; + QGraphicsScene _scene; +}; + +class QGLFramebufferObject; +class QSimpleCanvasServer; +class QSimpleCanvasPrivate +{ +public: + QSimpleCanvasPrivate(QSimpleCanvas *canvas) + : q(canvas), timer(0), root(0), lrpTime(0), canvasServer(0), focusItem(0), + lastFocusItem(0), lastMouseItem(0), isSetup(false), + view(0) +#if defined(QFX_RENDER_OPENGL) + ,egl(q, this), basicShadersInstance(0) +#endif + { + } + + QSimpleCanvas *q; + QSimpleCanvas::CanvasMode mode; + bool isSimpleCanvas() const { return mode == QSimpleCanvas::SimpleCanvas; } + bool isGraphicsView() const { return mode == QSimpleCanvas::GraphicsView; } + +#if defined(QFX_RENDER_OPENGL) + QRectF oldDirty; +#else + QRect oldDirty; +#endif + QRegion resetDirty(); + void paint(QPainter &p); + + + int timer; + + QSimpleCanvasLayer *root; + QList<QSimpleCanvasItem *> dirtyItems; + int lrpTime; + + QTime frameTimer; + QTime lrpTimer; + + QSimpleCanvasServer *canvasServer; + + QStack<QSimpleCanvasItem *> focusPanels; + QHash<QSimpleCanvasItem *, QSimpleCanvasItem *> focusPanelData; + QSimpleCanvasItem *focusItem; + QSimpleCanvasItem *lastFocusItem; + + QRect dirtyItemClip() const; + void clearFocusPanel(QSimpleCanvasItem *); + void setActiveFocusPanel(QSimpleCanvasItem *, Qt::FocusReason focusReason = Qt::OtherFocusReason); + void switchToFocusPanel(QSimpleCanvasItem *, QSimpleCanvasItem *, Qt::FocusReason focusReason); + + void setFocusItem(QSimpleCanvasItem *item, Qt::FocusReason focusReason, + bool overwrite = true); + void clearFocusItem(QSimpleCanvasItem *item); + + void clearActiveFocusItem(QSimpleCanvasItem *, Qt::FocusReason focusReason); + void setActiveFocusItem(QSimpleCanvasItem *, Qt::FocusReason focusReason); + void installMouseFilter(QSimpleCanvasItem *); + void removeMouseFilter(QSimpleCanvasItem *); + QList<QSimpleCanvasItem *> mouseFilters; + bool filter(QMouseEvent *e); + bool deliverMousePress(QSimpleCanvasItem *, QMouseEvent *); + QGraphicsSceneMouseEvent *mouseEventToSceneMouseEvent(QMouseEvent *, const QPoint &); + QSimpleCanvasItem *lastMouseItem; + + bool isSetup; + + void init(QSimpleCanvas::CanvasMode mode); + + QSimpleCanvasGraphicsView *view; + +#if defined(QFX_RENDER_OPENGL) + CanvasEGLWidget egl; + GLBasicShaders *basicShaders() const + { +#if defined(QFX_RENDER_OPENGL2) + if(!basicShadersInstance) + basicShadersInstance = new GLBasicShaders; + return basicShadersInstance; +#else + return 0; +#endif + } + mutable GLBasicShaders *basicShadersInstance; + + QList<QGLFramebufferObject *> frameBuffers; + QGLFramebufferObject *acquire(int, int); + void release(QGLFramebufferObject *); + void paintGL(); +#endif +}; + +#endif + +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvas_software.cpp b/src/declarative/canvas/qsimplecanvas_software.cpp new file mode 100644 index 0000000..06e221e --- /dev/null +++ b/src/declarative/canvas/qsimplecanvas_software.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qsimplecanvas.h" +#include "qsimplecanvasitem_p.h" + + +QT_BEGIN_NAMESPACE +QRect QSimpleCanvasItemPrivate::setupPainting(int version, const QRect &bounding) +{ + Q_Q(QSimpleCanvasItem); + + QRectF boundingRectActive = q->boundingRect(); + QRect rv = + data()->transformActive.mapRect(boundingRectActive).toAlignedRect() & bounding; + QRect myBounding = bounding; + if(q->clip()) + myBounding &= rv; + + for(int ii = 0; ii < children.count(); ++ii) { + QSimpleCanvasItem *child = children.at(ii); + + qreal visible = child->visible(); + child->d_func()->data()->activeOpacity = data()->activeOpacity; + if(visible != 1) + child->d_func()->data()->activeOpacity *= visible; + + if(child->d_func()->data()->activeOpacity != 0) { + // Calculate child's transform + qreal x = child->x(); + qreal y = child->y(); + qreal scale = child->scale(); + QSimpleCanvasItem::Flip flip = child->flip(); + + QSimpleCanvas::Matrix &am = child->d_func()->data()->transformActive; + am = data()->transformActive; + if(x != 0 || y != 0) + am.translate(x, y); + if(scale != 1) { + QPointF to = child->d_func()->transformOrigin(); + if(to.x() != 0. || to.y() != 0.) + am.translate(to.x(), to.y()); + am.scale(scale, scale); + if(to.x() != 0. || to.y() != 0.) + am.translate(-to.x(), -to.y()); + } + + if(child->d_func()->data()->transformUser) + am = *child->d_func()->data()->transformUser * am; + + if(flip) { + QRectF br = child->boundingRect(); + am.translate(br.width() / 2., br.height() / 2); + am.scale((flip & QSimpleCanvasItem::HorizontalFlip)?-1:1, + (flip & QSimpleCanvasItem::VerticalFlip)?-1:1); + am.translate(-br.width() / 2., -br.height() / 2); + } + child->d_func()->data()->transformValid = true; + rv |= child->d_func()->setupPainting(version, myBounding); + } + } + + data()->lastPaintRect = rv; + return rv; +} + +void QSimpleCanvasItemPrivate::paint(QPainter &p) +{ + Q_Q(QSimpleCanvasItem); + + QRect oldUcr; + if(clip) { + + p.save(); + QRectF boundingRectActive = q->boundingRect(); + + QRect cr; + switch(clip) { + case QSimpleCanvasItem::ClipToHeight: + { + qWarning("QSimpleCanvasItem: ClipToHeight not implemented"); + QRect r = p.clipRegion().boundingRect(); + cr = QRect(r.x(), 0, r.width(), + boundingRectActive.height()); + } + break; + case QSimpleCanvasItem::ClipToWidth: + { + qWarning("QSimpleCanvasItem: ClipToWidth not implemented"); + QRect r = p.clipRegion().boundingRect(); + cr = QRect(0, r.y(), boundingRectActive.width(), + r.height()); + } + break; + case QSimpleCanvasItem::ClipToRect: + cr = boundingRectActive.toAlignedRect(); + break; + default: + break; + } + + p.setWorldTransform(data()->transformActive); + if(p.clipRegion().isEmpty()) { + p.setClipRect(cr); + } else { + p.setClipRect(cr, Qt::IntersectClip); + } + + if(p.clipRegion().isEmpty()) { + p.restore(); + return; + } + } + + zOrderChildren(); + + int upto = 0; + for(upto = 0; upto < children.count(); ++upto) { + QSimpleCanvasItem *c = children.at(upto); + if(c->z() < 0) { + paintChild(p, c); + } else { + break; + } + } + + p.setWorldTransform(data()->transformActive); + q->paintContents(p); + + for(; upto < children.count(); ++upto) { + QSimpleCanvasItem *c = children.at(upto); + paintChild(p, c); + } + + if(clip) + p.restore(); +} + +void QSimpleCanvasItemPrivate::paintChild(QPainter &p, QSimpleCanvasItem *c) +{ + if(c->d_func()->data()->activeOpacity != 0) { + + qreal op = p.opacity(); + p.setOpacity(c->d_func()->data()->activeOpacity); + + c->d_func()->paint(p); + + p.setOpacity(op); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvasfilter.cpp b/src/declarative/canvas/qsimplecanvasfilter.cpp new file mode 100644 index 0000000..00c88c5 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvasfilter.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qsimplecanvasfilter.h" +#include "qsimplecanvasfilter_p.h" +#include "qsimplecanvasitem_p.h" +#include "qsimplecanvas.h" +#include "qsimplecanvas_p.h" + +#if defined(QFX_RENDER_OPENGL2) +#include <glsave.h> +#include <QtOpenGL/qglframebufferobject.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Filter + \brief A Filter is a graphical filter that can be applied to items. +*/ + +QSimpleCanvasFilter::QSimpleCanvasFilter(QObject *parent) +: QObject(parent), d(new QSimpleCanvasFilterPrivate(this)) +{ +} + +QSimpleCanvasFilter::~QSimpleCanvasFilter() +{ + if(d->item) d->item->setFilter(0); + delete d; d = 0; +} + +bool QSimpleCanvasFilter::enabled() const +{ + return d->enabled; +} + +/*! + \qmlproperty bool Filter::enabled + \brief Controls whether the filter is applied. +*/ + +void QSimpleCanvasFilter::setEnabled(bool e) +{ + if(e == d->enabled) + return; + d->enabled = e; + emit enabledChanged(); + update(); +} + +QGLFramebufferObject *QSimpleCanvasFilter::renderToFBO(float scale, const QRectF &src, const QPoint &offset, Layer) +{ + // XXX - respect src +#if defined(QFX_RENDER_OPENGL2) + Q_UNUSED(src); + Q_UNUSED(offset); + + QSimpleCanvasItem *item = d->item; + QRect br = item->itemBoundingRect(); + if(br.isEmpty()) + return 0; + QGLFramebufferObject *fbo = + item->canvas()->d->acquire(int(br.width() * scale), int(br.height() * scale)); + + GLSaveViewport sv; GLSaveScissor ss; + qreal oldOpacity = item->d_func()->data()->activeOpacity; + item->d_func()->data()->activeOpacity = 1; + + fbo->bind(); + + glClearColor(0,0,0,0); + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, int(br.width() * scale), int(br.height() * scale)); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, int(br.width() * scale), int(br.height() * scale)); + + QMatrix4x4 mat; + mat.flipCoordinates(); + mat.translate(-1, -1, 0); + mat.scale(2. / (br.width()), 2. / (br.height()), 1. / (1024. * 1024.)); + + renderToScreen(mat); + + fbo->release(); + + item->d_func()->data()->activeOpacity = oldOpacity; + return fbo; +#else + Q_UNUSED(src); + Q_UNUSED(offset); + Q_UNUSED(scale); + return 0; +#endif +} + +QGLFramebufferObject *QSimpleCanvasFilter::renderToFBO(const QRectF &src, const QPoint &offset, Layer) +{ + // XXX - respect src +#if defined(QFX_RENDER_OPENGL2) + Q_UNUSED(src); + Q_UNUSED(offset); + + QSimpleCanvasItem *item = d->item; + QRect br = item->itemBoundingRect(); + if(br.isEmpty()) + return 0; + QGLFramebufferObject *fbo = + item->canvas()->d->acquire(br.width(), br.height()); + + GLSaveViewport sv; GLSaveScissor ss; + qreal oldOpacity = item->d_func()->data()->activeOpacity; + item->d_func()->data()->activeOpacity = 1; + + fbo->bind(); + + glClearColor(0,0,0,0); + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, br.width(), br.height()); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, br.width(), br.height()); + + QMatrix4x4 mat; + mat.flipCoordinates(); + mat.translate(-1, -1, 0); + mat.scale(2. / br.width(), 2. / br.height(), 1. / (1024. * 1024.)); + + renderToScreen(mat); + + fbo->release(); + + item->d_func()->data()->activeOpacity = oldOpacity; + return fbo; +#else + Q_UNUSED(src); + Q_UNUSED(offset); + return 0; +#endif +} + +QGLFramebufferObject *QSimpleCanvasFilter::acquireFBO(const QSize &s) +{ +#if defined(QFX_RENDER_OPENGL2) + QSize size; + QSimpleCanvasItem *item = d->item; + if(size.isNull()) { + QRect br = item->itemBoundingRect(); + size = br.size(); + } else { + size = s; + } + + QGLFramebufferObject *fbo = + item->canvas()->d->acquire(s.width(), s.height()); + + return fbo; +#else + Q_UNUSED(s); + return 0; +#endif +} + +void QSimpleCanvasFilter::releaseFBO(QGLFramebufferObject *fbo) +{ +#if defined(QFX_RENDER_OPENGL2) + d->item->d_func()->canvas->d->release(fbo); +#else + Q_UNUSED(fbo); +#endif +} + +void QSimpleCanvasFilter::renderToScreen(const QRectF &src, Layer layer) +{ + // XXX - respect src +#if defined(QFX_RENDER_OPENGL2) + Q_UNUSED(src); + Q_UNUSED(layer); + d->item->d_func()->paint(d->params, layer); +#else + Q_UNUSED(src); + Q_UNUSED(layer); +#endif +} + +void QSimpleCanvasFilter::renderToScreen(const QSimpleCanvas::Matrix &trans, const QRectF &src, Layer layer) +{ + // XXX - respect src +#if defined(QFX_RENDER_OPENGL2) + Q_UNUSED(src); + QSimpleCanvas::Matrix old = d->item->d_func()->data()->transformActive; + d->item->d_func()->data()->transformActive = trans; + QSimpleCanvasItemPrivate::GLPaintParameters params = d->params; + params.forceParamRefresh = true; + d->item->d_func()->paint(params, layer); + d->item->d_func()->data()->transformActive = old; +#else + Q_UNUSED(trans); + Q_UNUSED(src); + Q_UNUSED(layer); +#endif +} + +QSimpleCanvasItem *QSimpleCanvasFilter::item() const +{ + return d->item; +} + +void QSimpleCanvasFilter::setItem(QSimpleCanvasItem *i) +{ + if(d->item == i) + return; + if(d->item) { + d->item->setFilter(0); + d->item = 0; + } + + if(i->filter() != this) { + i->setFilter(this); + } else { + d->item = i; + } +} + +#if defined(QFX_RENDER_OPENGL2) +void QSimpleCanvasFilterPrivate::doFilterGL(QSimpleCanvasItem::GLPainter &p, const QSimpleCanvasItemPrivate::GLPaintParameters &prms) +{ + params = prms; + q->filterGL(p); +} +#endif + +QRectF QSimpleCanvasFilter::itemBoundingRect(const QRectF &r) const +{ + return r; +} + +void QSimpleCanvasFilter::filterGL(QSimpleCanvasItem::GLPainter &p) +{ + Q_UNUSED(p); +} + +void QSimpleCanvasFilter::filter(QPainter &) +{ +} + +void QSimpleCanvasFilter::update() +{ + if(d->item) + d->item->update(); +} + +#if defined(QFX_RENDER_OPENGL2) +bool QSimpleCanvasFilterPrivate::isSimpleItem(QSimpleCanvasItem *item, QSimpleCanvasItem **out, QSimpleCanvas::Matrix *mout) +{ + if(item->options() & QSimpleCanvasItem::SimpleItem && !item->hasChildren()) { + *out = item; + return true; + } else if(!(item->options() & QSimpleCanvasItem::HasContents) && + item->children().count() == 1) { + QSimpleCanvasItem *child = item->children().first(); + if(child->filter() && child->filter()->enabled()) + return false; + bool rv = isSimpleItem(child, out, mout); + if(rv) + *mout *= child->d_func()->localTransform(); + return rv; + } else { + return false; + } +} +#endif + +bool QSimpleCanvasFilter::isSimpleItem(QSimpleCanvasItem **out, QSimpleCanvas::Matrix *mout) +{ +#if defined(QFX_RENDER_OPENGL2) + return d->isSimpleItem(item(), out, mout); +#else + Q_UNUSED(out); + Q_UNUSED(mout); +#endif + + return false; +} + +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvasfilter.h b/src/declarative/canvas/qsimplecanvasfilter.h new file mode 100644 index 0000000..cb75ddf --- /dev/null +++ b/src/declarative/canvas/qsimplecanvasfilter.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 QtDeclarative 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 QSIMPLECANVASFILTER_H +#define QSIMPLECANVASFILTER_H + +#include <QtCore/qobject.h> +#include <qfxglobal.h> +#include <qsimplecanvasitem.h> +#include <qsimplecanvas.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QSimpleCanvasFilterPrivate; +class QRectF; +class QPoint; +class QGLFramebufferObject; +class GLShaderProgram; +class Q_DECLARATIVE_EXPORT QSimpleCanvasFilter : public QObject +{ +Q_OBJECT +public: + QSimpleCanvasFilter(QObject *parent); + virtual ~QSimpleCanvasFilter(); + + enum Layer { ChildrenUnderItem = 0x01, + Item = 0x02, + ChildrenAboveItem = 0x04, + All = 0x07 }; + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); + bool enabled() const; + void setEnabled(bool); + + QSimpleCanvasItem *item() const; + void setItem(QSimpleCanvasItem *); +Q_SIGNALS: + void enabledChanged(); + +protected: + + virtual QRectF itemBoundingRect(const QRectF &) const; + virtual void filterGL(QSimpleCanvasItem::GLPainter &p); + virtual void filter(QPainter &p); + + QGLFramebufferObject *renderToFBO(const QRectF &src = QRect(), const QPoint &offset = QPoint(), Layer = All); + QGLFramebufferObject *renderToFBO(float scale, const QRectF &src = QRect(), const QPoint &offset = QPoint(), Layer = All); + QGLFramebufferObject *acquireFBO(const QSize & = QSize()); + void releaseFBO(QGLFramebufferObject *); + + void renderToScreen(const QRectF &src = QRect(), Layer = All); + void renderToScreen(const QSimpleCanvas::Matrix &trans, const QRectF &src = QRect(), Layer = All); + + void update(); + + bool isSimpleItem(QSimpleCanvasItem **, QSimpleCanvas::Matrix *); + +private: + friend class QSimpleCanvasFilterPrivate; + friend class QSimpleCanvasItemPrivate; + friend class QSimpleCanvasItem; + + QSimpleCanvasFilterPrivate *d; +}; + + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/canvas/qsimplecanvasfilter_p.h b/src/declarative/canvas/qsimplecanvasfilter_p.h new file mode 100644 index 0000000..1c72993 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvasfilter_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QSIMPLECANVASFILTER_P_H +#define QSIMPLECANVASFILTER_P_H + +#include "qsimplecanvasitem.h" +#include "qsimplecanvasitem_p.h" + + +QT_BEGIN_NAMESPACE +class QSimpleCanvasFilterPrivate +{ +public: + QSimpleCanvasFilterPrivate(QSimpleCanvasFilter *_q) + : q(_q), item(0), enabled(true) {} + + QSimpleCanvasFilter *q; + + QSimpleCanvasItem *item; + bool enabled; +#if defined(QFX_RENDER_OPENGL) + QSimpleCanvasItemPrivate::GLPaintParameters params; + + void doFilterGL(QSimpleCanvasItem::GLPainter &, const QSimpleCanvasItemPrivate::GLPaintParameters &); + + bool isSimpleItem(QSimpleCanvasItem *item, QSimpleCanvasItem **out, QSimpleCanvas::Matrix *mout); +#endif +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/canvas/qsimplecanvasitem.cpp b/src/declarative/canvas/qsimplecanvasitem.cpp new file mode 100644 index 0000000..3d3bf84 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvasitem.cpp @@ -0,0 +1,1841 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qsimplecanvasitem.h" +#include "qsimplecanvas.h" +#include "qsimplecanvasitem_p.h" +#include "qsimplecanvas_p.h" +#include <qfxitem.h> +#include <QGraphicsSceneEvent> + + +QT_BEGIN_NAMESPACE +QSimpleCanvasItemData::QSimpleCanvasItemData() +: buttons(Qt::NoButton), flip(QSimpleCanvasItem::NoFlip), + dirty(false), transformValid(true), x(0), y(0), z(0), visible(1), + transformUser(0), activeOpacity(1) +{ +} + +QSimpleCanvasItemData::~QSimpleCanvasItemData() +{ + if(transformUser) + delete transformUser; +} + +/*! + \class QSimpleCanvasItem + \brief The QSimpleCanvasItem class is the base class of canvas items. + */ +QSimpleCanvasLayer::QSimpleCanvasLayer() +{ +} + +QSimpleCanvasLayer::QSimpleCanvasLayer(QSimpleCanvasItem *parent) +: QSimpleCanvasItem(parent) +{ +} + +void QSimpleCanvasLayer::addChild(QSimpleCanvasItem *c) +{ + QSimpleCanvasItem::addChild(c); +} + +void QSimpleCanvasLayer::addDirty(QSimpleCanvasItem *) +{ +} + +void QSimpleCanvasLayer::remDirty(QSimpleCanvasItem *) +{ +} + +QSimpleCanvasLayer *QSimpleCanvasLayer::layer() +{ + return this; +} + +QSimpleCanvasItem::Options QSimpleCanvasItem::options() const +{ + Q_D(const QSimpleCanvasItem); + return (QSimpleCanvasItem::Options)d->options; +} + +bool QSimpleCanvasItem::mouseFilter(QGraphicsSceneMouseEvent *) +{ + return false; +} + +void QSimpleCanvasItem::mousePressEvent(QGraphicsSceneMouseEvent *) +{ +} + +void QSimpleCanvasItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *) +{ +} + +void QSimpleCanvasItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) +{ +} + +void QSimpleCanvasItem::mouseMoveEvent(QGraphicsSceneMouseEvent *) +{ +} + +void QSimpleCanvasItem::hoverEnterEvent(QGraphicsSceneHoverEvent *) +{ +} + +void QSimpleCanvasItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) +{ +} + +void QSimpleCanvasItem::mouseUngrabEvent() +{ +} + +void QSimpleCanvasItem::keyPressEvent(QKeyEvent *) +{ +} + +void QSimpleCanvasItem::keyReleaseEvent(QKeyEvent *) +{ +} + +void QSimpleCanvasItem::focusOutEvent(QFocusEvent *) +{ +} + +void QSimpleCanvasItem::focusInEvent(QFocusEvent *) +{ +} + +void QSimpleCanvasItem::activePanelInEvent() +{ +} + +void QSimpleCanvasItem::activePanelOutEvent() +{ +} + +void QSimpleCanvasItem::inputMethodEvent(QInputMethodEvent *) +{ +} + +QVariant QSimpleCanvasItem::inputMethodQuery(Qt::InputMethodQuery) const +{ + return QVariant(); +} + +void QSimpleCanvasItem::childrenChanged() +{ +} + +QRectF QSimpleCanvasItem::boundingRect() const +{ + Q_D(const QSimpleCanvasItem); + return QRectF(0., 0., d->width, d->height); +} + +void QSimpleCanvasItem::paintContents(QPainter &) +{ +} + +void QSimpleCanvasItem::paintGLContents(GLPainter &) +{ +} + +uint QSimpleCanvasItem::glSimpleItemData(float *vertices, float *texVertices, + GLTexture **texture, uint count) +{ + Q_UNUSED(vertices); + Q_UNUSED(texVertices); + Q_UNUSED(texture); + Q_UNUSED(count); + return 0; +} + +void QSimpleCanvasItem::canvasChanged() +{ +} + +void QSimpleCanvasItem::focusChanged(bool) +{ +} + +void QSimpleCanvasItem::activeFocusChanged(bool) +{ +} + +void QSimpleCanvasItem::parentChanged(QSimpleCanvasItem *, QSimpleCanvasItem *) +{ +} + +GLBasicShaders *QSimpleCanvasItem::basicShaders() const +{ +#if defined(QFX_RENDER_OPENGL2) + return canvas()->d->basicShaders(); +#else + return 0; +#endif +} + +/*! + Returns the item's (0, 0) point relative to its parent. + */ +QPointF QSimpleCanvasItem::pos() const +{ + return QPointF(x(),y()); +} + +/*! + Returns the item's (0, 0) point mapped to scene coordinates. + */ +QPointF QSimpleCanvasItem::scenePos() const +{ + return mapToScene(QPointF(0, 0)); +} + +/*! + \enum QSimpleCanvasItem::TransformOrigin + + Controls the point about which simple transforms like scale apply. + + \o TopLeft The top-left corner of the item. + \o TopCenter The center point of the top of the item. + \o TopRight The top-right corner of the item. + \o MiddleLeft The left most point of the vertical middle. + \o Center The center of the item. + \o MiddleRight The right most point of the vertical middle. + \o BottomLeft The bottom-left corner of the item. + \o BottomCenter The center point of the bottom of the item. + \o BottomRight The bottom-right corner of the item. +*/ + +/*! + Returns the current transform origin. +*/ +QSimpleCanvasItem::TransformOrigin QSimpleCanvasItem::transformOrigin() const +{ + Q_D(const QSimpleCanvasItem); + return d->origin; +} + +/*! + Set the transform \a origin. +*/ +void QSimpleCanvasItem::setTransformOrigin(TransformOrigin origin) +{ + Q_D(QSimpleCanvasItem); + if(origin != d->origin) { + d->origin = origin; + update(); + } +} + +QPointF QSimpleCanvasItem::transformOriginPoint() const +{ + Q_D(const QSimpleCanvasItem); + return d->transformOrigin(); +} + +/*! + Returns the canvas the item is on, or 0 if the item is not on a canvas. + */ +QSimpleCanvas *QSimpleCanvasItem::canvas() const +{ + Q_D(const QSimpleCanvasItem); + return d->canvas; +} + +/*! + Returns the parent if the item, or 0 if the item has no parent. + */ +QSimpleCanvasItem *QSimpleCanvasItem::parent() const +{ + Q_D(const QSimpleCanvasItem); + return d->parent; +} + +void QSimpleCanvasItemPrivate::zOrderChildren() +{ + if(!needsZOrder || children.count() <= 1) + return; + + needsZOrder = false; + // This is a bubble sort for a reason - it is the fastest sort for a mostly + // ordered list. We only expect z ordering to change infrequently. + bool swap = true; + int c = 0; + while(swap) { + ++c; + swap = false; + QSimpleCanvasItem *item = children.first(); + qreal z = item->z(); + for(int ii = 1; ii < children.count(); ++ii) { + QSimpleCanvasItem *i2 = children.at(ii); + qreal z2 = i2->z(); + if(z2 < z) { + swap = true; + children[ii] = item; + children[ii - 1] = i2; + } else { + item = i2; + z = z2; + } + } + } +} + +void QSimpleCanvasItemPrivate::canvasChanged(QSimpleCanvas *newCanvas, QSimpleCanvas *oldCanvas) +{ + Q_Q(QSimpleCanvasItem); + canvas = newCanvas; + if(options & QSimpleCanvasItem::MouseFilter) { + if(oldCanvas) oldCanvas->d->removeMouseFilter(q); + if(newCanvas) newCanvas->d->installMouseFilter(q); + } + if(newCanvas) { + if(!oldCanvas && hasFocus) + newCanvas->d->setFocusItem(q, Qt::OtherFocusReason, false); + if(wantsActiveFocusPanelPendingCanvas) { + hasBeenActiveFocusPanel = true; + newCanvas->d->setActiveFocusPanel(q); + wantsActiveFocusPanelPendingCanvas = false; + } + } + + for(int ii = 0; ii < children.count(); ++ii) + children.at(ii)->d_func()->canvasChanged(newCanvas, oldCanvas); + q->canvasChanged(); +} + +void QSimpleCanvasItem::setFocus(bool focus) +{ + Q_D(QSimpleCanvasItem); + if(d->hasFocus == focus) + return; + QSimpleCanvas *c = canvas(); + + if(c) { + if(focus) + c->d->setFocusItem(this, Qt::OtherFocusReason); + else + c->d->clearFocusItem(this); + } else { + d->setFocus(focus); + focusChanged(d->hasFocus); + } +} + +qreal QSimpleCanvasItem::x() const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) + return d->graphicsItem->x(); + else if(d->data_ptr) + return d->data()->x; + else + return 0; +} + +qreal QSimpleCanvasItem::y() const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) + return d->graphicsItem->y(); + else if(d->data_ptr) + return d->data()->y; + else + return 0; +} + +qreal QSimpleCanvasItem::z() const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) + return d->graphicsItem->zValue(); + else if(d->data_ptr) + return d->data()->z; + else + return 0; +} + +void QSimpleCanvasItem::setX(qreal x) +{ + Q_D(QSimpleCanvasItem); + if(x == this->x()) + return; + + qreal oldX = this->x(); + + if(d->graphicsItem) { + d->graphicsItem->setPos(x, y()); + } else { + d->data()->x = x; + update(); + } + + geometryChanged(QRectF(this->x(), y(), width(), height()), + QRectF(oldX, y(), width(), height())); +} + +void QSimpleCanvasItem::setY(qreal y) +{ + Q_D(QSimpleCanvasItem); + if(y == this->y()) + return; + + qreal oldY = this->y(); + + if(d->graphicsItem) { + d->graphicsItem->setPos(x(), y); + } else { + d->data()->y = y; + update(); + } + + geometryChanged(QRectF(x(), this->y(), width(), height()), + QRectF(x(), oldY, width(), height())); +} + +void QSimpleCanvasItem::setZ(qreal z) +{ + Q_D(QSimpleCanvasItem); + if(z == this->z()) + return; + + if(d->graphicsItem) { + d->graphicsItem->setZValue(z); + } else { + if(d->data()->z == z) + return; + + d->data()->z = z; + if(parent()) + static_cast<QSimpleCanvasItemPrivate*>(parent()->d_ptr)->needsZOrder = true; + update(); + } +} + +qreal QSimpleCanvasItem::width() const +{ + Q_D(const QSimpleCanvasItem); + return d->width; +} + +void QSimpleCanvasItem::setWidth(qreal w) +{ + Q_D(QSimpleCanvasItem); + d->widthValid = true; + if(d->width == w) + return; + + qreal oldWidth = d->width; + + d->width = w; + update(); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), oldWidth, height())); +} + +void QSimpleCanvasItem::setImplicitWidth(qreal w) +{ + Q_D(QSimpleCanvasItem); + if(d->width == w || widthValid()) + return; + + qreal oldWidth = d->width; + + d->width = w; + update(); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), oldWidth, height())); +} + +bool QSimpleCanvasItem::widthValid() const +{ + Q_D(const QSimpleCanvasItem); + return d->widthValid; +} + +qreal QSimpleCanvasItem::height() const +{ + Q_D(const QSimpleCanvasItem); + return d->height; +} + +void QSimpleCanvasItem::setHeight(qreal h) +{ + Q_D(QSimpleCanvasItem); + d->heightValid = true; + if(d->height == h) + return; + + qreal oldHeight = d->height; + + d->height = h; + update(); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), width(), oldHeight)); +} + +void QSimpleCanvasItem::setImplicitHeight(qreal h) +{ + Q_D(QSimpleCanvasItem); + if(d->height == h || heightValid()) + return; + + qreal oldHeight = d->height; + + d->height = h; + update(); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), width(), oldHeight)); +} + +bool QSimpleCanvasItem::heightValid() const +{ + Q_D(const QSimpleCanvasItem); + return d->heightValid; +} + +void QSimpleCanvasItem::setPos(const QPointF &point) +{ + Q_D(QSimpleCanvasItem); + qreal oldX = x(); + qreal oldY = y(); + + if(d->graphicsItem) { + d->graphicsItem->setPos(point); + } else { + d->data()->x = point.x(); + d->data()->y = point.y(); + update(); + } + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(oldX, oldY, width(), height())); +} + +qreal QSimpleCanvasItem::scale() const +{ + Q_D(const QSimpleCanvasItem); + return d->scale; +} + +void QSimpleCanvasItem::setScale(qreal s) +{ + Q_D(QSimpleCanvasItem); + d->scale = s; + if(d->graphicsItem) { + QTransform t; + QPointF to = transformOriginPoint(); + if(to.x() != 0. || to.y() != 0.) + t.translate(to.x(), to.y()); + t.scale(s, s); + if(to.x() != 0. || to.y() != 0.) + t.translate(-to.x(), -to.y()); + d->graphicsItem->setTransform(t * d->graphicsItem->transform); + } else { + update(); + } +} + +bool QSimpleCanvasItem::isVisible() const +{ + if(visible() <= 0) + return false; + else if(!parent()) + return true; + else + return parent()->isVisible(); +} + +qreal QSimpleCanvasItem::visible() const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) + return d->graphicsItem->opacity(); + else if(d->data_ptr) + return d->data()->visible; + else + return 1; +} + +void QSimpleCanvasItem::setVisible(qreal v) +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) { + d->graphicsItem->setOpacity(v); + } else { + if(v == visible()) + return; + if(v == 0) + update(); + + d->data()->visible = v; + + if(v != 0) + update(); + } +} + +void QSimpleCanvasItem::addChild(QSimpleCanvasItem *c) +{ + Q_D(QSimpleCanvasItem); + d->children.append(c); + if(d->graphicsItem) { + // XXX - GraphicsView does not preserve the stacking order of items + c->setZ(d->children.count()); + } else { + d->needsZOrder = true; + } + childrenChanged(); +} + +void QSimpleCanvasItem::remChild(QSimpleCanvasItem *c) +{ + Q_D(QSimpleCanvasItem); + d->children.removeAll(c); + childrenChanged(); +} + +QSimpleCanvasFilter *QSimpleCanvasItem::filter() const +{ + Q_D(const QSimpleCanvasItem); + return d->filter; +} + +/*! +QSimpleCanvasItem takes ownership of filter. +*/ +void QSimpleCanvasItem::setFilter(QSimpleCanvasFilter *f) +{ + Q_D(QSimpleCanvasItem); + if(!d || f == d->filter) + return; + + d->filter = f; + if(d->filter) + d->filter->setItem(this); + update(); +} + +const QList<QSimpleCanvasItem *> &QSimpleCanvasItem::children() const +{ + Q_D(const QSimpleCanvasItem); + return d->children; +} + +bool QSimpleCanvasItem::hasChildren() const +{ + Q_D(const QSimpleCanvasItem); + return !d->children.isEmpty(); +} + +QSimpleCanvasLayer *QSimpleCanvasItem::layer() +{ + if(parent()) + return parent()->layer(); + else + return 0; +} + +void QSimpleCanvasItem::update() +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) { + d->graphicsItem->update(); + } else { + if(!parent()) + return; + + if(d->data()->dirty || 0. == d->data()->visible) return; + + QSimpleCanvasLayer *l = layer(); + if(l == this && parent()) + l = parent()->layer(); + if(l) { + l->addDirty(this); + d->data()->dirty = true; + d->data()->transformValid = false; + } + } +} + +bool QSimpleCanvasItem::clip() const +{ + Q_D(const QSimpleCanvasItem); + return d->clip; +} + +void QSimpleCanvasItem::setClip(bool c) +{ + Q_D(const QSimpleCanvasItem); + if(bool(d->clip) == c) + return; + + if(c) + setClipType(ClipToRect); + else + setClipType(NoClip); + + update(); +} + +QSimpleCanvasItem::ClipType QSimpleCanvasItem::clipType() const +{ + Q_D(const QSimpleCanvasItem); + return d->clip; +} + +void QSimpleCanvasItem::setClipType(ClipType c) +{ + Q_D(QSimpleCanvasItem); + d->clip = c; + if(d->graphicsItem) + d->graphicsItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape, bool(c)); + else + update(); +} + +Qt::MouseButtons QSimpleCanvasItem::acceptedMouseButtons() const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) + return d->graphicsItem->acceptedMouseButtons(); + else if(d->data_ptr) + return (Qt::MouseButtons)d->data()->buttons; + else + return Qt::NoButton; +} + +void QSimpleCanvasItem::setAcceptedMouseButtons(Qt::MouseButtons buttons) +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) + d->graphicsItem->setAcceptedMouseButtons(buttons); + else + d->data()->buttons = buttons; +} + + +QRect QSimpleCanvasItem::itemBoundingRect() +{ + return boundingRect().toAlignedRect(); +} + +QPointF QSimpleCanvasItemPrivate::adjustFrom(const QPointF &p) const +{ +#if defined(QFX_RENDER_OPENGL) + if(!canvas) + return p; + + QPointF rv(-1. + 2. * p.x() / qreal(canvas->width()), + 1 - 2. * p.y() / qreal(canvas->height())); + + return rv; +#else + return p; +#endif +} + +QRectF QSimpleCanvasItemPrivate::adjustFrom(const QRectF &r) const +{ +#if defined(QFX_RENDER_OPENGL) + if(!canvas) + return r; + + qreal width = r.width() * 2. / qreal(canvas->width()); + qreal height = r.height() * 2. / qreal(canvas->height()); + qreal x = -1. + 2. * r.x() / qreal(canvas->width()); + qreal y = 1. - 2. * r.y() / qreal(canvas->height()) - height; + + return QRectF(x, y, width, height); +#else + return r; +#endif +} + +QPointF QSimpleCanvasItemPrivate::adjustTo(const QPointF &p) const +{ +#if defined(QFX_RENDER_OPENGL) + if(!canvas) + return p; + + QPointF rv(0.5 * (p.x() + 1.) * qreal(canvas->width()), + 0.5 * (1. - p.y()) * qreal(canvas->height())); + + return rv; +#else + return p; +#endif +} + +QRectF QSimpleCanvasItemPrivate::adjustTo(const QRectF &r) const +{ +#if defined(QFX_RENDER_OPENGL) + if(!canvas) + return r; + + qreal width = 0.5 * r.width() * qreal(canvas->width()); + qreal height = 0.5 * r.height() * qreal(canvas->height()); + qreal x = 0.5 * (r.x() + 1.) * qreal(canvas->width()); + qreal y = 0.5 * (1. - r.y()) * qreal(canvas->height()) - height; + + return QRectF(x, y, width, height); +#else + return r; +#endif +} + +QPointF QSimpleCanvasItem::mapFromScene(const QPointF &p) const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) { + return d->graphicsItem->mapFromScene(p); + } else { + QPointF mp = d->adjustFrom(p); + d->freshenTransforms(); +#if defined(QFX_RENDER_OPENGL) + // m20X + m21Y + m22Z + m23 = 1 + // Z = (1 - m23 - m20X - m21Y) / m22 + + QMatrix4x4 inv = d->data()->transformActive.inverted(); + qreal z_s = (1 - inv(2,3) - inv(2,0) * mp.x() - inv(2, 1) * mp.y()) / inv(2, 2); + + QVector3D vec(mp.x(), mp.y(), z_s); + QVector3D r = inv.map(vec); + + return r.toPointF(); +#else + return d->data()->transformActive.inverted().map(mp); +#endif + } +} + +QRectF QSimpleCanvasItem::mapFromScene(const QRectF &r) const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) { + return d->graphicsItem->mapFromScene(r).boundingRect(); + } else { + QRectF mr = d->adjustFrom(r); + d->freshenTransforms(); +#if defined(QFX_RENDER_OPENGL) + // m20X + m21Y + m22Z + m23 = 1 + // Z = (1 - m23 - m20X - m21Y) / m22 + + QMatrix4x4 inv = d->data()->transformActive.inverted(); + qreal tl_z_s = (1 - inv(2,3) - inv(2,0) * mr.topLeft().x() - inv(2, 1) * mr.topLeft().y()) / inv(2, 2); + qreal tr_z_s = (1 - inv(2,3) - inv(2,0) * mr.topRight().x() - inv(2, 1) * mr.topRight().y()) / inv(2, 2); + qreal bl_z_s = (1 - inv(2,3) - inv(2,0) * mr.bottomLeft().x() - inv(2, 1) * mr.bottomLeft().y()) / inv(2, 2); + qreal br_z_s = (1 - inv(2,3) - inv(2,0) * mr.bottomRight().x() - inv(2, 1) * mr.bottomRight().y()) / inv(2, 2); + + QVector3D tl(mr.topLeft().x(), mr.topLeft().y(), tl_z_s); + QVector3D tr(mr.topRight().x(), mr.topRight().y(), tr_z_s); + QVector3D bl(mr.bottomLeft().x(), mr.bottomLeft().y(), bl_z_s); + QVector3D br(mr.bottomRight().x(), mr.bottomRight().y(), br_z_s); + + tl = inv.map(tl); tr = inv.map(tr); bl = inv.map(bl); br = inv.map(br); + + 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)); +#else + return d->data()->transformActive.inverted().mapRect(mr); +#endif + } +} + +QPointF QSimpleCanvasItem::mapToScene(const QPointF &p) const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) { + return d->graphicsItem->mapToScene(p); + } else { + d->freshenTransforms(); + QPointF rp = d->data()->transformActive.map(p); + return d->adjustTo(rp); + } +} + +QRectF QSimpleCanvasItem::mapToScene(const QRectF &r) const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) { + return d->graphicsItem->mapToScene(r).boundingRect(); + } else { + d->freshenTransforms(); + QRectF rr = d->data()->transformActive.mapRect(r); + return d->adjustTo(rr); + } +} + +int QSimpleCanvasItemPrivate::nextTransformVersion = 1; + +void QSimpleCanvasItemPrivate::freshenTransforms() const +{ + if(freshenNeeded()) + doFreshenTransforms(); +} + +bool QSimpleCanvasItemPrivate::freshenNeeded() const +{ +#if 0 + return parent && + (data()->transformVersion == -1 || + data()->parentTransformVersion == -1 || + parent->d_func()->data()->transformVersion != data()->parentTransformVersion); +#else + const QSimpleCanvasItemPrivate *me = this; + while(me) { + if(me->data_ptr && !me->data_ptr->transformValid) + return true; + if(me->parent) + me = me->parent->d_func(); + else + me = 0; + } + return false; +#endif +} + +void QSimpleCanvasItemPrivate::doFreshenTransforms() const +{ + Q_Q(const QSimpleCanvasItem); + if(parent) + parent->d_func()->doFreshenTransforms(); + + if(freshenNeeded()) { + if(parent) + data()->transformActive = parent->d_func()->data()->transformActive; + else + data()->transformActive = QSimpleCanvas::Matrix(); + data()->transformActive.translate(q->x(), q->y()); + if(scale != 1.) { + QPointF to = transformOrigin(); + if(to.x() != 0. || to.y() != 0.) + data()->transformActive.translate(to.x(), to.y()); + data()->transformActive.scale(scale, scale); + if(to.x() != 0. || to.y() != 0.) + data()->transformActive.translate(-to.x(), -to.y()); + } + + Q_Q(const QSimpleCanvasItem); +#if defined(QFX_RENDER_OPENGL) + if(q->d_func()->data()->transformUser) + data()->transformActive *= *q->d_func()->data()->transformUser; +#endif + + if(data()->flip) { + QRectF br = q->boundingRect(); + data()->transformActive.translate(br.width() / 2., br.height() / 2); +#if defined(QFX_RENDER_OPENGL) + data()->transformActive.rotate(180, (data()->flip & QSimpleCanvasItem::VerticalFlip)?1:0, (data()->flip & QSimpleCanvasItem::HorizontalFlip)?1:0, 0); +#else + data()->transformActive.scale((data()->flip & QSimpleCanvasItem::HorizontalFlip)?-1:1, + (data()->flip & QSimpleCanvasItem::VerticalFlip)?-1:1); +#endif + data()->transformActive.translate(-br.width() / 2., -br.height() / 2); + } + } +} + +QSimpleCanvas::Matrix QSimpleCanvasItem::transform() const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) + return QSimpleCanvasConfig::transformToMatrix(d->graphicsItem->transform); + else if(d->data()->transformUser) + return *d->data()->transformUser; + else + return QSimpleCanvas::Matrix(); +} + +void QSimpleCanvasItem::setTransform(const QSimpleCanvas::Matrix &m) +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) { + d->graphicsItem->transform = QSimpleCanvasConfig::matrixToTransform(m); + d->graphicsItem->setTransform(QTransform::fromScale(d->scale, d->scale) * d->graphicsItem->transform); + } else { + if(!d->data()->transformUser) + d->data()->transformUser = new QSimpleCanvas::Matrix; + *d->data()->transformUser = m; + update(); + } +} + +QSimpleCanvasItem *QSimpleCanvasItem::mouseGrabberItem() const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) { + QGraphicsScene *s = d->graphicsItem->scene(); + if(s) { + QGraphicsItem *item = s->mouseGrabberItem(); + QSimpleGraphicsItem *dgi = static_cast<QSimpleGraphicsItem *>(item); + return dgi?static_cast<QSimpleCanvasItem*>(dgi->owner):0; + } + } else { + QSimpleCanvas *c = canvas(); + if(c) + return c->d->lastMouseItem; + } + return 0; +} + +void QSimpleCanvasItem::ungrabMouse() +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) { + d->graphicsItem->ungrabMouse(); + } else { + QSimpleCanvas *c = canvas(); + if(c && c->d->lastMouseItem == this) { + c->d->lastMouseItem->mouseUngrabEvent(); + c->d->lastMouseItem = 0; + } + } +} + +void QSimpleCanvasItem::grabMouse() +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) { + d->graphicsItem->grabMouse(); + } else { + QSimpleCanvas *c = canvas(); + if(c) { + if(c->d->lastMouseItem != this) { + if(c->d->lastMouseItem) + c->d->lastMouseItem->mouseUngrabEvent(); + c->d->lastMouseItem = this; + } + } + } +} + +bool QSimpleCanvasItem::isFocusable() const +{ + Q_D(const QSimpleCanvasItem); + return d->focusable; +} + +void QSimpleCanvasItem::setFocusable(bool f) +{ + Q_D(QSimpleCanvasItem); + d->focusable = f; +} + +bool QSimpleCanvasItem::hasFocus() const +{ + Q_D(const QSimpleCanvasItem); + return d->hasFocus; +} + +void QSimpleCanvasItemPrivate::setFocus(bool f) +{ + hasFocus = f; +} + +void QSimpleCanvasItemPrivate::setActiveFocus(bool f) +{ + hasActiveFocus = f; + + if(graphicsItem) { + if (f) { + if (!(graphicsItem->flags() & QGraphicsItem::ItemIsFocusable)) + graphicsItem->setFlag(QGraphicsItem::ItemIsFocusable); + graphicsItem->setFocus(); + } else { + graphicsItem->clearFocus(); + if ((graphicsItem->flags() & QGraphicsItem::ItemIsFocusable) && !focusable) + graphicsItem->setFlag(QGraphicsItem::ItemIsFocusable, false); + } + + } +} + +QSimpleCanvasItem::Flip QSimpleCanvasItem::flip() const +{ + Q_D(const QSimpleCanvasItem); + if(d->graphicsItem) + return NoFlip; + else if(d->data_ptr) + return d->data()->flip; + else + return NoFlip; +} + +void QSimpleCanvasItem::setFlip(Flip f) +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) + return; + + if(d->data()->flip == f) + return; + + d->data()->flip = f; + update(); +} + +/*! + Places the item under \a item in the parent item's stack. + + The item itself and \a item must be siblings, or this method has no effect. + + \sa stackOver(), stackAt() + */ +void QSimpleCanvasItem::stackUnder(QSimpleCanvasItem *item) +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) + return; // XXX + + QSimpleCanvasItem *p = parent(); + if(!p || !item || item == this) return; + + QSimpleCanvasItemPrivate *parent_d_ptr = static_cast<QSimpleCanvasItemPrivate*>(p->d_ptr); + int idx = parent_d_ptr->children.indexOf(item); + if(idx == -1) return; + + parent_d_ptr->children.removeAll(this); + idx = parent_d_ptr->children.indexOf(item); + parent_d_ptr->children.insert(idx + 1, this); + parent_d_ptr->needsZOrder = true; + + p->childrenChanged(); +} + +/*! + Places the item over \a item in the parent item's stack. + + The item itself and \a item must be siblings, or this method has no effect. + + \sa stackUnder(), stackAt() + */ +void QSimpleCanvasItem::stackOver(QSimpleCanvasItem *item) +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) + return; // XXX + + QSimpleCanvasItem *p = parent(); + if(!p || !item || item == this) return; + + QSimpleCanvasItemPrivate *parent_d_ptr = static_cast<QSimpleCanvasItemPrivate*>(p->d_ptr); + int idx = parent_d_ptr->children.indexOf(item); + if(idx == -1) return; + + parent_d_ptr->children.removeAll(this); + idx = parent_d_ptr->children.indexOf(item); + parent_d_ptr->children.insert(idx, this); + parent_d_ptr->needsZOrder = true; + + p->childrenChanged(); +} + +/*! + Places the item at position \a index in the parent item's stack. + + If index is zero or less, the item is placed at the beginning of the + stack. If the index is greater than the number of items in the stack, the + item is placed at the end. + + \sa stackOver(), stackUnder() + */ +void QSimpleCanvasItem::stackAt(int index) +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) + return; // XXX + + QSimpleCanvasItem *p = parent(); + if(!p) return; + + QSimpleCanvasItemPrivate *parent_d_ptr = static_cast<QSimpleCanvasItemPrivate*>(p->d_ptr); + parent_d_ptr->children.removeAll(this); + + if(index < 0) index = 0; + if(index > parent_d_ptr->children.size()) index = parent_d_ptr->children.size(); + + parent_d_ptr->children.insert(index, this); + parent_d_ptr->needsZOrder = true; + p->childrenChanged(); +} + +/*! + Returns the current stacking index for the child \a item. + + If \a item is not a child, -1 is returned. + + \sa stackAt() + */ +int QSimpleCanvasItem::indexForChild(QSimpleCanvasItem *item) +{ + Q_D(QSimpleCanvasItem); + return d->children.indexOf(item); +} + +bool QSimpleCanvasItem::eventFilter(QObject *o, QEvent *e) +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) { + switch(e->type()) { + case QEvent::GraphicsSceneMouseDoubleClick: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseRelease: + if(mouseFilter(static_cast<QGraphicsSceneMouseEvent *>(e))) + return true; + break; + default: + break; + } + } + + return QObject::eventFilter(o, e); +} + +void QSimpleCanvasItem::setOptions(Options options, bool set) +{ + Q_D(QSimpleCanvasItem); + Options old = (Options)d->options; + + if(options & IsFocusPanel) { + if(!set) { + qWarning("QSimpleCanvasItem::setOptions: Cannot unset IsFocusPanel"); + return; + } else if(hasChildren()) { + qWarning("QSimpleCanvasItem::setOptions: Cannot set IsFocusPanel once item has children"); + return; + } + } + + if(options & IsFocusRealm) { + if(!set) { + qWarning("QSimpleCanvasItem::setOptions: Cannot unset IsFocusRealm"); + return; + } + } + + if(set) + d->options |= options; + else + d->options &= ~options; + + if((d->options & IsFocusPanel) && (d->options & IsFocusRealm)) { + qWarning("QSimpleCanvasItem::setOptions: Cannot set both IsFocusPanel and IsFocusRealm. IsFocusRealm will be unset."); + d->options &= ~IsFocusRealm; + } + + if((old & MouseFilter) != (d->options & MouseFilter)) { + if(d->graphicsItem) { + if(d->options & MouseFilter) + d->gvAddMouseFilter(); + else + d->gvRemoveMouseFilter(); + } else { + QSimpleCanvas *c = canvas(); + if(c) { + if(d->options & MouseFilter) + c->d->installMouseFilter(this); + else + c->d->removeMouseFilter(this); + } + } + } +} + +QSimpleCanvasItem::QSimpleCanvasItem(QSimpleCanvasItemPrivate &dd, QSimpleCanvasItem *parent) +: QObject(dd, parent) +{ +} + +QSimpleCanvasItem::QSimpleCanvasItem(QSimpleCanvasItem *p) +: QObject(*(new QSimpleCanvasItemPrivate), p) +{ +} + +QSimpleCanvasItem::~QSimpleCanvasItem() +{ + Q_D(QSimpleCanvasItem); + if(d->graphicsItem) { + if ((d->options & (IsFocusPanel|IsFocusRealm)) && d->canvas) + d->canvas->d->focusPanelData.remove(this); + if (d->hasFocus && d->canvas) { + QSimpleCanvasItem *prnt = parent(); + while (prnt && !(prnt->options() & (IsFocusPanel|IsFocusRealm))) + prnt = prnt->parent(); + if (prnt && d->canvas->d->focusPanelData.value(prnt) == this) + d->canvas->d->focusPanelData.remove(prnt); + } + if(d->filter) + delete d->filter; + + qDeleteAll(children()); + if(parent()) + parent()->remChild(this); + delete d->graphicsItem; + } else { + setOptions(MouseFilter, false); + + if(d->canvas){ + if(d->canvas->focusItem() == this) + d->canvas->d->focusItem = 0; + if(d->canvas->d->lastFocusItem == this) + d->canvas->d->lastFocusItem = 0; + if(d->hasBeenActiveFocusPanel) + d->canvas->d->clearFocusPanel(this); + if(d->hasFocus) + d->canvas->d->clearFocusItem(this); + } + + while(!d->children.isEmpty()) { + QSimpleCanvasItem *child = d->children.takeFirst(); + delete child; + } + + delete d->filter; + + if (parent() && d->data_ptr && d->data()->dirty) { + QSimpleCanvasLayer *l = parent()->layer(); + if(l) { + l->remDirty(this); + } + } + if (d->parent) + d->parent->remChild(this); + + + if(d->data_ptr) + delete d->data_ptr; + } +} + +QSimpleCanvasItem::operator QGraphicsItem *() +{ + Q_D(QSimpleCanvasItem); + if(!d->graphicsItem) { + if(parent()) { + qWarning("QSimpleCanvasItem: Only the root item can be converted into a QGraphicsItem"); + return 0; + } + d->convertToGraphicsItem(); + } + return d->graphicsItem; +} + +QPointF QSimpleCanvasItemPrivate::transformOrigin() const +{ + Q_Q(const QSimpleCanvasItem); + + QRectF br = q->boundingRect(); + + switch(origin) { + default: + case QSimpleCanvasItem::TopLeft: + return QPointF(0, 0); + case QSimpleCanvasItem::TopCenter: + return QPointF(br.width() / 2., 0); + case QSimpleCanvasItem::TopRight: + return QPointF(br.width(), 0); + case QSimpleCanvasItem::MiddleLeft: + return QPointF(0, br.height() / 2.); + case QSimpleCanvasItem::Center: + return QPointF(br.width() / 2., br.height() / 2.); + case QSimpleCanvasItem::MiddleRight: + return QPointF(br.width(), br.height() / 2.); + case QSimpleCanvasItem::BottomLeft: + return QPointF(0, br.height()); + case QSimpleCanvasItem::BottomCenter: + return QPointF(br.width() / 2., br.height()); + case QSimpleCanvasItem::BottomRight: + return QPointF(br.width(), br.height()); + } +} + +void QSimpleCanvasItemPrivate::setParentInternal(QSimpleCanvasItem *p) +{ + Q_Q(QSimpleCanvasItem); + QSimpleCanvasItem *oldParent = parent; + if(graphicsItem) { + if(oldParent) + oldParent->remChild(q); + + parent = p; + graphicsItem->setParentItem(p->d_func()->graphicsItem); + + if(parent) + p->addChild(q); + + } else { + bool canvasChange = false; + if(p) + canvasChange = (p->d_func()->canvas != canvas); + QSimpleCanvas *old = canvas; + + QSimpleCanvasLayer *o = q->layer(); + if(q->parent()) { + q->update(); + q->parent()->remChild(q); + } + parent = p; + QSimpleCanvasLayer *n = 0; + if(q->parent()) { + q->parent()->addChild(q); + n = q->layer(); + } + + if(o != n) { + data()->dirty = false; + data()->transformValid = false; + if(o) o->remDirty(q); + if(n) n->addDirty(q); + } + + if(canvasChange) + canvasChanged(p->d_func()->canvas, old); + + q->update(); + } +} + +void QSimpleCanvasItemPrivate::convertToGraphicsItem(QGraphicsItem *parent) +{ + Q_Q(QSimpleCanvasItem); + Q_ASSERT(!graphicsItem); + graphicsItem = new QSimpleGraphicsItem(q); + if(parent) + graphicsItem->setParentItem(parent); + + QSimpleCanvasItemData *old = data_ptr; + data_ptr = 0; + + if(old) { + q->QSimpleCanvasItem::setX(old->x); + q->QSimpleCanvasItem::setY(old->y); + q->QSimpleCanvasItem::setZ(old->z); + q->QSimpleCanvasItem::setVisible(old->visible); + if(old->transformUser) + q->QSimpleCanvasItem::setTransform(*old->transformUser); + q->QSimpleCanvasItem::setFlip(old->flip); + q->QSimpleCanvasItem::setAcceptedMouseButtons((Qt::MouseButtons)old->buttons); + delete old; + } + + if(scale != 1) { + qreal s = scale; + scale = 1; + q->QSimpleCanvasItem::setScale(s); + } + + q->setClipType(clip); + + for(int ii = 0; ii < children.count(); ++ii) { + static_cast<QSimpleCanvasItemPrivate*>(children.at(ii)->d_ptr)->convertToGraphicsItem(graphicsItem); + if(children.at(ii)->z() == 0) + children.at(ii)->setZ(ii); + } +} + +/*! + \fn void QSimpleCanvasItem::setParent(QSimpleCanvasItem *parent) + + Sets the parent of the item to \a parent. + */ +void QSimpleCanvasItem::setParent(QSimpleCanvasItem *p) +{ + Q_D(QSimpleCanvasItem); + if(p == parent() || !p) return; + + QObject::setParent(p); + + if(d->graphicsItem && !static_cast<QSimpleCanvasItemPrivate*>(p->d_ptr)->graphicsItem) + qWarning("QSimpleCanvasItem: Cannot reparent a QGraphicsView item to a QSimpleCanvas item"); + + if(static_cast<QSimpleCanvasItemPrivate*>(p->d_ptr)->graphicsItem && !d->graphicsItem) { + d->setParentInternal(0); + d->convertToGraphicsItem(); + } + + QSimpleCanvasItem *oldParent = d->parent; + d->setParentInternal(p); + parentChanged(p, oldParent); +} + +int QSimpleCanvasItemPrivate::dump(int indent) +{ + Q_Q(QSimpleCanvasItem); + QByteArray ba(indent * 2, ' '); + + QByteArray state; + if(options & QSimpleCanvasItem::MouseFilter) + state.append("i"); + else + state.append("-"); + if(options & QSimpleCanvasItem::HoverEvents) + state.append("h"); + else + state.append("-"); + if(options & QSimpleCanvasItem::MouseEvents) + state.append("m"); + else + state.append("-"); + if(options & QSimpleCanvasItem::HasContents) + state.append("c"); + else + state.append("-"); + if(options & QSimpleCanvasItem::SimpleItem) + state.append("s"); + else + state.append("-"); + if(options & QSimpleCanvasItem::IsFocusPanel) { + if(q->activeFocusPanel()) + state.append("P"); + else + state.append("p"); + } else { + state.append("-"); + } + if(options & QSimpleCanvasItem::IsFocusRealm) + state.append("r"); + else + state.append("-"); + if(q->hasFocus()) { + if(q->hasActiveFocus()) + state.append("F"); + else + state.append("f"); + } else { + if(q->hasActiveFocus()) + state.append("X"); + else + state.append("-"); + } + + QByteArray name; + QFxItem *i = qobject_cast<QFxItem *>(q); + if(i) + name = i->id().toLatin1(); + qWarning().nospace() << ba.constData() << state.constData() << " " << children.count() << " " << q << " " << name.constData(); + + int rv = 0; + + for(int ii = 0; ii < children.count(); ++ii) + rv += children.at(ii)->d_func()->dump(indent + 1); + + return rv + 1; +} + +bool QSimpleCanvasItemPrivate::checkFocusState(FocusStateCheckDatas d, + FocusStateCheckRDatas *r) +{ + Q_Q(QSimpleCanvasItem); + + bool rv = true; + bool isRealm = (options & QSimpleCanvasItem::IsFocusPanel || + options & QSimpleCanvasItem::IsFocusRealm); + + if(options & QSimpleCanvasItem::IsFocusPanel) { + + if(q->activeFocusPanel()) { + if(d & InActivePanel) { + qWarning() << "State ERROR: Nested active focus panels"; + rv = false; + } + + d |= InActivePanel; + } else { + d &= ~InActivePanel; + } + + } + + if(q->hasActiveFocus()) { + if(!(d & InActivePanel)) { + qWarning() << "State ERROR: Active focus in non-active panel"; + rv = false; + } + + if(d & InRealm && !(d & InActiveFocusedRealm)) { + qWarning() << "State ERROR: Active focus in non-active-focused realm"; + rv = false; + } + + if(!q->hasFocus()) { + qWarning() << "State ERROR: Active focus on element that does not have focus"; + rv = false; + } + + if(*r & SeenActiveFocus) { + qWarning() << "State ERROR: Two active focused elements in same realm"; + rv = false; + } + + *r |= SeenActiveFocus; + } + + if(q->hasFocus()) { + if(*r & SeenFocus) { + qWarning() << "State ERROR: Two focused elements in same realm"; + rv = false; + } + + *r |= SeenFocus; + } + + if(options & QSimpleCanvasItem::IsFocusRealm) { + d |= InRealm; + + if(q->hasActiveFocus()) + d |= InActiveFocusedRealm; + else + d &= ~InActiveFocusedRealm; + } + + FocusStateCheckRDatas newR = NoCheckRData; + if(isRealm) + r = &newR; + + for(int ii = 0; ii < children.count(); ++ii) + rv &= children.at(ii)->d_func()->checkFocusState(d, r); + + return rv; +} + +bool QSimpleCanvasItem::activeFocusPanel() const +{ + QSimpleCanvas *c = canvas(); + if(!c) { + Q_D(const QSimpleCanvasItem); + return d->wantsActiveFocusPanelPendingCanvas; + } else { + return c->activeFocusPanel() == this; + } +} + +void QSimpleCanvasItem::setActiveFocusPanel(bool b) +{ + if(!(options() & IsFocusPanel)) { + qWarning("QSimpleCanvasItem::setActiveFocusPanel: Item is not a focus panel"); + return; + } + + QSimpleCanvas *c = canvas(); + Q_D(QSimpleCanvasItem); + if(c) { + if(b) { + d->hasBeenActiveFocusPanel = true; + c->d->setActiveFocusPanel(this); + } else if(d->hasBeenActiveFocusPanel) { + d->hasBeenActiveFocusPanel = false; + c->d->clearFocusPanel(this); + } + } else { + d->wantsActiveFocusPanelPendingCanvas = b; + } +} + +bool QSimpleCanvasItem::hasActiveFocus() const +{ + Q_D(const QSimpleCanvasItem); + return d->hasActiveFocus; +} + +QSimpleCanvasItem *QSimpleCanvasItem::findFirstFocusChild() const +{ + Q_D(const QSimpleCanvasItem); + + const QList<QSimpleCanvasItem *> &children = d->children; + + for (int i = 0; i < children.count(); ++i) { + QSimpleCanvasItem *child = children.at(i); + if(child->options() & IsFocusPanel) + continue; + + if (child->isFocusable()) + return child; + + QSimpleCanvasItem *testFocus = child->findFirstFocusChild(); + if (testFocus) + return testFocus; + } + + return 0; +} + +QSimpleCanvasItem *QSimpleCanvasItem::findLastFocusChild() const +{ + Q_D(const QSimpleCanvasItem); + + const QList<QSimpleCanvasItem *> &children = d->children; + + for (int i = children.count()-1; i >= 0; --i) { + QSimpleCanvasItem *child = children.at(i); + if(child->options() & IsFocusPanel) + continue; + + if (child->isFocusable()) + return child; + QSimpleCanvasItem *testFocus = child->findLastFocusChild(); + if (testFocus) + return testFocus; + } + + return 0; +} + +QSimpleCanvasItem *QSimpleCanvasItem::findPrevFocus(QSimpleCanvasItem *item) +{ + QSimpleCanvasItem *focusChild = item->findLastFocusChild(); + if (focusChild) + return focusChild; + + if(item->options() & IsFocusPanel) { + if(item->isFocusable()) + return item; + else + return 0; + } + + QSimpleCanvasItem *parent = item->parent(); + while (parent) { + const QList<QSimpleCanvasItem *> &children = parent->d_func()->children; + + int idx = children.indexOf(item); + QSimpleCanvasItem *testFocus = 0; + if (idx > 0) { + while (--idx >= 0) { + testFocus = children.at(idx); + if(testFocus->options() & IsFocusPanel) + continue; + if (testFocus->isFocusable()) + return testFocus; + testFocus = testFocus->findLastFocusChild(); + if (testFocus) + return testFocus; + } + } + if(parent->options() & IsFocusPanel) { + if(parent->isFocusable()) + return parent; + else + return 0; + } + item = parent; + parent = parent->parent(); + } + + return 0; +} + +QSimpleCanvasItem *QSimpleCanvasItem::findNextFocus(QSimpleCanvasItem *item) +{ + QSimpleCanvasItem *focusChild = item->findFirstFocusChild(); + if (focusChild) + return focusChild; + + if(item->options() & IsFocusPanel) { + if(item->isFocusable()) + return item; + else + return 0; + } + + QSimpleCanvasItem *parent = item->parent(); + while (parent) { + const QList<QSimpleCanvasItem *> &children = parent->d_func()->children; + + int idx = children.indexOf(item); + QSimpleCanvasItem *testFocus = 0; + if (idx >= 0) { + while (++idx < children.count()) { + testFocus = children.at(idx); + if(testFocus->options() & IsFocusPanel) + continue; + if (testFocus->isFocusable()) + return testFocus; + testFocus = testFocus->findFirstFocusChild(); + if (testFocus) + return testFocus; + } + } + if(parent->options() & IsFocusPanel) { + if(parent->isFocusable()) + return parent; + else + return 0; + } + item = parent; + parent = parent->parent(); + } + + return 0; +} + +QImage QSimpleCanvasItem::string(const QString &str, const QColor &c, const QFont &f) +{ + QFontMetrics fm(f); + QSize size(fm.width(str), fm.height()*(str.count(QLatin1Char('\n'))+1)); //fm.boundingRect(str).size(); + QImage img(size, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter p(&img); + p.setPen(c); + p.setFont(f); + p.drawText(img.rect(), Qt::AlignVCenter, str); + return img; +} + +void QSimpleCanvasItem::geometryChanged(const QRectF &, const QRectF &) +{ +} + +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvasitem.h b/src/declarative/canvas/qsimplecanvasitem.h new file mode 100644 index 0000000..d51f2c8 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvasitem.h @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QSIMPLECANVASITEM_H +#define QSIMPLECANVASITEM_H + +#include <gfxtimeline.h> +#include <qfxglobal.h> +#include <qsimplecanvas.h> +#include <QObject> +#include <QGraphicsItem> +class QPainter; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QRect; +class QSimpleCanvas; +class QMouseEvent; +class QKeyEvent; +class QSimpleCanvasItemPrivate; +class QSimpleCanvasLayer; +class QPointF; +class QRectF; +class QGraphicsSceneHoverEvent; +class QSimpleCanvasFilter; +class GLTexture; +class QGLShaderProgram; + +class Q_DECLARATIVE_EXPORT QSimpleCanvasItem : public QObject +{ + Q_OBJECT + Q_CAST_INTERFACES(QGraphicsItem) + Q_DECLARE_PRIVATE(QSimpleCanvasItem) + Q_ENUMS(TransformOrigin) + Q_PROPERTY(TransformOrigin transformOrigin READ transformOrigin WRITE setTransformOrigin); + +public: + enum ClipType { NoClip = 0x00, + ClipToHeight = 0x01, + ClipToWidth = 0x02, + ClipToRect = 0x03 }; + enum Option { NoOption = 0x00000000, + MouseFilter = 0x00000001, + HoverEvents = 0x00000002, + MouseEvents = 0x00000004, + HasContents = 0x00000008, + SimpleItem = 0x00000010, + IsFocusPanel = 0x00000020, + IsFocusRealm = 0x00000040, + AcceptsInputMethods = 0x00000080}; + Q_DECLARE_FLAGS(Options, Option); + + QSimpleCanvasItem(QSimpleCanvasItem *parent=0); + virtual ~QSimpleCanvasItem(); + operator QGraphicsItem *(); + + bool clip() const; + void setClip(bool); + ClipType clipType() const; + void setClipType(ClipType); + + Options options() const; + void setOptions(Options, bool set = true); + + Qt::MouseButtons acceptedMouseButtons() const; + void setAcceptedMouseButtons(Qt::MouseButtons buttons); + + qreal x() const; + qreal y() const; + qreal z() const; + QPointF pos() const; + void setX(qreal); + void setY(qreal); + virtual void setZ(qreal); + void setPos(const QPointF &); + + qreal width() const; + void setWidth(qreal); + void setImplicitWidth(qreal); + bool widthValid() const; + qreal height() const; + void setHeight(qreal); + void setImplicitHeight(qreal); + bool heightValid() const; + + QPointF scenePos() const; + + enum TransformOrigin { + TopLeft, TopCenter, TopRight, + MiddleLeft, Center, MiddleRight, + BottomLeft, BottomCenter, BottomRight + }; + TransformOrigin transformOrigin() const; + void setTransformOrigin(TransformOrigin); + QPointF transformOriginPoint() const; + + + qreal scale() const; + virtual void setScale(qreal); + + enum Flip { NoFlip = 0, + VerticalFlip = 0x01, + HorizontalFlip = 0x02, + VerticalAndHorizontalFlip = 0x03 }; + Flip flip() const; + void setFlip(Flip); + + qreal visible() const; + virtual void setVisible(qreal); + bool isVisible() const; + + QSimpleCanvas *canvas() const; + + QSimpleCanvasItem *parent() const; + void setParent(QSimpleCanvasItem *); + void stackUnder(QSimpleCanvasItem *); + void stackOver(QSimpleCanvasItem *); + void stackAt(int idx); + int indexForChild(QSimpleCanvasItem *); + + QRect itemBoundingRect(); + + class GLPainter + { + public: + GLPainter(QSimpleCanvasItem *i) : item(i), activeOpacity(1) {} + QSimpleCanvasItem *item; + QSimpleCanvas::Matrix activeTransform; + qreal activeOpacity; + QRect sceneClipRect; + + QGLShaderProgram *useTextureShader(); + QGLShaderProgram *useColorShader(const QColor &); + void drawImage(const QPointF &, const GLTexture &); + void drawImage(const QRectF &, const GLTexture &); + private: + GLPainter(const GLPainter &); + GLPainter &operator=(const GLPainter &); + }; + + + QRectF boundingRect() const; + virtual void paintContents(QPainter &); + virtual void paintGLContents(GLPainter &); + virtual uint glSimpleItemData(float *vertices, float *texVertices, + GLTexture **texture, uint count); + + void update(); + + virtual QSimpleCanvasLayer *layer(); + + bool hasChildren() const; + const QList<QSimpleCanvasItem *> &children() const; + + QPointF mapFromScene(const QPointF &) const; + QRectF mapFromScene(const QRectF &) const; + QPointF mapToScene(const QPointF &) const; + QRectF mapToScene(const QRectF &) const; + + QSimpleCanvas::Matrix transform() const; + void setTransform(const QSimpleCanvas::Matrix &); + + QSimpleCanvasFilter *filter() const; + void setFilter(QSimpleCanvasFilter *); + + QSimpleCanvasItem *mouseGrabberItem() const; + void ungrabMouse(); + void grabMouse(); + + virtual bool isFocusable() const; + void setFocusable(bool); + virtual bool hasFocus() const; + void setFocus(bool); + bool activeFocusPanel() const; + void setActiveFocusPanel(bool); + + bool hasActiveFocus() const; + + QSimpleCanvasItem *findFirstFocusChild() const; + QSimpleCanvasItem *findLastFocusChild() const; + static QSimpleCanvasItem *findPrevFocus(QSimpleCanvasItem *item); + static QSimpleCanvasItem *findNextFocus(QSimpleCanvasItem *item); + + GLBasicShaders *basicShaders() const; + + static QImage string(const QString &, const QColor & = Qt::black, const QFont & = QFont()); + +protected: + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual void addChild(QSimpleCanvasItem *); + virtual void remChild(QSimpleCanvasItem *); + virtual void canvasChanged(); + virtual void childrenChanged(); + virtual void parentChanged(QSimpleCanvasItem *, QSimpleCanvasItem *); + virtual void focusChanged(bool); + virtual void activeFocusChanged(bool); + virtual bool eventFilter(QObject *, QEvent *); + +public: + // Events + virtual bool mouseFilter(QGraphicsSceneMouseEvent *); + virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *); + virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseUngrabEvent(); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void focusOutEvent(QFocusEvent *e); + virtual void focusInEvent(QFocusEvent *e); + virtual void activePanelInEvent(); + virtual void activePanelOutEvent(); + virtual void inputMethodEvent(QInputMethodEvent* event); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + +private: + friend class QSimpleCanvas; + friend class QSimpleCanvasPrivate; + friend class QSimpleCanvasRootLayer; + friend class QSimpleCanvasItem::GLPainter; + friend class QSimpleCanvasFilter; + friend class QGraphicsQSimpleCanvasItem; + friend class QSimpleGraphicsItem; + friend class CanvasEGLWidget; + friend class QSimpleCanvasFilterPrivate; + +public: + QSimpleCanvasItem(QSimpleCanvasItemPrivate &dd, QSimpleCanvasItem *parent); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSimpleCanvasItem::Options); + +class QSimpleCanvasLayer : public QSimpleCanvasItem +{ +public: + QSimpleCanvasLayer(QSimpleCanvasItem *parent); + + virtual void addChild(QSimpleCanvasItem *); + virtual void addDirty(QSimpleCanvasItem *); + virtual void remDirty(QSimpleCanvasItem *); + virtual QSimpleCanvasLayer *layer(); + +private: + friend class QSimpleCanvas; + friend class QSimpleCanvasRootLayer; + QSimpleCanvasLayer(); +}; + + + +#endif // _GFXCANVASITEM_H_ + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/canvas/qsimplecanvasitem_p.h b/src/declarative/canvas/qsimplecanvasitem_p.h new file mode 100644 index 0000000..7f66be5 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvasitem_p.h @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QSIMPLECANVASITEM_P_H +#define QSIMPLECANVASITEM_P_H + +#include "private/qobject_p.h" +#include "qsimplecanvas.h" +#include "qsimplecanvasitem.h" +#include "qsimplecanvasfilter.h" + +#if defined(QFX_RENDER_OPENGL2) +#include <glbasicshaders.h> +#endif + +#include "qgraphicsitem.h" + + +QT_BEGIN_NAMESPACE +class QSimpleGraphicsItem : public QGraphicsItem +{ +public: + QSimpleGraphicsItem(QSimpleCanvasItem *); + virtual ~QSimpleGraphicsItem(); + + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + virtual QRectF boundingRect() const; + + QTransform transform; +protected: + virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + virtual bool sceneEvent(QEvent *); + virtual QVariant itemChange(GraphicsItemChange, const QVariant &); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void focusInEvent(QFocusEvent *event); + +private: + friend class QSimpleCanvasItem; + QSimpleCanvasItem *owner; +}; + +class QSimpleCanvasItemData +{ +public: + QSimpleCanvasItemData(); + ~QSimpleCanvasItemData(); + + // 5 bits is all that's needed to store Qt::MouseButtons + int buttons:5; + QSimpleCanvasItem::Flip flip:2; + bool dirty:1; + bool transformValid:1; + + qreal x; + qreal y; + qreal z; + float visible; + + QSimpleCanvas::Matrix *transformUser; + QSimpleCanvas::Matrix transformActive; + + float activeOpacity; + +#if defined(QFX_RENDER_OPENGL) + QRectF lastPaintRect; +#else + QRect lastPaintRect; +#endif +}; + +class QSimpleCanvasFilter; +class QGraphicsQSimpleCanvasItem; +class QSimpleCanvasItemPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QSimpleCanvasItem); +public: + QSimpleCanvasItemPrivate() + : parent(0), canvas(0), filter(0), clip(QSimpleCanvasItem::NoClip), + origin(QSimpleCanvasItem::TopLeft), options(QSimpleCanvasItem::NoOption), + focusable(false), wantsActiveFocusPanelPendingCanvas(false), + hasBeenActiveFocusPanel(false), + hasFocus(false), hasActiveFocus(false), needsZOrder(false), + widthValid(false), heightValid(false), width(0), height(0), scale(1), + graphicsItem(0), data_ptr(0) + { + } + + virtual ~QSimpleCanvasItemPrivate() {} + + QSimpleCanvasItem *parent; + QSimpleCanvas *canvas; + QList<QSimpleCanvasItem *> children; + + QSimpleCanvasFilter *filter; + + QSimpleCanvasItem::ClipType clip:3; + QSimpleCanvasItem::TransformOrigin origin:4; + int options:8; + bool focusable:1; + bool wantsActiveFocusPanelPendingCanvas:1; + bool hasBeenActiveFocusPanel:1; + bool hasFocus:1; + bool hasActiveFocus:1; + bool needsZOrder:1; + bool widthValid:1; + bool heightValid:1; + + void setFocus(bool f); + void setActiveFocus(bool f); + + qreal width; + qreal height; + qreal scale; + + QSimpleGraphicsItem *graphicsItem; + inline QSimpleCanvasItemData *data() const { + if(!data_ptr) data_ptr = new QSimpleCanvasItemData; + return data_ptr; + } + mutable QSimpleCanvasItemData *data_ptr; + + void gvRemoveMouseFilter(); + void gvAddMouseFilter(); + + void canvasChanged(QSimpleCanvas *newCanvas, QSimpleCanvas *oldCanvas); + + void freshenTransforms() const; + + QPointF adjustFrom(const QPointF &) const; + QRectF adjustFrom(const QRectF &) const; + QPointF adjustTo(const QPointF &) const; + QRectF adjustTo(const QRectF &) const; + + QPointF transformOrigin() const; + + void setParentInternal(QSimpleCanvasItem *); + void convertToGraphicsItem(QGraphicsItem * = 0); + +#if defined(QFX_RENDER_QPAINTER) + void paint(QPainter &); + void paintChild(QPainter &, QSimpleCanvasItem *); + QRect setupPainting(int version, const QRect &bounding); +#else + struct GLPaintParameters + { + QRect sceneRect; + QRectF boundingRect; + QRect clipRect; +#if defined(QFX_RENDER_OPENGL2) + uchar stencilValue; +#endif + float opacity; + bool forceParamRefresh; + }; +#if defined(QFX_RENDER_OPENGL2) + QRectF setupPainting(int version, const QRect &bounding); +#elif defined(QFX_RENDER_OPENGL1) + QRectF setupPainting(int version, const QRect &bounding, unsigned int *zero); +#endif + void setupChildState(QSimpleCanvasItem *); + + void paint(GLPaintParameters &, QSimpleCanvasFilter::Layer = QSimpleCanvasFilter::All); +#if defined(QFX_RENDER_OPENGL1) + void paintNoClip(GLPaintParameters &, QSimpleCanvasFilter::Layer = QSimpleCanvasFilter::All); +#endif + void paintChild(const GLPaintParameters &, QSimpleCanvasItem *); + void simplePaintChild(const GLPaintParameters &, QSimpleCanvasItem *); + + inline GLBasicShaders *basicShaders() const; + + QSimpleCanvas::Matrix localTransform() const; + +#endif + + void zOrderChildren(); + static int nextTransformVersion; + bool freshenNeeded() const; + void doFreshenTransforms() const; + + // Debugging + int dump(int); + enum FocusStateCheckData { NoCheckData = 0x00, + InActivePanel = 0x01, + InRealm = 0x02, + InActiveFocusedRealm = 0x04 + }; + Q_DECLARE_FLAGS(FocusStateCheckDatas, FocusStateCheckData); + enum FocusStateCheckRData { NoCheckRData = 0x00, + SeenFocus = 0x01, + SeenActiveFocus = 0x02 }; + Q_DECLARE_FLAGS(FocusStateCheckRDatas, FocusStateCheckRData); + bool checkFocusState(FocusStateCheckDatas, FocusStateCheckRDatas *); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QSimpleCanvasItemPrivate::FocusStateCheckDatas); +Q_DECLARE_OPERATORS_FOR_FLAGS(QSimpleCanvasItemPrivate::FocusStateCheckRDatas); + +#endif // QSIMPLECANVASITEM_P_H + +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvasserver.cpp b/src/declarative/canvas/qsimplecanvasserver.cpp new file mode 100644 index 0000000..7eebe65 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvasserver.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qsimplecanvasserver_p.h" +#include "qdebug.h" +#ifndef Q_OS_WIN32 +#include <arpa/inet.h> +#endif + +QT_BEGIN_NAMESPACE + +QSimpleCanvasServer::QSimpleCanvasServer(int port, QObject *parent) +: QObject(parent), _tcpServer(new QTcpServer(this)) +{ + QObject::connect(_tcpServer, SIGNAL(newConnection()), + this, SLOT(newConnection())); + + _time.start(); + + if(!_tcpServer->listen(QHostAddress::Any, port)) { + qWarning() << "QSimpleCanvasServer: Cannot listen on port" << port; + return; + } +} + +void QSimpleCanvasServer::newConnection() +{ + QTcpSocket *socket = _tcpServer->nextPendingConnection(); + QObject::connect(socket, SIGNAL(disconnected()), + this, SLOT(disconnected())); + _tcpClients << socket; +} + +void QSimpleCanvasServer::addTiming(quint32 paint, + quint32 repaint, + quint32 timeBetweenFrames) +{ + /* + quint32 data[3]; + data[0] = ::htonl(paint); + data[1] = ::htonl(repaint); + data[2] = ::htonl(timeBetweenFrames); + */ + + int e = _time.elapsed(); + QString d = QString::number(paint) + QLatin1String(" ") + QString::number(repaint) + QLatin1String(" ") + QString::number(timeBetweenFrames) + QLatin1String(" ") + QString::number(e) + QLatin1String("\n"); + QByteArray ba = d.toLatin1(); + + // XXX + for(int ii = 0; ii < _tcpClients.count(); ++ii) +// _tcpClients.at(ii)->write((const char *)data, 12); + _tcpClients.at(ii)->write(ba.constData(), ba.length()); +} + +void QSimpleCanvasServer::disconnected() +{ + QTcpSocket *socket = static_cast<QTcpSocket *>(sender()); + + for(int ii = 0; ii < _tcpClients.count(); ++ii) { + if(_tcpClients.at(ii) == socket) { + socket->disconnect(); + socket->deleteLater(); + _tcpClients.removeAt(ii); + return; + } + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/canvas/qsimplecanvasserver_p.h b/src/declarative/canvas/qsimplecanvasserver_p.h new file mode 100644 index 0000000..7d53357 --- /dev/null +++ b/src/declarative/canvas/qsimplecanvasserver_p.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 QtDeclarative 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 QSIMPLECANVASSERVER_P_H +#define QSIMPLECANVASSERVER_P_H + +#include "qobject.h" +#include "qtcpserver.h" +#include "qtcpsocket.h" +#include "qdatetime.h" + + +QT_BEGIN_NAMESPACE +class QSimpleCanvasServer : public QObject +{ +Q_OBJECT +public: + QSimpleCanvasServer(int port, QObject *parent); + + void addTiming(quint32, quint32, quint32); + +private Q_SLOTS: + void newConnection(); + void disconnected(); + +private: + QTcpServer *_tcpServer; + QList<QTcpSocket *> _tcpClients; + QTime _time; +}; + +QT_END_NAMESPACE +#endif // GFXCANVASSERVER_P_H diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro new file mode 100644 index 0000000..30704d0 --- /dev/null +++ b/src/declarative/declarative.pro @@ -0,0 +1,25 @@ +TARGET = QtDeclarative +QPRO_PWD = $$PWD +QT = core gui xml script network +contains(QT_CONFIG, svg): QT += svg +DEFINES += QT_BUILD_DECLARATIVE_LIB +DEFINES += QT_NO_USING_NAMESPACE +win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 +solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 + +unix:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui QtXml + +include(../qbase.pri) + +#modules +include(3rdparty/3rdparty.pri) +include(util/util.pri) +include(fx/fx.pri) +include(canvas/canvas.pri) +include(qml/qml.pri) +include(timeline/timeline.pri) +include(extra/extra.pri) +include(widgets/widgets.pri) +include(test/test.pri) + +contains(QT_CONFIG, opengles2)|contains(QT_CONFIG, opengles1):include(opengl/opengl.pri) diff --git a/src/declarative/extra/extra.pri b/src/declarative/extra/extra.pri new file mode 100644 index 0000000..83978f1 --- /dev/null +++ b/src/declarative/extra/extra.pri @@ -0,0 +1,24 @@ +SOURCES += \ + extra/qnumberformat.cpp \ + extra/qmlnumberformatter.cpp \ + extra/qfxintegermodel.cpp + +HEADERS += \ + extra/qnumberformat.h \ + extra/qmlnumberformatter.h \ + extra/qfxintegermodel.h + +contains(QT_CONFIG, xmlpatterns) { + QT+=xmlpatterns + SOURCES += extra/qmlxmllistmodel.cpp + HEADERS += extra/qmlxmllistmodel.h +} + +# SQL is permanently enabled :-/ +#contains(QT_CONFIG, sql) { + QT+= sql + SOURCES += extra/qmlsqlquery.cpp \ + extra/qmlsqlconnection.cpp + HEADERS += extra/qmlsqlquery.h \ + extra/qmlsqlconnection.h +#} diff --git a/src/declarative/extra/qfxintegermodel.cpp b/src/declarative/extra/qfxintegermodel.cpp new file mode 100644 index 0000000..3c4d0d9 --- /dev/null +++ b/src/declarative/extra/qfxintegermodel.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxintegermodel.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxIntegerModel, IntegerModel); + +class QFxIntegerModelPrivate +{ +public: + QFxIntegerModelPrivate() : min(0), max(0) {} + int min; + int max; +}; + +QFxIntegerModel::QFxIntegerModel(QObject *parent) + : QListModelInterface(parent) +{ + d = new QFxIntegerModelPrivate; +} + +QFxIntegerModel::~QFxIntegerModel() +{ + delete d; +} + +int QFxIntegerModel::minimum() const +{ + return d->min; +} + +void QFxIntegerModel::setMinimum(int min) +{ + d->min = min; +} + +int QFxIntegerModel::maximum() const +{ + return d->max; +} + +void QFxIntegerModel::setMaximum(int max) +{ + d->max = max; +} + +int QFxIntegerModel::count() const +{ + return qMax(0, d->max - d->min + 1); +} + +QHash<int,QVariant> QFxIntegerModel::data(int index, const QList<int> &roles) const +{ + QHash<int,QVariant> returnHash; + + for (int i = 0; i < roles.size(); ++i) { + int role = roles.at(i); + QVariant info; + switch(role) { + case Qt::DisplayRole: + info = QVariant(QString::number(d->min+index)); + break; + default: + break; + } + returnHash.insert(role, info); + } + return returnHash; +} + +QString QFxIntegerModel::toString(int role) const +{ + switch(role) { + case Qt::DisplayRole: + return QLatin1String("display"); + default: + return QLatin1String(""); + } +} + +QList<int> QFxIntegerModel::roles() const +{ + return QList<int>() << Qt::DisplayRole; +} + +QT_END_NAMESPACE diff --git a/src/declarative/extra/qfxintegermodel.h b/src/declarative/extra/qfxintegermodel.h new file mode 100644 index 0000000..3a48a56 --- /dev/null +++ b/src/declarative/extra/qfxintegermodel.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 QtDeclarative 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 QFXINTMODEL_H +#define QFXINTMODEL_H + +#include <QObject> +#include "qml.h" +#include <qlistmodelinterface.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxIntegerModelPrivate; +class Q_DECLARATIVE_EXPORT QFxIntegerModel : public QListModelInterface +{ + Q_OBJECT +public: + QFxIntegerModel(QObject *parent=0); + ~QFxIntegerModel(); + + Q_PROPERTY(int minimum READ minimum WRITE setMinimum); + int minimum() const; + void setMinimum(int); + + Q_PROPERTY(int maximum READ maximum WRITE setMaximum); + int maximum() const; + void setMaximum(int); + + int count() const; + QHash<int, QVariant> data(int index, const QList<int> &roles) const; + QList<int> roles() const; + QString toString(int role) const; + +private: + QFxIntegerModelPrivate *d; +}; + +QML_DECLARE_TYPE(QFxIntegerModel); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/extra/qmlnumberformatter.cpp b/src/declarative/extra/qmlnumberformatter.cpp new file mode 100644 index 0000000..a12c4e6 --- /dev/null +++ b/src/declarative/extra/qmlnumberformatter.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 QtDeclarative 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 "qmlnumberformatter.h" +#include "private/qobject_p.h" + + +QT_BEGIN_NAMESPACE +//TODO: set locale +// docs +// this is a wrapper around qnumberformat (test integration) +// if number or format haven't been explictly set, text should be an empty string + +class QmlNumberFormatterPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlNumberFormatter) +public: + QmlNumberFormatterPrivate() : locale(QLocale::system()), number(0), classComplete(true) {} + + void updateText(); + + QLocale locale; + QString format; + QNumberFormat numberFormat; + QString text; + qreal number; + bool classComplete; +}; +/*! + \qmlclass NumberFormatter + \brief The NumberFormatter allows you to control the format of a number string. + + The format property documentation has more details on how the format can be manipulated. + + In the following example, the text element will display the text "1,234.57". + \code + <NumberFormatter id="Formatter" number="1234.5678" format="##,##0.##"/> + <Text text="{Formatter.text}"/> + \endcode + + */ +/*! + \class QmlNumberFormatter + \ingroup utility + \brief The QmlNumberFormatter class allows you to format a number to a particular string format/locale specific number format. +*/ + +QmlNumberFormatter::QmlNumberFormatter(QObject *parent) +: QObject(*(new QmlNumberFormatterPrivate), parent) +{ +} + +QmlNumberFormatter::~QmlNumberFormatter() +{ +} + +/*! + \qmlproperty string NumberFormatter::text + + The number in the specified format. + <br> + If no format is specified the text will be empty. +*/ + +QString QmlNumberFormatter::text() const +{ + Q_D(const QmlNumberFormatter); + return d->text; +} + +/*! + \qmlproperty qreal NumberFormatter::number + + A single point precision number. (Doubles are not yet supported) + +*/ +qreal QmlNumberFormatter::number() const +{ + Q_D(const QmlNumberFormatter); + return d->number; +} + +/*! + \qmlproperty string NumberFormatter::format + + The particular format the number will adhere to during the conversion to text. + <br> + The format syntax follows a style similar to the Unicode Standard (UTS35). + + The table below shows the characters, patterns that can be used in the format. + + <table border="0" align="center"> + <tr style="background-color: #D6E2E8"><th> Character </th><th> Meaning </th></tr> + <tr><td> # </td><td> Any digit(s), zero shows as absent (for leading/trailing zeroes) </td></tr> + <tr><td> 0 </td><td> Implicit digit. Zero will show in the case that the input number is too small.</td></tr> + <tr><td> . </td><td> Decimal separator. Output decimal seperator will be dependant on system locale.</td></tr> + <tr><td> , </td><td> Grouping separator. The number of digits (either #, or 0) between the grouping separator and the decimal (or the rightmost digit) will determine the groupingSize)</td></tr> + <tr><td> other </td><td> Any other character will be taken as a string literal and placed directly into the output string </td></tr> + </table> + + Invalid formats will not guarantee a meaningful text output.<br> + + \note <i>Input numbers that are too long for the given format will be rounded dependent on precison based on the position of the decimal point </i> + + The following table illustrates the output text created by applying some examples of numeric formats to the formatter. + + <table border="0" align="center"> + <tr style="background-color: #D6E2E8"><th> Format </th><th> Number </th><th> Output </th></tr> + <tr><td> ### </td><td> 123456 </td><td> 123456 </td></tr> + <tr><td> 000 </td><td> 123456 </td><td> 123456 </td></tr> + <tr><td> ###### </td><td> 1234 </td><td> 1234 </td></tr> + <tr><td> 000000 </td><td> 1234 </td><td> 001234 </td></tr> + <tr><td> ##,##0.## </td><td> 1234.456 </td><td> 1,234.46 (for US locale)<br> 1 234,46 (for FR locale)</td></tr> + <tr><td> 000000,000.# </td><td> 123456 </td><td> 000,123,456 (for US locale)<br> 000 123 456 (for FR locale)</td></tr> + <tr><td> 0.0### </td><td> 0.999997 </td><td> 1.0 </td></tr> + <tr><td> (000) 000 - 000 </td><td> 12345678 </td><td> (012) 345 - 678 </td></tr> + <tr><td> #A</td><td>12</td><td>12A</td></tr> + </table> + +*/ +QString QmlNumberFormatter::format() const +{ + Q_D(const QmlNumberFormatter); + return d->format; +} + +void QmlNumberFormatter::setNumber(const qreal &number) +{ + Q_D(QmlNumberFormatter); + if (d->number == number) + return; + d->number = number; + d->updateText(); +} + +void QmlNumberFormatter::setFormat(const QString &format) +{ + Q_D(QmlNumberFormatter); + //no format checking + if (format.isEmpty()) + d->format = QString::null; + else + d->format = format; + d->updateText(); +} + +void QmlNumberFormatterPrivate::updateText() +{ + Q_Q(QmlNumberFormatter); + if (!classComplete) + return; + + QNumberFormat tempFormat; + tempFormat.setFormat(format); + tempFormat.setNumber(number); + + text = tempFormat.text(); + + emit q->textChanged(); +} + +void QmlNumberFormatter::classBegin() +{ + Q_D(QmlNumberFormatter); + d->classComplete = false; +} + +void QmlNumberFormatter::classComplete() +{ + Q_D(QmlNumberFormatter); + d->classComplete = true; + d->updateText(); +} + +QML_DEFINE_TYPE(QmlNumberFormatter, NumberFormatter); +QT_END_NAMESPACE diff --git a/src/declarative/extra/qmlnumberformatter.h b/src/declarative/extra/qmlnumberformatter.h new file mode 100644 index 0000000..e053be5 --- /dev/null +++ b/src/declarative/extra/qmlnumberformatter.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 QtDeclarative 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 QMLNUMBERFORMATTER_H +#define QMLNUMBERFORMATTER_H + +#include <qml.h> +#include "qnumberformat.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlNumberFormatterPrivate; +class Q_DECLARATIVE_EXPORT QmlNumberFormatter : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QString text READ text NOTIFY textChanged) + Q_PROPERTY(QString format READ format WRITE setFormat) + Q_PROPERTY(qreal number READ number WRITE setNumber) +public: + QmlNumberFormatter(QObject *parent=0); + ~QmlNumberFormatter(); + + QString text() const; + + qreal number() const; + void setNumber(const qreal &); + + QString format() const; + void setFormat(const QString &); + + virtual void classBegin(); + virtual void classComplete(); + +Q_SIGNALS: + void textChanged(); + +private: + Q_DISABLE_COPY(QmlNumberFormatter) + Q_DECLARE_PRIVATE(QmlNumberFormatter) +}; + +QML_DECLARE_TYPE(QmlNumberFormatter); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/extra/qmlsqlconnection.cpp b/src/declarative/extra/qmlsqlconnection.cpp new file mode 100644 index 0000000..3e2032c --- /dev/null +++ b/src/declarative/extra/qmlsqlconnection.cpp @@ -0,0 +1,441 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlsqlconnection.h" +#include "private/qobject_p.h" + +#include <QSqlError> +#include <QSqlDriver> +#include <QDebug> + +#include <qml.h> +#include <qmlcontext.h> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(QmlSqlConnection, SqlConnection); + +class QmlSqlConnectionPrivate: public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlSqlConnection) +public: + QmlSqlConnectionPrivate() : port(0) {} + + int port; + QString name, databaseName, connectionOptions; + QString hostName, userName, password, driver; + QmlContext *context; +}; + +/*! + \qmlclass SqlConnection QmlSqlConnection + \brief The SqlConnection element describes a connection to an SQL database. +*/ + +/*! + \qmlproperty QString SqlConnection::name + \default + + Defines the connection's name. The name allows the connection to be + retrieved using the connection property of SqlQuery or when + QSqlDatabase::database() +*/ + +/*! + \qmlproperty QStringList SqlConnection::tables + + Defines the set of tables that exist in the database for the connection. +*/ + +/*! + \qmlproperty QString SqlConnection::databaseName + + Defines the connection's database name. This is used when opening the + connection to the database. +*/ + +/*! + \qmlproperty QString SqlConnection::driver + + Defines the driver type of the connection. This is used when opening the + connection to the database. +*/ + +/*! + \qmlproperty QString SqlConnection::connectOptions + + Defines the options used when connecting to the database. These are used + when opening the connection to the database. +*/ + +/*! + \qmlproperty QString SqlConnection::hostName + + Defines the connection's host name. This is used when opening the + connection to the database. +*/ + +/*! + \qmlproperty int SqlConnection::port + + Defines the connection's port number. This is used when opening the + connection to the database. +*/ + +/*! + \qmlproperty QString SqlConnection::userName + + Defines the connection's user name. This is used when opening the + connection to the database. +*/ + +/*! + \qmlproperty QString SqlConnection::password + + Defines the connection's password. This is used when opening the + connection to the database. +*/ + +/*! + \qmlproperty QString SqlConnection::lastError + + Defines the last error, if one occurred, when working with the database. + If the error occurred in conjunction with an SQL query the error will be + defined by SqlQuery::lastError. +*/ + +/*! + \class QmlSqlConnection + \brief The QmlSqlConnection class manages a connection to an SQL database. + + \qmltext + + The SqlConnection element works in a similar way to + QSqlDatabase::addDatabase(). It allows setting the database properties + such that the connection does not need to be set up in C++ code. + It differs from QSqlDatabase::addDatabase() in that it will automatically + open the database. + + The database can then either be used from an SqlQuery element using its id + as a bind, or using its name. If the database is set up externally to + Qml the query should connect using its name. + + \qml + <SqlConnection id="myConnection"> + <name>qmlConnection</name> + <driver>QSQLITE</driver> + <databaseName>"mydb.sqlite"</databaseName> + </SqlConnection> + <SqlQuery id="listmodel" connection="{myConnection}">SELECT * FROM mytable</SqlQuery> + <SqlQuery id="othermodel" connection="qmlConnection">SELECT * FROM myothertable</SqlQuery> + \endqml + + \endqmltext +*/ + +/*! + Constructs a QmlSqlConnection with the given \a parent. +*/ +QmlSqlConnection::QmlSqlConnection(QObject *parent) +: QObject(*(new QmlSqlConnectionPrivate), parent) +{ + Q_D(QmlSqlConnection); + d->context = QmlContext::activeContext(); +} + +/*! + Destroys the QmlSqlConnection. +*/ +QmlSqlConnection::~QmlSqlConnection() +{ + QSqlDatabase db = database(); + if (db.isOpen()) + db.close(); +} + +/*! + Returns the connection's name. + This is equivalent to QSqlDatabase::connectionName(). + \sa setName() +*/ +QString QmlSqlConnection::name() const +{ + Q_D(const QmlSqlConnection); + return d->name; +} + +/*! + Returns the connection's database name. + This is equivalent to QSqlDatabase::databaseName(). + \sa setDatabaseName() +*/ +QString QmlSqlConnection::databaseName() const +{ + Q_D(const QmlSqlConnection); + return d->databaseName; +} + +/*! + Returns the connect options string used for this connection. + + \sa setConnectOptions() +*/ +QString QmlSqlConnection::connectOptions() const +{ + Q_D(const QmlSqlConnection); + return d->connectionOptions; +} + +/*! + Returns the connection's host name. + \sa setHostName() +*/ +QString QmlSqlConnection::hostName() const +{ + Q_D(const QmlSqlConnection); + return d->hostName; +} + +/*! + Returns the connection's port number. The value is undefined if the port + number has not been set. + + \sa setPort() +*/ +int QmlSqlConnection::port() const +{ + Q_D(const QmlSqlConnection); + return d->port; +} + +/*! + Returns the connection's user name. + + \sa setUserName() +*/ +QString QmlSqlConnection::userName() const +{ + Q_D(const QmlSqlConnection); + return d->userName; +} + +/*! + Returns the connection's password. + + \sa setPassword)() +*/ +QString QmlSqlConnection::password() const +{ + Q_D(const QmlSqlConnection); + return d->password; +} + +/*! + Returns the connection's driver name. + + \sa setDriver() +*/ +QString QmlSqlConnection::driver() const +{ + Q_D(const QmlSqlConnection); + return d->driver; +} + +/*! + Returns a list of the database's tables. +*/ +QStringList QmlSqlConnection::tables() const +{ + return database().tables(); +} + +/*! + Sets the connection's name to the given \a name. + + \sa name() +*/ +void QmlSqlConnection::setName(const QString &name) +{ + Q_D(QmlSqlConnection); + d->name = name; +} + +/*! + Set's the connection's database name to the given \a name. + + \sa databaseName() +*/ +void QmlSqlConnection::setDatabaseName(const QString &name) +{ + Q_D(QmlSqlConnection); + d->databaseName = name; +} + +/*! + Sets the connection's options to the given \a options. + + \sa connectOptions(), QSqlDatabase::setConnectOptions() +*/ +void QmlSqlConnection::setConnectOptions(const QString &options) +{ + Q_D(QmlSqlConnection); + d->connectionOptions = options; +} + +/*! + Sets the connection's host name to the given \a name. + + \sa hostName() +*/ +void QmlSqlConnection::setHostName(const QString &name) +{ + Q_D(QmlSqlConnection); + d->hostName = name; +} + +/*! + Sets the connection's port number to the given \a port. + + \sa port() +*/ +void QmlSqlConnection::setPort(int port) +{ + Q_D(QmlSqlConnection); + d->port = port; +} + +/*! + Sets the connection's user name to the given \a name. + + \sa userName() +*/ +void QmlSqlConnection::setUserName(const QString &name) +{ + Q_D(QmlSqlConnection); + d->userName = name; +} + +/*! + Sets the connection's password to the given \a password. + + \sa password() +*/ +void QmlSqlConnection::setPassword(const QString &password) +{ + Q_D(QmlSqlConnection); + d->password = password; +} + +/*! + Returns information about the last error that occurred on the database. + + Failures that occur in conjunction with an individual query are + reported by QmlSqlQuery::lastError() +*/ +QString QmlSqlConnection::lastError() const +{ + return database().lastError().text(); +} + +/*! + Sets the connection's driver to the specified driver \a type. + + \sa driver(), QSqlDatabase::addDatabase() +*/ +void QmlSqlConnection::setDriver(const QString &type) +{ + Q_D(QmlSqlConnection); + d->driver = type; +} + +/*! + \reimp +*/ +void QmlSqlConnection::classComplete() +{ +} + +/*! + Returns the database object associated with this connection. + If the database is not yet open, it will open the database + passed on the settings specified for the SQL connection. +*/ +QSqlDatabase QmlSqlConnection::database() const +{ + Q_D(const QmlSqlConnection); + + QSqlDatabase db; + if (QSqlDatabase::connectionNames().contains(d->name)) { + db = QSqlDatabase::database(d->name); + } else { + db = QSqlDatabase::addDatabase( + d->driver.isEmpty() + ? QLatin1String("QSQLITE") + : d->driver, + d->name.isEmpty() + ? QLatin1String(QSqlDatabase::defaultConnection) + : d->name); + } + if (db.isOpen()) + return db; + if ((d->driver.isEmpty() || d->driver == QLatin1String("QSQLITE")) && d->context) { + // SQLITE uses files for databases, hence use relative pathing + // if possible. + QUrl url = d->context->resolvedUrl(d->databaseName); + if (url.isRelative() || url.scheme() == QLatin1String("file")) + db.setDatabaseName(url.toLocalFile()); + else + db.setDatabaseName(d->databaseName); + } else { + db.setDatabaseName(d->databaseName); + } + db.setConnectOptions(d->connectionOptions); + db.setHostName(d->hostName); + db.setPassword(d->password); + db.setPort(d->port); + db.setUserName(d->userName); + if (!db.open()) + qWarning() << "Failed to open database" << lastError(); + + return db; +} + +QT_END_NAMESPACE diff --git a/src/declarative/extra/qmlsqlconnection.h b/src/declarative/extra/qmlsqlconnection.h new file mode 100644 index 0000000..2cc5774 --- /dev/null +++ b/src/declarative/extra/qmlsqlconnection.h @@ -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 QtDeclarative 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 QMLSQLCONNECTION_H +#define QMLSQLCONNECTION_H + +#include <qml.h> +#include <QSqlDatabase> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QSqlDatabase; +class QmlSqlConnectionPrivate; +class Q_DECLARATIVE_EXPORT QmlSqlConnection : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(QStringList tables READ tables); + Q_PROPERTY(QString databaseName READ databaseName WRITE setDatabaseName); + Q_PROPERTY(QString driver READ driver WRITE setDriver); + Q_PROPERTY(QString connectOptions READ connectOptions WRITE setConnectOptions); + Q_PROPERTY(QString hostName READ hostName WRITE setHostName); + Q_PROPERTY(int port READ port WRITE setPort); + Q_PROPERTY(QString userName READ userName WRITE setUserName); + Q_PROPERTY(QString password READ password WRITE setPassword); + Q_PROPERTY(QString lastError READ lastError); + Q_CLASSINFO("DefaultProperty", "name") +public: + QmlSqlConnection(QObject *parent = 0); + ~QmlSqlConnection(); + + QString name() const; + void setName(const QString &); + + QStringList tables() const; + + QString databaseName() const; + void setDatabaseName(const QString &); + + QString connectOptions() const; + void setConnectOptions(const QString &); + + QString hostName() const; + void setHostName(const QString &); + + int port() const; + void setPort(int); + + QString userName() const; + void setUserName(const QString &); + + QString password() const; + void setPassword(const QString &); + + QString driver() const; + void setDriver(const QString &); + + QString lastError() const; + + virtual void classComplete(); + + QSqlDatabase database() const; +private: + Q_DISABLE_COPY(QmlSqlConnection) + Q_DECLARE_PRIVATE(QmlSqlConnection) +}; + +QML_DECLARE_TYPE(QmlSqlConnection); + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QMLXMLLISTMODEL_H + diff --git a/src/declarative/extra/qmlsqlquery.cpp b/src/declarative/extra/qmlsqlquery.cpp new file mode 100644 index 0000000..39d3aa2 --- /dev/null +++ b/src/declarative/extra/qmlsqlquery.cpp @@ -0,0 +1,696 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlsqlquery.h" +#include "qmlsqlconnection.h" +#include "private/qobject_p.h" + +#include <QDebug> +#include <QSqlQuery> +#include <QSqlError> +#include <QSqlField> +#include <QSqlRecord> +#include <QSqlDatabase> +#include <QSqlDriver> + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QmlSqlBind, SqlBind); +QML_DEFINE_TYPE(QmlSqlQuery, SqlQuery); + +class QmlSqlBindPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlSqlBind) +public: + QmlSqlBindPrivate() {} + + QString name; + QVariant value; +}; + +/*! + \qmlclass SqlBind QmlSqlBind + \brief the SqlBind element specifies a value binding for an SqlQuery element. +*/ + +/*! + \class QmlSqlBind + \brief the QmlSqlBind class specifies a value binding for a QmlSqlQuery. + + \qmltext + + \qml + By using bindings its possible to cause a SqlQuery to update itself + when values bound through the SqlBind change. Hence in the example + below the results for the SqlQuery will change as searchText changes. + + If the query is not a SELECT statement, the effects of the bound + values will only apply when the SqlQuery exec() slot is called. + + <SqlQuery> + SELECT * FROM mytable WHERE name LIKE :value + <bindings> + <SqlBind name=":value" value="{searchText + '%'}"/> + </bindings> + </SqlQuery> + <SqlQuery> + SELECT * FROM mytable WHERE type = ? + <bindings> + <SqlBind value="simple"/> + </bindings> + <SqlQuery> + \endqml + \endqmltext +*/ + +/*! + \fn void QmlSqlBind::valueChanged() + + This signal is emitted when the value property of the SqlBind changes. +*/ + +/*! + \qmlproperty QString SqlBind::name + + Defines the placeholder name of the bind. If no name is specified the + bind will use its position within the SqlQuery's bindings to bind + into the query. +*/ + +/*! + \qmlproperty QVariant SqlBind::value + + Defines the value to bind into the query. +*/ + +/*! + Constructs a QmlSqlVind with the given \a parent +*/ +QmlSqlBind::QmlSqlBind(QObject *parent) +: QObject(*(new QmlSqlBindPrivate()), parent) +{ +} + +/*! + Destroys the QmlSqlBind. +*/ +QmlSqlBind::~QmlSqlBind() +{ +} + +/*! + Returns the binding's name. + + \sa setName() +*/ +QString QmlSqlBind::name() const +{ + Q_D(const QmlSqlBind); + return d->name; +} + +/*! + Returns the binding's value. + + \sa setValue() +*/ +QVariant QmlSqlBind::value() const +{ + Q_D(const QmlSqlBind); + return d->value; +} + +/*! + Sets the binding's name to the given \a name. + + \sa name() +*/ +void QmlSqlBind::setName(const QString &name) +{ + Q_D(QmlSqlBind); + d->name = name; +} + +/*! + Sets the binding's value to the given \a value. + + \sa value() +*/ +void QmlSqlBind::setValue(const QVariant &value) +{ + Q_D(QmlSqlBind); + if (d->value != value) { + d->value = value; + emit valueChanged(); + } +} + +/*! + \reimp +*/ +void QmlSqlBind::classComplete() +{ +} + +class QmlSqlQueryPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlSqlQuery) +public: + QmlSqlQueryPrivate(QmlSqlQuery *owner) : isSel(false), query(NULL), requireCache(false), count(-1), binds(owner) {} + void prepareQuery() const; + void bindQuery() const; + void cacheQuery() const; + void grabRoles() const; + + QString queryText; + bool isSel; + QVariant connectionVariant; + mutable QSqlQuery *query; + mutable bool requireCache; + mutable int count; + mutable QList<int> roles; + mutable QStringList roleNames; + mutable QVector< QVector< QVariant > > cache; + + class QmlSqlBindList : public QmlList<QmlSqlBind *> + { + public: + QmlSqlBindList(QmlSqlQuery *owner) + : q(owner){} + + void append(QmlSqlBind *o) { + m_contents.append(o); + QObject::connect(o, SIGNAL(valueChanged()), q, SLOT(resetBinds())); + } + void clear() { m_contents.clear(); } + int count() const { return m_contents.count(); } + void removeAt(int pos) { return m_contents.removeAt(pos); } + QmlSqlBind *at(int pos) const { return m_contents.at(pos); } + void insert(int pos, QmlSqlBind *o) { m_contents.insert(pos, o); } + private: + QList<QmlSqlBind *> m_contents; + QmlSqlQuery *q; + }; + + QmlSqlBindList binds; +}; + +/*! + \qmlclass SqlQuery QmlSqlQuery + \brief The SqlQuery element describes a query into an SQL database. +*/ + +/* + \class QmlSqlQuery + \brief the QmlSqlQuery class manages a query into an SQL database. + + \qmltext + The SqlQuery element has three parts. The first is the query itself, + which can either be specified using the query property or by the + default text for the element. The second specifies the connection + to the database. This can either be a bound id from an SqlConnection + or the connections name. If the connection is specified in a QML + SqlConnection it is recommend to bind to the id to help ensure the + database is complete before attempting to attach to it. If no + connectoin is specified the default connection is used. + + It is also possible to bind values into the query using the bindings + property. See SqlBind for more information on how to bind values into + the query. + + If the query is a select statement it can be used as a model for a ListView. + The roles will be the columns of the result set. Use the SQL AS keyword + in the query if you want to override the column names from that of the + table selected. You should also use the AS keyword if there is no + appropriate table column name for the result column. + + \qml + <SqlQuery connection="{qmlConnectionId}" query="DELETE FROM mytable"/> + <SqlQuery connection="connectionName"> + SELECT * FROM mytable + </SqlQuery> + <SqlQuery>SELECT id AS recordId, (firstName || ' ' || lastName) AS fullName FROM mytable</SqlQuery> + \endqml + \endqmltext +*/ + +/*! + \qmlproperty QString SqlQuery::query + \default + + Defines the query text. +*/ + +/*! + \qmlproperty QVariant SqlQuery::connection + + Defines the connection to an SQL database used by the query. +*/ + +/*! + \qmlproperty QString SqlQuery::lastError + + Defines the last error, if one occurred, when working with the query. +*/ + +/*! + \qmlproperty list<SqlBind> SqlQuery::bindings + + The bindings property contains the list of values to bind into the + query. See SqlBind for more information. +*/ + +/*! + Constructs a QmlSqlQuery with the given \a parent. +*/ +QmlSqlQuery::QmlSqlQuery(QObject *parent) +: QListModelInterface(*(new QmlSqlQueryPrivate(this)), parent) +{ +} + +/*! + Destroys the QmlSqlQuery. +*/ +QmlSqlQuery::~QmlSqlQuery() +{ + Q_D(QmlSqlQuery); + if (d->query) + delete d->query; +} + +/*! + Returns the query's bound variables. +*/ +QmlList<QmlSqlBind *> *QmlSqlQuery::bindings() +{ + Q_D(QmlSqlQuery); + return &d->binds; +} + +/*! + Returns the query's bound variables. +*/ +const QmlList<QmlSqlBind *> *QmlSqlQuery::bindings() const +{ + Q_D(const QmlSqlQuery); + return &d->binds; +} + +/*! + Returns the query text. + + \sa setQuery() +*/ +QString QmlSqlQuery::query() const +{ + Q_D(const QmlSqlQuery); + return d->queryText; +} + +/*! + Sets the query text to the given \a text. +*/ +void QmlSqlQuery::setQuery(const QString &text) +{ + Q_D(QmlSqlQuery); + if (text != d->queryText) { + d->queryText = text; + + static const QLatin1String select("select"); + d-> isSel = text.trimmed().indexOf(select, 0, Qt::CaseInsensitive) == 0; + + if (d->query) + resetQuery(); + } +} + +/*! + Returns the query's connection specifier. + + \sa setConnection() +*/ +QVariant QmlSqlQuery::connection() const +{ + Q_D(const QmlSqlQuery); + return d->connectionVariant; +} + +/*! + Sets the query's connection specifier. + + \sa connection() +*/ +void QmlSqlQuery::setConnection(const QVariant &connection) +{ + Q_D(QmlSqlQuery); + if (connection != d->connectionVariant) { + d->connectionVariant = connection; + if (d->query) + resetQuery(); + else if (d->count == 0) // data has been requested + d->prepareQuery(); + } +} + +/*! + Returns the set of values for a given set of requested \a roles for the + specified \a row of the query result set. Returns an empty hash if the + query is not a select statement. + + \sa count(), roles(), toString() +*/ +QHash<int,QVariant> QmlSqlQuery::data(int row, const QList<int> &roles) const +{ + Q_D(const QmlSqlQuery); + if (!d->query) + d->prepareQuery(); + QHash<int, QVariant> result; + + if (!d->isSel) + return result; + + Q_ASSERT(row >= 0 && row <= d->count); + + if (!d->requireCache) + d->query->seek(row); + + for (int i = 0; i < roles.count(); ++i) { + int column = roles[i]; + Q_ASSERT(column >= 0 && column < d->cache.size()); + if (d->requireCache) + result.insert(column, d->cache[column].at(row)); + else + result.insert(column, d->query->value(column)); + } + return result; +} + +/*! + Returns the number of rows in the query result set. Returns 0 if + the query is not a select statement. + + \sa data(), roles(), toString() +*/ +int QmlSqlQuery::count() const +{ + Q_D(const QmlSqlQuery); + if (!d->query) + d->prepareQuery(); + return d->count; +} + +/*! + Returns the list of role integer identifiers for the query result set. + Returns and empty list if the query is not a select statement. + + \sa data(), count(), toString() +*/ +QList<int> QmlSqlQuery::roles() const +{ + Q_D(const QmlSqlQuery); + if (!d->query) + d->prepareQuery(); + + if (!d->isSel) + return QList<int>(); + + if (d->roleNames.isEmpty() && !d->requireCache) { + d->query->seek(0); + d->grabRoles(); + } + + return d->roles; +} + +/*! + Returns the corresponding role name for the given \a role identifier. + + \sa data(), roles(), toString() +*/ +QString QmlSqlQuery::toString(int role) const +{ + Q_D(const QmlSqlQuery); + if (!d->query) + d->prepareQuery(); + + if (d->roleNames.isEmpty() && !d->requireCache) { + d->query->seek(0); + d->grabRoles(); + } + + return d->roleNames[role]; +} + +/*! + Returns information about the last error that occurred on the query. +*/ +QString QmlSqlQuery::lastError() const +{ + Q_D(const QmlSqlQuery); + if (d->query) + return d->query->lastError().text(); + return QString(); +} + +/*! + \reimp +*/ +void QmlSqlQuery::classComplete() +{ + Q_D(QmlSqlQuery); + if (!d->query) + d->prepareQuery(); +} + +/*! + \internal + Rebinds the query, and if needed, emits rows inserted or rows + added so any attached ListView elements will correctly. + + This slot is called automatically when the SqlBind elements in + the bindings property of the query change. +*/ +void QmlSqlQuery::resetBinds() +{ + Q_D(QmlSqlQuery); + if (!d->query) + return; + int oldcount = d->count; + d->cache.resize(0); + d->roles.clear(); + d->roleNames.clear(); + d->bindQuery(); + if (d->isSel) { + if (!d->query->isActive()) { + if (!d->query->exec()) + qWarning() << "failed to execute query" << d->query->lastQuery() << d->query->boundValues() << d->query->lastError().text(); + } + d->cacheQuery(); // may finish query + emitChanges(oldcount); + } +} + +/*! + Executes the query. For SELECT statements this is normally only required + if the database changes outside of the SQL query element. Statements that + modify the database, such as UPDATE and INSERT, will not be applied until + this function is called. +*/ +void QmlSqlQuery::exec() +{ + Q_D(QmlSqlQuery); + if (!d->query) + d->prepareQuery(); + Q_ASSERT(d->query); + + if (d->isSel) { + int oldcount = d->count; + d->cache.resize(0); + d->roles.clear(); + d->roleNames.clear(); + if (!d->query->exec()) + qWarning() << "failed to execute query" << d->query->lastQuery() << d->query->boundValues() << d->query->lastError().text(); + d->cacheQuery(); // may finish query + emitChanges(oldcount); + } else { + if (!d->query->exec()) + qWarning() << "failed to execute query" << d->query->lastQuery() << d->query->boundValues() << d->query->lastError().text(); + d->query->finish(); + } +} + +/*! + \internal + + Resets the query and query cache. +*/ +void QmlSqlQuery::resetQuery() +{ + Q_D(QmlSqlQuery); + Q_ASSERT(d->query != 0); + delete d->query; + d->query = 0; + d->cache.resize(0); + d->roles.clear(); + d->roleNames.clear(); + int oldcount = d->count; + d->prepareQuery(); + emitChanges(oldcount); +} + +/*! + \internal + + emits row number changes based of differences between the given + \a oldcount and the current count of the query result set. +*/ +void QmlSqlQuery::emitChanges(int oldcount) +{ + Q_D(QmlSqlQuery); + if (d->count > oldcount) + emit itemsInserted(oldcount, d->count-oldcount); + else if (d->count < oldcount) + emit itemsRemoved(d->count, oldcount-d->count); + if (d->count > 0 && oldcount > 0) + emit itemsChanged(0, qMin(d->count, oldcount), roles()); +} + +/* + Prepares the query. If the query starts with SELECT it is assumed to + be a SELECT statement and the query is also executed. +*/ +void QmlSqlQueryPrivate::prepareQuery() const +{ + QObject *object = qvariant_cast<QObject*>(connectionVariant); + QmlSqlConnection *connection = qobject_cast<QmlSqlConnection *>(object); + QString connectionString = qvariant_cast<QString>(connectionVariant); + + Q_ASSERT(query == 0); + QSqlDatabase db = connection ? connection->database() + : QSqlDatabase::database(connectionString.isEmpty() + ? QLatin1String(QSqlDatabase::defaultConnection) + : connectionString, false); + + if (!db.isOpen()) { + count = 0; + return; + } + + query = new QSqlQuery(db); + + requireCache = + query->driver()->hasFeature(QSqlDriver::SimpleLocking) + || !query->driver()->hasFeature(QSqlDriver::QuerySize); + + if (requireCache) + query->setForwardOnly(true); + if (!query->prepare(queryText)) + qWarning() << "failed to prepare query" << query->lastQuery() << query->lastError().text(); + bindQuery(); + if (isSel) { + if (!query->exec()) + qWarning() << "failed to execute query" << query->lastQuery() << query->boundValues() << query->lastError().text(); + cacheQuery(); + } +} + +/* + Binds values into the prepared query using the bindings property of the SqlQuery element. +*/ +void QmlSqlQueryPrivate::bindQuery() const +{ + for (int i = 0; i < binds.count(); ++i) { + QmlSqlBind *bind = binds.at(i); + if (bind->name().isEmpty()) { + query->bindValue(i, bind->value()); + } else { + query->bindValue(bind->name(), bind->value()); + } + } +} + +/* + If the query is connected to a database with simple locking or + that cannot ask for the count of a result set, caches the required + data of the query and finishes the query to release locks. + + Otherwise just caches the count of the query. +*/ +void QmlSqlQueryPrivate::cacheQuery() const +{ + if (requireCache) { + int row = 0; + while (query->next()) { + if (roleNames.isEmpty()) { + grabRoles(); + cache.resize(roleNames.count()); + } + Q_ASSERT(cache.size() > 0); + for (int i = 0; i < cache.size(); ++i) { + cache[i].append(query->value(i)); + } + row++; + } + count = row; + query->finish(); + } else { + count = query->size(); + } +} + +/* + Gets the column names for the roles of the SqlQuery element. + + The query must be active and on a valid row. +*/ +void QmlSqlQueryPrivate::grabRoles() const +{ + Q_ASSERT(query); + Q_ASSERT(query->isValid()); + Q_ASSERT(roleNames.isEmpty()); + Q_ASSERT(roles.isEmpty()); + + QSqlRecord record = query->record(); + for (int i = 0; i < record.count(); ++i) { + roleNames.append(record.fieldName(i)); + roles.append(i); + } +} +QT_END_NAMESPACE diff --git a/src/declarative/extra/qmlsqlquery.h b/src/declarative/extra/qmlsqlquery.h new file mode 100644 index 0000000..984483c --- /dev/null +++ b/src/declarative/extra/qmlsqlquery.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLSQLQUERYMODEL_H +#define QMLSQLQUERYMODEL_H + +#include <qml.h> +#include <QListModelInterface> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlSqlBindPrivate; +class Q_DECLARATIVE_EXPORT QmlSqlBind : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QVariant value READ value WRITE setValue) + + Q_CLASSINFO("DefaultValue", "value"); + +public: + QmlSqlBind(QObject *parent = 0); + ~QmlSqlBind(); + + QString name() const; + QVariant value() const; + + void setName(const QString &name); + void setValue(const QVariant &); + + virtual void classComplete(); + +Q_SIGNALS: + void valueChanged(); + +private: + Q_DISABLE_COPY(QmlSqlBind) + Q_DECLARE_PRIVATE(QmlSqlBind) +}; + +QML_DECLARE_TYPE(QmlSqlBind); + +class QSqlQuery; +class QmlSqlQueryPrivate; +class Q_DECLARATIVE_EXPORT QmlSqlQuery : public QListModelInterface, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QString query READ query WRITE setQuery) + Q_PROPERTY(QVariant connection READ connection WRITE setConnection) + Q_PROPERTY(QString lastError READ lastError) + + Q_PROPERTY(QmlList<QmlSqlBind *> *bindings READ bindings) + + Q_CLASSINFO("DefaultProperty", "query") +public: + QmlSqlQuery(QObject *parent = 0); + ~QmlSqlQuery(); + + QString query() const; + void setQuery(const QString &); + + QVariant connection() const; + void setConnection(const QVariant &); + + virtual QHash<int,QVariant> data(int index, const QList<int> &roles = (QList<int>())) const; + virtual int count() const; + virtual QList<int> roles() const; + virtual QString toString(int role) const; + + QString lastError() const; + + virtual void classComplete(); + + QmlList<QmlSqlBind *> *bindings(); + const QmlList<QmlSqlBind *> *bindings() const; + +public slots: + void exec(); + +private slots: + void resetBinds(); + void resetQuery(); + +private: + void emitChanges(int oldcount); + + Q_DISABLE_COPY(QmlSqlQuery) + Q_DECLARE_PRIVATE(QmlSqlQuery) +}; + +QML_DECLARE_TYPE(QmlSqlQuery); + +QT_END_NAMESPACE + +QT_END_HEADER +#endif + diff --git a/src/declarative/extra/qmlxmllistmodel.cpp b/src/declarative/extra/qmlxmllistmodel.cpp new file mode 100644 index 0000000..13faab2 --- /dev/null +++ b/src/declarative/extra/qmlxmllistmodel.cpp @@ -0,0 +1,357 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlxmllistmodel.h" +#include "private/qobject_p.h" + +#include <QtDeclarative/qmlcontext.h> +#include <QtDeclarative/qmlengine.h> +#include <QDebug> +#include <QXmlQuery> +#include <QXmlResultItems> +#include <QXmlNodeModelIndex> +#include <QBuffer> +#include <QNetworkRequest> +#include <QNetworkReply> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(XmlListModelRole, Role); +QML_DEFINE_TYPE(QmlXmlListModel, XmlListModel); + +//TODO: do something smart while waiting for data to load +// error handling (currently quite fragile) +// profile doQuery and doSubquery +// some sort of loading indication while we wait for initial data load (status property similar to QWebImage?) +// support complex/nested objects? +// how do we handle data updates (like rss feed -- usually items inserted at beginning) + +/*! + \qmlclass XmlListModel + \brief The XmlListModel class allows you to specify a model using XQuery. + + XmlListModel allows you to construct a model from XML data that can then be used as a data source + for the view classes (ListView, PathView, GridView) and any other classes that interact with model + data (like Repeater). + + The following is an example of a model containing news from a Yahoo RSS feed: + \qml + <XmlListModel id="FeedModel" src="http://rss.news.yahoo.com/rss/oceania" query="doc($src)/rss/channel/item"> + <Role name="title" query="title/string()"/> + <Role name="link" query="link/string()"/> + <Role name="description" query="description/string()" isCData="true"/> + </XmlListModel> + \endqml + \note The model is currently static, so the above is really just a snapshot of an RSS feed. +*/ + +class QmlXmlListModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlXmlListModel) +public: + QmlXmlListModelPrivate() : size(-1), highestRole(Qt::UserRole), reply(0), context(0), roleObjects(this) {} + + QString src; + QString query; + QString namespaces; + QList<int> roles; + QStringList roleNames; + mutable QList<QList<QVariant> > data; + int size; + int highestRole; + QNetworkReply *reply; + mutable QByteArray xml; + QString prefix; + QmlContext *context; + + struct RoleList : public QmlConcreteList<XmlListModelRole *> + { + RoleList(QmlXmlListModelPrivate *p) + : model(p) {} + virtual void append(XmlListModelRole *role) { + QmlConcreteList<XmlListModelRole *>::append(role); + model->roles << model->highestRole; + model->roleNames << role->name(); + ++model->highestRole; + } + //XXX clear, removeAt, and insert need to invalidate any cached data (in data table) as well + // (and the model should emit the appropriate signals) + virtual void clear() + { + model->roles.clear(); + model->roleNames.clear(); + QmlConcreteList<XmlListModelRole *>::clear(); + } + virtual void removeAt(int i) + { + model->roles.removeAt(i); + model->roleNames.removeAt(i); + QmlConcreteList<XmlListModelRole *>::removeAt(i); + } + virtual void insert(int i, XmlListModelRole *role) + { + QmlConcreteList<XmlListModelRole *>::insert(i, role); + model->roles.insert(i, model->highestRole); + model->roleNames.insert(i, role->name()); + ++model->highestRole; + } + + QmlXmlListModelPrivate *model; + }; + + RoleList roleObjects; +}; + +QmlXmlListModel::QmlXmlListModel(QObject *parent) + : QListModelInterface(*(new QmlXmlListModelPrivate), parent) +{ + Q_D(QmlXmlListModel); + d->context = QmlContext::activeContext(); +} + +QmlXmlListModel::~QmlXmlListModel() +{ +} + +QmlList<XmlListModelRole *> *QmlXmlListModel::roleObjects() +{ + Q_D(QmlXmlListModel); + return &d->roleObjects; +} + +QHash<int,QVariant> QmlXmlListModel::data(int index, const QList<int> &roles) const +{ + Q_D(const QmlXmlListModel); + QHash<int, QVariant> rv; + + if (index > d->data.count() - 1) + doSubquery(index); + + for (int i = 0; i < roles.size(); ++i) { + int role = roles.at(i); + int roleIndex = d->roles.indexOf(role); + rv.insert(role, d->data.at(index).at(roleIndex)); + } + return rv; +} + +int QmlXmlListModel::count() const +{ + Q_D(const QmlXmlListModel); + return d->size; +} + +QList<int> QmlXmlListModel::roles() const +{ + Q_D(const QmlXmlListModel); + return d->roles; +} + +QString QmlXmlListModel::toString(int role) const +{ + Q_D(const QmlXmlListModel); + int index = d->roles.indexOf(role); + if (index == -1) + return QString(); + return d->roleNames.at(index); +} + +QString QmlXmlListModel::src() const +{ + Q_D(const QmlXmlListModel); + return d->src; +} + +void QmlXmlListModel::setSrc(const QString &src) +{ + Q_D(QmlXmlListModel); + d->src = src; +} + +QString QmlXmlListModel::query() const +{ + Q_D(const QmlXmlListModel); + return d->query; +} + +void QmlXmlListModel::setQuery(const QString &query) +{ + Q_D(QmlXmlListModel); + d->query = query; +} + +QString QmlXmlListModel::namespaceDeclarations() const +{ + Q_D(const QmlXmlListModel); + return d->namespaces; +} + +void QmlXmlListModel::setNamespaceDeclarations(const QString &declarations) +{ + Q_D(QmlXmlListModel); + d->namespaces = declarations; +} + +void QmlXmlListModel::classComplete() +{ + fetch(); +} + +void QmlXmlListModel::fetch() +{ + Q_D(QmlXmlListModel); + if (d->src.isEmpty()) { + qWarning() << "Can't fetch empty src string"; + //clear existing data? + //int count = d->data.count(); + //d->data.clear(); + //emit itemsRemoved(0, count); + return; + } + + QNetworkRequest req((QUrl(d->src))); + req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + d->reply = d->context->engine()->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), + this, SLOT(requestFinished())); +} + +void QmlXmlListModel::requestFinished() +{ + Q_D(QmlXmlListModel); + if(d->reply->error() != QNetworkReply::NoError) { + d->reply->deleteLater(); + d->reply = 0; + } else { + QByteArray data = d->reply->readAll(); + doQuery(data); + d->reply->deleteLater(); + d->reply = 0; + } +} + +void QmlXmlListModel::doQuery(QByteArray &rawData) +{ + Q_D(QmlXmlListModel); + QString r; + QXmlQuery query; + QBuffer rawBuffer(&rawData); + rawBuffer.open(QIODevice::ReadOnly); + query.bindVariable(QLatin1String("src"), &rawBuffer); + query.setQuery(d->namespaces + d->query); + query.evaluateTo(&r); + //qDebug() << r; + + //always need a single root element + QByteArray xml = "<dummy:items xmlns:dummy=\"http://qtsotware.com/dummy\">\n" + r.toUtf8() + "</dummy:items>"; + QBuffer b(&xml); + b.open(QIODevice::ReadOnly); + //qDebug() << xml; + + QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + d->namespaces; + QString prefix = QLatin1String("doc($inputDocument)/dummy:items") + + d->query.mid(d->query.lastIndexOf(QLatin1Char('/'))); + + //figure out how many items we are dealing with + int count = -1; + { + QXmlResultItems result; + QXmlQuery countquery; + countquery.bindVariable(QLatin1String("inputDocument"), &b); + countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1String(")")); + countquery.evaluateTo(&result); + QXmlItem item(result.next()); + if (item.isAtomicValue()) + count = item.toAtomicValue().toInt(); + b.seek(0); + prefix += QLatin1String("[%1]/"); + } + //qDebug() << count; + + QXmlQuery subquery; + subquery.bindVariable(QLatin1String("inputDocument"), &b); + d->prefix = namespaces + prefix; + d->xml = xml; + + d->size = count; + emit itemsInserted(0, count); +} + +void QmlXmlListModel::doSubquery(int index) const +{ + Q_D(const QmlXmlListModel); + //qDebug() << "doSubQuery" << index; + QBuffer b(&d->xml); + b.open(QIODevice::ReadOnly); + + QXmlQuery subquery; + subquery.bindVariable(QLatin1String("inputDocument"), &b); + + //XXX should we use an array of objects or something else rather than a table? + for (int j = d->data.count(); j <= index; ++j) { + QList<QVariant> resultList; + for (int i = 0; i < d->roleObjects.size(); ++i) { + XmlListModelRole *role = d->roleObjects.at(i); + subquery.setQuery(d->prefix.arg(j+1) + role->query()); + if (role->isStringList()) { + QStringList data; + subquery.evaluateTo(&data); + resultList << QVariant(data); + //qDebug() << data; + } else { + QString s; + subquery.evaluateTo(&s); + if (role->isCData()) { + //un-escape + s.replace(QLatin1String("<"), QLatin1String("<")); + s.replace(QLatin1String(">"), QLatin1String(">")); + s.replace(QLatin1String("&"), QLatin1String("&")); + } + resultList << s; + //qDebug() << s; + } + b.seek(0); + } + d->data << resultList; + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/extra/qmlxmllistmodel.h b/src/declarative/extra/qmlxmllistmodel.h new file mode 100644 index 0000000..a8f3087 --- /dev/null +++ b/src/declarative/extra/qmlxmllistmodel.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLXMLLISTMODEL_H +#define QMLXMLLISTMODEL_H + +#include <qml.h> +#include <QListModelInterface> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlContext; +class Q_DECLARATIVE_EXPORT XmlListModelRole : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QString query READ query WRITE setQuery) + Q_PROPERTY(bool isCData READ isCData WRITE setIsCData) + Q_PROPERTY(bool isStringList READ isStringList WRITE setIsStringList) + +public: + XmlListModelRole() : m_isList(false), m_isCData(false) {} + ~XmlListModelRole() {} + + QString name() const { return m_name; } + void setName(const QString &name) { m_name = name; } + + QString query() const { return m_query; } + void setQuery(const QString &query) { m_query = query; } + + bool isStringList() const { return m_isList; } + void setIsStringList(bool b) { m_isList = b; } + + bool isCData() const { return m_isCData; } + void setIsCData(bool b) { m_isCData = b; } + +private: + QString m_name; + QString m_query; + bool m_isList; + bool m_isCData; +}; +QML_DECLARE_TYPE(XmlListModelRole); + +class QmlXmlListModelPrivate; +class Q_DECLARATIVE_EXPORT QmlXmlListModel : public QListModelInterface, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QString src READ src WRITE setSrc) + Q_PROPERTY(QString query READ query WRITE setQuery) + Q_PROPERTY(QString namespaceDeclarations READ namespaceDeclarations WRITE setNamespaceDeclarations) + Q_PROPERTY(QmlList<XmlListModelRole *> *roles READ roleObjects) + Q_CLASSINFO("DefaultProperty", "roles") +public: + QmlXmlListModel(QObject *parent = 0); + ~QmlXmlListModel(); + + virtual QHash<int,QVariant> data(int index, const QList<int> &roles = (QList<int>())) const; + virtual int count() const; + virtual QList<int> roles() const; + virtual QString toString(int role) const; + + QmlList<XmlListModelRole *> *roleObjects(); + + QString src() const; + void setSrc(const QString&); + + QString query() const; + void setQuery(const QString&); + + QString namespaceDeclarations() const; + void setNamespaceDeclarations(const QString&); + + virtual void classComplete(); + void fetch(); + +protected: + void doQuery(QByteArray &rawData); + void doSubquery(int index) const; + +private Q_SLOTS: + void requestFinished(); + +private: + Q_DECLARE_PRIVATE(QmlXmlListModel) + Q_DISABLE_COPY(QmlXmlListModel) +}; + +QML_DECLARE_TYPE(QmlXmlListModel); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLXMLLISTMODEL_H diff --git a/src/declarative/extra/qnumberformat.cpp b/src/declarative/extra/qnumberformat.cpp new file mode 100644 index 0000000..79e328a --- /dev/null +++ b/src/declarative/extra/qnumberformat.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qnumberformat.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QNumberFormat,NumberFormat); + +QNumberFormat::QNumberFormat(QObject *parent) : QObject(parent), _number(0), _type(Decimal), + _groupingSize(0) +{ + _locale = QLocale::system(); + _groupingSeparator = _locale.groupSeparator(); + _decimalSeparator = _locale.decimalPoint(); + _currencySymbol = QLatin1Char('$'); +} + +QNumberFormat::~QNumberFormat() +{ + +} + +void QNumberFormat::updateText() +{ + QTime t; + t.start(); + static int totalTime; + + handleFormat(); + + totalTime += t.elapsed(); + emit textChanged(); +} + +void QNumberFormat::handleFormat() +{ + // ### is extremely messy + if (_format.isEmpty()) { + _text = QString(QLatin1String("%1")).arg(_number, -1, 'f', -1); + return; + } + + QString inputString; + + // ### possible to use the following parsed data in the future + + int remainingLength = _format.size(); + int currentIndex = _format.size()-1; + + int maxDigits = 0; + int minDigits = 0; + int decimalLength = 0; + + while (remainingLength > 0) { + switch(_format.at(currentIndex).unicode()) { + case ',': + if (decimalLength && !_groupingSize) + setGroupingSize(maxDigits - decimalLength); + else if (!_groupingSize) + setGroupingSize(maxDigits); + break; + case '.': + if (!decimalLength) + decimalLength = maxDigits; + break; + case '0': + minDigits++; + case '#': + maxDigits++; + break; + default: + break; + } + currentIndex--; + remainingLength--; + } + + // round given the decimal length/precision + inputString = QString(QLatin1String("%1")).arg(_number, -1, 'f', decimalLength); + + QStringList parts = inputString.split(QLatin1Char('.')); + QStringList formatParts = _format.split(QLatin1Char('.')); + + if (formatParts.size() > 2 || parts.size() > 2 ) + return; + + QString formatInt = formatParts.at(0); + + QString formatDec; + if (formatParts.size() == 2) + formatDec = formatParts.at(1); + + QString integer = parts.at(0); + + QString decimal; + if (parts.size() == 2) + decimal = parts.at(1); + + QString outputDecimal = formatDecimal(formatDec, decimal); + QString outputInteger = formatInteger(formatInt, integer); + + // insert separators + if (_groupingSize) { + unsigned int count = 0; + for (int i = outputInteger.size()-1; i > 0; i--) { + if (outputInteger.at(i).digitValue() >= 0) { + if (count == _groupingSize - 1) { + count = 0; + outputInteger.insert(i, _groupingSeparator); + } + else + count++; + } + } + } + if (!outputDecimal.isEmpty()) + _text = outputInteger + _decimalSeparator + outputDecimal; + else + _text = outputInteger; +} + +QString QNumberFormat::formatInteger(const QString &formatInt, const QString &integer) +{ + if (formatInt.isEmpty() || integer.isEmpty()) + return QString(); + + QString outputInteger; + int formatIndex = formatInt.size()-1; + + //easier for carry? + for (int index= integer.size()-1; index >= 0; index--) { + if (formatIndex < 0) { + outputInteger.push_front(integer.at(index)); + } + else { + switch(formatInt.at(formatIndex).unicode()) { + case '0': + if (index > integer.size()-1) { + outputInteger.push_front(QLatin1Char('0')); + break; + } + case '#': + outputInteger.push_front(integer.at(index)); + break; + case ',': + index++; + break; + default: + outputInteger.push_front(formatInt.at(formatIndex)); + index++; + break; + } + formatIndex--; + } + } + while (formatIndex >= 0) { + if (formatInt.at(formatIndex).unicode() != '#' && formatInt.at(formatIndex).unicode() != ',') + outputInteger.push_front(formatInt.at(formatIndex)); + formatIndex--; + } + return outputInteger; +} + +QString QNumberFormat::formatDecimal(const QString &formatDec, const QString &decimal) +{ + QString outputDecimal; + + // up to max 6 decimal places + for (int index=formatDec.size()-1; index >= 0; index--) { + switch(formatDec.at(index).unicode()) { + case '0': + outputDecimal.push_front(decimal.at(index)); + break; + case '#': + if (decimal.at(index) != QLatin1Char('0') || outputDecimal.size() > 0) + outputDecimal.push_front(decimal.at(index)); + break; + default: + outputDecimal.push_front(formatDec.at(index)); + break; + } + } + return outputDecimal; +} +QT_END_NAMESPACE diff --git a/src/declarative/extra/qnumberformat.h b/src/declarative/extra/qnumberformat.h new file mode 100644 index 0000000..6ee333c --- /dev/null +++ b/src/declarative/extra/qnumberformat.h @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 NUMBERFORMAT_H +#define NUMBERFORMAT_H + +#include "qml.h" +#include <QtGui> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +// TODO +// be able to set Locale, instead of default system for dynamic formatting +// add currency support +// add additional syntax, extend to format scientific, percentiles, significant digits etc + + +class QNumberFormat : public QObject +{ + Q_OBJECT + Q_ENUMS(NumberType) +public: + QNumberFormat(QObject *parent=0); + ~QNumberFormat(); + + enum NumberType { + Percent, + Scientific, + Currency, + Decimal + }; + + //external property, only visible + Q_PROPERTY(QString text READ text NOTIFY textChanged); + + //mutatable properties to modify the output (text) + Q_PROPERTY(qreal number READ number WRITE setNumber); + Q_PROPERTY(QString format READ format WRITE setFormat); + Q_PROPERTY(QLocale locale READ locale WRITE setLocale); + + //Format specific settings + Q_PROPERTY(unsigned short groupingSeparator READ groupingSeparator WRITE setGroupingSeparator); + Q_PROPERTY(unsigned short decimalSeperator READ decimalSeparator WRITE setDecimalSeparator); + Q_PROPERTY(unsigned int groupingSize READ groupingSize WRITE setGroupingSize); + Q_PROPERTY(unsigned short currencySymbol READ currencySymbol WRITE setCurrencySymbol); + + + QString text() const { return _text; } + + qreal number() const { return _number; } + void setNumber(qreal n) { + if (_number == n) + return; + _number = n; + updateText(); + } + + QString format() const { return _format; } + void setFormat(const QString &format) { + if (format.isEmpty()) + _format = QString::null; + else if (_format == format) + return; + + _format = format; + updateText(); + } + + QLocale locale() const { return _locale; } + void setLocale(const QLocale &locale) { _locale = locale; updateText(); } + + //Do we deal with unicode standard? or create our own + // ### since this is the backend for the number conversions, we will use the unicode + // the front-end will handle the QChar/QString -> short int + + unsigned short groupingSeparator() { return _groupingSeparator.unicode(); } + void setGroupingSeparator(unsigned short unicodeSymbol) + { + _groupingSeparator = QChar(unicodeSymbol); + } + + unsigned short decimalSeparator() { return _decimalSeparator.unicode(); } + void setDecimalSeparator(unsigned short unicodeSymbol) + { + _decimalSeparator = QChar(unicodeSymbol); + } + + unsigned short currencySymbol() { return _currencySymbol.unicode(); } + void setCurrencySymbol(unsigned short unicodeSymbol) + { + _currencySymbol = QChar(unicodeSymbol); + } + + unsigned int groupingSize() { return _groupingSize; } + void setGroupingSize(unsigned int size) + { + _groupingSize = size; + } + +Q_SIGNALS: + void textChanged(); + +private: + void updateText(); + void handleFormat(); + QString formatInteger(const QString &formatInt, const QString &integer); + QString formatDecimal(const QString &formatDec, const QString &decimal); + + qreal _number; + NumberType _type; + QChar _groupingSeparator; + QChar _decimalSeparator; + QChar _currencySymbol; + unsigned int _groupingSize; + + QLocale _locale; + QString _format; + + // only hooked member at the moment + QString _text; + +}; +QML_DECLARE_TYPE(QNumberFormat); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/fx/fx.pri b/src/declarative/fx/fx.pri new file mode 100644 index 0000000..b8b62b9 --- /dev/null +++ b/src/declarative/fx/fx.pri @@ -0,0 +1,94 @@ +HEADERS += \ + fx/qfxanchors.h \ + fx/qfxanchors_p.h \ + fx/qfxanimatedimageitem.h \ + fx/qfxblendedimage.h \ + fx/qfxblurfilter.h \ + fx/qfxcomponentinstance.h \ + fx/qfxcomponentinstance_p.h \ + fx/qfxcontentwrapper.h \ + fx/qfxcontentwrapper_p.h \ + fx/qfxflickable.h \ + fx/qfxflickable_p.h \ + fx/qfxfocuspanel.h \ + fx/qfxfocusrealm.h \ + fx/qfxgridview.h \ + fx/qfxhighlightfilter.h \ + fx/qfximage.h \ + fx/qfximageitem.h \ + fx/qfximageitem_p.h \ + fx/qfximage_p.h \ + fx/qfxitem.h \ + fx/qfxitem_p.h \ + fx/qfxkeyactions.h \ + fx/qfxkeyproxy.h \ + fx/qfxlayouts.h \ + fx/qfxlayouts_p.h \ + fx/qfxmouseregion.h \ + fx/qfxmouseregion_p.h \ + fx/qfxpainted.h \ + fx/qfxpainted_p.h \ + fx/qfxparticles.h \ + fx/qfxpath.h \ + fx/qfxpath_p.h \ + fx/qfxpathview.h \ + fx/qfxpathview_p.h \ + fx/qfxrect.h \ + fx/qfxrect_p.h \ + fx/qfxreflectionfilter.h \ + fx/qfxrepeater.h \ + fx/qfxrepeater_p.h \ + fx/qfxscalegrid.h \ + fx/qfxshadowfilter.h \ + fx/qfxtextedit.h \ + fx/qfxtextedit_p.h \ + fx/qfxtext.h \ + fx/qfxtext_p.h \ + fx/qfxtransform.h \ + fx/qfxpixmap.cpp \ + fx/qfxvisualitemmodel.h \ + fx/qfxlistview.h \ + fx/qfxwidgetcontainer.h \ + +SOURCES += \ + fx/qfxanchors.cpp \ + fx/qfxanimatedimageitem.cpp \ + fx/qfxblendedimage.cpp \ + fx/qfxblurfilter.cpp \ + fx/qfxcomponentinstance.cpp \ + fx/qfxcontentwrapper.cpp \ + fx/qfxflickable.cpp \ + fx/qfxfocuspanel.cpp \ + fx/qfxfocusrealm.cpp \ + fx/qfxgridview.cpp \ + fx/qfxhighlightfilter.cpp \ + fx/qfximage.cpp \ + fx/qfximageitem.cpp \ + fx/qfxitem.cpp \ + fx/qfxkeyactions.cpp \ + fx/qfxkeyproxy.cpp \ + fx/qfxlayouts.cpp \ + fx/qfxmouseregion.cpp \ + fx/qfxpainted.cpp \ + fx/qfxparticles.cpp \ + fx/qfxpath.cpp \ + fx/qfxpathview.cpp \ + fx/qfxrect.cpp \ + fx/qfxreflectionfilter.cpp \ + fx/qfxrepeater.cpp \ + fx/qfxscalegrid.cpp \ + fx/qfxshadowfilter.cpp \ + fx/qfxtext.cpp \ + fx/qfxtextedit.cpp \ + fx/qfxtransform.cpp \ + fx/qfxpixmap.cpp \ + fx/qfxvisualitemmodel.cpp \ + fx/qfxlistview.cpp \ + fx/qfxwidgetcontainer.cpp \ + +contains(QT_CONFIG, webkit) { + QT+=webkit + SOURCES += fx/qfxwebview.cpp + HEADERS += fx/qfxwebview.h +} + diff --git a/src/declarative/fx/qfxanchors.cpp b/src/declarative/fx/qfxanchors.cpp new file mode 100644 index 0000000..b7a7dd2 --- /dev/null +++ b/src/declarative/fx/qfxanchors.cpp @@ -0,0 +1,857 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxanchors_p.h" +#include "qfxitem.h" +#include <QDebug> +#include <QtDeclarative/qmlinfo.h> + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxAnchors,Anchors); + +//TODO: should we cache relationships, so we don't have to check each time (parent-child or sibling)? +//TODO: baseline support +//TODO: support non-parent, non-sibling (need to find lowest common ancestor) + +//### const item? +//local position +static qreal position(QFxItem *item, QFxAnchorLine::AnchorLine anchorLine) +{ + qreal ret = 0.0; + switch(anchorLine) { + case QFxAnchorLine::Left: + ret = item->x(); + break; + case QFxAnchorLine::Right: + ret = item->x() + item->width(); + break; + case QFxAnchorLine::Top: + ret = item->y(); + break; + case QFxAnchorLine::Bottom: + ret = item->y() + item->height(); + break; + case QFxAnchorLine::HCenter: + ret = item->x() + item->width()/2; + break; + case QFxAnchorLine::VCenter: + ret = item->y() + item->height()/2; + break; + case QFxAnchorLine::Baseline: + ret = item->y() + item->baselineOffset(); + break; + default: + break; + } + + return ret; +} + +//position when origin is 0,0 +static qreal adjustedPosition(QFxItem *item, QFxAnchorLine::AnchorLine anchorLine) +{ + int ret = 0; + switch(anchorLine) { + case QFxAnchorLine::Left: + ret = 0; + break; + case QFxAnchorLine::Right: + ret = item->width(); + break; + case QFxAnchorLine::Top: + ret = 0; + break; + case QFxAnchorLine::Bottom: + ret = item->height(); + break; + case QFxAnchorLine::HCenter: + ret = item->width()/2; + break; + case QFxAnchorLine::VCenter: + ret = item->height()/2; + break; + case QFxAnchorLine::Baseline: + ret = item->baselineOffset(); + break; + default: + break; + } + + return ret; +} + +/*! + \internal + \class QFxAnchors + \ingroup layouts + \brief The QFxAnchors class provides a way to lay out items relative to other items. + + \warning Currently, only anchoring to siblings or parent is supported. +*/ + +QFxAnchors::QFxAnchors(QObject *parent) + : QObject(*new QFxAnchorsPrivate(), parent) +{ + +} + +void QFxAnchors::fillChanged() +{ + Q_D(QFxAnchors); + if (!d->fill) + return; + + if (d->fill == d->item->itemParent()) { //child-parent + d->item->setPos(QPointF(leftMargin(), topMargin())); + } else if (d->fill->itemParent() == d->item->itemParent()) { //siblings + d->item->setPos(QPointF(d->fill->x()+leftMargin(), d->fill->y()+topMargin())); + } + d->item->setWidth(d->fill->width()-leftMargin()-rightMargin()); + d->item->setHeight(d->fill->height()-topMargin()-bottomMargin()); +} + +/*! + \property QFxAnchors::fill + \brief what item the item should fill. + + This is a convenience property. It is the same as anchoring the left, right, top, and bottom + to another item's left, right, top, and bottom. +*/ +QFxItem *QFxAnchors::fill() const +{ + Q_D(const QFxAnchors); + return d->fill; +} + +void QFxAnchors::setFill(QFxItem *f) +{ + Q_D(QFxAnchors); + if(d->fill) { + QObject::disconnect(d->fill, SIGNAL(leftChanged()), this, SLOT(fillChanged())); + QObject::disconnect(d->fill, SIGNAL(topChanged()), this, SLOT(fillChanged())); + QObject::disconnect(d->fill, SIGNAL(widthChanged()), this, SLOT(fillChanged())); + QObject::disconnect(d->fill, SIGNAL(heightChanged()), this, SLOT(fillChanged())); + QObject::disconnect(this, SIGNAL(leftMarginChanged()), this, SLOT(fillChanged())); + QObject::disconnect(this, SIGNAL(topMarginChanged()), this, SLOT(fillChanged())); + QObject::disconnect(this, SIGNAL(rightMarginChanged()), this, SLOT(fillChanged())); + QObject::disconnect(this, SIGNAL(bottomMarginChanged()), this, SLOT(fillChanged())); + } + + d->fill = f; + + if (d->fill) { + if (d->fill == d->item->itemParent()) { //child-parent + QObject::connect(d->fill, SIGNAL(widthChanged()), this, SLOT(fillChanged())); + QObject::connect(d->fill, SIGNAL(heightChanged()), this, SLOT(fillChanged())); + } else if (f->itemParent() == d->item->itemParent()) { //siblings + QObject::connect(d->fill, SIGNAL(leftChanged()), this, SLOT(fillChanged())); + QObject::connect(d->fill, SIGNAL(topChanged()), this, SLOT(fillChanged())); + QObject::connect(d->fill, SIGNAL(widthChanged()), this, SLOT(fillChanged())); + QObject::connect(d->fill, SIGNAL(heightChanged()), this, SLOT(fillChanged())); + } else { + qmlInfo(d->item) << "Can't anchor to an item that isn't a parent or sibling."; + } + } + QObject::connect(this, SIGNAL(leftMarginChanged()), this, SLOT(fillChanged())); + QObject::connect(this, SIGNAL(topMarginChanged()), this, SLOT(fillChanged())); + QObject::connect(this, SIGNAL(rightMarginChanged()), this, SLOT(fillChanged())); + QObject::connect(this, SIGNAL(bottomMarginChanged()), this, SLOT(fillChanged())); + fillChanged(); //### can/should we defer until component completion? +} + +/*! + \property QFxAnchors::centeredIn + \brief what item the item should stay centered in the middle of. + + This is a convenience property. It is the same as anchoring the horizontalCenter + and verticalCenter to another item's horizontalCenter and verticalCenter. +*/ +QFxItem *QFxAnchors::centeredIn() const +{ + Q_D(const QFxAnchors); + return d->centeredIn; +} + +void QFxAnchors::setCenteredIn(QFxItem* c) +{ + Q_D(QFxAnchors); + if(!c){ + qmlInfo(d->item) << "Cannot center in null item."; + return; + } + if(c != d->item->itemParent() && c->itemParent() != d->item->itemParent()){ + qmlInfo(d->item) << "Can't anchor to an item that isn't a parent or sibling."; + return; + } + d->centeredIn = c; + setHorizontalCenter(c->horizontalCenter()); + setVerticalCenter(c->verticalCenter()); +} + +void QFxAnchorsPrivate::connectVHelper(const QFxAnchorLine &edge, const char *slotString) +{ + //### should we do disconnects first? (will it be called more than once?) + Q_Q(QFxAnchors); + if (edge.item == item->itemParent()) { //child-parent + switch(edge.anchorLine) { + case QFxAnchorLine::Bottom: + case QFxAnchorLine::VCenter: + QObject::connect(edge.item, SIGNAL(heightChanged()), q, slotString); + break; + case QFxAnchorLine::Top: //no connection needed + default: + break; + } + } else if (edge.item->itemParent() == item->itemParent()) { //siblings + switch(edge.anchorLine) { + case QFxAnchorLine::Top: + QObject::connect(edge.item, SIGNAL(topChanged()), q, slotString); + break; + case QFxAnchorLine::Bottom: + QObject::connect(edge.item, SIGNAL(bottomChanged()), q, slotString); + break; + case QFxAnchorLine::VCenter: + QObject::connect(edge.item, SIGNAL(vcenterChanged()), q, slotString); + break; + default: + break; + } + } else { + qmlInfo(item) << "Can't anchor to an item that isn't a parent or sibling."; + } +} + +void QFxAnchors::connectVAnchors() +{ + Q_D(QFxAnchors); + if (!d->checkVValid()) + return; + + if (d->usedAnchors & HasTopAnchor) { + const char *slotStr = SLOT(updateTopAnchor()); + + //Handle stretching connections (if we have multiple horizontal anchors) + QFxAnchorLine *edge = 0; + if (d->usedAnchors & HasBottomAnchor) { + edge = &d->bottom; + connect(this, SIGNAL(bottomMarginChanged()), this, slotStr); + } else if (d->usedAnchors & HasVCenterAnchor) { + edge = &d->vCenter; + connect(this, SIGNAL(verticalCenterOffsetChanged()), this, slotStr); + } + if (edge) { + //we need to stretch + d->connectVHelper(*edge, slotStr); + } + + //Handle top + d->connectVHelper(d->top, slotStr); + connect(this, SIGNAL(topMarginChanged()), this, slotStr); + updateTopAnchor(); + } else if (d->usedAnchors & HasBottomAnchor) { + const char *slotStr = SLOT(updateBottomAnchor()); + + //Handle stretching connections (if we have multiple horizontal anchors) + if (d->usedAnchors & HasVCenterAnchor) { + d->connectVHelper(d->vCenter, slotStr); + connect(this, SIGNAL(verticalCenterOffsetChanged()), this, slotStr); + } + + //Handle bottom + d->connectVHelper(d->bottom, slotStr); + connect(this, SIGNAL(bottomMarginChanged()), this, slotStr); + updateBottomAnchor(); + } else if (d->usedAnchors & HasVCenterAnchor) { + //Handle vCenter + const char *slotStr = SLOT(updateVCenterAnchor()); + d->connectVHelper(d->vCenter, slotStr); + connect(this, SIGNAL(verticalCenterOffsetChanged()), this, slotStr); + updateVCenterAnchor(); + } +} + +void QFxAnchorsPrivate::connectHHelper(const QFxAnchorLine &edge, const char *slotString) +{ + //### should we do disconnects first? (will it be called more than once?) + Q_Q(QFxAnchors); + if (edge.item == item->itemParent()) { //child-parent + switch(edge.anchorLine) { + case QFxAnchorLine::Right: + case QFxAnchorLine::HCenter: + QObject::connect(edge.item, SIGNAL(widthChanged()), q, slotString); + break; + case QFxAnchorLine::Left: //no connection needed + default: + break; + } + } else if (edge.item->itemParent() == item->itemParent()) { //siblings + switch(edge.anchorLine) { + case QFxAnchorLine::Left: + QObject::connect(edge.item, SIGNAL(leftChanged()), q, slotString); + break; + case QFxAnchorLine::Right: + QObject::connect(edge.item, SIGNAL(rightChanged()), q, slotString); + break; + case QFxAnchorLine::HCenter: + QObject::connect(edge.item, SIGNAL(hcenterChanged()), q, slotString); + break; + default: + break; + } + } else { + qmlInfo(item) << "Can't anchor to an item that isn't a parent or sibling."; + } +} + +void QFxAnchors::connectHAnchors() +{ + Q_D(QFxAnchors); + if (!d->checkHValid()) + return; + + if (d->usedAnchors & HasLeftAnchor) { + const char *slotStr = SLOT(updateLeftAnchor()); + + //Handle stretching connections (if we have multiple horizontal anchors) + QFxAnchorLine *edge = 0; + if (d->usedAnchors & HasRightAnchor) { + edge = &d->right; + connect(this, SIGNAL(rightMarginChanged()), this, slotStr); + } else if (d->usedAnchors & HasHCenterAnchor) { + edge = &d->hCenter; + connect(this, SIGNAL(horizontalCenterOffsetChanged()), this, slotStr); + } + if (edge) { + //we need to stretch + d->connectHHelper(*edge, slotStr); + } + + //Handle left + d->connectHHelper(d->left, slotStr); + connect(this, SIGNAL(leftMarginChanged()), this, slotStr); + updateLeftAnchor(); + } else if (d->usedAnchors & HasRightAnchor) { + const char *slotStr = SLOT(updateRightAnchor()); + + //Handle stretching connections (if we have multiple horizontal anchors) + if (d->usedAnchors & HasHCenterAnchor) { + d->connectHHelper(d->hCenter, slotStr); + connect(this, SIGNAL(horizontalCenterOffsetChanged()), this, slotStr); + } + + //Handle right + d->connectHHelper(d->right, slotStr); + connect(this, SIGNAL(rightMarginChanged()), this, slotStr); + updateRightAnchor(); + } else if (d->usedAnchors & HasHCenterAnchor) { + //Handle hCenter + const char *slotStr = SLOT(updateHCenterAnchor()); + d->connectHHelper(d->hCenter, slotStr); + connect(this, SIGNAL(horizontalCenterOffsetChanged()), this, slotStr); + updateHCenterAnchor(); + } +} + +bool QFxAnchorsPrivate::calcStretch(const QFxAnchorLine &edge1, + const QFxAnchorLine &edge2, + int offset1, + int offset2, + QFxAnchorLine::AnchorLine line, + int &stretch) +{ + bool edge1IsParent = (edge1.item == item->itemParent()); + bool edge2IsParent = (edge2.item == item->itemParent()); + bool edge1IsSibling = (edge1.item->itemParent() == item->itemParent()); + bool edge2IsSibling = (edge2.item->itemParent() == item->itemParent()); + + bool invalid = false; + if ((edge2IsParent && edge1IsParent) || (edge2IsSibling && edge1IsSibling)) { + stretch = ((int)position(edge2.item, edge2.anchorLine) + offset2) + - ((int)position(edge1.item, edge1.anchorLine) + offset1); + } else if (edge2IsParent && edge1IsSibling) { + stretch = ((int)position(edge2.item, edge2.anchorLine) + offset2) + - ((int)position(item->itemParent(), line) + + (int)position(edge1.item, edge1.anchorLine) + offset1); + } else if (edge2IsSibling && edge1IsParent) { + stretch = ((int)position(item->itemParent(), line) + (int)position(edge2.item, edge2.anchorLine) + offset2) + - ((int)position(edge1.item, edge1.anchorLine) + offset1); + } else + invalid = true; + + return invalid; +} + +void QFxAnchors::updateTopAnchor() +{ + Q_D(QFxAnchors); + if (d->usedAnchors & HasTopAnchor) { + //Handle stretching + bool invalid = true; + int height = 0; + if (d->usedAnchors & HasBottomAnchor) { + invalid = d->calcStretch(d->top, d->bottom, d->topMargin, -d->bottomMargin, QFxAnchorLine::Top, height); + } else if (d->usedAnchors & HasVCenterAnchor) { + invalid = d->calcStretch(d->top, d->vCenter, d->topMargin, d->vCenterOffset, QFxAnchorLine::Top, height); + height *= 2; + } + if (!invalid) + d->item->setHeight(height); + + //Handle top + if (d->top.item == d->item->itemParent()) { + d->item->setY(adjustedPosition(d->top.item, d->top.anchorLine) + d->topMargin); + } else if (d->top.item->itemParent() == d->item->itemParent()) { + d->item->setY(position(d->top.item, d->top.anchorLine) + d->topMargin); + } + } +} + +void QFxAnchors::updateBottomAnchor() +{ + Q_D(QFxAnchors); + if (d->usedAnchors & HasBottomAnchor) { + //Handle stretching (top + bottom case is handled in updateLeftAnchor) + if (d->usedAnchors & HasVCenterAnchor) { + int height = 0; + bool invalid = d->calcStretch(d->vCenter, d->bottom, d->vCenterOffset, -d->bottomMargin, + QFxAnchorLine::Top, height); + if (!invalid) + d->item->setHeight(height*2); + } + + //Handle bottom + if (d->bottom.item == d->item->itemParent()) { + d->item->setY(adjustedPosition(d->bottom.item, d->bottom.anchorLine) - d->item->height() - d->bottomMargin); + } else if (d->bottom.item->itemParent() == d->item->itemParent()) { + d->item->setY(position(d->bottom.item, d->bottom.anchorLine) - d->item->height() - d->bottomMargin); + } + } +} + +void QFxAnchors::updateVCenterAnchor() +{ + Q_D(QFxAnchors); + if (d->usedAnchors & HasVCenterAnchor) { + //(stetching handled in other update functions) + + //Handle vCenter + if (d->vCenter.item == d->item->itemParent()) { + d->item->setY(adjustedPosition(d->vCenter.item, d->vCenter.anchorLine) + - d->item->height()/2 + d->vCenterOffset); + } else if (d->vCenter.item->itemParent() == d->item->itemParent()) { + d->item->setY(position(d->vCenter.item, d->vCenter.anchorLine) - d->item->height()/2 + d->vCenterOffset); + } + } +} + +void QFxAnchors::updateLeftAnchor() +{ + Q_D(QFxAnchors); + if (d->usedAnchors & HasLeftAnchor) { + //Handle stretching + bool invalid = true; + int width = 0; + if (d->usedAnchors & HasRightAnchor) { + invalid = d->calcStretch(d->left, d->right, d->leftMargin, -d->rightMargin, QFxAnchorLine::Left, width); + } else if (d->usedAnchors & HasHCenterAnchor) { + invalid = d->calcStretch(d->left, d->hCenter, d->leftMargin, d->hCenterOffset, QFxAnchorLine::Left, width); + width *= 2; + } + if (!invalid) + d->item->setWidth(width); + + //Handle left + if (d->left.item == d->item->itemParent()) { + d->item->setX(adjustedPosition(d->left.item, d->left.anchorLine) + d->leftMargin); + } else if (d->left.item->itemParent() == d->item->itemParent()) { + d->item->setX(position(d->left.item, d->left.anchorLine) + d->leftMargin); + } + } +} + +void QFxAnchors::updateRightAnchor() +{ + Q_D(QFxAnchors); + if (d->usedAnchors & HasRightAnchor) { + //Handle stretching (left + right case is handled in updateLeftAnchor) + if (d->usedAnchors & HasHCenterAnchor) { + int width = 0; + bool invalid = d->calcStretch(d->hCenter, d->right, d->hCenterOffset, -d->rightMargin, + QFxAnchorLine::Left, width); + if (!invalid) + d->item->setWidth(width*2); + } + + //Handle right + if (d->right.item == d->item->itemParent()) { + d->item->setX(adjustedPosition(d->right.item, d->right.anchorLine) - d->item->width() - d->rightMargin); + } else if (d->right.item->itemParent() == d->item->itemParent()) { + d->item->setX(position(d->right.item, d->right.anchorLine) - d->item->width() - d->rightMargin); + } + } +} + +void QFxAnchors::updateHCenterAnchor() +{ + Q_D(QFxAnchors); + if (d->usedAnchors & HasHCenterAnchor) { + //Handle hCenter + if (d->hCenter.item == d->item->itemParent()) { + d->item->setX(adjustedPosition(d->hCenter.item, d->hCenter.anchorLine) - d->item->width()/2 + d->hCenterOffset); + } else if (d->hCenter.item->itemParent() == d->item->itemParent()) { + d->item->setX(position(d->hCenter.item, d->hCenter.anchorLine) - d->item->width()/2 + d->hCenterOffset); + } + } +} + +QFxAnchorLine QFxAnchors::top() const +{ + Q_D(const QFxAnchors); + return d->top; +} + +void QFxAnchors::setTop(const QFxAnchorLine &edge) +{ + Q_D(QFxAnchors); + if (!d->checkVAnchorValid(edge)) + return; + + d->usedAnchors |= HasTopAnchor; + + d->checkVValid(); + + d->top = edge; +} + +QFxAnchorLine QFxAnchors::bottom() const +{ + Q_D(const QFxAnchors); + return d->bottom; +} + +void QFxAnchors::setBottom(const QFxAnchorLine &edge) +{ + Q_D(QFxAnchors); + if (!d->checkVAnchorValid(edge)) + return; + + d->usedAnchors |= HasBottomAnchor; + + d->checkVValid(); + + d->bottom = edge; + +} + +QFxAnchorLine QFxAnchors::verticalCenter() const +{ + Q_D(const QFxAnchors); + return d->vCenter; +} + +void QFxAnchors::setVerticalCenter(const QFxAnchorLine &edge) +{ + Q_D(QFxAnchors); + if (!d->checkVAnchorValid(edge)) + return; + + d->usedAnchors |= HasVCenterAnchor; + + d->checkVValid(); + + d->vCenter = edge; +} + +QFxAnchorLine QFxAnchors::left() const +{ + Q_D(const QFxAnchors); + return d->left; +} + +void QFxAnchors::setLeft(const QFxAnchorLine &edge) +{ + Q_D(QFxAnchors); + if (!d->checkHAnchorValid(edge)) + return; + + d->usedAnchors |= HasLeftAnchor; + + d->checkHValid(); + + d->left = edge; +} + +QFxAnchorLine QFxAnchors::right() const +{ + Q_D(const QFxAnchors); + return d->right; +} + +void QFxAnchors::setRight(const QFxAnchorLine &edge) +{ + Q_D(QFxAnchors); + if (!d->checkHAnchorValid(edge)) + return; + + d->usedAnchors |= HasRightAnchor; + + d->checkHValid(); + + d->right = edge; + +} + +QFxAnchorLine QFxAnchors::horizontalCenter() const +{ + Q_D(const QFxAnchors); + return d->hCenter; +} + +void QFxAnchors::setHorizontalCenter(const QFxAnchorLine &edge) +{ + Q_D(QFxAnchors); + if (!d->checkHAnchorValid(edge)) + return; + + d->usedAnchors |= HasHCenterAnchor; + + d->checkHValid(); + + d->hCenter = edge; +} + +int QFxAnchors::leftMargin() const +{ + Q_D(const QFxAnchors); + return d->leftMargin; +} + +void QFxAnchors::setLeftMargin(int offset) +{ + Q_D(QFxAnchors); + if (d->leftMargin == offset) + return; + d->leftMargin = offset; + emit leftMarginChanged(); +} + +int QFxAnchors::rightMargin() const +{ + Q_D(const QFxAnchors); + return d->rightMargin; +} + +void QFxAnchors::setRightMargin(int offset) +{ + Q_D(QFxAnchors); + if (d->rightMargin == offset) + return; + d->rightMargin = offset; + emit rightMarginChanged(); +} + +int QFxAnchors::horizontalCenterOffset() const +{ + Q_D(const QFxAnchors); + return d->hCenterOffset; +} + +void QFxAnchors::setHorizontalCenterOffset(int offset) +{ + Q_D(QFxAnchors); + if (d->hCenterOffset == offset) + return; + d->hCenterOffset = offset; + emit horizontalCenterOffsetChanged(); +} + +int QFxAnchors::topMargin() const +{ + Q_D(const QFxAnchors); + return d->topMargin; +} + +void QFxAnchors::setTopMargin(int offset) +{ + Q_D(QFxAnchors); + if (d->topMargin == offset) + return; + d->topMargin = offset; + emit topMarginChanged(); +} + +int QFxAnchors::bottomMargin() const +{ + Q_D(const QFxAnchors); + return d->bottomMargin; +} + +void QFxAnchors::setBottomMargin(int offset) +{ + Q_D(QFxAnchors); + if (d->bottomMargin == offset) + return; + d->bottomMargin = offset; + emit bottomMarginChanged(); +} + +int QFxAnchors::verticalCenterOffset() const +{ + Q_D(const QFxAnchors); + return d->vCenterOffset; +} + +void QFxAnchors::setVerticalCenterOffset(int offset) +{ + Q_D(QFxAnchors); + if (d->vCenterOffset == offset) + return; + d->vCenterOffset = offset; + emit verticalCenterOffsetChanged(); +} + +#if 0 +/*! + \property QFxAnchors::baseline + \brief what the baseline of the item should be anchored to (aligned with). + + The baseline of a Text item is the imaginary line on which the text sits. Controls containing + text usually set their baseline to the baseline of their text. + + For non-text items, a default baseline offset of two-thirds of the item's height is used + to determine the baseline. +*/ +int QFxAnchors::baseline() const +{ + return d->item->baseline(); +} + +void QFxAnchors::setBaseline(int baseline) +{ + d->usedAnchors |= HasBaselineAnchor; + + if (d->usedAnchors & HasTopAnchor && d->usedAnchors & HasBottomAnchor) { + qmlInfo(d->item) << "Can't specify top, bottom, and baseline anchors"; + return; + } + + if (d->usedAnchors & HasTopAnchor) { + int hoffset = baseline - d->item->baseline(); + d->item->setHeight(d->item->height() + hoffset); + } else { + if (d->usedAnchors & HasBottomAnchor) { + int hoffset = d->item->baseline() - baseline; + d->item->setHeight(d->item->height() + hoffset); + } + + int boffset = d->item->baseline() - d->item->top(); + QFxItem *parentItem = d->item->itemParent(); + if (parentItem) + d->item->setY(baseline - boffset - parentItem->top()); + else + d->item->setY(baseline - boffset); + } +} +#endif + +QFxAnchors::UsedAnchors QFxAnchors::usedAnchors() const +{ + Q_D(const QFxAnchors); + return d->usedAnchors; +} + +void QFxAnchors::setItem(QFxItem *item) +{ + Q_D(QFxAnchors); + d->item = item; +} + +bool QFxAnchorsPrivate::checkHValid() const +{ + if (usedAnchors & QFxAnchors::HasLeftAnchor && + usedAnchors & QFxAnchors::HasRightAnchor && + usedAnchors & QFxAnchors::HasHCenterAnchor) { + qmlInfo(item) << "Can't specify left, right, and hcenter anchors"; + return false; + } + + return true; +} + +bool QFxAnchorsPrivate::checkHAnchorValid(QFxAnchorLine anchor) const +{ + if (anchor.anchorLine & QFxAnchorLine::Vertical_Mask) { + qmlInfo(item) << "Can't anchor a horizontal edge to a vertical edge."; + return false; + }else if(anchor.item == item){ + qmlInfo(item) << "Can't anchor item to self."; + return false; + } + + return true; +} + +bool QFxAnchorsPrivate::checkVValid() const +{ + if (usedAnchors & QFxAnchors::HasTopAnchor && + usedAnchors & QFxAnchors::HasBottomAnchor && + usedAnchors & QFxAnchors::HasVCenterAnchor) { + qmlInfo(item) << "Can't specify top, bottom, and vcenter anchors"; + return false; + } + + return true; +} + +bool QFxAnchorsPrivate::checkVAnchorValid(QFxAnchorLine anchor) const +{ + if (anchor.anchorLine & QFxAnchorLine::Horizontal_Mask) { + qmlInfo(item) << "Can't anchor a vertical edge to a horizontal edge."; + return false; + }else if(anchor.item == item){ + qmlInfo(item) << "Can't anchor item to self."; + return false; + } + + return true; +} +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxanchors.h b/src/declarative/fx/qfxanchors.h new file mode 100644 index 0000000..3a250b9 --- /dev/null +++ b/src/declarative/fx/qfxanchors.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXANCHORS_H +#define QFXANCHORS_H + +#include <qfxglobal.h> +#include <QObject> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxItem; +class QFxAnchorsPrivate; + +class QFxAnchorLine +{ +public: + QFxAnchorLine() : item(0), anchorLine(Left) + { + } + + enum AnchorLine { + Left = 0x01, + Right = 0x02, + Top = 0x04, + Bottom = 0x08, + HCenter = 0x10, + VCenter = 0x20, + Baseline = 0x40, + Horizontal_Mask = Left | Right | HCenter, + Vertical_Mask = Top | Bottom | VCenter | Baseline + }; + + QFxItem *item; + AnchorLine anchorLine; +}; + +Q_DECLARE_METATYPE(QFxAnchorLine); + +class Q_DECLARATIVE_EXPORT QFxAnchors : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QFxAnchorLine left READ left WRITE setLeft); + Q_PROPERTY(QFxAnchorLine right READ right WRITE setRight); + Q_PROPERTY(QFxAnchorLine horizontalCenter READ horizontalCenter WRITE setHorizontalCenter); + Q_PROPERTY(QFxAnchorLine top READ top WRITE setTop); + Q_PROPERTY(QFxAnchorLine bottom READ bottom WRITE setBottom); + Q_PROPERTY(QFxAnchorLine verticalCenter READ verticalCenter WRITE setVerticalCenter); + Q_PROPERTY(int leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged); + Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged); + Q_PROPERTY(int horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged()); + Q_PROPERTY(int topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged); + Q_PROPERTY(int bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged); + Q_PROPERTY(int verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged()); + Q_PROPERTY(QFxItem *fill READ fill WRITE setFill); + Q_PROPERTY(QFxItem *centeredIn READ centeredIn WRITE setCenteredIn); + +public: + QFxAnchors(QObject *parent=0); + + enum UsedAnchor { + HasLeftAnchor = 0x01, + HasRightAnchor = 0x02, + HasTopAnchor = 0x04, + HasBottomAnchor = 0x08, + HasHCenterAnchor = 0x10, + HasVCenterAnchor = 0x20, + HasBaselineAnchor = 0x40 + }; + Q_DECLARE_FLAGS(UsedAnchors, UsedAnchor); + + QFxAnchorLine left() const; + void setLeft(const QFxAnchorLine &edge); + + QFxAnchorLine right() const; + void setRight(const QFxAnchorLine &edge); + + QFxAnchorLine horizontalCenter() const; + void setHorizontalCenter(const QFxAnchorLine &edge); + + QFxAnchorLine top() const; + void setTop(const QFxAnchorLine &edge); + + QFxAnchorLine bottom() const; + void setBottom(const QFxAnchorLine &edge); + + QFxAnchorLine verticalCenter() const; + void setVerticalCenter(const QFxAnchorLine &edge); + + int leftMargin() const; + void setLeftMargin(int); + + int rightMargin() const; + void setRightMargin(int); + + int horizontalCenterOffset() const; + void setHorizontalCenterOffset(int); + + int topMargin() const; + void setTopMargin(int); + + int bottomMargin() const; + void setBottomMargin(int); + + int verticalCenterOffset() const; + void setVerticalCenterOffset(int); + + QFxItem *fill() const; + void setFill(QFxItem *); + + QFxItem *centeredIn() const; + void setCenteredIn(QFxItem *); + + UsedAnchors usedAnchors() const; + + void setItem(QFxItem *item); + + void connectHAnchors(); + void connectVAnchors(); + +Q_SIGNALS: + void leftMarginChanged(); + void rightMarginChanged(); + void topMarginChanged(); + void bottomMarginChanged(); + void verticalCenterOffsetChanged(); + void horizontalCenterOffsetChanged(); + +private Q_SLOTS: + void fillChanged(); + void updateLeftAnchor(); + void updateRightAnchor(); + void updateHCenterAnchor(); + void updateTopAnchor(); + void updateBottomAnchor(); + void updateVCenterAnchor(); + +private: + //### should item be a friend? (and make some of the public methods private or protected) + Q_DISABLE_COPY(QFxAnchors) + Q_DECLARE_PRIVATE(QFxAnchors) +}; + +QML_DECLARE_TYPE(QFxAnchors); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/fx/qfxanchors_p.h b/src/declarative/fx/qfxanchors_p.h new file mode 100644 index 0000000..3a5d1c7 --- /dev/null +++ b/src/declarative/fx/qfxanchors_p.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 QtDeclarative 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 QFXANCHORS_P_H +#define QFXANCHORS_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 "qfxanchors.h" +#include "private/qobject_p.h" + + +QT_BEGIN_NAMESPACE +class QFxAnchorsPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QFxAnchors) +public: + QFxAnchorsPrivate() + : item(0), usedAnchors(0), fill(0), centeredIn(0), leftMargin(0), rightMargin(0), + topMargin(0), bottomMargin(0), vCenterOffset(0), hCenterOffset(0) + { + } + + void init() + { + } + + bool checkHValid() const; + bool checkVValid() const; + bool checkHAnchorValid(QFxAnchorLine anchor) const; + bool checkVAnchorValid(QFxAnchorLine anchor) const; + void connectHHelper(const QFxAnchorLine &anchorLine, const char *slotString); + void connectVHelper(const QFxAnchorLine &anchorLine, const char *slotString); + bool calcStretch(const QFxAnchorLine &edge1, const QFxAnchorLine &edge2, int offset1, int offset2, QFxAnchorLine::AnchorLine line, int &stretch); + + QFxItem *item; + QFxAnchors::UsedAnchors usedAnchors; + + QFxItem *fill; + QFxItem *centeredIn; + + QFxAnchorLine left; + QFxAnchorLine right; + QFxAnchorLine top; + QFxAnchorLine bottom; + QFxAnchorLine vCenter; + QFxAnchorLine hCenter; + + int leftMargin; + int rightMargin; + int topMargin; + int bottomMargin; + int vCenterOffset; + int hCenterOffset; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/fx/qfxanimatedimageitem.cpp b/src/declarative/fx/qfxanimatedimageitem.cpp new file mode 100644 index 0000000..cc11b56 --- /dev/null +++ b/src/declarative/fx/qfxanimatedimageitem.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <QMovie> +#include <QtDeclarative/qmlcontext.h> +#include <QtDeclarative/qmlengine.h> +#include "qfxanimatedimageitem.h" +#include "qfxanimatedimageitem_p.h" +#include <QNetworkRequest> +#include <QNetworkReply> + +QT_BEGIN_NAMESPACE + +/*! + \class QFxAnimatedImageItem + \internal +*/ + +/*! + \qmlclass AnimatedImage + \inherits Image + + This item provides for playing animations stored as images containing a series of frames, + such as GIF files. The full list of supported formats can be determined with + QMovie::supportedFormats(). + + \table + \row + \o \image animatedimageitem.gif + \o + \code +<Item width="{anim.width}" height="{anim.height+8}"> + <AnimatedImage id="anim" file="pics/games-anim.gif"/> + <Rect color="red" width="4" height="8" y="{anim.height}" + x="{(anim.width-width)*anim.currentFrame/(anim.frameCount-1)}"/> +</Item> + \endcode + \endtable +*/ +QML_DEFINE_TYPE(QFxAnimatedImageItem, AnimatedImage); + +QFxAnimatedImageItem::QFxAnimatedImageItem(QFxItem *parent) + : QFxImage(*(new QFxAnimatedImageItemPrivate), parent) +{ +} + +QFxAnimatedImageItem::QFxAnimatedImageItem(QFxAnimatedImageItemPrivate &dd, QFxItem *parent) + : QFxImage(dd, parent) +{ +} + +QFxAnimatedImageItem::~QFxAnimatedImageItem() +{ + Q_D(QFxAnimatedImageItem); + delete d->_movie; +} + +/*! + \qmlproperty bool AnimatedImage::playing + This property holds whether the animated image is playing or not + + Defaults to true, so as to start playing immediately. +*/ +bool QFxAnimatedImageItem::isPlaying() const +{ + Q_D(const QFxAnimatedImageItem); + if(!d->_movie) + return false; + return d->_movie->state()==QMovie::Running; +} + +void QFxAnimatedImageItem::setPlaying(bool play) +{ + Q_D(QFxAnimatedImageItem); + if(!d->_movie) + return; + if(play) + d->_movie->start(); + else + d->_movie->stop(); +} + +/*! + \qmlproperty int AnimatedImage::currentFrame + \qmlproperty int AnimatedImage::frameCount + + currentFrame is the frame that is currently visible. Watching when this changes can + allow other things to animate at the same time as the image. frameCount is the number + of frames in the animation. For some animation formats, frameCount is unknown and set to zero. +*/ +int QFxAnimatedImageItem::currentFrame() const +{ + Q_D(const QFxAnimatedImageItem); + if(!d->_movie) + return -1; + return d->_movie->currentFrameNumber(); +} + +void QFxAnimatedImageItem::setCurrentFrame(int frame) +{ + Q_D(QFxAnimatedImageItem); + if(!d->_movie) + return; + d->_movie->jumpToFrame(frame); +} + +int QFxAnimatedImageItem::frameCount() const +{ + Q_D(const QFxAnimatedImageItem); + if(!d->_movie) + return 0; + return d->_movie->frameCount(); +} + +void QFxAnimatedImageItem::setSource(const QString &url) +{ + Q_D(QFxAnimatedImageItem); + if(url == d->source) + return; + + delete d->_movie; + d->_movie = 0; + + if(d->reply) { + d->reply->deleteLater(); + d->reply = 0; + } + + d->source = url; + d->url = itemContext()->resolvedUrl(url); + + if(url.isEmpty()) { + delete d->_movie; + d->status = Idle; + } else { + d->status = Loading; + QNetworkRequest req(d->url); + req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + d->reply = itemContext()->engine()->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), + this, SLOT(movieRequestFinished())); + } + + emit statusChanged(d->status); +} + +void QFxAnimatedImageItem::movieRequestFinished() +{ + Q_D(QFxAnimatedImageItem); + d->_movie = new QMovie(d->reply); + if(!d->_movie->isValid()){ + qWarning() << "Error Reading File " << d->url; + delete d->_movie; + d->_movie = 0; + return; + } + connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)), + this, SIGNAL(playingChanged())); + connect(d->_movie, SIGNAL(frameChanged(int)), + this, SLOT(movieUpdate())); + d->_movie->setCacheMode(QMovie::CacheAll); + d->_movie->start(); + setPixmap(d->_movie->currentPixmap()); +} + +void QFxAnimatedImageItem::movieUpdate() +{ + Q_D(QFxAnimatedImageItem); + setPixmap(d->_movie->currentPixmap()); + emit frameChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxanimatedimageitem.h b/src/declarative/fx/qfxanimatedimageitem.h new file mode 100644 index 0000000..121fe62 --- /dev/null +++ b/src/declarative/fx/qfxanimatedimageitem.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXANIMATEDIMAGEITEM_H +#define QFXANIMATEDIMAGEITEM_H + +#include <qfximage.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QMovie; +class QFxAnimatedImageItemPrivate; + +class Q_DECLARATIVE_EXPORT QFxAnimatedImageItem : public QFxImage +{ + Q_OBJECT + + Q_PROPERTY(bool playing READ isPlaying WRITE setPlaying NOTIFY playingChanged) + Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY frameChanged) + Q_PROPERTY(int frameCount READ frameCount) +public: + QFxAnimatedImageItem(QFxItem *parent=0); + ~QFxAnimatedImageItem(); + + bool isPlaying() const; + void setPlaying(bool play); + + int currentFrame() const; + void setCurrentFrame(int frame); + + int frameCount() const; + + // Extends QFxImage's src property*/ + virtual void setSource(const QString&); + +Q_SIGNALS: + void playingChanged(); + void frameChanged(); + +private Q_SLOTS: + void movieUpdate(); + void movieRequestFinished(); + +protected: + QFxAnimatedImageItem(QFxAnimatedImageItemPrivate &dd, QFxItem *parent); + +private: + Q_DISABLE_COPY(QFxAnimatedImageItem) + Q_DECLARE_PRIVATE(QFxAnimatedImageItem) +}; + +QML_DECLARE_TYPE(QFxAnimatedImageItem); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/fx/qfxanimatedimageitem_p.h b/src/declarative/fx/qfxanimatedimageitem_p.h new file mode 100644 index 0000000..cb5da63 --- /dev/null +++ b/src/declarative/fx/qfxanimatedimageitem_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXANIMATEDIMAGE_P_H +#define QFXANIMATEDIMAGE_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 "qfximage_p.h" + +QT_BEGIN_NAMESPACE + +class QMovie; + +class QFxAnimatedImageItemPrivate : public QFxImagePrivate +{ + Q_DECLARE_PUBLIC(QFxAnimatedImageItem) + +public: + QFxAnimatedImageItemPrivate() + : _movie(0) + { + } + + QMovie *_movie; +}; + +QT_END_NAMESPACE + +#endif // QFXANIMATEDIMAGE_P_H diff --git a/src/declarative/fx/qfxblendedimage.cpp b/src/declarative/fx/qfxblendedimage.cpp new file mode 100644 index 0000000..773018f --- /dev/null +++ b/src/declarative/fx/qfxblendedimage.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxblendedimage.h" +#include <QtDeclarative/qmlcontext.h> + +#if defined(QFX_RENDER_OPENGL2) +#include <glbasicshaders.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QFxBlendedImage + \brief The QFxBlendedImage blends two different images depending on a blend ratio. + + This class can be used to simulate blur on slow devices by setting secondaryFile with + a pre-rendered blurred version of primaryFile. + + Note that this class will only work under OpenGL. On the software canvas it will display + only the primary image unless the blend is > 0.75, in which case it will display only the + secondary image. +*/ +QFxBlendedImage::QFxBlendedImage(QFxItem *parent) +: QFxItem(parent), _blend(0), dirty(false) +{ +#if defined(QFX_RENDER_OPENGL2) + setOptions(HasContents); +#endif +} + +/*! + \property QFxBlendedImage::primaryUrl + \brief the URL of the first image to be displayed in this item. +*/ +QString QFxBlendedImage::primaryUrl() const +{ + return primSrc; +} + +void QFxBlendedImage::primaryLoaded() +{ + primPix = QFxPixmap(primUrl); + dirty = true; + update(); +} + +void QFxBlendedImage::setPrimaryUrl(const QString &url) +{ + if (primSrc == url) + return; + if (!primSrc.isEmpty()) + QFxPixmap::cancelGet(primUrl,this,SLOT(primaryLoaded())); + primSrc = url; + primUrl = itemContext()->resolvedUrl(url); + if (!primSrc.isEmpty()) + QFxPixmap::get(itemContext()->engine(), primUrl,this,SLOT(primaryLoaded())); +} + +/*! + \property QFxBlendedImage::secondaryFile + \brief the URL of the second image to be displayed in this item. +*/ +QString QFxBlendedImage::secondaryUrl() const +{ + return secSrc; +} + +void QFxBlendedImage::secondaryLoaded() +{ + secPix = QFxPixmap(secUrl); + dirty = true; + update(); +} + +void QFxBlendedImage::setSecondaryUrl(const QString &url) +{ + if (secSrc == url) + return; + if (!secSrc.isEmpty()) + QFxPixmap::cancelGet(secUrl,this,SLOT(secondaryLoaded())); + secSrc = url; + secUrl = itemContext()->resolvedUrl(url); + if (!secSrc.isEmpty()) + QFxPixmap::get(itemContext()->engine(), secUrl,this,SLOT(secondaryLoaded())); +} + +/*! + \property QFxBlendedImage::blend + \brief the ratio used to blend the two images. + + If blend has a value of 0, only the first image will be displayed. + If blend has a value of 1, only the second image will be displayed. +*/ +qreal QFxBlendedImage::blend() const +{ + return _blend; +} + +void QFxBlendedImage::setBlend(qreal b) +{ + _blend = b; + update(); +} + +#if defined(QFX_RENDER_QPAINTER) + +void QFxBlendedImage::paintContents(QPainter &p) +{ + if (primSrc.isNull() && secSrc.isNull()) + return; + if (_blend < 0.75) + p.drawImage(0, 0, primPix); + else + p.drawImage(0, 0, secPix); +} + +#elif defined(QFX_RENDER_OPENGL2) + +void QFxBlendedImage::paintGLContents(GLPainter &p) +{ + static DualTextureBlendShader *shader = 0; + if(!shader) + shader = new DualTextureBlendShader(); + + if(dirty) { + prim.clear(); + sec.clear(); + prim.setImage(primPix); + sec.setImage(secPix); + + dirty = false; + } + + if(prim.isNull() || sec.isNull()) { + + return; + } + + GLfloat vertices[8]; + GLfloat texVertices[8]; + + float widthV = width(); + float heightV = height(); + if(!widthV) + widthV = qMax(primPix.width(), secPix.width()); + if(!heightV) + heightV = qMax(primPix.height(), secPix.height()); + + vertices[0] = 0; vertices[1] = heightV; + vertices[2] = widthV; vertices[3] = heightV; + vertices[4] = 0; vertices[5] = 0; + vertices[6] = widthV; vertices[7] = 0; + + texVertices[0] = 0; texVertices[1] = 0; + texVertices[2] = 1; texVertices[3] = 0; + texVertices[4] = 0; texVertices[5] = 1; + texVertices[6] = 1; texVertices[7] = 1; + + if(_blend == 0 || _blend == 1) { + QGLShaderProgram *tshader = p.useTextureShader(); + + GLTexture *tex = 0; + + if(_blend == 0) + tex = &prim; + else + tex = &sec; + + tshader->setAttributeArray(SingleTextureShader::Vertices, vertices, 2); + tshader->setAttributeArray(SingleTextureShader::TextureCoords, texVertices, 2); + + glBindTexture(GL_TEXTURE_2D, tex->texture()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + tshader->disableAttributeArray(SingleTextureShader::Vertices); + tshader->disableAttributeArray(SingleTextureShader::TextureCoords); + } else { + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, prim.texture()); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, sec.texture()); + + shader->enable(); + shader->setOpacity(1); + qreal b = _blend; + if(b > 1) b = 1; + else if(b < 0) b = 0; + shader->setBlend(b); + shader->setTransform(p.activeTransform); + + shader->setAttributeArray(DualTextureBlendShader::Vertices, vertices, 2); + shader->setAttributeArray(DualTextureBlendShader::TextureCoords, texVertices, 2); + shader->setAttributeArray(DualTextureBlendShader::BlendTextureCoords, texVertices, 2); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + shader->disableAttributeArray(DualTextureBlendShader::Vertices); + shader->disableAttributeArray(DualTextureBlendShader::TextureCoords); + shader->disableAttributeArray(DualTextureBlendShader::BlendTextureCoords); + + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + } +} +#endif + +QML_DEFINE_TYPE(QFxBlendedImage,BlendedImage); +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxblendedimage.h b/src/declarative/fx/qfxblendedimage.h new file mode 100644 index 0000000..96d3135 --- /dev/null +++ b/src/declarative/fx/qfxblendedimage.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 QtDeclarative 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 QFXBLENDEDIMAGE_H +#define QFXBLENDEDIMAGE_H + +#include <qfxitem.h> +#if defined(QFX_RENDER_OPENGL2) +#include <gltexture.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QFxBlendedImage : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(QString primaryUrl READ primaryUrl WRITE setPrimaryUrl) + Q_PROPERTY(QString secondaryUrl READ secondaryUrl WRITE setSecondaryUrl) + Q_PROPERTY(qreal blend READ blend WRITE setBlend) +public: + QFxBlendedImage(QFxItem *parent=0); + + QString primaryUrl() const; + void setPrimaryUrl(const QString &); + QString secondaryUrl() const; + void setSecondaryUrl(const QString &); + + qreal blend() const; + void setBlend(qreal); + +#if defined(QFX_RENDER_QPAINTER) + void paintContents(QPainter &painter); +#elif defined(QFX_RENDER_OPENGL2) + void paintGLContents(GLPainter &); +#endif + +private Q_SLOTS: + void primaryLoaded(); + void secondaryLoaded(); + +private: + QString primSrc; + QString secSrc; + QUrl primUrl; + QUrl secUrl; + + qreal _blend; + bool dirty; +#if defined(QFX_RENDER_OPENGL2) + GLTexture prim; + GLTexture sec; +#endif + QFxPixmap primPix; + QFxPixmap secPix; +}; +QML_DECLARE_TYPE(QFxBlendedImage); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QFXBLENDEDIMAGE_H diff --git a/src/declarative/fx/qfxblurfilter.cpp b/src/declarative/fx/qfxblurfilter.cpp new file mode 100644 index 0000000..8cc9380 --- /dev/null +++ b/src/declarative/fx/qfxblurfilter.cpp @@ -0,0 +1,462 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxblurfilter.h" +#include <private/qsimplecanvasitem_p.h> + +#if defined(QFX_RENDER_OPENGL2) +#include <glsave.h> +#include <QtOpenGL/qglframebufferobject.h> +#include <glbasicshaders.h> +#endif + +QT_BEGIN_NAMESPACE +class QFxBlurFilterPrivate +{ +public: + QFxBlurFilterPrivate() + : radius(0) + { + } + qreal radius; +}; + +/*! + \qmlclass Blur + \brief The Blur filter blurs an item and its contents. + \inherits Filter + + Blurring reduces the clarity of a visual element. The following example + shows an icon at a blur radius of 0, 5 and 10. + + \table + \row + \o + \code + <HorizontalLayout> + <Image src="icon.png"> + <filter><Blur radius="0" /></filter> + </Image> + <Image src="icon.png"> + <filter><Blur radius="5" /></filter> + </Image> + <Image src="icon.png"> + <filter><Blur radius="10" /></filter> + </Image> + </HorizontalLayout> + \endcode + \row + \o \image blur_example.png + \endtable + + Bluring is only supported when Qt Declarative is compiled for OpenGL ES 2.0. + Otherwise the Blur filter has no effect. + */ +/*! + \internal + \class QFxBlurFilter + \ingroup effects + \brief The QFxBlurFilter class allows you to blur an item. +*/ + +QFxBlurFilter::QFxBlurFilter(QObject *parent) +: QSimpleCanvasFilter(parent), d(new QFxBlurFilterPrivate) +{ +} + +QFxBlurFilter::~QFxBlurFilter() +{ + delete d; d = 0; +} + +/*! + \qmlproperty real Blur::radius + + Sets the blur kernel radius. The larger the radius the more blurry the item will appear. A radius of 0 (or less) is equivalent to no blur. + */ + +/*! + \property QFxBlurFilter::radius + \brief the radius of the blur. +*/ +qreal QFxBlurFilter::radius() const +{ + return d->radius; +} + +void QFxBlurFilter::setRadius(qreal radius) +{ + if(d->radius == radius) return; + d->radius = radius; + emit radiusChanged(radius); + update(); +} + +QRectF QFxBlurFilter::itemBoundingRect(const QRectF &r) const +{ + QRectF rv = r; + if(d->radius > 0) + rv.adjust(-d->radius, -d->radius, d->radius, d->radius); + return rv; +} + +#include <math.h> +void QFxBlurFilter::filterGL(QSimpleCanvasItem::GLPainter &p) +{ +#if defined(QFX_RENDER_OPENGL2) +#if 1 + if(d->radius <= 0) { + renderToScreen(); + return; + } + float radius = d->radius; + QSimpleCanvasItem *item = this->item(); + + QRect r = item->itemBoundingRect(); + float blurScale = 1.0; + QRect tr = QRect(QPoint(0, 0), r.size() * blurScale); + radius *= blurScale; + + QGLFramebufferObject *fbo = renderToFBO(blurScale); + if(!fbo) + return; + + float height = r.height(); + float width = r.width(); + + float texWidth = float(tr.width()) / float(fbo->width()); + float texHeight = float(tr.height()) / float(fbo->height()); + + int steps = int(::ceil(radius)); + int dispSteps = int(::ceil(d->radius)); + float xstep = texWidth * radius / float(steps * fbo->width()); + float xinc = steps / float(fbo->width()); + + glDisable(GL_BLEND); + + // Render x pass + QSize xSize(tr.width() + 2 * steps, tr.height()); + QGLFramebufferObject *xBlur = acquireFBO(xSize); + float xWidth = float(xSize.width()) / float(xBlur->width()); + float xHeight = float(xSize.height()) / float(xBlur->height()); + { + xBlur->bind(); + + GLSaveViewport sv; GLSaveScissor ss; + glClearColor(0,0,0,0); + glDisable(GL_SCISSOR_TEST); + glViewport(0, 0, xBlur->width(), xBlur->height()); + glClear(GL_COLOR_BUFFER_BIT); + + float vert[] = { 0, xHeight, + xWidth, xHeight, + 0, 0, + xWidth, 0 }; + float texVert[] = { -xinc, 0, + texWidth + xinc, 0, + -xinc, texHeight, + texWidth + xinc, texHeight }; + + QMatrix4x4 trans; + trans.translate(-1, -1); + trans.scale(2, 2); + BlurTextureShader *shader = item->basicShaders()->blurTexture(); + shader->enable(); + shader->setTransform(trans); + if(steps > 1) { + shader->setStep(xstep * 2); + shader->setSteps(steps / 2); + } else { + shader->setStep(xstep); + shader->setSteps(steps); + } + shader->setMode(BlurTextureShader::Horizontal); + + glBindTexture(GL_TEXTURE_2D, fbo->texture()); + + shader->setAttributeArray(BlurTextureShader::Vertices, vert, 2); + shader->setAttributeArray(BlurTextureShader::TextureCoords, texVert, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(BlurTextureShader::Vertices); + shader->disableAttributeArray(BlurTextureShader::TextureCoords); + xBlur->release(); + } + + // Render y pass + QSize ySize(xSize.width(), tr.height() + 2 * steps); + QGLFramebufferObject *yBlur = acquireFBO(ySize); + + float yWidth = float(ySize.width()) / float(yBlur->width()); + float yHeight = float(ySize.height()) / float(yBlur->height()); + float ystep = radius / float(steps * xBlur->height()); + float yinc = steps / float(xBlur->height()); + { + yBlur->bind(); + + GLSaveViewport sv; GLSaveScissor ss; + glClearColor(0,0,0,0); + glDisable(GL_SCISSOR_TEST); + glViewport(0, 0, yBlur->width(), yBlur->height()); + glClear(GL_COLOR_BUFFER_BIT); + + float vert[] = { 0, yHeight, + yWidth, yHeight, + 0, 0, + yWidth, 0 }; + float texVert[] = { 0, -yinc, + xWidth, -yinc, + 0, xHeight + yinc, + xWidth, xHeight + yinc }; + + QMatrix4x4 trans; + trans.translate(-1, -1); + trans.scale(2, 2); + BlurTextureShader *shader = item->basicShaders()->blurTexture(); + shader->enable(); + shader->setTransform(trans); + if(steps > 1) { + shader->setStep(ystep * 2); + shader->setSteps(steps / 2); + } else { + shader->setStep(ystep); + shader->setSteps(steps); + } + shader->setMode(BlurTextureShader::Vertical); + + glBindTexture(GL_TEXTURE_2D, xBlur->texture()); + + shader->setAttributeArray(BlurTextureShader::Vertices, vert, 2); + shader->setAttributeArray(BlurTextureShader::TextureCoords, texVert, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(BlurTextureShader::Vertices); + shader->disableAttributeArray(BlurTextureShader::TextureCoords); + yBlur->release(); + } + + glEnable(GL_BLEND); + + // Render display pass + { + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + float vert[] = { -dispSteps, height + dispSteps, + width + dispSteps, height + dispSteps, + -dispSteps, -dispSteps, + width + dispSteps, -dispSteps }; + float texVert[] = { 0, 0, + yWidth, 0, + 0, yHeight, + yWidth, yHeight }; + SingleTextureShader *shader = item->basicShaders()->singleTexture(); + shader->enable(); + shader->setTransform(p.activeTransform); + + glBindTexture(GL_TEXTURE_2D, yBlur->texture()); + + shader->setAttributeArray(SingleTextureShader::Vertices, vert, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, texVert, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(SingleTextureShader::Vertices); + shader->disableAttributeArray(SingleTextureShader::TextureCoords); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + + releaseFBO(yBlur); + releaseFBO(xBlur); + releaseFBO(fbo); +#else +#if 0 + if(d->radius <= 0) { + renderToScreen(); + return; + } + QSimpleCanvasItem *item = this->item(); + + QRect r = item->itemBoundingRect(); + + float scale = 0.5; + float scalePercent = scale / d->radius; + QGLFramebufferObject *fbo = renderToFBO(scalePercent); + if(!fbo) + return; + + QGLFramebufferObject *xfbo = acquireFBO(QSize(scale * r.width(), fbo->height())); + QGLFramebufferObject *yfbo = acquireFBO(QSize(scale * r.width(), scale * r.height())); + + + BlurTextureShader *shader = item->basicShaders()->blurTexture(); + shader->enable(); + shader->setTransform(QMatrix4x4()); + + // Render up - x + { + shader->setMode(BlurTextureShader::Horizontal); + shader->setStep(1. / float(xfbo->width())); + + GLSaveViewport vp; + xfbo->bind(); + glClearColor(0,0,0,0); + glViewport(0, 0, xfbo->width(), xfbo->height()); + glClear(GL_COLOR_BUFFER_BIT); + + float oWidth = -1. + 2. * float(r.width()) * scale / float(xfbo->width()); + float oHeight = -1. + 2. * float(r.height()) * scalePercent / float(xfbo->height()); + float vert[] = { + -1, -1, + oWidth, -1, + -1, oHeight, + + -1, oHeight, + oWidth, oHeight, + oWidth, -1 + }; + + float tWidth = r.width() * scalePercent / fbo->width(); + float tHeight = r.height() * scalePercent / fbo->height(); + float texVert[] = { + 0, 0, + tWidth, 0, + 0, tHeight, + + 0, tHeight, + tWidth, tHeight, + tWidth, 0 + }; + + glBindTexture(GL_TEXTURE_2D, fbo->texture()); + shader->setAttributeArray(BlurTextureShader::Vertices, vert, 2); + shader->setAttributeArray(BlurTextureShader::TextureCoords, texVert, 2); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + xfbo->release(); + } + + // Render up - y + { + shader->setMode(BlurTextureShader::Vertical); + shader->setStep(1. / float(yfbo->height())); + + GLSaveViewport vp; + yfbo->bind(); + glClearColor(0,0,0,0); + glViewport(0, 0, yfbo->width(), yfbo->height()); + glClear(GL_COLOR_BUFFER_BIT); + + float oWidth = -1. + 2. * r.width() * scale / yfbo->width(); + float oHeight = -1. + 2. * r.height() * scale / yfbo->height(); + float vert[] = { + -1, -1, + oWidth, -1, + -1, oHeight, + + -1, oHeight, + oWidth, oHeight, + oWidth, -1 + }; + + float tWidth = r.width() * scale / xfbo->width(); + float tHeight = r.height() * scalePercent / xfbo->height(); + float texVert[] = { + 0, 0, + tWidth, 0, + 0, tHeight, + + 0, tHeight, + tWidth, tHeight, + tWidth, 0 + }; + + glBindTexture(GL_TEXTURE_2D, xfbo->texture()); + shader->setAttributeArray(BlurTextureShader::Vertices, vert, 2); + shader->setAttributeArray(BlurTextureShader::TextureCoords, texVert, 2); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + yfbo->release(); + } + + shader->disableAttributeArray(BlurTextureShader::Vertices); + shader->disableAttributeArray(BlurTextureShader::TextureCoords); + + float width = r.width(); + float height = r.height(); + //paint to screen + { + float texWidth = r.width() * scale / float(yfbo->width()); + float texHeight = r.height() * scale / float(yfbo->height()); + + GLfloat vertices[] = { 0, height, + width, height, + 0, 0, + width, 0 }; + GLfloat texVertices[] = { 0, 0, + texWidth, 0, + 0, texHeight, + texWidth, texHeight }; + + glBindTexture(GL_TEXTURE_2D, yfbo->texture()); + + SingleTextureOpacityShader *shader = + item->basicShaders()->singleTextureOpacity(); + shader->enable(); + shader->setTransform(p.activeTransform); + shader->setOpacity(p.activeOpacity); + shader->setAttributeArray(SingleTextureVertexOpacityShader::Vertices, vertices, 2); + shader->setAttributeArray(SingleTextureVertexOpacityShader::TextureCoords, texVertices, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(SingleTextureVertexOpacityShader::Vertices); + shader->disableAttributeArray(SingleTextureVertexOpacityShader::TextureCoords); + } + + + releaseFBO(fbo); + releaseFBO(xfbo); + releaseFBO(yfbo); +#endif +#endif +#else + Q_UNUSED(p); +#endif + +} + +QML_DEFINE_TYPE(QFxBlurFilter,Blur); +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxblurfilter.h b/src/declarative/fx/qfxblurfilter.h new file mode 100644 index 0000000..7a2b5b9 --- /dev/null +++ b/src/declarative/fx/qfxblurfilter.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXBLURFILTER_H +#define QFXBLURFILTER_H + +#include <qsimplecanvasfilter.h> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxBlurFilterPrivate; +class Q_DECLARATIVE_EXPORT QFxBlurFilter : public QSimpleCanvasFilter +{ + Q_OBJECT + Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) +public: + QFxBlurFilter(QObject *parent=0); + virtual ~QFxBlurFilter(); + + qreal radius() const; + void setRadius(qreal); + +Q_SIGNALS: + void radiusChanged(qreal); + +protected: + virtual QRectF itemBoundingRect(const QRectF &) const; + virtual void filterGL(QSimpleCanvasItem::GLPainter &p); + +private: + QFxBlurFilterPrivate *d; +}; +QML_DECLARE_TYPE(QFxBlurFilter); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QFXBLURFILTER_H diff --git a/src/declarative/fx/qfxcomponentinstance.cpp b/src/declarative/fx/qfxcomponentinstance.cpp new file mode 100644 index 0000000..d561d05 --- /dev/null +++ b/src/declarative/fx/qfxcomponentinstance.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxcomponentinstance.h" +#include "qfxcomponentinstance_p.h" +#include <qfxperf.h> +#include <qfxcontentwrapper.h> +#include <QtDeclarative/qmlinfo.h> + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxComponentInstance,ComponentInstance); + +/*! + \internal + \class QFxComponentInstance + \qmlclass ComponentInstance + + \brief The QFxComponentInstance class provides a way to instantiate an item from a component. + */ + +/*! + \qmlclass ComponentInstance QFxComponentInstance + \brief The ComponentInstance element allows you to instantiate an arbitrary component. +*/ +QFxComponentInstance::QFxComponentInstance(QFxItem *parent) + : QFxItem(*(new QFxComponentInstancePrivate), parent) +{ + setOptions(IsFocusRealm); +} + +QFxComponentInstance::QFxComponentInstance(QFxComponentInstancePrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + setOptions(IsFocusRealm); +} + +QmlComponent *QFxComponentInstance::component() const +{ + Q_D(const QFxComponentInstance); + return d->component; +} + +void QFxComponentInstance::setComponent(QmlComponent *c) +{ + Q_D(QFxComponentInstance); + if(d->component) { + qmlInfo(this) << "component is a write-once property."; + return; + } + d->component = c; + create(); +} + +void QFxComponentInstance::create() +{ + Q_D(QFxComponentInstance); + if(d->component) { + QObject *obj= d->component->create(itemContext()); + if(obj) { + QFxItem *objitem = qobject_cast<QFxItem *>(obj); + if(objitem) { + d->instance = objitem; + objitem->setItemParent(this); + objitem->setFocus(true); + connect(objitem, SIGNAL(widthChanged()), this, SLOT(updateSize())); + connect(objitem, SIGNAL(heightChanged()), this, SLOT(updateSize())); + updateSize(); + emit instanceChanged(); + } else { + delete obj; + } + } + } +} + +void QFxComponentInstance::updateSize() +{ + QFxItem *i = instance(); + if(i) { + if(!widthValid()) + setImplicitWidth(i->width()); + if(!heightValid()) + setImplicitHeight(i->height()); + } +} + +QFxItem *QFxComponentInstance::instance() const +{ + Q_D(const QFxComponentInstance); + return d->instance; +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxcomponentinstance.h b/src/declarative/fx/qfxcomponentinstance.h new file mode 100644 index 0000000..64af355 --- /dev/null +++ b/src/declarative/fx/qfxcomponentinstance.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 QtDeclarative 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 QFXCOMPONENTINSTANCE_H +#define QFXCOMPONENTINSTANCE_H + +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxComponentInstancePrivate; +class Q_DECLARATIVE_EXPORT QFxComponentInstance : public QFxItem +{ + Q_OBJECT + Q_PROPERTY(QmlComponent *component READ component WRITE setComponent) + Q_PROPERTY(QFxItem *instance READ instance); + Q_CLASSINFO("DefaultProperty", "component") +public: + QFxComponentInstance(QFxItem *parent=0); + + QmlComponent *component() const; + void setComponent(QmlComponent *); + + QFxItem *instance() const; + +Q_SIGNALS: + void instanceChanged(); + +private slots: + void updateSize(); + +private: + void create(); + +protected: + QFxComponentInstance(QFxComponentInstancePrivate &dd, QFxItem *parent); + +private: + Q_DECLARE_PRIVATE(QFxComponentInstance) +}; +QML_DECLARE_TYPE(QFxComponentInstance); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXCOMPONENTINSTANCE_H diff --git a/src/declarative/fx/qfxcomponentinstance_p.h b/src/declarative/fx/qfxcomponentinstance_p.h new file mode 100644 index 0000000..defeb74 --- /dev/null +++ b/src/declarative/fx/qfxcomponentinstance_p.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 QtDeclarative 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 QFXCOMPONENTINSTANCE_P_H +#define QFXCOMPONENTINSTANCE_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 "qfxitem_p.h" + + +QT_BEGIN_NAMESPACE +class QFxComponentInstancePrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxComponentInstance) + +public: + QFxComponentInstancePrivate() + : component(0), instance(0) + { + } + + QmlComponent *component; + QFxItem *instance; +}; + +QT_END_NAMESPACE + +#endif // QFXCOMPONENTINSTANCE_P_H diff --git a/src/declarative/fx/qfxcontentwrapper.cpp b/src/declarative/fx/qfxcontentwrapper.cpp new file mode 100644 index 0000000..80710ca --- /dev/null +++ b/src/declarative/fx/qfxcontentwrapper.cpp @@ -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 QtDeclarative 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 "qfxcontentwrapper.h" +#include "qfxcontentwrapper_p.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxContentWrapper,ContentWrapper); + +QFxContentWrapper::QFxContentWrapper(QFxItem *parent) +: QFxItem(*(new QFxContentWrapperPrivate), parent) +{ +} + +QFxContentWrapper::QFxContentWrapper(QFxContentWrapperPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ +} + +QList<QFxItem *> *QFxContentWrapper::content() +{ + Q_D(QFxContentWrapper); + return &(d->_content); +} + +void QFxContentWrapper::componentComplete() +{ + QFxItem::componentComplete(); + if (content()->size() < 1) + return; + + QList<QSimpleCanvasItem *> nodes; + nodes.append(this); + QFxItem *target = findContent(nodes); + if (!target) + return; + target = target->itemParent(); + + QList<QFxItem*> myContent(*content()); + for(int ii = 0; ii < myContent.count(); ++ii) + myContent.at(ii)->setParent(target); +} + +QFxItem *QFxContentWrapper::findContent(QList<QSimpleCanvasItem *> &nodes) +{ + QSimpleCanvasItem *item = nodes.takeFirst(); + if (qobject_cast<QFxContent*>(item)) + return static_cast<QFxItem *>(item); + nodes << item->children(); + if (nodes.isEmpty()) + return 0; + return findContent(nodes); +} + +QML_DEFINE_TYPE(QFxContent,Content); + +/*! + \qmlclass Content QFxContent + \ingroup utility + \brief Content is used as a placeholder for the content of a component. + \inherits Item + + In some cases the content of a component is not defined by the component itself. + For example, the items placed in a group box need to be specified external to + the where the group box component itself is defined. + In cases like these Content can be used to specify at what location in the component + the content should be placed. It is used in conjuntion with the content property of + the component instance: any items listed as content will be placed in the location + specified by Content. + + Example: + \code + <!--GroupBox component definition--> + <Rect width="{parent.width}" color="white" pen.width="2" pen.color="#adaeb0" radius="10" clip="false" height="{contents.height}"> + <VerticalLayout id="layout" width="{parent.width}"> + <Content/> + </VerticalLayout> + </Rect> + + <!--component use--> + <GroupBox> + <content> + <Text text="First Item"/> + ... + </content> + </GroupBox> + \endcode +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxcontentwrapper.h b/src/declarative/fx/qfxcontentwrapper.h new file mode 100644 index 0000000..5d5a7e1 --- /dev/null +++ b/src/declarative/fx/qfxcontentwrapper.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 QtDeclarative 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 QFXCONTENTWRAPPER_H +#define QFXCONTENTWRAPPER_H + +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxContentWrapperPrivate; +class Q_DECLARATIVE_EXPORT QFxContentWrapper : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(QList<QFxItem *>* content READ content DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "content") +public: + QFxContentWrapper(QFxItem *parent=0); + + QList<QFxItem *> *content(); + +private: + void create(); + QFxItem *findContent(QList<QSimpleCanvasItem *> &nodes); + +protected: + void componentComplete(); + QFxContentWrapper(QFxContentWrapperPrivate &dd, QFxItem *parent); + +private: + Q_DECLARE_PRIVATE(QFxContentWrapper) +}; +QML_DECLARE_TYPE(QFxContentWrapper); + +class Q_DECLARATIVE_EXPORT QFxContent : public QFxItem +{ + Q_OBJECT +public: + QFxContent(QFxItem *parent=0) : QFxItem(parent) {} +}; +QML_DECLARE_TYPE(QFxContent); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXCONTENTWRAPPER_H diff --git a/src/declarative/fx/qfxcontentwrapper_p.h b/src/declarative/fx/qfxcontentwrapper_p.h new file mode 100644 index 0000000..4f42e00 --- /dev/null +++ b/src/declarative/fx/qfxcontentwrapper_p.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 QtDeclarative 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 QFXCONTENTWRAPPER_P_H +#define QFXCONTENTWRAPPER_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 "qfxitem_p.h" +#include "qfxcontentwrapper.h" + + +QT_BEGIN_NAMESPACE +class QFxContentWrapperPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxContentWrapper); +public: + QFxContentWrapperPrivate() { } + + QList<QFxItem *> _content; +}; + +QT_END_NAMESPACE +#endif // QFXCONTENTWRAPPER_P_H diff --git a/src/declarative/fx/qfxflickable.cpp b/src/declarative/fx/qfxflickable.cpp new file mode 100644 index 0000000..7e13036 --- /dev/null +++ b/src/declarative/fx/qfxflickable.cpp @@ -0,0 +1,1113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxflickable.h" +#include "qfxflickable_p.h" + +#include <QGraphicsSceneMouseEvent> +#include <gfxeasing.h> +#include <QPointer> +#include <QTimer> + +QT_BEGIN_NAMESPACE + +ElasticValue::ElasticValue(GfxValue &val) + : _value(val) +{ + _to = _value.value(); + _myValue = _to; + _velocity = 0; +} + +void ElasticValue::setValue(qreal to) +{ + if (_to != to) { + _to = to; + _startTime.start(); + if (state() != Running) + start(); + } +} + +void ElasticValue::clear() +{ + stop(); + _velocity = 0.0; + _myValue = _value.value(); +} + +void ElasticValue::updateCurrentTime(int) +{ + const qreal Tension = 0.1; + int elapsed = _startTime.restart(); + if (!elapsed) + return; + qreal dist = _to - _value.value(); + qreal move = Tension * dist * qAbs(dist); + if (elapsed < 100 && _velocity != 0.0) + move = (elapsed * move + (100 - elapsed) * _velocity) / 100; + _myValue += move * elapsed / 1000; + _value.setValue(qRound(_myValue)); // moving sub-pixel can be ugly. +// _value.setValue(_myValue); + _velocity = move; + if (qAbs(_velocity) < 5.0) + clear(); + emit updated(); +} + +QFxFlickablePrivate::QFxFlickablePrivate() + : _flick(new QFxItem), _moveX(_flick, &QFxItem::setX), _moveY(_flick, &QFxItem::setY) + , vWidth(-1), vHeight(-1), overShoot(true), flicked(false), moving(false), stealMouse(false) + , pressed(false), maxVelocity(-1), locked(false), dragMode(QFxFlickable::Hard) + , elasticY(_moveY), elasticX(_moveX), velocityDecay(100), xVelocity(this), yVelocity(this) + , vTime(0), atXEnd(false), atXBeginning(true), pageXPosition(0.), pageWidth(0.) + , atYEnd(false), atYBeginning(true), pageYPosition(0.), pageHeight(0.) +{ + fixupXEvent = GfxEvent::gfxEvent<QFxFlickablePrivate, &QFxFlickablePrivate::fixupX>(&_moveX, this); + fixupYEvent = GfxEvent::gfxEvent<QFxFlickablePrivate, &QFxFlickablePrivate::fixupY>(&_moveY, this); +} + +void QFxFlickablePrivate::init() +{ + Q_Q(QFxFlickable); + _flick->setParent(q); + QObject::connect(&_tl, SIGNAL(updated()), q, SLOT(ticked())); + QObject::connect(&_tl, SIGNAL(completed()), q, SLOT(movementEnding())); + q->setAcceptedMouseButtons(Qt::NoButton); + q->setOptions(QSimpleCanvasItem::MouseFilter | QSimpleCanvasItem::MouseEvents); + QObject::connect(_flick, SIGNAL(leftChanged()), q, SIGNAL(positionChanged())); + QObject::connect(_flick, SIGNAL(topChanged()), q, SIGNAL(positionChanged())); + QObject::connect(&elasticX, SIGNAL(updated()), q, SLOT(ticked())); + QObject::connect(&elasticY, SIGNAL(updated()), q, SLOT(ticked())); +} + +void QFxFlickablePrivate::fixupX() +{ + Q_Q(QFxFlickable); + if(!q->xflick() || _moveX.timeLine()) + return; + + vTime = _tl.time(); + + if(_moveX.value() > q->minXExtent() || q->maxXExtent() > 0) { + _tl.move(_moveX, q->minXExtent(), GfxEasing(GfxEasing::InOutQuad), 200); + flicked = false; + //emit flickingChanged(); + } else if(_moveX.value() < q->maxXExtent()) { + _tl.move(_moveX, q->maxXExtent(), GfxEasing(GfxEasing::InOutQuad), 200); + flicked = false; + //emit flickingChanged(); + } +} + +void QFxFlickablePrivate::fixupY() +{ + Q_Q(QFxFlickable); + if(!q->yflick() || _moveY.timeLine()) + return; + + vTime = _tl.time(); + + if(_moveY.value() > q->minYExtent() || (q->maxYExtent() > q->minYExtent())) { + _tl.move(_moveY, q->minYExtent(), GfxEasing(GfxEasing::InOutQuad), 200); + //emit flickingChanged(); + } else if(_moveY.value() < q->maxYExtent()) { + _tl.move(_moveY, q->maxYExtent(), GfxEasing(GfxEasing::InOutQuad), 200); + //emit flickingChanged(); + } else { + flicked = false; + } +} + +void QFxFlickablePrivate::updateBeginningEnd() +{ + Q_Q(QFxFlickable); + bool pageChange = false; + bool atBoundaryChange = false; + + // Vertical + const int viewheight = q->height(); + const int maxyextent = int(-q->maxYExtent()); + const qreal ypos = -_moveY.value(); + qreal pagePos = ((ypos * 100.0) / (maxyextent + viewheight)) / 100.0; + qreal pageSize = ((viewheight * 100.0) / (maxyextent + viewheight)) / 100.0; + bool atBeginning = (ypos <= 0.0); + bool atEnd = (maxyextent <= ypos); + + if (pageSize != pageHeight) { + pageHeight = pageSize; + pageChange = true; + } + if (pagePos != pageYPosition) { + pageYPosition = pagePos; + pageChange = true; + } + if (atBeginning != atYBeginning) { + atYBeginning = atBeginning; + atBoundaryChange = true; + } + if (atEnd != atYEnd) { + atYEnd = atEnd; + atBoundaryChange = true; + } + + // Horizontal + const int viewwidth = q->width(); + const int maxxextent = int(-q->maxXExtent()); + const qreal xpos = -_moveX.value(); + pagePos = ((xpos * 100.0) / (maxxextent + viewwidth)) / 100.0; + pageSize = ((viewwidth * 100.0) / (maxxextent + viewwidth)) / 100.0; + atBeginning = (xpos <= 0.0); + atEnd = (maxxextent <= xpos); + + if (pageSize != pageWidth) { + pageWidth = pageSize; + pageChange = true; + } + if (pagePos != pageXPosition) { + pageXPosition = pagePos; + pageChange = true; + } + if (atBeginning != atXBeginning) { + atXBeginning = atBeginning; + atBoundaryChange = true; + } + if (atEnd != atXEnd) { + atXEnd = atEnd; + atBoundaryChange = true; + } + + if (pageChange) + emit q->pageChanged(); + if (atBoundaryChange) + emit q->isAtBoundaryChanged(); +} + +static const int FlickThreshold = 5; + +QML_DEFINE_TYPE(QFxFlickable,Flickable); + +/*! + \qmlclass Flickable + \brief The Flickable element provides a surface that can be "flicked". + \inherits Item + + Flickable places its children on a surface that can be dragged and flicked. + + \code + <Flickable width="200" height="200" viewportWidth="{image.width}" viewportHeight="{image.height}"> + <Image id="image" file="bigimage.png"/> + </Flickable> + \endcode + + \image flickable.gif + + \note Flickable does not automatically clip its contents. If + it is not full-screen it is likely that \c clip should be set + to true. + + \note Due to an implementation detail items placed inside a flickable cannot anchor to it by + id, use 'parent' instead. +*/ + +/*! + \internal + \class QFxFlickable + \brief The QFxFlickable class provides a view that can be "flicked". + + \ingroup widgets + + QFxFlickable allows its children to be dragged and flicked. + +\code +<Flickable width="320" height="480" viewportWidth="{image.width}" viewportHeight="{image.height}"> + <Image id="image" file="bigimage.png"/> +</Flickable> +\endcode + + Note that QFxFlickable does not automatically clip its contents. If + it is not full-screen it is likely that QFxItem::clip should be set + to true. + +*/ + +QFxFlickable::QFxFlickable(QFxItem *parent) + : QFxItem(*(new QFxFlickablePrivate), parent) +{ + Q_D(QFxFlickable); + d->init(); +} + +QFxFlickable::QFxFlickable(QFxFlickablePrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + Q_D(QFxFlickable); + d->init(); +} + +QFxFlickable::~QFxFlickable() +{ +} + +/*! + \qmlproperty int Flickable::xPosition + \qmlproperty int Flickable::yPosition + + These properties hold the surface coordinate currently at the top-left + corner of the Flickable. For example, if you flick an image up 100 pixels, + \c yPosition will be 100. +*/ + +/*! + \property QFxFlickable::xPosition + \brief the x position of the view. + + The xPosition represents the left-most visible coordinate in the view. +*/ +qreal QFxFlickable::xPosition() const +{ + Q_D(const QFxFlickable); + return -d->_moveX.value(); +} + +void QFxFlickable::setXPosition(qreal pos) +{ + Q_D(QFxFlickable); + pos = qRound(pos); + if (-pos != d->_moveX.value()) { + d->_tl.reset(d->_moveX); + d->_moveX.setValue(-pos); + viewportMoved(); + } +} + +/*! + \property QFxFlickable::yPosition + \brief the y position of the view. + + The yPosition represents the top-most visible coordinate in the view. +*/ +qreal QFxFlickable::yPosition() const +{ + Q_D(const QFxFlickable); + return -d->_moveY.value(); +} + +void QFxFlickable::setYPosition(qreal pos) +{ + Q_D(QFxFlickable); + pos = qRound(pos); + if (-pos != d->_moveY.value()) { + d->_tl.reset(d->_moveY); + d->_moveY.setValue(-pos); + viewportMoved(); + } +} + +/*! + \qmlproperty bool Flickable::locked + + A user cannot drag or flick a Flickable that is locked. + + This property is useful for temporarily disabling flicking. This allows + special interaction with Flickable's children: for example, you might want to + freeze a flickable map while viewing detailed information on a location popup that is a child of the Flickable. +*/ + +/*! + \property QFxFlickable::locked + \brief determines whether the user can move the view. + + If the Flickable is locked, the user cannot move the view. +*/ +bool QFxFlickable::isLocked() const +{ + Q_D(const QFxFlickable); + return d->locked; +} + +void QFxFlickable::setLocked(bool lock) +{ + Q_D(QFxFlickable); + d->locked = lock; +} + +/*! + \qmlproperty enumeration Flickable::dragMode + This property contains the kind of 'physics' applied when dragging the surface. + + Two modes are supported: + \list + \i Hard - the view follows the user's input exactly. + \i Elastic - the view moves elastically in response to the user's input. + \endlist +*/ + +/*! + \property QFxFlickable::dragMode + \brief sets the kind of 'physics' applied when dragging the view. + + Two modes are supported: + \list + \i Hard - the view follows the user's input exactly. + \i Elastic - the view moves elastically in response to the user's input. + \endlist +*/ +QFxFlickable::DragMode QFxFlickable::dragMode() const +{ + Q_D(const QFxFlickable); + return d->dragMode; +} + +void QFxFlickable::setDragMode(DragMode mode) +{ + Q_D(QFxFlickable); + d->dragMode = mode; +} + +/*! + \qmlproperty real Flickable::xVelocity + \qmlproperty real Flickable::yVelocity + + The instantaneous velocity of movement along the x and y axes, in pixels/sec. +*/ + +/*! + \property QFxFlickable::xVelocity + \brief provides the instantaneous velocity of movement in the x-axis (pixels/sec). +*/ +qreal QFxFlickable::xVelocity() const +{ + Q_D(const QFxFlickable); + return d->xVelocity.value(); +} + +/*! + \property QFxFlickable::yVelocity + \brief provides the instantaneous velocity of movement in the y-axis (pixels/sec). +*/ +qreal QFxFlickable::yVelocity() const +{ + Q_D(const QFxFlickable); + return d->yVelocity.value(); +} + +/*! + \qmlproperty bool Flickable::atXBeginning + \qmlproperty bool Flickable::atXEnd + \qmlproperty bool Flickable::atYBeginning + \qmlproperty bool Flickable::atYEnd + + These properties are true if the flickable view is positioned at the beginning, + or end respecively. +*/ +bool QFxFlickable::isAtXEnd() const +{ + Q_D(const QFxFlickable); + return d->atXEnd; +} + +bool QFxFlickable::isAtXBeginning() const +{ + Q_D(const QFxFlickable); + return d->atXBeginning; +} + +bool QFxFlickable::isAtYEnd() const +{ + Q_D(const QFxFlickable); + return d->atYEnd; +} + +bool QFxFlickable::isAtYBeginning() const +{ + Q_D(const QFxFlickable); + return d->atYBeginning; +} + +/*! + \qmlproperty real Flickable::pageXPosition + \qmlproperty real Flickable::pageWidth + \qmlproperty real Flickable::pageYPosition + \qmlproperty real Flickable::pageHeight + + These properties describe the position and size of the currently viewed page. + The page size is defined as the percentage of the full view currently visible, + scaled to 0.0 - 1.0. The page position is also in the range 0.0 (beginning) to + 1.0 (end). + + These properties are typically used to draw a scrollbar, for example: + \code + <Rect opacity="0.5" anchors.right="{MyListView.right-2}" width="6" + y="{MyListView.pageYPosition * MyListView.height}" + height="{MyListView.pageHeight * MyListView.height}"/> + \endcode +*/ +qreal QFxFlickable::pageWidth() const +{ + Q_D(const QFxFlickable); + return d->pageWidth; +} + +qreal QFxFlickable::pageXPosition() const +{ + Q_D(const QFxFlickable); + return d->pageXPosition; +} + +qreal QFxFlickable::pageHeight() const +{ + Q_D(const QFxFlickable); + return d->pageHeight; +} + +qreal QFxFlickable::pageYPosition() const +{ + Q_D(const QFxFlickable); + return d->pageYPosition; +} + +void QFxFlickable::ticked() +{ + viewportMoved(); +} + +QFxItem *QFxFlickable::viewport() +{ + Q_D(QFxFlickable); + return d->_flick; +} + +qreal QFxFlickable::visibleX() const +{ + Q_D(const QFxFlickable); + return -d->_moveX.value(); +} + +qreal QFxFlickable::visibleY() const +{ + Q_D(const QFxFlickable); + return -d->_moveY.value(); +} + +void QFxFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxFlickable); + if (!d->locked && d->_tl.isActive() && (qAbs(d->velocityX) > 10 || qAbs(d->velocityY) > 10)) + d->stealMouse = true; // If we've been flicked then steal the click. + else + d->stealMouse = false; + d->pressed = true; + d->_tl.clear(); + d->velocityX = -1; + d->velocityY = -1; + d->lastPos = QPoint(); + d->lastPosTime.start(); + d->pressPos = event->pos(); + d->pressX = d->_moveX.value(); + d->pressY = d->_moveY.value(); + d->flicked = false; + d->pressTime.start(); + if (d->dragMode == Elastic) { + d->elasticX.clear(); + d->elasticY.clear(); + } + d->velocityTime.start(); +} + +void QFxFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxFlickable); + if (d->locked || d->lastPosTime.isNull()) + return; + bool rejectY = false; + bool rejectX = false; + bool moved = false; + + if(yflick()) { + int dy = int(event->pos().y() - d->pressPos.y()); + if (qAbs(dy) > FlickThreshold || d->pressTime.elapsed() > 200) { + qreal newY = dy + d->pressY; + const qreal minY = minYExtent(); + const qreal maxY = maxYExtent(); + if(newY > minY) + newY = minY + (newY - minY) / 2; + if(newY < maxY && maxY - minY < 0) + newY = maxY + (newY - maxY) / 2; + if(overShoot() || (newY <= minY && newY >= maxY)) { + if (d->dragMode == Hard) + d->_moveY.setValue(newY); + else + d->elasticY.setValue(newY); + moved = true; + } else if (!overShoot()) + rejectY = true; + if (qAbs(dy) > FlickThreshold) + d->stealMouse = true; + } + } + + if(xflick()) { + int dx = int(event->pos().x() - d->pressPos.x()); + if (qAbs(dx) > FlickThreshold || d->pressTime.elapsed() > 200) { + qreal newX = dx + d->pressX; + if(overShoot() || (newX <= minXExtent() && newX >= maxXExtent())) { + if (d->dragMode == Hard) + d->_moveX.setValue(newX); + else + d->elasticX.setValue(newX); + moved = true; + } else if (!overShoot()) + rejectX = true; + if (qAbs(dx) > FlickThreshold) + d->stealMouse = true; + } + } + + if(!d->lastPos.isNull()) { + qreal elapsed = qreal(d->lastPosTime.restart()) / 1000.; + if(elapsed <= 0) + elapsed = 1; + if(yflick()) { + qreal diff = event->pos().y() - d->lastPos.y(); + d->velocityY = diff / elapsed; + } + + if(xflick()) { + qreal diff = event->pos().x() - d->lastPos.x(); + d->velocityX = diff / elapsed; + } + } + + if(rejectY) d->velocityY = 0; + if(rejectX) d->velocityX = 0; + + if (moved) { + viewportMoved(); + movementStarting(); + } + + d->lastPos = event->pos(); +} + +void QFxFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *) +{ + Q_D(QFxFlickable); + d->pressed = false; + if (d->lastPosTime.isNull()) + return; + + if (d->dragMode == Elastic) { + d->elasticY.clear(); + d->elasticX.clear(); + } + + d->vTime = d->_tl.time(); + if(qAbs(d->velocityY) > 10) { + qreal maxDistance = -1; + // -ve velocity means list is moving up + if(d->velocityY > 0) { + if(d->_moveY.value() < minYExtent()) + maxDistance = qAbs(minYExtent() -d->_moveY.value() + (d->overShoot?30:0)); + } else { + if(d->_moveY.value() > maxYExtent()) + maxDistance = qAbs(maxYExtent() - d->_moveY.value()) + (d->overShoot?30:0); + } + if(maxDistance > 0) { + qreal v = d->velocityY; + if(d->maxVelocity != -1 && d->maxVelocity < qAbs(v)) { + if(v < 0) + v = -d->maxVelocity; + else + v = d->maxVelocity; + } + d->_tl.accel(d->_moveY, v, 500, maxDistance); + d->_tl.execute(d->fixupYEvent); + d->flicked = true; + emit flickingChanged(); + emit flickStarted(); + } else { + d->fixupY(); + } + } else { + d->fixupY(); + } + if(qAbs(d->velocityX) > 10) { + qreal maxDistance = -1; + // -ve velocity means list is moving up + if(d->velocityX > 0) { + if(d->_moveX.value() < minXExtent()) + maxDistance = qAbs(minXExtent()) -d->_moveX.value() + (d->overShoot?30:0); + } else { + if(d->_moveX.value() > maxXExtent()) + maxDistance = qAbs(maxXExtent() - d->_moveX.value()) + (d->overShoot?30:0); + } + if(maxDistance > 0) { + qreal v = d->velocityX; + if(d->maxVelocity != -1 && d->maxVelocity < qAbs(v)) { + if(v < 0) + v = -d->maxVelocity; + else + v = d->maxVelocity; + } + d->_tl.accel(d->_moveX, v, 500, maxDistance); + d->_tl.execute(d->fixupXEvent); + d->flicked = true; + emit flickingChanged(); + emit flickStarted(); + } else { + d->fixupX(); + } + } else { + d->fixupX(); + } + d->stealMouse = false; + d->lastPosTime = QTime(); + + if(!d->_tl.isActive()) + movementEnding(); +} + +qreal QFxFlickable::minYExtent() const +{ + return 0.0; +} + +qreal QFxFlickable::minXExtent() const +{ + return 0.0; +} + +/* returns -ve */ +qreal QFxFlickable::maxXExtent() const +{ + return width() - vWidth(); +} +/* returns -ve */ +qreal QFxFlickable::maxYExtent() const +{ + return height() - vHeight(); +} + +void QFxFlickable::viewportMoved() +{ + Q_D(QFxFlickable); + //XXX should look at moveX here as well + if (d->flicked && (d->_moveY.value() > minYExtent() + (d->overShoot?30:0) + || d->_moveY.value() < maxYExtent() - (d->overShoot?30:0))){ + d->flicked = false; + emit flickingChanged(); + emit flickEnded(); + d->_tl.reset(d->_moveY); + d->fixupY(); + } + + int elapsed = d->velocityTime.elapsed(); + + if (elapsed) { + qreal prevY = d->lastFlickablePosition.x(); + qreal prevX = d->lastFlickablePosition.y(); + d->velocityTimeline.clear(); + if(d->pressed) { + qreal xVelocity = (prevX - d->_moveX.value()) * 1000 / elapsed; + qreal yVelocity = (prevY - d->_moveY.value()) * 1000 / elapsed; + d->velocityTimeline.move(d->xVelocity, xVelocity, d->velocityDecay); + d->velocityTimeline.move(d->xVelocity, 0, d->velocityDecay); + d->velocityTimeline.move(d->yVelocity, yVelocity, d->velocityDecay); + d->velocityTimeline.move(d->yVelocity, 0, d->velocityDecay); + } else { + if (d->_tl.time() != d->vTime) { + qreal xVelocity = (prevX - d->_moveX.value()) * 1000 / (d->_tl.time() - d->vTime); + qreal yVelocity = (prevY - d->_moveY.value()) * 1000 / (d->_tl.time() - d->vTime); + d->xVelocity.setValue(xVelocity); + d->yVelocity.setValue(yVelocity); + } + d->vTime = d->_tl.time(); + } + } + + d->lastFlickablePosition = QPointF(d->_moveY.value(), d->_moveX.value()); + d->velocityTime.restart(); + d->updateBeginningEnd(); +} + +void QFxFlickablePrivate::data_removeAt(int) +{ + // ### +} + +int QFxFlickablePrivate::data_count() const +{ + // ### + return 0; +} + +void QFxFlickablePrivate::data_append(QObject *o) +{ + Q_Q(QFxFlickable); + QFxItem *i = qobject_cast<QFxItem *>(o); + if(i) + _flick->children()->append(i); + else + o->setParent(q); +} + +void QFxFlickablePrivate::data_insert(int, QObject *) +{ + // ### +} + +QObject *QFxFlickablePrivate::data_at(int) const +{ + // ### + return 0; +} + +void QFxFlickablePrivate::data_clear() +{ + // ### +} + + +QmlList<QObject *> *QFxFlickable::flickableData() +{ + Q_D(QFxFlickable); + return &d->data; +} + +QmlList<QFxItem *> *QFxFlickable::flickableChildren() +{ + Q_D(QFxFlickable); + return d->_flick->children(); +} + +/*! + \qmlproperty bool Flickable::overShoot + This property holds the number of pixels the surface may overshoot the + Flickable's boundaries when flicked. + + If overShoot is non-zero the contents can be flicked beyond the boundary + of the Flickable before being moved back to the boundary. This provides + the feeling that the edges of the view are soft, rather than a hard + physical boundary. +*/ + +/*! + \property QFxFlickable::overShoot + \brief the number of pixels the view may overshoot the boundaries when flicked. + + If overShoot is non-zero the contents can be flicked beyond the boundary + of the view before being moved back to the boundary. This provides + the feeling that the edges of the view are soft, rather than a hard + physical boundary. +*/ +bool QFxFlickable::overShoot() const +{ + Q_D(const QFxFlickable); + return d->overShoot; +} + +void QFxFlickable::setOverShoot(bool o) +{ + Q_D(QFxFlickable); + d->overShoot = o; +} + +/*! + \qmlproperty int Flickable::viewportWidth + \qmlproperty int Flickable::viewportHeight + + The dimensions of the viewport (the surface controlled by Flickable). Typically this + should be set to the combined size of the items placed in the Flickable. + + \code + <Flickable width="320" height="480" viewportWidth="{image.width}" viewportHeight="{image.height}"> + <Image id="image" file="bigimage.png"/> + </Flickable> + \endcode +*/ + +/*! + \property QFxFlickable::viewportWidth + \brief the width of the view. +*/ +int QFxFlickable::viewportWidth() const +{ + Q_D(const QFxFlickable); + return d->vWidth; +} + +void QFxFlickable::setViewportWidth(int w) +{ + Q_D(QFxFlickable); + if (d->vWidth == w) + return; + d->vWidth = w; + if(w < 0) + d->_flick->setWidth(width()); + else + d->_flick->setWidth(w); + emit viewportWidthChanged(); + d->updateBeginningEnd(); +} + +void QFxFlickable::setWidth(int w) +{ + Q_D(QFxFlickable); + QFxItem::setWidth(w); + if(d->vWidth < 0) { + d->_flick->setWidth(w); + emit viewportWidthChanged(); + d->updateBeginningEnd(); + } +} + +void QFxFlickable::setHeight(int h) +{ + Q_D(QFxFlickable); + QFxItem::setHeight(h); + if(d->vHeight < 0) { + d->_flick->setHeight(h); + emit viewportHeightChanged(); + d->updateBeginningEnd(); + } +} + +/*! + \property QFxFlickable::viewportHeight + \brief the height of the view. +*/ +int QFxFlickable::viewportHeight() const +{ + Q_D(const QFxFlickable); + return d->vHeight; +} + +void QFxFlickable::setViewportHeight(int h) +{ + Q_D(QFxFlickable); + if (d->vHeight == h) + return; + d->vHeight = h; + if(h < 0) + d->_flick->setHeight(height()); + else + d->_flick->setHeight(h); + emit viewportHeightChanged(); + d->updateBeginningEnd(); +} + +int QFxFlickable::vWidth() const +{ + Q_D(const QFxFlickable); + if(d->vWidth < 0) + return width(); + else + return d->vWidth; +} + +int QFxFlickable::vHeight() const +{ + Q_D(const QFxFlickable); + if(d->vHeight < 0) + return height(); + else + return d->vHeight; +} + +bool QFxFlickable::xflick() const +{ + return vWidth() != width(); +} + +bool QFxFlickable::yflick() const +{ + return vHeight() != height(); +} + +bool QFxFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxFlickable); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())); + QFxItem *grabber = static_cast<QFxItem*>(mouseGrabberItem()); + if ((d->stealMouse || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) { + mouseEvent.setAccepted(false); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button))); + } + } + mouseEvent.setScenePos(event->scenePos()); + mouseEvent.setLastScenePos(event->lastScenePos()); + mouseEvent.setPos(mapFromScene(event->scenePos())); + mouseEvent.setLastPos(mapFromScene(event->lastScenePos())); + + switch(mouseEvent.type()) { + case QEvent::GraphicsSceneMouseMove: + mouseMoveEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMousePress: + mousePressEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMouseRelease: + mouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = static_cast<QFxItem*>(mouseGrabberItem()); + if (grabber && d->stealMouse && !grabber->keepMouseGrab()) + mouseGrabberItem()->ungrabMouse(); + + return d->stealMouse; + } else if (!d->lastPosTime.isNull()) { + d->lastPosTime = QTime(); + } + return false; +} + +bool QFxFlickable::mouseFilter(QGraphicsSceneMouseEvent *e) +{ + if(!isVisible()) + return false; + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + { + bool ret = sendMouseEvent(e); + if (e->type() == QEvent::GraphicsSceneMouseRelease) + return ret; + break; + } + default: + break; + } + + return false; +} + +/*! + \qmlproperty int Flickable::maximumFlickVelocity + This property holds the maximum velocity that the user can flick the view. +*/ + +/*! + \property QFxFlickable::maximumFlickVelocity + \brief the maximum velocity that the user can flick the view. +*/ +int QFxFlickable::maximumFlickVelocity() const +{ + Q_D(const QFxFlickable); + return d->maxVelocity; +} + +void QFxFlickable::setMaximumFlickVelocity(int v) +{ + Q_D(QFxFlickable); + if(v == d->maxVelocity) + return; + d->maxVelocity = v; +} + +bool QFxFlickable::isFlicking() const +{ + Q_D(const QFxFlickable); + return d->flicked; +} + +int QFxFlickable::velocityDecay() const +{ + Q_D(const QFxFlickable); + return d->velocityDecay; +} + +void QFxFlickable::setVelocityDecay(int decay) +{ + Q_D(QFxFlickable); + Q_ASSERT(decay >= 0); + if(decay == d->velocityDecay) + return; + d->velocityDecay = decay; + emit velocityDecayChanged(decay); +} + +bool QFxFlickable::isMoving() const +{ + Q_D(const QFxFlickable); + return d->moving; +} + +void QFxFlickable::movementStarting() +{ + Q_D(QFxFlickable); + if(!d->moving) { + d->moving = true; + emit movingChanged(); + emit movementStarted(); + } +} + +void QFxFlickable::movementEnding() +{ + Q_D(QFxFlickable); + if(d->moving) { + d->moving = false; + emit movingChanged(); + emit movementEnded(); + } + if (d->flicked) { + d->flicked = false; + emit flickingChanged(); + emit flickEnded(); + } + d->xVelocity.setValue(0); +} + +void QFxFlickablePrivate::updateVelocity() +{ + Q_Q(QFxFlickable); + emit q->velocityChanged(q->xVelocity(), q->yVelocity()); +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxflickable.h b/src/declarative/fx/qfxflickable.h new file mode 100644 index 0000000..1281788 --- /dev/null +++ b/src/declarative/fx/qfxflickable.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXFLICKABLE_H +#define QFXFLICKABLE_H + +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxFlickablePrivate; +class Q_DECLARATIVE_EXPORT QFxFlickable : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(bool overShoot READ overShoot WRITE setOverShoot) + Q_PROPERTY(int viewportWidth READ viewportWidth WRITE setViewportWidth NOTIFY viewportWidthChanged) + Q_PROPERTY(int viewportHeight READ viewportHeight WRITE setViewportHeight NOTIFY viewportHeightChanged) + Q_PROPERTY(qreal xPosition READ xPosition WRITE setXPosition NOTIFY positionChanged); + Q_PROPERTY(qreal yPosition READ yPosition WRITE setYPosition NOTIFY positionChanged); + Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) + Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) + Q_PROPERTY(int velocityDecay READ velocityDecay WRITE setVelocityDecay NOTIFY velocityDecayChanged) + Q_PROPERTY(int maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity) + Q_PROPERTY(bool locked READ isLocked WRITE setLocked) + Q_PROPERTY(DragMode dragMode READ dragMode WRITE setDragMode) + Q_PROPERTY(qreal xVelocity READ xVelocity NOTIFY velocityChanged) + Q_PROPERTY(qreal yVelocity READ yVelocity NOTIFY velocityChanged) + Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY isAtBoundaryChanged); + Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY isAtBoundaryChanged); + Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY isAtBoundaryChanged); + Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY isAtBoundaryChanged); + Q_PROPERTY(qreal pageXPosition READ pageXPosition NOTIFY pageChanged); + Q_PROPERTY(qreal pageYPosition READ pageYPosition NOTIFY pageChanged); + Q_PROPERTY(qreal pageWidth READ pageWidth NOTIFY pageChanged); + Q_PROPERTY(qreal pageHeight READ pageHeight NOTIFY pageChanged); + + Q_PROPERTY(QmlList<QObject *>* flickableData READ flickableData); + Q_PROPERTY(QmlList<QFxItem *>* flickableChildren READ flickableChildren); + Q_CLASSINFO("DefaultProperty", "flickableData") + +public: + QFxFlickable(QFxItem *parent=0); + ~QFxFlickable(); + + QmlList<QObject *> *flickableData(); + QmlList<QFxItem *> *flickableChildren(); + + bool overShoot() const; + void setOverShoot(bool); + + int viewportWidth() const; + void setViewportWidth(int); + + int viewportHeight() const; + void setViewportHeight(int); + + qreal xPosition() const; + void setXPosition(qreal pos); + + qreal yPosition() const; + void setYPosition(qreal pos); + + bool isMoving() const; + bool isFlicking() const; + + int velocityDecay() const; + void setVelocityDecay(int); + + int maximumFlickVelocity() const; + void setMaximumFlickVelocity(int); + + bool isLocked() const; + void setLocked(bool); + + Q_ENUMS(DragMode); + enum DragMode { Hard, Elastic }; + DragMode dragMode() const; + void setDragMode(DragMode mode); + + qreal xVelocity() const; + qreal yVelocity() const; + + bool isAtXEnd() const; + bool isAtXBeginning() const; + qreal pageXPosition() const; + qreal pageWidth() const; + + bool isAtYEnd() const; + bool isAtYBeginning() const; + qreal pageYPosition() const; + qreal pageHeight() const; + + virtual void setWidth(int); + virtual void setHeight(int); + QFxItem *viewport(); + +Q_SIGNALS: + void viewportWidthChanged(); + void viewportHeightChanged(); + void positionChanged(); + void movingChanged(); + void flickingChanged(); + void movementStarted(); + void movementEnded(); + void flickStarted(); + void flickEnded(); + void velocityDecayChanged(int); + void velocityChanged(qreal, qreal); + void isAtBoundaryChanged(); + void pageChanged(); + +protected: + virtual bool mouseFilter(QGraphicsSceneMouseEvent *); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + + qreal visibleX() const; + qreal visibleY() const; + +protected Q_SLOTS: + virtual void ticked(); + void movementStarting(); + void movementEnding(); + +protected: + virtual qreal minXExtent() const; + virtual qreal minYExtent() const; + virtual qreal maxXExtent() const; + virtual qreal maxYExtent() const; + int vWidth() const; + int vHeight() const; + virtual void viewportMoved(); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + + bool xflick() const; + bool yflick() const; + +protected: + QFxFlickable(QFxFlickablePrivate &dd, QFxItem *parent); + +private: + Q_DISABLE_COPY(QFxFlickable) + Q_DECLARE_PRIVATE(QFxFlickable) +}; +QML_DECLARE_TYPE(QFxFlickable); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/fx/qfxflickable_p.h b/src/declarative/fx/qfxflickable_p.h new file mode 100644 index 0000000..ddbfb31 --- /dev/null +++ b/src/declarative/fx/qfxflickable_p.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 QtDeclarative 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 QFXFLICKABLE_P_H +#define QFXFLICKABLE_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 "qdatetime.h" +#include "qfxflickable.h" +#include "qfxitem_p.h" +#include "qml.h" +#include "gfxvalueproxy.h" +#include "private/qmlanimation_p.h" + +QT_BEGIN_NAMESPACE + +class ElasticValue : public QAbstractAnimation { + Q_OBJECT +public: + ElasticValue(GfxValue &); + void setValue(qreal to); + void clear(); + + virtual int duration() const { return 10000; } + +protected: + virtual void updateCurrentTime(int); + +Q_SIGNALS: + void updated(); + +private: + qreal _to; + qreal _myValue; + qreal _velocity; + GfxValue &_value; + QTime _startTime; +}; + +class QFxFlickablePrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxFlickable) + +public: + QFxFlickablePrivate(); + void init(); + virtual void fixupX(); + virtual void fixupY(); + void updateBeginningEnd(); + +public: + QFxItem *_flick; + GfxValueProxy<QFxItem> _moveX; + GfxValueProxy<QFxItem> _moveY; + QmlTimeLine _tl; + int vWidth; + int vHeight; + bool overShoot; + bool flicked; + bool moving; + bool stealMouse; + bool pressed; + QTime lastPosTime; + QPointF lastPos; + QPointF pressPos; + qreal pressX; + qreal pressY; + qreal velocityX; + qreal velocityY; + QTime pressTime; + GfxEvent fixupXEvent; + GfxEvent fixupYEvent; + int maxVelocity; + bool locked; + QFxFlickable::DragMode dragMode; + ElasticValue elasticY; + ElasticValue elasticX; + QTime velocityTime; + QPointF lastFlickablePosition; + int velocityDecay; + + void updateVelocity(); + struct Velocity : public GfxValue + { + Velocity(QFxFlickablePrivate *p) + : parent(p) {} + virtual void setValue(qreal v) { + GfxValue::setValue(v); + parent->updateVelocity(); + } + QFxFlickablePrivate *parent; + }; + Velocity xVelocity; + Velocity yVelocity; + int vTime; + QmlTimeLine velocityTimeline; + bool atXEnd; + bool atXBeginning; + qreal pageXPosition; + qreal pageWidth; + bool atYEnd; + bool atYBeginning; + qreal pageYPosition; + qreal pageHeight; + + // flickableData property + void data_removeAt(int); + int data_count() const; + void data_append(QObject *); + void data_insert(int, QObject *); + QObject *data_at(int) const; + void data_clear(); + QML_DECLARE_LIST_PROXY(QFxFlickablePrivate, QObject *, data); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/fx/qfxfocuspanel.cpp b/src/declarative/fx/qfxfocuspanel.cpp new file mode 100644 index 0000000..1bca424 --- /dev/null +++ b/src/declarative/fx/qfxfocuspanel.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxfocuspanel.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxFocusPanel,FocusPanel); + +/*! + \qmlclass FocusPanel + \brief The FocusPanel element explicitly creates a focus panel. + \inherits Item + + Focus panels assist in keyboard focus handling when building QML + applications. All the details are covered in the + \l {qmlfocus}{keyboard focus documentation}. +*/ + +/*! + \internal + \class QFxFocusPanel +*/ + +QFxFocusPanel::QFxFocusPanel(QFxItem *parent) : + QFxItem(parent) +{ + setOptions(IsFocusPanel); +} + +QFxFocusPanel::~QFxFocusPanel() +{ +} + +/*! + \qmlproperty bool FocusPanel::active + + Sets whether the element is the active focus panel. +*/ + +bool QFxFocusPanel::isActive() const +{ + QSimpleCanvas *canvas = QSimpleCanvasItem::canvas(); + if(canvas) + return canvas->activeFocusPanel() == this; + else + return false; +} + +void QFxFocusPanel::setActive(bool a) +{ + setActiveFocusPanel(a); +} + +void QFxFocusPanel::activePanelInEvent() +{ + QFxItem::activePanelInEvent(); + emit activeChanged(); +} + +void QFxFocusPanel::activePanelOutEvent() +{ + QFxItem::activePanelOutEvent(); + emit activeChanged(); +} +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxfocuspanel.h b/src/declarative/fx/qfxfocuspanel.h new file mode 100644 index 0000000..38f7a15 --- /dev/null +++ b/src/declarative/fx/qfxfocuspanel.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXFOCUSPANEL_H +#define QFXFOCUSPANEL_H + +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_EXPORT QFxFocusPanel : public QFxItem +{ + Q_OBJECT + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) +public: + QFxFocusPanel(QFxItem *parent=0); + virtual ~QFxFocusPanel(); + + bool isActive() const; + void setActive(bool); + +Q_SIGNALS: + void activeChanged(); + +protected: + virtual void activePanelInEvent(); + virtual void activePanelOutEvent(); + +private: + Q_DISABLE_COPY(QFxFocusPanel) +}; + +QML_DECLARE_TYPE(QFxFocusPanel); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXFOCUSPANEL_H diff --git a/src/declarative/fx/qfxfocusrealm.cpp b/src/declarative/fx/qfxfocusrealm.cpp new file mode 100644 index 0000000..da3f1b2 --- /dev/null +++ b/src/declarative/fx/qfxfocusrealm.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxfocusrealm.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxFocusRealm,FocusRealm); + +/*! + \qmlclass FocusRealm + \brief The FocusRealm element explicitly creates a focus realm. + \inherits Item + + Focus realms assist in keyboard focus handling when building reusable QML + components. All the details are covered in the + \l {qmlfocus}{keyboard focus documentation}. +*/ + +/*! + \internal + \class QFxFocusRealm +*/ + +QFxFocusRealm::QFxFocusRealm(QFxItem *parent) : + QFxItem(parent) +{ + setOptions(IsFocusRealm); +} + +QFxFocusRealm::~QFxFocusRealm() +{ +} +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxfocusrealm.h b/src/declarative/fx/qfxfocusrealm.h new file mode 100644 index 0000000..6c35405 --- /dev/null +++ b/src/declarative/fx/qfxfocusrealm.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXFOCUSREALM_H +#define QFXFOCUSREALM_H + +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_EXPORT QFxFocusRealm : public QFxItem +{ + Q_OBJECT +public: + QFxFocusRealm(QFxItem *parent=0); + virtual ~QFxFocusRealm(); +}; + +QML_DECLARE_TYPE(QFxFocusRealm); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXFOCUSREALM_H diff --git a/src/declarative/fx/qfxgridview.cpp b/src/declarative/fx/qfxgridview.cpp new file mode 100644 index 0000000..f9a9f8c --- /dev/null +++ b/src/declarative/fx/qfxgridview.cpp @@ -0,0 +1,1469 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxvisualitemmodel.h" +#include "qlistmodelinterface.h" +#include "qmlfollow.h" +#include "private/qfxflickable_p.h" +#include "qfxgridview.h" + +QT_BEGIN_NAMESPACE + +class QFxGridViewAttached : public QObject +{ + Q_OBJECT +public: + QFxGridViewAttached(QObject *parent) + : QObject(parent), m_isCurrent(false), m_delayRemove(false) {} + ~QFxGridViewAttached() { + attachedProperties.remove(parent()); + } + + Q_PROPERTY(QFxGridView *view READ view); + QFxGridView *view() { return m_view; } + + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged); + bool isCurrentItem() const { return m_isCurrent; } + void setIsCurrentItem(bool c) { + if (m_isCurrent != c) { + m_isCurrent = c; + emit currentItemChanged(); + } + } + + Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged); + bool delayRemove() const { return m_delayRemove; } + void setDelayRemove(bool delay) { + if (m_delayRemove != delay) { + m_delayRemove = delay; + emit delayRemoveChanged(); + } + } + + static QFxGridViewAttached *properties(QObject *obj) { + if(!attachedProperties.contains(obj)) { + QFxGridViewAttached *rv = new QFxGridViewAttached(obj); + attachedProperties.insert(obj, rv); + return rv; + } + return attachedProperties.value(obj); + } + + void emitAdd() { emit add(); } + void emitRemove() { emit remove(); } + +signals: + void currentItemChanged(); + void delayRemoveChanged(); + void add(); + void remove(); + +public: + QFxGridView *m_view; + bool m_isCurrent; + bool m_delayRemove; + + static QHash<QObject*, QFxGridViewAttached*> attachedProperties; +}; + +QHash<QObject*, QFxGridViewAttached*> QFxGridViewAttached::attachedProperties; + + +//---------------------------------------------------------------------------- + +class FxGridItem +{ +public: + FxGridItem(QFxItem *i, QFxGridView *v) : item(i), view(v) { + attached = QFxGridViewAttached::properties(item); + attached->m_view = view; + } + ~FxGridItem() {} + + qreal rowPos() const { return (view->flow() == QFxGridView::LeftToRight ? item->y() : item->x()); } + qreal colPos() const { return (view->flow() == QFxGridView::LeftToRight ? item->x() : item->y()); } + qreal endRowPos() const { + return (view->flow() == QFxGridView::LeftToRight + ? item->y() + (item->height() > 0 ? item->height() : 1) + : item->x() + (item->width() > 0 ? item->width() : 1)) - 1; + } + void setPosition(qreal col, qreal row) { + if (view->flow() == QFxGridView::LeftToRight) { + item->setPos(QPointF(col, row)); + } else { + item->setPos(QPointF(row, col)); + } + } + + QFxItem *item; + QFxGridView *view; + QFxGridViewAttached *attached; + int index; +}; + +//---------------------------------------------------------------------------- + +class QFxGridViewPrivate : public QFxFlickablePrivate +{ + Q_DECLARE_PUBLIC(QFxGridView); + +public: + QFxGridViewPrivate() + : model(0), currentItem(0), tmpCurrent(0), flow(QFxGridView::LeftToRight) + , visiblePos(0), visibleIndex(0) , currentIndex(-1) + , cellWidth(100), cellHeight(100), columns(1) + , highlightComponent(0), highlight(0), trackedItem(0) + , moveReason(Other), buffer(0), highlightXAnimator(0), highlightYAnimator(0) + , keyPressed(false), ownModel(false), wrap(false), autoHighlight(true) + , fixCurrentVisibility(false) {} + + void init(); + void clear(); + FxGridItem *getItem(int modelIndex); + FxGridItem *createItem(int modelIndex); + void releaseItem(FxGridItem *item); + void refill(qreal from, qreal to); + + void updateGrid(); + void layout(bool removed=false); + void updateTrackedItem(); + void createHighlight(); + void updateHighlight(); + void updateCurrent(int modelIndex); + + FxGridItem *visibleItem(int modelIndex) const { + if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { + for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return item; + } + } + return 0; + } + + qreal position() const { + Q_Q(const QFxGridView); + return flow == QFxGridView::LeftToRight ? q->yPosition() : q->xPosition(); + } + void setPosition(qreal pos) { + Q_Q(QFxGridView); + if (flow == QFxGridView::LeftToRight) + q->setYPosition(pos); + else + q->setXPosition(pos); + } + int size() const { + Q_Q(const QFxGridView); + return flow == QFxGridView::LeftToRight ? q->height() : q->width(); + } + qreal startPosition() const { + qreal pos = 0; + if (!visibleItems.isEmpty()) + pos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); + return pos; + } + + qreal endPosition() const { + qreal pos = 0; + if (model && model->count()) + pos = rowPosAt(model->count() - 1) + rowSize(); + return pos; + } + + bool isValid() const { + return model && model->count() && (!ownModel || model->delegate()); + } + + int rowSize() const { + return flow == QFxGridView::LeftToRight ? cellHeight : cellWidth; + } + int colSize() const { + return flow == QFxGridView::LeftToRight ? cellWidth : cellHeight; + } + + qreal colPosAt(int modelIndex) const { + if (FxGridItem *item = visibleItem(modelIndex)) + return item->colPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = (visibleIndex - modelIndex) % columns; + int col = visibleItems.first()->colPos() / colSize(); + col = (columns - count + col) % columns; + return col * colSize(); + } else { + int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns; + return visibleItems.last()->colPos() - count * colSize(); + } + } + return 0; + } + qreal rowPosAt(int modelIndex) const { + if (FxGridItem *item = visibleItem(modelIndex)) + return item->rowPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int firstCol = visibleItems.first()->colPos() / colSize(); + int col = visibleIndex - modelIndex + (columns - firstCol - 1); + int rows = col / columns; + return visibleItems.first()->rowPos() - rows * rowSize(); + } else { + int count = modelIndex - visibleItems.last()->index; + int col = visibleItems.last()->colPos() + count * colSize(); + int rows = col / (columns * colSize()); + return visibleItems.last()->rowPos() + rows * rowSize(); + } + } + return 0; + } + + // Map a model index to visibleItems list index. + // These may differ if removed items are still present in the visible list, + // e.g. doing a removal animation + int mapFromModel(int modelIndex) const { + if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) + return -1; + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *listItem = visibleItems.at(i); + if (listItem->index == modelIndex) + return i + visibleIndex; + if (listItem->index > modelIndex) + return -1; + } + return -1; // Not in visibleList + } + + // for debugging only + void checkVisible() const { + int skip = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *listItem = visibleItems.at(i); + if (listItem->index == -1) { + ++skip; + } else if (listItem->index != visibleIndex + i - skip) { + qDebug() << "index" << visibleIndex << i << listItem->index; + for (int j = 0; j < visibleItems.count(); j++) + qDebug() << " index" << j << "item index" << visibleItems.at(j)->index; + abort(); + } + } + } + + QFxVisualItemModel *model; + QVariant modelVariant; + QList<FxGridItem*> visibleItems; + FxGridItem *currentItem; + QFxItem *tmpCurrent; + QFxGridView::Flow flow; + int visiblePos; + int visibleIndex; + int currentIndex; + int cellWidth; + int cellHeight; + int columns; + QmlComponent *highlightComponent; + FxGridItem *highlight; + FxGridItem *trackedItem; + enum MovementReason { Other, Key, Mouse }; + MovementReason moveReason; + int buffer; + QmlFollow *highlightXAnimator; + QmlFollow *highlightYAnimator; + + int keyPressed : 1; + int ownModel : 1; + int wrap : 1; + int autoHighlight : 1; + int fixCurrentVisibility : 1; +}; + +void QFxGridViewPrivate::init() +{ + Q_Q(QFxGridView); + q->setOptions(QFxGridView::IsFocusRealm); +} + +void QFxGridViewPrivate::clear() +{ + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + visiblePos = 0; + visibleIndex = 0; + if (currentItem) { + FxGridItem *tmpItem = currentItem; + currentItem = 0; + currentIndex = -1; + releaseItem(tmpItem); + } + createHighlight(); + trackedItem = 0; +} + +FxGridItem *QFxGridViewPrivate::getItem(int modelIndex) +{ + if (currentItem && modelIndex == currentIndex) + return currentItem; + if (FxGridItem *listItem = visibleItem(modelIndex)) + return listItem; + return createItem(modelIndex); +} + +FxGridItem *QFxGridViewPrivate::createItem(int modelIndex) +{ + Q_Q(QFxGridView); + // create object + FxGridItem *listItem = 0; + if (QFxItem *item = model->item(modelIndex, false)) { + listItem = new FxGridItem(item, q); + listItem->index = modelIndex; + // complete + model->completeItem(); + listItem->item->setZ(modelIndex + 1); + listItem->item->setParent(q->viewport()); + } + return listItem; +} + + +void QFxGridViewPrivate::releaseItem(FxGridItem *item) +{ + Q_Q(QFxGridView); + if (item != currentItem) { + if (trackedItem == item) { + QObject::disconnect(trackedItem->item, SIGNAL(topChanged()), q, SLOT(trackedPositionChanged())); + QObject::disconnect(trackedItem->item, SIGNAL(leftChanged()), q, SLOT(trackedPositionChanged())); + trackedItem = 0; + } + model->release(item->item); + delete item; + } +} + +void QFxGridViewPrivate::refill(qreal from, qreal to) +{ + Q_Q(QFxGridView); + if (!isValid() || !q->isComponentComplete()) + return; + + from -= buffer; + to += buffer; + bool changed = false; + + int colPos = 0; + int rowPos = 0; + int modelIndex = 0; + if (visibleItems.count()) { + rowPos = visibleItems.last()->rowPos(); + colPos = visibleItems.last()->colPos() + colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + modelIndex = visibleItems.at(i)->index + 1; + } + + FxGridItem *item = 0; + while (modelIndex < model->count() && rowPos <= to) { + //qDebug() << "refill: append item" << modelIndex; + item = getItem(modelIndex); + item->setPosition(colPos, rowPos); + visibleItems.append(item); + colPos += colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + ++modelIndex; + changed = true; + } + + if (visibleItems.count()) { + rowPos = visibleItems.first()->rowPos(); + colPos = visibleItems.first()->colPos() - colSize(); + if (colPos < 0) { + colPos = colSize() * (columns - 1); + rowPos -= rowSize(); + } + } + while (visibleIndex > 0 && rowPos + rowSize() - 1 >= from){ + //qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos; + item = getItem(visibleIndex-1); + --visibleIndex; + item->setPosition(colPos, rowPos); + visibleItems.prepend(item); + colPos -= colSize(); + if (colPos < 0) { + colPos = colSize() * (columns - 1); + rowPos -= rowSize(); + } + changed = true; + } + + while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endRowPos() < from) { + if (item->attached->delayRemove()) + break; + //qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos(); + if (item->index != -1) + visibleIndex++; + visibleItems.removeFirst(); + releaseItem(item); + changed = true; + } + while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->rowPos() > to) { + if (item->attached->delayRemove()) + break; + //qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; + visibleItems.removeLast(); + releaseItem(item); + changed = true; + } + if (changed) { + if (flow == QFxGridView::LeftToRight) + q->setViewportHeight(endPosition() - startPosition()); + else + q->setViewportWidth(endPosition() - startPosition()); + } +} + +void QFxGridViewPrivate::updateGrid() +{ + Q_Q(QFxGridView); + columns = (int)qMax((flow == QFxGridView::LeftToRight ? q->width() : q->height()) / colSize(), 1.); + if (isValid()) { + if (flow == QFxGridView::LeftToRight) + q->setViewportHeight(endPosition() - startPosition()); + else + q->setViewportWidth(endPosition() - startPosition()); + } +} + +void QFxGridViewPrivate::layout(bool removed) +{ + Q_Q(QFxGridView); + if (visibleItems.count()) { + qreal rowPos = visibleItems.first()->rowPos(); + qreal colPos = visibleItems.first()->colPos(); + if (visibleIndex % columns != 0) { + if (removed) + rowPos -= rowSize(); + colPos = (visibleIndex % columns) * colSize(); + visibleItems.first()->setPosition(colPos, rowPos); + } else if (colPos != 0) { + colPos = 0; + visibleItems.first()->setPosition(colPos, rowPos); + } + for (int i = 1; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems.at(i); + colPos += colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + item->setPosition(colPos, rowPos); + } + } + q->refill(); + q->trackedPositionChanged(); + updateHighlight(); + if (flow == QFxGridView::LeftToRight) { + q->setViewportHeight(endPosition() - startPosition()); + fixupY(); + } else { + q->setViewportWidth(endPosition() - startPosition()); + fixupX(); + } +} + +void QFxGridViewPrivate::updateTrackedItem() +{ + Q_Q(QFxGridView); + FxGridItem *item = currentItem; + if (highlight) + item = highlight; + + if (trackedItem && item != trackedItem) { + QObject::disconnect(trackedItem->item, SIGNAL(topChanged()), q, SLOT(trackedPositionChanged())); + QObject::disconnect(trackedItem->item, SIGNAL(leftChanged()), q, SLOT(trackedPositionChanged())); + trackedItem = 0; + } + + if (!trackedItem && item) { + trackedItem = item; + QObject::connect(trackedItem->item, SIGNAL(topChanged()), q, SLOT(trackedPositionChanged())); + QObject::connect(trackedItem->item, SIGNAL(leftChanged()), q, SLOT(trackedPositionChanged())); + q->trackedPositionChanged(); + } + if (trackedItem) + q->trackedPositionChanged(); +} + +void QFxGridViewPrivate::createHighlight() +{ + Q_Q(QFxGridView); + if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; + delete highlight->item; + delete highlight; + highlight = 0; + delete highlightXAnimator; + delete highlightYAnimator; + highlightXAnimator = 0; + highlightYAnimator = 0; + } + + if (!highlightComponent) + return; + + if (currentItem) { + QmlContext *highlightContext = new QmlContext(q->itemContext()); + QObject *nobj = highlightComponent->create(highlightContext); + if (nobj) { + highlightContext->setParent(nobj); + QFxItem *item = qobject_cast<QFxItem *>(nobj); + if (item) { + item->setParent(q->viewport()); + highlight = new FxGridItem(item, q); + highlightXAnimator = new QmlFollow(q); + highlightXAnimator->setTarget(QmlMetaProperty(highlight->item, QLatin1String("x"))); + highlightXAnimator->setSpring(3); + highlightXAnimator->setDamping(0.3); + highlightXAnimator->setEnabled(autoHighlight); + highlightYAnimator = new QmlFollow(q); + highlightYAnimator->setTarget(QmlMetaProperty(highlight->item, QLatin1String("y"))); + highlightYAnimator->setSpring(3); + highlightYAnimator->setDamping(0.3); + highlightYAnimator->setEnabled(autoHighlight); + } else { + delete highlightContext; + } + } + } +} + +void QFxGridViewPrivate::updateHighlight() +{ + if ((!currentItem && highlight) || (currentItem && !highlight)) + createHighlight(); + updateTrackedItem(); + if (currentItem && autoHighlight && highlight) { + // auto-update highlight + highlightXAnimator->setSourceValue(currentItem->item->x()); + highlightYAnimator->setSourceValue(currentItem->item->y()); + highlight->item->setWidth(currentItem->item->width()); + highlight->item->setHeight(currentItem->item->height()); + } +} + +void QFxGridViewPrivate::updateCurrent(int modelIndex) +{ + Q_Q(QFxGridView); + if (!isValid() || modelIndex < 0 || modelIndex >= model->count()) { + if (currentItem) { + FxGridItem *item = currentItem; + currentItem = 0; + currentIndex = 0; + updateHighlight(); + releaseItem(item); + emit q->currentIndexChanged(); + } + return; + } + + if (currentItem && currentIndex == modelIndex) { + updateHighlight(); + return; + } + + if (tmpCurrent) { + delete tmpCurrent; + tmpCurrent = 0; + } + int oldCurrentIndex = currentIndex; + FxGridItem *oldCurrentItem = currentItem; + currentIndex = -1; + currentItem = visibleItem(modelIndex); + if (!currentItem) { + currentItem = getItem(modelIndex); + currentItem->setPosition(colPosAt(modelIndex), rowPosAt(modelIndex)); + } + currentIndex = modelIndex; + fixCurrentVisibility = true; + if (oldCurrentItem && oldCurrentItem->item != currentItem->item) + oldCurrentItem->attached->setIsCurrentItem(false); + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + updateHighlight(); + emit q->currentIndexChanged(); + // Release the old current item + if (oldCurrentItem && !visibleItem(oldCurrentIndex)) + releaseItem(oldCurrentItem); +} + +//---------------------------------------------------------------------------- + +/*! + \qmlclass GridView + \inherits Flickable + \brief The GridView element provides a grid view of items provided by a model. + + The model is typically provided by a QAbstractListModel "C++ model object", + but can also be created directly in XML. + + The items are laid out top to bottom (vertically) or left to right (horizontally) + and may be flicked to scroll. + + The below example creates a very simple grid, using an XML model. + \code + <resources> + <ListModel id="contactModel"> + <Contact> + <firstName>John</firstName> + <lastName>Smith</lastName> + </Contact> + <Contact> + <firstName>Bill</firstName> + <lastName>Jones</lastName> + </Contact> + <Contact> + <firstName>Jane</firstName> + <lastName>Doe</lastName> + </Contact> + </ListModel> + <Component id="contactDelegate"> + <Rect pen.color="blue" z="-1" height="20" width="80" color="white" radius="2"> + <Text id="name" text="{firstName + ' ' + lastName}" font.size="11"/> + </Rect> + </Component> + </resources> + + <GridView id="Grid" width="160" height="240" cellWidth="80" cellHeight="20" clip="true" + model="{contactModel}" delegate="{contactDelegate}"/> + \endcode +*/ +QFxGridView::QFxGridView(QFxItem *parent) + : QFxFlickable(*(new QFxGridViewPrivate), parent) +{ + Q_D(QFxGridView); + d->init(); +} + +QFxGridView::~QFxGridView() +{ + Q_D(QFxGridView); + if (d->ownModel) + delete d->model; +} + +/*! + \qmlproperty model GridView::model + This property holds the model providing data for the grid. + + The model provides a set of data that is used to create the items for the view. + For large or dynamic datasets the model is usually provided by a C++ model object. + The C++ model object must be a \l QListModelInterface subclass, a \l VisualModel, + or a simple list. + + Models can also be created directly in XML, using the \l ListModel element. For example: + \code + <ListModel id="contactModel"> + <Contact> + <firstName>John</firstName> + <lastName>Smith</lastName> + </Contact> + <Contact> + <firstName>Bill</firstName> + <lastName>Jones</lastName> + </Contact> + <Contact> + <firstName>Jane</firstName> + <lastName>Doe</lastName> + </Contact> + </ListModel> + + <GridView model="{contactModel}" .../> + \endcode +*/ +QVariant QFxGridView::model() const +{ + Q_D(const QFxGridView); + return d->modelVariant; +} + +void QFxGridView::setModel(const QVariant &model) +{ + Q_D(QFxGridView); + if (d->model) { + disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + } + d->clear(); + d->modelVariant = model; + QObject *object = qvariant_cast<QObject*>(model); + QFxVisualItemModel *vim = 0; + if (object && (vim = qobject_cast<QFxVisualItemModel *>(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QFxVisualItemModel(itemContext()); + d->ownModel = true; + } + d->model->setModel(model); + } + if (d->model) { + if (d->currentIndex >= d->model->count() || d->currentIndex < 0) + setCurrentIndex(0); + else + d->updateCurrent(d->currentIndex); + connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + refill(); + emit countChanged(); + } +} + +/*! + \qmlproperty component GridView::delegate + + The delegate provides a template describing what each item in the view should look and act like. + + Here is an example delegate: + \code + <Component id="contactDelegate"> + <Item id="wrapper"> + <Image id="pic" width="100" height="100" file="{portrait}"/> + <Text id="name" text="{firstName + ' ' + lastName}" + anchors.left="{pic.right}" anchors.leftMargin="5"/> + </Item> + </Component> + ... + <GridView delegate="{contactDelegate}" .../> + \endcode +*/ +QmlComponent *QFxGridView::delegate() const +{ + Q_D(const QFxGridView); + return d->model ? d->model->delegate() : 0; +} + +void QFxGridView::setDelegate(QmlComponent *delegate) +{ + Q_D(QFxGridView); + if (!d->ownModel) { + d->model = new QFxVisualItemModel(itemContext()); + d->ownModel = true; + } + d->model->setDelegate(delegate); + d->updateCurrent(d->currentIndex); + refill(); +} + +/*! + \qmlproperty int GridView::currentIndex + \qmlproperty Item GridView::current + + \c currentIndex holds the index of the current item. + \c current is the current item. Note that the position of the current item + may only be approximate until it becomes visible in the view. +*/ +int QFxGridView::currentIndex() const +{ + Q_D(const QFxGridView); + return d->currentIndex; +} + +void QFxGridView::setCurrentIndex(int index) +{ + Q_D(QFxGridView); + if (d->isValid() && index != d->currentIndex && index < d->model->count() && index >= 0) + d->updateCurrent(index); + else + d->currentIndex = index; +} + +QFxItem *QFxGridView::currentItem() +{ + Q_D(QFxGridView); + if (!d->currentItem) { + // Always return something valid + if (!d->tmpCurrent) + d->tmpCurrent = new QFxItem(viewport()); + return d->tmpCurrent; + } + return d->currentItem->item; +} + +/*! + \qmlproperty int GridView::count + This property holds the number of items in the view. +*/ +int QFxGridView::count() const +{ + Q_D(const QFxGridView); + if (d->model) + return d->model->count(); + return 0; +} + +/*! + \qmlproperty component GridView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component will be created for each view. + The geometry of the resultant component instance will be managed by the view + so as to stay with the current item, unless the autoHighlight property is false. + + The below example demonstrates how to make a simple highlight: + \code + <Component id="ListHighlight"> + <Rect color="lightsteelblue" radius="4"/> + </Component> + <GridView highlight="{ListHighlight}"> + \endcode + + \sa autoHighlight +*/ +QmlComponent *QFxGridView::highlight() const +{ + Q_D(const QFxGridView); + return d->highlightComponent; +} + +void QFxGridView::setHighlight(QmlComponent *highlight) +{ + Q_D(QFxGridView); + delete d->highlightComponent; + d->highlightComponent = highlight; + d->updateCurrent(d->currentIndex); +} + +/*! + \qmlproperty component GridView::autoHighlight + This property sets whether the highlight is managed by the view. + + If autoHighlight is true, the highlight will be moved smoothly + to follow the current item. If autoHighlight is false, the + highlight will not be moved by the view, and must be implemented + by the highlight, for example: + + \code + <Component id="Highlight"> + <Rect id="Wrapper" color="#242424" radius="4" width="320" height="60" > + <y> + <Follow source="{Wrapper.GridView.view.current.y}" spring="3" damping="0.2"/> + </y> + <x> + <Follow source="{Wrapper.GridView.view.current.x}" spring="3" damping="0.2"/> + </x> + </Rect> + </Component> + \endcode +*/ +bool QFxGridView::autoHighlight() const +{ + Q_D(const QFxGridView); + return d->autoHighlight; +} + +void QFxGridView::setAutoHighlight(bool autoHighlight) +{ + Q_D(QFxGridView); + d->autoHighlight = autoHighlight; + if (d->highlightXAnimator) { + d->highlightXAnimator->setEnabled(d->autoHighlight); + d->highlightYAnimator->setEnabled(d->autoHighlight); + } + d->updateHighlight(); +} + +/*! + \qmlproperty enumeration GridView::flow + This property holds the flow of the grid. + + Possible values are \c LeftToRight (default) and \c TopToBottom. + + If \a flow is \c LeftToRight, the view will scroll vertically. + If \a flow is \c TopToBottom, the view will scroll horizontally. +*/ +QFxGridView::Flow QFxGridView::flow() const +{ + Q_D(const QFxGridView); + return d->flow; +} + +void QFxGridView::setFlow(Flow flow) +{ + Q_D(QFxGridView); + if (d->flow != flow) { + d->flow = flow; + if (d->flow == LeftToRight) + setViewportWidth(-1); + else + setViewportHeight(-1); + d->clear(); + d->updateGrid(); + refill(); + d->updateCurrent(d->currentIndex); + } +} + +/*! + \qmlproperty bool GridView::wrap + This property holds whether the grid wraps key navigation + + If this property is true then key presses to move off of one end of the grid will cause the + selection to jump to the other side. +*/ +bool QFxGridView::isWrapEnabled() const +{ + Q_D(const QFxGridView); + return d->wrap; +} + +void QFxGridView::setWrapEnabled(bool wrap) +{ + Q_D(QFxGridView); + d->wrap = wrap; +} + +/*! + \qmlproperty int GridView::cacheBuffer + This property holds the number of off-screen pixels to cache. + + This property determines the number of pixels above the top of the view + and below the bottom of the view to cache. Setting this value can make + scrolling the view smoother at the expense of additional memory usage. +*/ + +/*! + \property QFxGridView::cacheBuffer + \brief sets the number of off-screen pixels to cache. + + This property determines the number of pixels above the top of the view + and below the bottom of the view to cache. Setting this value can make + scrolling the view smoother at the expense of additional memory usage. +*/ +int QFxGridView::cacheBuffer() const +{ + Q_D(const QFxGridView); + return d->buffer; +} + +void QFxGridView::setCacheBuffer(int buffer) +{ + Q_D(QFxGridView); + if(d->buffer != buffer) { + d->buffer = buffer; + if (isComponentComplete()) + refill(); + } +} + +/*! + \qmlproperty int GridView::cellWidth + This property holds the width of each cell in the grid + + The default cellWidth is 100. +*/ +int QFxGridView::cellWidth() const +{ + Q_D(const QFxGridView); + return d->cellWidth; +} + +void QFxGridView::setCellWidth(int cellWidth) +{ + Q_D(QFxGridView); + if (cellWidth != d->cellWidth && cellWidth > 0) { + d->cellWidth = qMax(1, cellWidth); + d->updateGrid(); + emit cellSizeChanged(); + d->layout(); + } +} + +/*! + \qmlproperty int GridView::cellHeight + This property holds the height of each cell in the grid + + The default cellHeight is 100. +*/ +int QFxGridView::cellHeight() const +{ + Q_D(const QFxGridView); + return d->cellHeight; +} + +void QFxGridView::setCellHeight(int cellHeight) +{ + Q_D(QFxGridView); + if (cellHeight != d->cellHeight && cellHeight > 0) { + d->cellHeight = qMax(1, cellHeight); + d->updateGrid(); + emit cellSizeChanged(); + d->layout(); + } +} + +/*! + \reimp +*/ +void QFxGridView::setHeight(int height) +{ + Q_D(QFxGridView); + QFxFlickable::setHeight(height); + if (isComponentComplete()) { + d->updateGrid(); + d->layout(); + } +} + +/*! + \reimp +*/ +void QFxGridView::setWidth(int width) +{ + Q_D(QFxGridView); + QFxFlickable::setWidth(width); + if (isComponentComplete()) { + d->updateGrid(); + d->layout(); + } +} + +/*! + \reimp +*/ +void QFxGridView::viewportMoved() +{ + QFxFlickable::viewportMoved(); + refill(); +} + +/*! + \reimp +*/ +qreal QFxGridView::minYExtent() const +{ + Q_D(const QFxGridView); + if (d->flow == QFxGridView::TopToBottom) + return QFxFlickable::minYExtent(); + return -d->startPosition(); +} + +/*! + \reimp +*/ +qreal QFxGridView::maxYExtent() const +{ + Q_D(const QFxGridView); + if (d->flow == QFxGridView::TopToBottom) + return QFxFlickable::maxYExtent(); + return -(d->endPosition() - height()); +} + +/*! + \reimp +*/ +qreal QFxGridView::minXExtent() const +{ + Q_D(const QFxGridView); + if (d->flow == QFxGridView::LeftToRight) + return QFxFlickable::minXExtent(); + return -d->startPosition(); +} + +/*! + \reimp +*/ +qreal QFxGridView::maxXExtent() const +{ + Q_D(const QFxGridView); + if (d->flow == QFxGridView::LeftToRight) + return QFxFlickable::maxXExtent(); + return -(d->endPosition() - height()); +} + +/*! + \reimp +*/ +void QFxGridView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QFxGridView); + if (d->model && d->model->count() && !d->locked) { + if ((d->flow == QFxGridView::LeftToRight && event->key() == Qt::Key_Up) + || (d->flow == QFxGridView::TopToBottom && event->key() == Qt::Key_Left)) { + if (currentIndex() >= d->columns || d->wrap) { + d->keyPressed = true; + d->moveReason = QFxGridViewPrivate::Key; + int index = currentIndex() - d->columns; + setCurrentIndex(index >= 0 ? index : d->model->count()-1); + event->accept(); + } + return; + } else if ((d->flow == QFxGridView::LeftToRight && event->key() == Qt::Key_Down) + || (d->flow == QFxGridView::TopToBottom && event->key() == Qt::Key_Right)) { + if (currentIndex() < d->model->count() - d->columns || d->wrap) { + d->keyPressed = true; + d->moveReason = QFxGridViewPrivate::Key; + int index = currentIndex()+d->columns; + setCurrentIndex(index < d->model->count() ? index : 0); + event->accept(); + } + return; + } else if ((d->flow == QFxGridView::LeftToRight && event->key() == Qt::Key_Left) + || (d->flow == QFxGridView::TopToBottom && event->key() == Qt::Key_Up)) { + if (currentIndex() > 0 || d->wrap) { + d->keyPressed = true; + d->moveReason = QFxGridViewPrivate::Key; + int index = currentIndex() - 1; + setCurrentIndex(index >= 0 ? index : d->model->count()-1); + event->accept(); + } + return; + } else if ((d->flow == QFxGridView::LeftToRight && event->key() == Qt::Key_Right) + || (d->flow == QFxGridView::TopToBottom && event->key() == Qt::Key_Down)) { + if (currentIndex() < d->model->count() - 1 || d->wrap) { + d->keyPressed = true; + d->moveReason = QFxGridViewPrivate::Key; + int index = currentIndex() + 1; + setCurrentIndex(index < d->model->count() ? index : 0); + event->accept(); + } + return; + } + } + d->moveReason = QFxGridViewPrivate::Other; + QFxFlickable::keyPressEvent(event); +} + +/*! + \reimp +*/ +void QFxGridView::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QFxGridView); + d->keyPressed = false; + QFxFlickable::keyReleaseEvent(event); +} + +/*! + \reimp +*/ +void QFxGridView::componentComplete() +{ + Q_D(QFxGridView); + QFxFlickable::componentComplete(); + d->updateGrid(); + if (d->currentIndex < 0) + d->updateCurrent(0); + refill(); +} + +void QFxGridView::trackedPositionChanged() +{ + Q_D(QFxGridView); + if (!d->trackedItem) + return; + if (!isFlicking() && !d->pressed && d->moveReason == QFxGridViewPrivate::Key) { + if (d->trackedItem->rowPos() < d->position()) { + d->setPosition(d->trackedItem->rowPos()); + } else if (d->trackedItem->endRowPos() > d->position() + d->size()) { + qreal pos = d->trackedItem->endRowPos() - d->size(); + if (d->rowSize() > d->size()) + pos = d->trackedItem->rowPos(); + d->setPosition(pos); + } + } +} + +void QFxGridView::itemsInserted(int modelIndex, int count) +{ + Q_D(QFxGridView); + if (!d->visibleItems.count() || d->model->count() <= 1) { + refill(); + d->updateCurrent(qMax(0, qMin(d->currentIndex, d->model->count()-1))); + emit countChanged(); + return; + } + + int index = d->mapFromModel(modelIndex); + if (index == -1) { + int i = d->visibleItems.count() - 1; + while (i > 0 && d->visibleItems.at(i)->index == -1) + --i; + if (d->visibleItems.at(i)->index + 1 == modelIndex) { + // Special case of appending an item to the model. + index = d->visibleIndex + d->visibleItems.count(); + } else { + if (modelIndex + count - 1 < d->visibleIndex) { + // Insert before visible items + d->visibleIndex += count; + for (int i = 0; i < d->visibleItems.count(); ++i) { + FxGridItem *listItem = d->visibleItems.at(i); + if (listItem->index != -1 && listItem != d->currentItem) + listItem->index += count; + } + } + if (d->currentIndex >= modelIndex) { + // adjust current item index + d->currentIndex += count; + if (d->currentItem) + d->currentItem->index = d->currentIndex; + } + d->layout(); + emit countChanged(); + return; + } + } + + // At least some of the added items will be visible + int insertCount = count; + if (index < d->visibleIndex) { + insertCount -= d->visibleIndex - index; + index = d->visibleIndex; + modelIndex = d->visibleIndex; + } + + index -= d->visibleIndex; + int to = d->buffer+d->position()+d->size()-1; + int colPos, rowPos; + if (index < d->visibleItems.count()) { + colPos = d->visibleItems.at(index)->colPos(); + rowPos = d->visibleItems.at(index)->rowPos(); + } else { + // appending items to visible list + colPos = d->visibleItems.at(index-1)->colPos() + d->colSize(); + rowPos = d->visibleItems.at(index-1)->rowPos(); + if (colPos > d->colSize() * (d->columns-1)) { + colPos = 0; + rowPos += d->rowSize(); + } + } + + QList<FxGridItem*> added; + int i = 0; + for (; i < insertCount && rowPos + d->rowSize() - 1 <= to; ++i) { + int mod = (modelIndex+i) % d->columns; + while (mod++ < d->columns && modelIndex + i < d->model->count() && i < insertCount) { + FxGridItem *item = d->createItem(modelIndex + i); + d->visibleItems.insert(index, item); + item->setPosition(colPos, rowPos); + added.append(item); + colPos += d->colSize(); + if (colPos > d->colSize() * (d->columns-1)) { + colPos = 0; + rowPos += d->rowSize(); + } + ++index; + ++i; + } + } + + if (d->currentIndex >= modelIndex) { + // adjust current item index + d->currentIndex += count; + if (d->currentItem) { + d->currentItem->index = d->currentIndex; + d->currentItem->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex)); + } + } + if (i < insertCount) { + // We didn't insert all our new items, which means anything + // beyond the current index is not visible - remove it. + while (d->visibleItems.count() > index) + d->releaseItem(d->visibleItems.takeLast()); + } else { + // Update the indexes of the following visible items. + for (; index < d->visibleItems.count(); ++index) { + FxGridItem *listItem = d->visibleItems.at(index); + if (listItem != d->currentItem) { + if (listItem->index != -1) + listItem->index += count; + } + } + } + // everything is in order now - emit add() signal + foreach(FxGridItem *item, added) + item->attached->emitAdd(); + d->layout(); + emit countChanged(); +} + +void QFxGridView::itemsRemoved(int modelIndex, int count) +{ + Q_D(QFxGridView); + + int index = d->mapFromModel(modelIndex); + if (index == -1) { + if (modelIndex + count - 1 < d->visibleIndex) { + // Items removed before our visible items. + d->visibleIndex -= count; + for (int i = 0; i < d->visibleItems.count(); ++i) { + FxGridItem *listItem = d->visibleItems.at(i); + if (listItem->index != -1 && listItem != d->currentItem) + listItem->index -= count; + } + } + if (d->currentIndex >= modelIndex + count) { + d->currentIndex -= count; + if (d->currentItem) + d->currentItem->index -= count; + } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) { + // current item has been removed. + if (d->currentItem) { + FxGridItem *item = d->currentItem; + d->currentItem = 0; + d->releaseItem(item); + } + d->currentIndex = -1; + d->updateCurrent(qMin(modelIndex, d->model->count()-1)); + } + d->layout(true); + emit countChanged(); + return; + } + + // Remove the items from the visible list, skipping anything already marked for removal + QList<FxGridItem*>::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxGridItem *item = *it; + if (item->index == -1 || item->index < modelIndex) { + // already removed, or before removed items + ++it; + } else if (item->index >= modelIndex + count) { + // after removed items + if (item != d->currentItem) + item->index -= count; + ++it; + } else { + // removed item + item->attached->emitRemove(); + if (item->attached->delayRemove()) { + item->index = -1; + connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection); + ++it; + } else { + it = d->visibleItems.erase(it); + d->releaseItem(item); + } + } + } + + // fix current + if (d->currentIndex >= modelIndex + count) { + d->currentIndex -= count; + if (d->currentItem) + d->currentItem->index -= count; + } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) { + // current item has been removed. + if (d->currentItem && !d->currentItem->attached->delayRemove()) { + FxGridItem *item = d->currentItem; + d->currentItem = 0; + d->releaseItem(item); + } + d->currentItem = 0; + d->currentIndex = -1; + d->updateCurrent(qMin(modelIndex, d->model->count()-1)); + } + + // update visibleIndex + for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { + if ((*it)->index != -1) { + d->visibleIndex = (*it)->index; + break; + } + } + + if (d->visibleItems.isEmpty()) { + d->visibleIndex = 0; + d->setPosition(0); + refill(); + } else { + // Correct the positioning of the items + d->layout(); + } + emit countChanged(); +} + +void QFxGridView::destroyRemoved() +{ + Q_D(QFxGridView); + for (QList<FxGridItem*>::Iterator it = d->visibleItems.begin(); + it != d->visibleItems.end();) { + FxGridItem *listItem = *it; + if (listItem->index == -1 && listItem->attached->delayRemove() == false) { + d->releaseItem(listItem); + it = d->visibleItems.erase(it); + } else { + ++it; + } + } + + // Correct the positioning of the items + d->layout(); +} + +void QFxGridView::refill() +{ + Q_D(QFxGridView); + d->refill(d->position(), d->position()+d->size()-1); +} + + +QObject *QFxGridView::qmlAttachedProperties(QObject *obj) +{ + return QFxGridViewAttached::properties(obj); +} + +QML_DEFINE_TYPE(QFxGridView,GridView); + +QT_END_NAMESPACE + +#include "qfxgridview.moc" diff --git a/src/declarative/fx/qfxgridview.h b/src/declarative/fx/qfxgridview.h new file mode 100644 index 0000000..c612804 --- /dev/null +++ b/src/declarative/fx/qfxgridview.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXGRIDVIEW_H +#define QFXGRIDVIEW_H + +#include <qfxflickable.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxVisualItemModel; +class QFxGridViewPrivate; +class Q_DECLARATIVE_EXPORT QFxGridView : public QFxFlickable +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QFxGridView); + + Q_PROPERTY(QVariant model READ model WRITE setModel); + Q_CLASSINFO("DefaultProperty", "delegate"); + Q_PROPERTY(QmlComponent *delegate READ delegate WRITE setDelegate); + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged); + Q_PROPERTY(QFxItem *current READ currentItem NOTIFY currentIndexChanged); + Q_PROPERTY(int count READ count NOTIFY countChanged); + Q_PROPERTY(QmlComponent *highlight READ highlight WRITE setHighlight); + Q_PROPERTY(bool autoHighlight READ autoHighlight WRITE setAutoHighlight); + Q_PROPERTY(Flow flow READ flow WRITE setFlow); + Q_PROPERTY(bool wrap READ isWrapEnabled WRITE setWrapEnabled); + Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer); + Q_PROPERTY(int cellWidth READ cellWidth WRITE setCellWidth NOTIFY cellSizeChanged); + Q_PROPERTY(int cellHeight READ cellHeight WRITE setCellHeight NOTIFY cellSizeChanged); + +public: + QFxGridView(QFxItem *parent=0); + ~QFxGridView(); + + QVariant model() const; + void setModel(const QVariant &); + + QmlComponent *delegate() const; + void setDelegate(QmlComponent *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + QFxItem *currentItem(); + int count() const; + + QmlComponent *highlight() const; + void setHighlight(QmlComponent *highlight); + + bool autoHighlight() const; + void setAutoHighlight(bool); + + Q_ENUMS(Flow); + enum Flow { LeftToRight, TopToBottom }; + Flow flow() const; + void setFlow(Flow); + + bool isWrapEnabled() const; + void setWrapEnabled(bool); + + int cacheBuffer() const; + void setCacheBuffer(int); + + int cellWidth() const; + void setCellWidth(int); + + int cellHeight() const; + void setCellHeight(int); + + virtual void setHeight(int height); + virtual void setWidth(int width); + + static QObject *qmlAttachedProperties(QObject *); + +Q_SIGNALS: + void countChanged(); + void currentIndexChanged(); + void cellSizeChanged(); + +protected: + virtual void viewportMoved(); + virtual qreal minYExtent() const; + virtual qreal maxYExtent() const; + virtual qreal minXExtent() const; + virtual qreal maxXExtent() const; + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + virtual void componentComplete(); + +private Q_SLOTS: + void trackedPositionChanged(); + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void destroyRemoved(); + +private: + void refill(); +}; + +QML_DECLARE_TYPE(QFxGridView); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/fx/qfxhighlightfilter.cpp b/src/declarative/fx/qfxhighlightfilter.cpp new file mode 100644 index 0000000..6c6277a --- /dev/null +++ b/src/declarative/fx/qfxhighlightfilter.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 QtDeclarative 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 "qfxhighlightfilter.h" +#include <qfxpixmap.h> +#include <qmlcontext.h> + +#if defined(QFX_RENDER_OPENGL2) +#include <gltexture.h> +#include <glbasicshaders.h> +#include <QtOpenGL/qglframebufferobject.h> +#include <glsave.h> +#endif + +QT_BEGIN_NAMESPACE + +class QFxHighlightFilterPrivate +{ +public: + QFxHighlightFilterPrivate() + : ctxt(QmlContext::activeContext()), + xOffset(0), yOffset(0), tiled(false) {} + + QmlContext *ctxt; + QString source; + QUrl url; + int xOffset; + int yOffset; + bool tiled; +#if defined(QFX_RENDER_OPENGL2) + GLTexture tex; +#endif +}; + +/*! + \qmlclass Highlight + \brief The Highlight filter adds a highlight to an item. + \inherits Filter + + \code +<Text id="highlighttext" color="red" font.size="32" text="Highlight"> + <filter> + <Highlight src="pics/highlight.png"> + <xOffset> + <NumericAnimation running="true" repeat="true" + from="320" to="-320" duration="2000"/> + </xOffset> + </Highlight> + </filter> +</Text> + \endcode + \image highlight.gif + + Highlighting is only supported when Qt Declarative is compiled for OpenGL ES 2.0. + Otherwise the Highlight filter has no effect. +*/ + +/*! + \internal + \class QFxHighlightFilter + \ingroup effects + \brief The QFxHightlightFilter class allows you to add a highlight to an item. +*/ + +QFxHighlightFilter::QFxHighlightFilter(QObject *parent) +: QSimpleCanvasFilter(parent), d(new QFxHighlightFilterPrivate) +{ +#if defined(QFX_RENDER_OPENGL2) + d->tex.setHorizontalWrap(GLTexture::ClampToEdge); + d->tex.setVerticalWrap(GLTexture::ClampToEdge); +#endif +} + +QFxHighlightFilter::~QFxHighlightFilter() +{ + delete d; + d = 0; +} + +/*! + \qmlproperty string Highlight::src + This property holds the URL of the image to be used as the highlight. +*/ + +/*! + \property QFxHighlightFilter::src + \brief the URL of the image to be used as the highlight. +*/ +QString QFxHighlightFilter::source() const +{ + return d->source; +} + +void QFxHighlightFilter::imageLoaded() +{ + QImage img = QFxPixmap(d->url); +#if defined(QFX_RENDER_OPENGL2) + if(!img.isNull()) + d->tex.setImage(img); +#endif + emit sourceChanged(d->source); + update(); +} + +void QFxHighlightFilter::setSource(const QString &f) +{ + if (d->source == f) + return; + if(!d->source.isEmpty()) + QFxPixmap::cancelGet(d->url, this, SLOT(imageLoaded())); + d->source = f; + d->url = QmlContext::activeContext()->resolvedUrl(f); +#if defined(QFX_RENDER_OPENGL2) + d->tex.clear(); +#endif + if(!f.isEmpty()) + QFxPixmap::get(d->ctxt->engine(), d->url, this, SLOT(imageLoaded())); + else + emit sourceChanged(d->source); +} + +/*! + \qmlproperty bool Highlight::tiled + This property holds whether or not the highlight should be tiled. +*/ + +/*! + \property QFxHighlightFilter::tiled + \brief whether or not the highlight should be tiled. +*/ +bool QFxHighlightFilter::tiled() const +{ + return d->tiled; +} + +void QFxHighlightFilter::setTiled(bool t) +{ + if(t == d->tiled) + return; + + d->tiled = t; + +#if defined(QFX_RENDER_OPENGL2) + if(d->tiled) { + d->tex.setHorizontalWrap(GLTexture::ClampToEdge); + d->tex.setVerticalWrap(GLTexture::ClampToEdge); + } else { + d->tex.setHorizontalWrap(GLTexture::Repeat); + d->tex.setVerticalWrap(GLTexture::Repeat); + } +#endif + + emit tiledChanged(d->tiled); +} + +/*! + \qmlproperty int Highlight::xOffset + \qmlproperty int Highlight::yOffset + These properties hold the position of the highlight, relative to the item. +*/ + +/*! + \property QFxHighlightFilter::xOffset + \brief the x position of the highlight, relative to the item. +*/ +int QFxHighlightFilter::xOffset() const +{ + return d->xOffset; +} + +void QFxHighlightFilter::setXOffset(int x) +{ + if(x == d->xOffset) + return; + + d->xOffset = x; + emit offsetChanged(d->xOffset, d->yOffset); +#if defined(QFX_RENDER_OPENGL2) + update(); +#endif +} + +/*! + \property QFxHighlightFilter::yOffset + \brief the y position of the highlight, relative to the item. +*/ +int QFxHighlightFilter::yOffset() const +{ + return d->yOffset; +} + +void QFxHighlightFilter::setYOffset(int y) +{ + if(y == d->yOffset) + return; + + d->yOffset = y; + emit offsetChanged(d->xOffset, d->yOffset); +#if defined(QFX_RENDER_OPENGL2) + update(); +#endif +} + +void QFxHighlightFilter::filterGL(QSimpleCanvasItem::GLPainter &p) +{ +#if defined(QFX_RENDER_OPENGL2) + if(d->tex.isNull()) { + renderToScreen(); + } else { + QSimpleCanvasItem *item = this->item(); + + QRect r = item->itemBoundingRect(); + + QGLFramebufferObject *fbo = renderToFBO(); + + float width = r.width(); + float height = r.height(); + + float texWidth = width / float(fbo->width()); + float texHeight = height / float(fbo->height()); + + GLfloat vert[] = { 0, height, + width, height, + 0, 0, + width, 0 }; + GLfloat texVert[] = { 0, 0, + texWidth, 0, + 0, texHeight, + texWidth, texHeight }; + float texXOffset = 0; + float texYOffset = 0; + + if(xOffset()) + texXOffset = float(xOffset()) / float(d->tex.width()); + if(yOffset()) + texYOffset = float(yOffset()) / float(d->tex.height()); + + GLfloat addTexVert[] = { texXOffset, texYOffset, + 1 + texXOffset, texYOffset, + texXOffset, 1 + texYOffset, + 1 + texXOffset, 1 + texYOffset }; + + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo->texture()); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, d->tex.texture()); + + DualTextureAddShader *shader = item->basicShaders()->dualTextureAdd(); + shader->enable(); + shader->setTransform(p.activeTransform); + shader->setOpacity(p.activeOpacity); + + shader->setAttributeArray(DualTextureAddShader::Vertices, vert, 2); + shader->setAttributeArray(DualTextureAddShader::TextureCoords, texVert, 2); + shader->setAttributeArray(DualTextureAddShader::AddTextureCoords, addTexVert, 2); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + shader->disableAttributeArray(DualTextureAddShader::Vertices); + shader->disableAttributeArray(DualTextureAddShader::TextureCoords); + shader->disableAttributeArray(DualTextureAddShader::AddTextureCoords); + + glActiveTexture(GL_TEXTURE0); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + releaseFBO(fbo); + } +#else + Q_UNUSED(p); +#endif +} + +QML_DEFINE_TYPE(QFxHighlightFilter,Highlight); + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxhighlightfilter.h b/src/declarative/fx/qfxhighlightfilter.h new file mode 100644 index 0000000..6204242 --- /dev/null +++ b/src/declarative/fx/qfxhighlightfilter.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 QtDeclarative 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 QFXHIGHLIGHTFILTER_H +#define QFXHIGHLIGHTFILTER_H + +#include <qsimplecanvasfilter.h> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxHighlightFilterPrivate; +class Q_DECLARATIVE_EXPORT QFxHighlightFilter : public QSimpleCanvasFilter +{ + Q_OBJECT + + Q_PROPERTY(QString src READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(bool tiled READ tiled WRITE setTiled NOTIFY tiledChanged) + Q_PROPERTY(int xOffset READ xOffset WRITE setXOffset NOTIFY offsetChanged) + Q_PROPERTY(int yOffset READ yOffset WRITE setYOffset NOTIFY offsetChanged) +public: + QFxHighlightFilter(QObject *parent=0); + virtual ~QFxHighlightFilter(); + + QString source() const; + void setSource(const QString &); + + bool tiled() const; + void setTiled(bool); + + int xOffset() const; + void setXOffset(int); + int yOffset() const; + void setYOffset(int); + +Q_SIGNALS: + void sourceChanged(const QString &); + void offsetChanged(int x, int y); + void tiledChanged(bool); + +private Q_SLOTS: + void imageLoaded(); + +protected: + virtual void filterGL(QSimpleCanvasItem::GLPainter &p); + +private: + QFxHighlightFilterPrivate *d; +}; +QML_DECLARE_TYPE(QFxHighlightFilter); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXHIGHLIGHTFILTER_H diff --git a/src/declarative/fx/qfximage.cpp b/src/declarative/fx/qfximage.cpp new file mode 100644 index 0000000..a137bc3 --- /dev/null +++ b/src/declarative/fx/qfximage.cpp @@ -0,0 +1,935 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfximage.h" +#include "qfximage_p.h" +#include <qfxperf.h> +#if defined(QFX_RENDER_OPENGL) +#include <glsave.h> +#endif +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QFile> +#include <QtDeclarative/qmlengine.h> + +QT_BEGIN_NAMESPACE + + +QML_DEFINE_TYPE(QFxImage,Image); + +/*! + \qmlclass Image QFxImage + \brief The Image element allows you to add bitmaps to a scene. + \inherits Item + + The Image element supports untransformed, stretched, grid-scaled and tiled images. For an explanation of grid-scaling see the scaleGrid property description or the QFxScaleGrid class description. + + Examples: + \table + \row + \o \image declarative-qtlogo1.png + \o Untransformed + \code + <Image src="pics/qtlogo.png"/> + \endcode + \row + \o \image declarative-qtlogo2.png + \o Stretched + \code + <Image width="160" height="160" src="pics/qtlogo.png"/> + \endcode + \row + \o \image declarative-qtlogo4.png + \o Grid-scaled + \code + <Image scaleGrid.left="20" scaleGrid.right="10" + scaleGrid.top="14" scaleGrid.bottom="14" + width="160" height="160" src="pics/qtlogo.png"/> + \endcode + \row + \o \image declarative-qtlogo3.png + \o Tiled + \code + <Image tile="true" + width="160" height="160" src="pics/qtlogo.png"/> + \endcode + \endtable + */ + +/*! + \internal + \class QFxImage Image + \brief The QFxImage class provides an image item that you can add to a QFxView. + + \ingroup coreitems + + Example: + \code + <Image src="pics/star.png"/> + \endcode + + A QFxImage object can be instantiated in Qml using the tag \l Image. +*/ + +QFxImage::QFxImage(QFxItem *parent) + : QFxItem(*(new QFxImagePrivate), parent) +{ + setOptions(SimpleItem | HasContents, true); +} + +QFxImage::QFxImage(QFxImagePrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + setOptions(SimpleItem | HasContents, true); +} + +QFxImage::~QFxImage() +{ + Q_D(const QFxImage); + if(d->reply) + d->reply->deleteLater(); +} + +/*! + \property QFxImage::pixmap + \brief the image displayed in this item. + + This property contains the image currently being displayed by this item, + which may be an empty pixmap if nothing is currently displayed. If this + property is set, the src property will be unset. This property is intended + to be used only in C++, not in XML. +*/ +QPixmap QFxImage::pixmap() const +{ + Q_D(const QFxImage); + return d->_pix.pixmap(); +} + +void QFxImage::setPixmap(const QPixmap &pix) +{ + Q_D(QFxImage); + d->url = QUrl(); + d->_pix.setPixmap(pix); + d->_opaque=false; + d->_pix.setOpaque(false); + + setImplicitWidth(d->_pix.width()); + setImplicitHeight(d->_pix.height()); + +#if defined(QFX_RENDER_OPENGL) + d->_texDirty = true; + d->_tex.clear(); +#endif + update(); +} + +/*! + \qmlproperty int Image::scaleGrid.left + \qmlproperty int Image::scaleGrid.right + \qmlproperty int Image::scaleGrid.top + \qmlproperty int Image::scaleGrid.bottom + + \target ImagexmlpropertiesscaleGrid + + A scale grid uses 4 grid lines (2 horizontal and 2 vertical) to break an image into 9 sections, as shown below: + + \image declarative-scalegrid.png + + When the image is scaled: + \list + \i the corners (sections 1, 3, 7, and 9) are not scaled at all + \i the middle (section 5) is scaled both horizontally and vertically + \i sections 2 and 8 are scaled horizontally + \i sections 4 and 6 are scaled vertically + \endlist + + Each scale grid property (left, right, top, and bottom) specifies an offset from the respective side. For example, \c scaleGrid.bottom="10" sets the bottom scale grid line 10 pixels up from the bottom of the image. + + A scale grid can also be specified using a \l {Imagexmlpropertysrc}{.sci file}. +*/ +QFxScaleGrid *QFxImage::scaleGrid() +{ + Q_D(QFxImage); + return d->scaleGrid(); +} + +/*! + \qmlproperty bool Image::tile + + Set this property to enable image tiling. Normally the Image element scales the + bitmap file to its size. If tiling is enabled, the bitmap is repeated as a set + of unscaled tiles, clipped to the size of the Image. + + \code + <Item> + <Image src="tile.png" /> + <Image x="80" width="100" height="100" src="tile.png" /> + <Image x="190" width="100" height="100" tile="true" src="tile.png" /> + </Item> + \endcode + \image declarative-image_tile.png + + If both tiling and the scaleGrid are set, tiling takes precedence. +*/ +bool QFxImage::isTiled() const +{ + Q_D(const QFxImage); + return d->_tiled; +} + +void QFxImage::setTiled(bool tile) +{ + Q_D(QFxImage); + d->_tiled = tile; +} + +/*! + \qmlproperty bool Image::opaque + + Set this property if you know that the image is opaque to give your + application a significant performance boost. + + \note + This is a performance hint to Qt Declarative. Unfortunately whether or not an image + is opaque is not automatically detected. Setting this property to true when + the image is not opaque will lead to drawing artifacts. However, leaving it as + false will always work correctly - although possibly not at maximum performance. + */ + +/*! + \property QFxImage::opaque + \brief whether the image is opaque (non-transparent). + + This property is provided purely for the purpose of optimization. An opaque + image can be optimized more than a non-opaque one. +*/ +bool QFxImage::isOpaque() const +{ + Q_D(const QFxImage); + return d->_opaque; +} + +void QFxImage::setOpaque(bool o) +{ + Q_D(QFxImage); + if(o == d->_opaque) + return; + d->_opaque = o; + d->_pix.setOpaque(o); + update(); +} + +void QFxImage::componentComplete() +{ + QFxItem::componentComplete(); +} + +/*! + \property QFxImage::scaleGrid + \brief the 3x3 grid used to scale an image, excluding the corners. +*/ + +/*! + \qmlproperty bool Image::smooth + + Set this property if you want the image to be smoothly filtered when scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the Image is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the image is stationary on + the screen. A common pattern when animating an image is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. + */ + +/*! + \property QFxImage::smooth + \brief whether the image is smoothly transformed. + + This property is provided purely for the purpose of optimization. Turning + smooth transforms off is faster, but looks worse; turning smooth + transformations on is slower, but looks better. + + By default smooth transformations are off. +*/ +bool QFxImage::smoothTransform() const +{ + Q_D(const QFxImage); + return d->_smooth; +} + +void QFxImage::setSmoothTransform(bool s) +{ + Q_D(QFxImage); + if(d->_smooth == s) + return; + d->_smooth = s; + update(); +} + +void QFxImage::dump(int depth) +{ + Q_D(QFxImage); + QByteArray ba(depth * 4, ' '); + qWarning() << ba.constData() << "URL:" << d->url; + QFxItem::dump(depth); +} + +#if defined(QFX_RENDER_QPAINTER) +void QFxImage::paintContents(QPainter &p) +{ + Q_D(QFxImage); + if(d->_pix.isNull()) + return; + + if(d->_smooth) { + p.save(); + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->_smooth); + } + + QSimpleCanvasConfig::Image pix = d->_pix; + + if (d->_tiled) { + p.save(); + p.setClipRect(0, 0, width(), height(), Qt::IntersectClip); + QRect me = QRect(0, 0, width(), height()); + + int pw = d->_pix.width(); + int ph = d->_pix.height(); + int yy = 0; + + while(yy < height()) { + int xx = 0; + while(xx < width()) { + p.drawImage(xx, yy, d->_pix); + xx += pw; + } + yy += ph; + } + + p.restore(); + } else if (!d->_scaleGrid || d->_scaleGrid->isNull()) { + if(width() != pix.width() || height() != pix.height()) { + QTransform scale; + scale.scale(width() / qreal(pix.width()), + height() / qreal(pix.height())); + QTransform old = p.transform(); + p.setWorldTransform(scale * old); + p.drawImage(0, 0, pix); + p.setWorldTransform(old); + } else { + p.drawImage(0, 0, pix); + } + } else { + const int sgl = d->_scaleGrid->left(); + const int sgr = d->_scaleGrid->right(); + const int sgt = d->_scaleGrid->top(); + const int sgb = d->_scaleGrid->bottom(); + const int xSide = sgl + sgr; + const int ySide = sgt + sgb; + + // Upper left + if(sgt && sgl) + p.drawImage(QRect(0, 0, sgl, sgt), pix, QRect(0, 0, sgl, sgt)); + // Upper middle + if(pix.width() - xSide && sgt) + p.drawImage(QRect(sgl, 0, width() - xSide, sgt), pix, + QRect(sgl, 0, pix.width() - xSide, sgt)); + // Upper right + if(sgt && pix.width() - sgr) + p.drawImage(QPoint(width()-sgr, 0), pix, + QRect(pix.width()-sgr, 0, sgr, sgt)); + // Middle left + if(sgl && pix.height() - ySide) + p.drawImage(QRect(0, sgt, sgl, height() - ySide), pix, + QRect(0, sgt, sgl, pix.height() - ySide)); + + // Middle + if(pix.width() - xSide && pix.height() - ySide) + p.drawImage(QRect(sgl, sgt, width() - xSide, height() - ySide), + pix, + QRect(sgl, sgt, pix.width() - xSide, pix.height() - ySide)); + // Middle right + if(sgr && pix.height() - ySide) + p.drawImage(QRect(width()-sgr, sgt, sgr, height() - ySide), pix, + QRect(pix.width()-sgr, sgt, sgr, pix.height() - ySide)); + // Lower left + if(sgl && sgr) + p.drawImage(QPoint(0, height() - sgb), pix, + QRect(0, pix.height() - sgb, sgl, sgb)); + // Lower Middle + if(pix.width() - xSide && sgb) + p.drawImage(QRect(sgl, height() - sgb, width() - xSide, sgb), pix, + QRect(sgl, pix.height() - sgb, pix.width() - xSide, sgb)); + // Lower Right + if(sgr && sgb) + p.drawImage(QPoint(width()-sgr, height() - sgb), pix, + QRect(pix.width()-sgr, pix.height() - sgb, sgr, sgb)); + } + + if(d->_smooth) { + p.restore(); + } +} +#elif defined(QFX_RENDER_OPENGL) +uint QFxImage::glSimpleItemData(float *vertices, float *texVertices, + GLTexture **texture, uint count) +{ + Q_D(QFxImage); + + if(d->_pix.isNull() || (d->_scaleGrid && !d->_scaleGrid->isNull())) + return 0; + + if(count < 8) + return 8; + + d->checkDirty(); + + float widthV = width(); + float heightV = height(); + + vertices[0] = 0; vertices[1] = heightV; + vertices[2] = widthV; vertices[3] = heightV; + vertices[4] = 0; vertices[5] = 0; + vertices[6] = widthV; vertices[7] = 0; + + *texture = &d->_tex; + + if(d->_tiled) { + float tileWidth = widthV / d->_pix.width(); + float tileHeight = heightV / d->_pix.height(); + texVertices[0] = 0; texVertices[1] = 0; + texVertices[2] = tileWidth; texVertices[3] = 0; + texVertices[4] = 0; texVertices[5] = tileHeight; + texVertices[6] = tileWidth; texVertices[7] = tileHeight; + } else { + texVertices[0] = 0; texVertices[1] = 0; + texVertices[2] = 1; texVertices[3] = 0; + texVertices[4] = 0; texVertices[5] = 1; + texVertices[6] = 1; texVertices[7] = 1; + } + + return 8; +} + +void QFxImagePrivate::checkDirty() +{ + if(_texDirty && !_pix.isNull()) { + _tex.setImage(_pix); + _tex.setHorizontalWrap(GLTexture::Repeat); + _tex.setVerticalWrap(GLTexture::Repeat); + } + _texDirty = false; +} + +#if defined(QFX_RENDER_OPENGL2) +void QFxImage::paintGLContents(GLPainter &p) +{ + Q_D(QFxImage); + if(d->_pix.isNull()) + return; + + QGLShaderProgram *shader = p.useTextureShader(); + + bool restoreBlend = false; + if(isOpaque() && p.activeOpacity == 1) { + glDisable(GL_BLEND); + restoreBlend = true; + } + + if(d->_tiled || (!d->_scaleGrid || d->_scaleGrid->isNull())) { + + GLfloat vertices[8]; + GLfloat texVertices[8]; + GLTexture *tex = 0; + + QFxImage::glSimpleItemData(vertices, texVertices, &tex, 8); + + shader->setAttributeArray(SingleTextureShader::Vertices, vertices, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, texVertices, 2); + + glBindTexture(GL_TEXTURE_2D, tex->texture()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + shader->disableAttributeArray(SingleTextureShader::Vertices); + shader->disableAttributeArray(SingleTextureShader::TextureCoords); + + } else { + d->checkDirty(); + + float imgWidth = d->_pix.width(); + float imgHeight = d->_pix.height(); + if(!imgWidth || !imgHeight) { + if (restoreBlend) + glEnable(GL_BLEND); + return; + } + + float widthV = width(); + float heightV = height(); + + float texleft = 0; + float texright = 1; + float textop = 1; + float texbottom = 0; + float imgleft = 0; + float imgright = widthV; + float imgtop = 0; + float imgbottom = heightV; + + const int sgl = d->_scaleGrid->left(); + const int sgr = d->_scaleGrid->right(); + const int sgt = d->_scaleGrid->top(); + const int sgb = d->_scaleGrid->bottom(); + + if(sgl) { + texleft = float(sgl) / imgWidth; + imgleft = sgl; + } + if(sgr) { + texright = 1. - float(sgr) / imgWidth; + imgright = widthV - sgr; + } + if(sgt) { + textop = 1. - float(sgb) / imgHeight; + imgtop = sgt; + } + if(sgb) { + texbottom = float(sgt) / imgHeight; + imgbottom = heightV - sgb; + } + + float vert1[] = { 0, 0, + 0, imgtop, + imgleft, 0, + imgleft, imgtop, + imgright, 0, + imgright, imgtop, + widthV, 0, + widthV, imgtop }; + float tex1[] = { 0, 1, + 0, textop, + texleft, 1, + texleft, textop, + texright, 1, + texright, textop, + 1, 1, + 1, textop }; + float vert2[] = { 0, imgtop, + 0, imgbottom, + imgleft, imgtop, + imgleft, imgbottom, + imgright, imgtop, + imgright, imgbottom, + widthV, imgtop, + widthV, imgbottom }; + float tex2[] = { 0, textop, + 0, texbottom, + texleft, textop, + texleft, texbottom, + texright, textop, + texright, texbottom, + 1, textop, + 1, texbottom }; + float vert3[] = { 0, imgbottom, + 0, heightV, + imgleft, imgbottom, + imgleft, heightV, + imgright, imgbottom, + imgright, heightV, + widthV, imgbottom, + widthV, heightV }; + float tex3[] = { 0, texbottom, + 0, 0, + texleft, texbottom, + texleft, 0, + texright, texbottom, + texright, 0, + 1, texbottom, + 1, 0 }; + + glBindTexture(GL_TEXTURE_2D, d->_tex.texture()); + + shader->setAttributeArray(SingleTextureShader::Vertices, vert1, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, tex1, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + shader->setAttributeArray(SingleTextureShader::Vertices, vert2, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, tex2, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + shader->setAttributeArray(SingleTextureShader::Vertices, vert3, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, tex3, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + + shader->disableAttributeArray(SingleTextureShader::Vertices); + shader->disableAttributeArray(SingleTextureShader::TextureCoords); + } + + if (restoreBlend) + glEnable(GL_BLEND); +} +#elif defined(QFX_RENDER_OPENGL1) +void QFxImage::paintGLContents(GLPainter &p) +{ + Q_D(QFxImage); + if(d->_pix.isNull()) + return; + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(p.activeTransform.data()); + + bool restoreBlend = false; + if(isOpaque() && p.activeOpacity == 1) { + glDisable(GL_BLEND); + restoreBlend = true; + } + + glEnable(GL_TEXTURE_2D); + if(p.activeOpacity == 1.) { + GLint i = GL_REPLACE; + glTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &i); + } else { + GLint i = GL_MODULATE; + glTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &i); + glColor4f(1, 1, 1, p.activeOpacity); + } + + if(d->_tiled || !d->_scaleGrid || d->_scaleGrid->isNull()) { + + GLfloat vertices[8]; + GLfloat texVertices[8]; + GLTexture *tex = 0; + + QFxImage::glSimpleItemData(vertices, texVertices, &tex, 8); + + glBindTexture(GL_TEXTURE_2D, tex->texture()); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, vertices); + glTexCoordPointer(2, GL_FLOAT, 0, texVertices); + + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); + + } else { + d->checkDirty(); + + float imgWidth = d->_pix.width(); + float imgHeight = d->_pix.height(); + if(!imgWidth || !imgHeight) { + if (restoreBlend) + glEnable(GL_BLEND); + return; + } + + float widthV = width(); + float heightV = height(); + + float texleft = 0; + float texright = 1; + float textop = 1; + float texbottom = 0; + float imgleft = 0; + float imgright = widthV; + float imgtop = 0; + float imgbottom = heightV; + + const int sgl = d->_scaleGrid->left(); + const int sgr = d->_scaleGrid->right(); + const int sgt = d->_scaleGrid->top(); + const int sgb = d->_scaleGrid->bottom(); + + if(sgl) { + texleft = float(sgl) / imgWidth; + imgleft = sgl; + } + if(sgr) { + texright = 1. - float(sgr) / imgWidth; + imgright = widthV - sgr; + } + if(sgt) { + textop = 1. - float(sgb) / imgHeight; + imgtop = sgt; + } + if(sgb) { + texbottom = float(sgt) / imgHeight; + imgbottom = heightV - sgb; + } + + float vert1[] = { 0, 0, + 0, imgtop, + imgleft, 0, + imgleft, imgtop, + imgright, 0, + imgright, imgtop, + widthV, 0, + widthV, imgtop }; + float tex1[] = { 0, 1, + 0, textop, + texleft, 1, + texleft, textop, + texright, 1, + texright, textop, + 1, 1, + 1, textop }; + float vert2[] = { 0, imgtop, + 0, imgbottom, + imgleft, imgtop, + imgleft, imgbottom, + imgright, imgtop, + imgright, imgbottom, + widthV, imgtop, + widthV, imgbottom }; + float tex2[] = { 0, textop, + 0, texbottom, + texleft, textop, + texleft, texbottom, + texright, textop, + texright, texbottom, + 1, textop, + 1, texbottom }; + float vert3[] = { 0, imgbottom, + 0, heightV, + imgleft, imgbottom, + imgleft, heightV, + imgright, imgbottom, + imgright, heightV, + widthV, imgbottom, + widthV, heightV }; + float tex3[] = { 0, texbottom, + 0, 0, + texleft, texbottom, + texleft, 0, + texright, texbottom, + texright, 0, + 1, texbottom, + 1, 0 }; + + glBindTexture(GL_TEXTURE_2D, d->_tex.texture()); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, vert1); + glTexCoordPointer(2, GL_FLOAT, 0, tex1); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + glVertexPointer(2, GL_FLOAT, 0, vert2); + glTexCoordPointer(2, GL_FLOAT, 0, tex2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + glVertexPointer(2, GL_FLOAT, 0, vert3); + glTexCoordPointer(2, GL_FLOAT, 0, tex3); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); + } + + if (restoreBlend) + glEnable(GL_BLEND); +} +#endif + +#endif + +QString QFxImage::propertyInfo() const +{ + Q_D(const QFxImage); + return d->url.toString(); +} + +QFxImage::Status QFxImage::status() const +{ + Q_D(const QFxImage); + return d->status; +} + +/*! + \qmlproperty string Image::src + + Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt. + + \target Imagexmlpropertysrc + + It can also handle .sci files, which are a Qml-specific format. A .sci file uses a simple text-based format that specifies + \list + \i the grid lines describing a \l {ImagexmlpropertiesscaleGrid}{scale grid}. + \i an image file. + \endlist + + The following .sci file sets grid line offsets of 10 on each side for the image \c picture.png: + \code + gridLeft: 10 + gridTop: 10 + gridBottom: 10 + gridRight: 10 + imageFile: picture.png + \endcode + + The URL may be absolute, or relative to the URL of the component. +*/ + +/*! + \property QFxImage::src + \brief the url of the image to be displayed in this item. + + The content specified can be of any image type loadable by QImage. Alternatively, + you can specify an sci format file, which specifies both an image and it's scale grid. +*/ +QString QFxImage::source() const +{ + Q_D(const QFxImage); + return d->source; +} + +void QFxImage::setSource(const QString &url) +{ + Q_D(QFxImage); + if(url == d->source) + return; + + if(d->reply) { + d->reply->deleteLater(); + d->reply = 0; + } + + if (!d->url.isEmpty()) + QFxPixmap::cancelGet(d->url, this, SLOT(requestFinished())); + if (!d->sciurl.isEmpty()) + QFxPixmap::cancelGet(d->sciurl, this, SLOT(requestFinished())); + + d->source = url; + d->url = itemContext()->resolvedUrl(url); + d->sciurl = QUrl(); + + if(url.isEmpty()) { + setPixmap(QPixmap()); + d->status = Idle; + } else { + d->status = Loading; + if (d->url.path().endsWith(QLatin1String(".sci"))) { +#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML + if (d->url.scheme() == QLatin1String("file")) { + QFile file(d->url.toLocalFile()); + file.open(QIODevice::ReadOnly); + setGridScaledImage(QFxGridScaledImage(&file)); + } else +#endif + { + QNetworkRequest req(d->url); + req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + d->reply = itemContext()->engine()->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), + this, SLOT(sciRequestFinished())); + } + } else { + QFxPixmap::get(itemContext()->engine(), d->url, this, SLOT(requestFinished())); + } + } + + emit statusChanged(d->status); +} + +void QFxImage::requestFinished() +{ + Q_D(QFxImage); + if (d->url.path().endsWith(QLatin1String(".sci"))) { + d->_pix = QFxPixmap(d->sciurl); + } else { + d->_pix = QFxPixmap(d->url); + d->_pix.setOpaque(d->_opaque); + setOptions(QFxImage::SimpleItem, true); + } + setImplicitWidth(d->_pix.width()); + setImplicitHeight(d->_pix.height()); + + d->status = Idle; +#if defined(QFX_RENDER_OPENGL) + d->_texDirty = true; + d->_tex.clear(); +#endif + emit statusChanged(d->status); + emit sourceChanged(d->source); + update(); +} + +void QFxImage::sciRequestFinished() +{ + Q_D(QFxImage); + if(d->reply->error() != QNetworkReply::NoError) { + d->status = Error; + d->reply->deleteLater(); + d->reply = 0; + emit statusChanged(d->status); + } else { + QFxGridScaledImage sci(d->reply); + d->reply->deleteLater(); + d->reply = 0; + setGridScaledImage(sci); + } +} + + +void QFxImage::setGridScaledImage(const QFxGridScaledImage& sci) +{ + Q_D(QFxImage); + if(!sci.isValid()) { + d->status = Error; + emit statusChanged(d->status); + } else { + d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl())); + QFxPixmap::get(itemContext()->engine(), d->sciurl, this, SLOT(requestFinished())); + QFxScaleGrid *sg = scaleGrid(); + sg->setTop(sci.gridTop()); + sg->setBottom(sci.gridBottom()); + sg->setLeft(sci.gridLeft()); + sg->setRight(sci.gridRight()); + setOptions(QFxImage::SimpleItem, false); + } +} + + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfximage.h b/src/declarative/fx/qfximage.h new file mode 100644 index 0000000..4eed0ef --- /dev/null +++ b/src/declarative/fx/qfximage.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXIMAGE_H +#define QFXIMAGE_H + +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxImagePrivate; +class Q_DECLARATIVE_EXPORT QFxImage : public QFxItem +{ + Q_OBJECT + Q_ENUMS(Status) + + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QString src READ source WRITE setSource NOTIFY sourceChanged) + + Q_PROPERTY(QFxScaleGrid *scaleGrid READ scaleGrid) + Q_PROPERTY(bool tile READ isTiled WRITE setTiled) + Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap DESIGNABLE false) + Q_PROPERTY(bool opaque READ isOpaque WRITE setOpaque) + Q_PROPERTY(bool smooth READ smoothTransform WRITE setSmoothTransform) +public: + QFxImage(QFxItem *parent=0); + ~QFxImage(); + + QFxScaleGrid *scaleGrid(); + + bool isTiled() const; + void setTiled(bool tile); + + QPixmap pixmap() const; + void setPixmap(const QPixmap &); + + bool isOpaque() const; + void setOpaque(bool); + + bool smoothTransform() const; + void setSmoothTransform(bool); + + enum Status { Idle, Loading, Error }; + Status status() const; + + QString source() const; + virtual void setSource(const QString &url); + + virtual void dump(int depth); + virtual QString propertyInfo() const; +#if defined(QFX_RENDER_QPAINTER) + void paintContents(QPainter &painter); +#elif defined(QFX_RENDER_OPENGL) + void paintGLContents(GLPainter &); + uint glSimpleItemData(float *vertices, float *texVertices, + GLTexture **texture, uint count); +#endif + +Q_SIGNALS: + void sourceChanged(const QString &); + void statusChanged(Status); + +protected: + QFxImage(QFxImagePrivate &dd, QFxItem *parent); + virtual void componentComplete(); + +private Q_SLOTS: + void requestFinished(); + void sciRequestFinished(); + +private: + Q_DISABLE_COPY(QFxImage) + Q_DECLARE_PRIVATE(QFxImage) + void setGridScaledImage(const QFxGridScaledImage& sci); +}; +QML_DECLARE_TYPE(QFxImage); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXIMAGE_H diff --git a/src/declarative/fx/qfximage_p.h b/src/declarative/fx/qfximage_p.h new file mode 100644 index 0000000..46aea49 --- /dev/null +++ b/src/declarative/fx/qfximage_p.h @@ -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 QtDeclarative 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 QFXIMAGE_P_H +#define QFXIMAGE_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 "qfxitem_p.h" + +#if defined(QFX_RENDER_OPENGL) +#include "gltexture.h" +#endif + +QT_BEGIN_NAMESPACE + +class QSvgRenderer; +class QWebPage; +class QNetworkReply; +class QIODevice; + +class QFxImagePrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxImage) + +public: + QFxImagePrivate() + : _scaleGrid(0), _tiled(false), _smooth(false), _opaque(false), +#if defined(QFX_RENDER_OPENGL) + _texDirty(true), +#endif + status(QFxImage::Idle), reply(0) + { + } + + ~QFxImagePrivate() + { + delete _scaleGrid; + } + + void setContent(QIODevice* dev, const QString &url); + + QFxScaleGrid *scaleGrid() + { + if(!_scaleGrid) + _scaleGrid = new QFxScaleGrid; + return _scaleGrid; + } + + QFxScaleGrid *_scaleGrid; + bool _tiled; + QFxPixmap _pix; + bool _smooth; + bool _opaque; +#if defined(QFX_RENDER_OPENGL) + void checkDirty(); + bool _texDirty; + GLTexture _tex; +#endif + + QFxImage::Status status; + QString source; + QUrl url; + QUrl sciurl; + QNetworkReply *reply; +}; + +QT_END_NAMESPACE + +#endif // QFXIMAGE_P_H diff --git a/src/declarative/fx/qfximageitem.cpp b/src/declarative/fx/qfximageitem.cpp new file mode 100644 index 0000000..6c257fa --- /dev/null +++ b/src/declarative/fx/qfximageitem.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfximageitem.h" +#include "qfximageitem_p.h" + +#include <QDebug> +#include <QPen> +#include <QFile> +#include <QEvent> +#include <QApplication> +#include <QGraphicsSceneMouseEvent> + +#if defined(QFX_RENDER_OPENGL2) +#include <QtOpenGL/qglframebufferobject.h> +#include <glsave.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QFxImageItem + \brief The QFxImageItem class is an abstract base class for QFxView items that render using QPainter. + \ingroup coreitems + + This is a convenience class allowing easy use of a QPainter within a custom + item. The contents of the item are are cached behind the scenes. + The dirtyCache() function should be called if the contents change to + ensure the cache is refreshed the next time painting occurs. + + To subclass QFxImageItem, you must reimplement drawContents() to draw + the contents of the item. +*/ + +/*! + \fn void QFxImageItem::drawContents(QPainter *painter, const QRect &rect) + + This function is called when the cache needs to be refreshed. When + sub-classing QFxImageItem this function should be implemented so as to + paint the contents of the item using the given \a painter for the + area of the contents specified by \a rect. +*/ + +/*! + \property QFxImageItem::contentsSize + \brief The size of the contents + + The contents size is the size of the item in regards to how it is painted + using the drawContents() function. This is distinct from the size of the + item in regards to height() and width(). +*/ + +/*! + \property QFxImageItem::smooth + \brief Setting for whether smooth scaling is enabled. +*/ + +/*! + Marks areas of the cache that intersect with the given \a rect as dirty and + in need of being refreshed. + + \sa clearCache() +*/ +void QFxImageItem::dirtyCache(const QRect& rect) +{ + Q_D(QFxImageItem); + for (int i=0; i < d->imagecache.count(); ) { + if (d->imagecache[i]->area.intersects(rect)) { + d->imagecache.removeAt(i); + } else { + ++i; + } + } +} + +/*! + Marks the entirety of the contents cache as dirty. + + \sa dirtyCache() +*/ +void QFxImageItem::clearCache() +{ + Q_D(QFxImageItem); + foreach (QFxImageItemPrivate::ImageCacheItem* i, d->imagecache) + delete i; + d->imagecache.clear(); +} + +/*! + Returns if smooth scaling of the cache contents is enabled. + + \sa setSmooth() +*/ +bool QFxImageItem::isSmooth() const +{ + Q_D(const QFxImageItem); + return d->smooth; +} + +/*! + Returns the size of the contents. + + \sa setContentsSize() +*/ +QSize QFxImageItem::contentsSize() const +{ + Q_D(const QFxImageItem); + return d->contentsSize; +} + +/*! + If \a smooth is true sets the image item to enable smooth scaling of + the cache contents. + + \sa isSmooth() +*/ +void QFxImageItem::setSmooth(bool smooth) +{ + Q_D(QFxImageItem); + if(d->smooth == smooth) return; + d->smooth = smooth; + clearCache(); + update(); +} + +/*! + Sets the size of the contents to the given \a size. + + \sa contentsSize() +*/ +void QFxImageItem::setContentsSize(const QSize &size) +{ + Q_D(QFxImageItem); + if(d->contentsSize == size) return; + d->contentsSize = size; + clearCache(); + update(); +} + +/*! + Constructs a new QFxImageItem with the given \a parent. +*/ +QFxImageItem::QFxImageItem(QFxItem *parent) + : QFxItem(*(new QFxImageItemPrivate), parent) +{ + init(); +} + +/*! + \internal + Constructs a new QFxImageItem with the given \a parent and + initialized private data member \a dd. +*/ +QFxImageItem::QFxImageItem(QFxImageItemPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + init(); +} + +/*! + Destroys the image item. +*/ +QFxImageItem::~QFxImageItem() +{ +} + +/*! + \internal +*/ +void QFxImageItem::init() +{ + connect(this,SIGNAL(widthChanged()),this,SLOT(clearCache())); + connect(this,SIGNAL(heightChanged()),this,SLOT(clearCache())); + connect(this,SIGNAL(visibleChanged()),this,SLOT(clearCache())); +} + +#if defined(QFX_RENDER_QPAINTER) +/*! + \reimp +*/ +void QFxImageItem::paintContents(QPainter &p) +#elif defined(QFX_RENDER_OPENGL) +/*! + \reimp +*/ +void QFxImageItem::paintGLContents(GLPainter &p) +#else +#error "What render?" +#endif +{ + Q_D(QFxImageItem); + const QRect content(QPoint(0,0),d->contentsSize); + if (content.width() <= 0 || content.height() <= 0) + return; + +#if defined(QFX_RENDER_QPAINTER) + if(d->smooth) { + p.save(); + p.setRenderHints(QPainter::Antialiasing, true); + p.setRenderHints(QPainter::SmoothPixmapTransform, true); + } + QRectF clipf = p.clipRegion().boundingRect(); + if (clipf.isEmpty()) + clipf = mapToScene(content); // ### Inefficient: Maps toScene and then fromScene + else + clipf = mapToScene(clipf); + +#elif defined(QFX_RENDER_OPENGL2) + p.useTextureShader(); + const QRectF clipf = p.sceneClipRect; + +#elif defined(QFX_RENDER_OPENGL1) + p.useTextureShader(); + const QRectF clipf = p.sceneClipRect; +#endif + + qreal hscale = widthValid() ? qreal(width()) / content.width() : heightValid() ? qreal(height()) / content.height() : 1.0; + qreal vscale = heightValid() ? qreal(height()) / content.height() : widthValid() ? qreal(width()) / content.width() : 1.0; + const QRect clip = mapFromScene(QRectF(clipf.x()/hscale,clipf.y()/vscale,clipf.width()/hscale,clipf.height()/vscale)).toRect(); + + QRegion topaint(clip); + topaint &= content; + QRegion uncached(content); + +#if defined(QFX_RENDER_OPENGL2) + glEnableVertexAttribArray(SingleTextureShader::Vertices); + glEnableVertexAttribArray(SingleTextureShader::TextureCoords); +#elif defined(QFX_RENDER_OPENGL1) + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); +#endif + + int cachesize=0; + for (int i=0; i<d->imagecache.count(); ++i) { + QRect area = d->imagecache[i]->area; + if (topaint.contains(area)) { + QRectF target(area.x()*hscale, area.y()*vscale, area.width()*hscale, area.height()*vscale); + p.drawImage(target.toRect(), d->imagecache[i]->image); + topaint -= area; + d->imagecache[i]->age=0; + } else { + d->imagecache[i]->age++; + } + cachesize += area.width()*area.height(); + uncached -= area; + } + + if (!topaint.isEmpty()) { + // Find a sensible larger area, otherwise will paint lots of tiny images. + QRect biggerrect = topaint.boundingRect().adjusted(-64,-64,128,128); + cachesize += biggerrect.width() * biggerrect.height(); + while (d->imagecache.count() && cachesize > d->max_imagecache_size) { + int oldest=-1; + int age=-1; + for (int i=0; i<d->imagecache.count(); ++i) { + int a = d->imagecache[i]->age; + if (a > age) { + oldest = i; + age = a; + } + } + cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height(); + uncached += d->imagecache[oldest]->area; + d->imagecache.removeAt(oldest); + } + const QRegion bigger = QRegion(biggerrect) & uncached; + const QVector<QRect> rects = bigger.rects(); + foreach (QRect r, rects) { +#if defined(QFX_RENDER_QPAINTER) + QImage img(r.size(),QImage::Format_ARGB32_Premultiplied); +#else + QImage img(r.size(),QImage::Format_ARGB32); +#endif + img.fill(0); + { + QPainter qp(&img); + qp.translate(-r.x(),-r.y()); + drawContents(&qp, r); + } + QFxImageItemPrivate::ImageCacheItem *newitem = new QFxImageItemPrivate::ImageCacheItem; + newitem->area = r; +#if defined(QFX_RENDER_QPAINTER) + newitem->image = QSimpleCanvasConfig::Image(QSimpleCanvasConfig::toImage(img)); +#else + newitem->image.setImage(img); +#endif + d->imagecache.append(newitem); + QRectF target(r.x()*hscale, r.y()*vscale, r.width()*hscale, r.height()*vscale); + p.drawImage(target.toRect(), newitem->image); + } + } +#if defined(QFX_RENDER_OPENGL2) + glDisableVertexAttribArray(SingleTextureShader::Vertices); + glDisableVertexAttribArray(SingleTextureShader::TextureCoords); +#elif defined(QFX_RENDER_OPENGL1) + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +#endif +#if defined(QFX_RENDER_QPAINTER) + if(d->smooth) + p.restore(); +#endif +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfximageitem.h b/src/declarative/fx/qfximageitem.h new file mode 100644 index 0000000..9ffd44e --- /dev/null +++ b/src/declarative/fx/qfximageitem.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXIMAGEITEM_H +#define QFXIMAGEITEM_H + +#include <qfxglobal.h> +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +/* +WARNING: SHORT TERM CLASS. INTENDED TO MERGE INTO QFxPainted +*/ + +class QFxImageItemPrivate; +class Q_DECLARATIVE_EXPORT QFxImageItem : public QFxItem +{ + Q_OBJECT +public: + QFxImageItem(QFxItem *parent=0); + ~QFxImageItem(); + + Q_PROPERTY(QSize contentsSize READ contentsSize WRITE setContentsSize); + Q_PROPERTY(bool smooth READ isSmooth WRITE setSmooth); + +#if defined(QFX_RENDER_QPAINTER) + void paintContents(QPainter &painter); +#elif defined(QFX_RENDER_OPENGL) + void paintGLContents(GLPainter &); +#endif + + bool isSmooth() const; + QSize contentsSize() const; + + void setSmooth(bool); + void setContentsSize(const QSize &); +protected: + QFxImageItem(QFxImageItemPrivate &dd, QFxItem *parent); + + virtual void drawContents(QPainter *p, const QRect &) = 0; + +protected Q_SLOTS: + void dirtyCache(const QRect &); + void clearCache(); + +private: + void init(); + Q_DISABLE_COPY(QFxImageItem) + Q_DECLARE_PRIVATE(QFxImageItem) +}; +QML_DECLARE_TYPE(QFxImageItem); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/fx/qfximageitem_p.h b/src/declarative/fx/qfximageitem_p.h new file mode 100644 index 0000000..80450ec --- /dev/null +++ b/src/declarative/fx/qfximageitem_p.h @@ -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 QtDeclarative 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 QFXIMAGEITEM_P_H +#define QFXIMAGEITEM_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 "qfxitem_p.h" +#include <qsimplecanvas.h> + +#if defined(QFX_RENDER_OPENGL) +#include "gltexture.h" +#endif + +QT_BEGIN_NAMESPACE + +class QFxImageItemPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxImageItem) + +public: + QFxImageItemPrivate() + : max_imagecache_size(1000*1000), smooth(false) + { + } + + struct ImageCacheItem { + ImageCacheItem() : age(0) {} + ~ImageCacheItem() { } + int age; + QRect area; +#if defined(QFX_RENDER_QPAINTER) + QSimpleCanvasConfig::Image image; +#else + GLTexture image; +#endif + }; + + QList<ImageCacheItem*> imagecache; + + const int max_imagecache_size; + bool smooth; + QSize contentsSize; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/fx/qfxitem.cpp b/src/declarative/fx/qfxitem.cpp new file mode 100644 index 0000000..2f4c220 --- /dev/null +++ b/src/declarative/fx/qfxitem.cpp @@ -0,0 +1,1751 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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> +#include <QPen> +#include <QFile> +#include <QEvent> +#include <QNetworkReply> +#include <QNetworkRequest> +#include <QGraphicsSceneMouseEvent> +#include <QtScript/qscriptengine.h> +#include <qfxperf.h> + +#include "qmlengine.h" +#include "qmlstate.h" +#include "qlistmodelinterface.h" + +#include "qfxtransform.h" +#include "qfxscalegrid.h" +#include "qfxview.h" +#include "qmlstategroup.h" + +#include "qfxitem_p.h" +#include "qfxitem.h" +#include <qsimplecanvasfilter.h> +#include <qmlcomponent.h> + + +QT_BEGIN_NAMESPACE +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif + +QML_DEFINE_NOCREATE_TYPE(QFxContents); +QML_DEFINE_TYPE(QFxItem,Item); +QML_DEFINE_NOCREATE_TYPE(QSimpleCanvasFilter); + +/*! + \defgroup animation Animation + \defgroup coreitems Basic Items + \defgroup effects Effects + \defgroup layouts Layouts + \defgroup states States and Transitions + \defgroup utility Utility + \defgroup views Views + \defgroup widgets Widgets +*/ + +/*! + \internal + \class QFxContents + \ingroup utility + \brief The QFxContents class gives access to the height and width of an item's contents. + +*/ + +QFxContents::QFxContents() : _height(0), _width(0) +{ +} + +/*! + \property QFxContents::height + \brief The height of the contents. +*/ +int QFxContents::height() const +{ + return _height; +} + +/*! + \property QFxContents::width + \brief The width of the contents. +*/ +int QFxContents::width() const +{ + return _width; +} + +//TODO: optimization: only check sender(), if there is one +void QFxContents::calcHeight() +{ + int oldheight = _height; + + int top = INT_MAX; + int bottom = 0; + foreach(const QSimpleCanvasItem *child, + _item->QSimpleCanvasItem::children()) { + if (child->y() + child->height() > bottom) + bottom = (int)child->y() + child->height(); + if (child->y() < top) + top = (int)child->y(); + } + _height = bottom - top; + + if (_height != oldheight) + emit heightChanged(); +} + +//TODO: optimization: only check sender(), if there is one +void QFxContents::calcWidth() +{ + int oldwidth = _width; + + int left = INT_MAX; + int right = 0; + foreach(const QSimpleCanvasItem *child, + _item->QSimpleCanvasItem::children()) { + if (child->x() + child->width() > right) + right = (int)child->x() + child->width(); + if (child->x() < left) + left = (int)child->x(); + } + _width = right - left; + + if (_width != oldwidth) + emit widthChanged(); +} + +void QFxContents::setItem(QFxItem *item) +{ + _item = item; + + foreach(const QSimpleCanvasItem *child, + _item->QSimpleCanvasItem::children()) { + connect(child, SIGNAL(bottomChanged()), this, SLOT(calcHeight())); + connect(child, SIGNAL(rightChanged()), this, SLOT(calcWidth())); + } + + calcHeight(); + calcWidth(); +} + +/*! + \qmlclass Item QFxItem + \brief The Item element is the most basic of all visual canvas members. + */ + +/*! + \class QFxItem Item + \brief The QFxItem class is a generic QFxView item. It is the base class for all other view items. + + \qmltext + All visual elements in Qt Declarative inherit from QFxItem. Although QFxItem + has no visual appearance, it defines all the properties that are + common across visual elements - like the x and y position, and the + width and height. + + QFxItem is also useful for grouping elements together. + + \qml + <Item> + <Image file="tile.png" /> + <Image x="80" width="100" height="100" file="tile.png" /> + <Image x="190" width="100" height="100" tile="true" file="tile.png" /> + </Item> + \endqml + \endqmltext + + \ingroup coreitems +*/ + +/*! + \fn void QFxItem::activeFocusChanged() + + This signal is emitted when this item gains active focus. +*/ + +/*! + \fn void QFxItem::baselineChanged() + + This signal is emitted when the baseline of the item changes. + + The baseline may change in response to a call to setBaselineOffset() + or due to the geometry of the item changing. + + \sa baselineOffset(), setBaselineOffset() +*/ + +/*! + \fn void QFxItem::baselineOffsetChanged() + + This signal is emitted when the baseline of the item is changed + via setBaselineOffset(). + + The baseline corresponds to the baseline of the text contained in + the element. It is useful for aligning the text in items placed + beside each other. The default baseline is positioned at + 2/3 of the height of the item. + + \sa baselineOffset(), setBaselineOffset() +*/ + +/*! + \fn void QFxItem::leftChanged() + + This signal is emitted when the left coordinate of the item changes. +*/ + +/*! + \fn void QFxItem::rightChanged() + + This signal is emitted when the right coordinate of the item changes. +*/ + +/*! + \fn void QFxItem::topChanged() + + This signal is emitted when the top coordinate of the item changes. +*/ + +/*! + \fn void QFxItem::bottomChanged() + + This signal is emitted when the bottom coordinate of the item changes. +*/ + +/*! + \fn void QFxItem::widthChanged() + + This signal is emitted when the width of the item changes. +*/ + +/*! + \fn void QFxItem::heightChanged() + + This signal is emitted when the height of the item changes. +*/ + +/*! + \fn void QFxItem::hcenterChanged() + + This signal is emitted when the horizontal center coordinate of the item changes. +*/ + +/*! + \fn void QFxItem::vcenterChanged() + + This signal is emitted when the vertical center coordinate of the item changes. +*/ + +/*! + \fn void QFxItem::scaleChanged() + + This signal is emitted when the scale of the item changes. +*/ + +/*! + \fn void QFxItem::stateChanged(const QString &state) + + This signal is emitted when the \a state of the item changes. + + \sa states-transitions +*/ + +/*! + \fn void QFxItem::keyPress() + + This signal is emitted when a key is pressed. + + The key event is available in QML via the QFxKeyEvent \c event + property. + + \qml + <Item onKeyPress="if (event.key == Qt.Key_Enter) state='Enter'"/> + \endqml + + \sa keyRelease() +*/ + +/*! + \fn void QFxItem::keyRelease() + + This signal is emitted when a key is released. + + The key event is available in QML via the QFxKeyEvent \c event + property. + + \qml + <Item onKeyRelease="if (event.key == Qt.Key_Enter) state='Enter'"/> + \endqml + + \sa keyPress() +*/ + +/*! + \fn void QFxItem::visibleChanged() + + This signal is emitted when the visibility of the item changes. + + \sa setVisible() +*/ + +/*! + \fn void QFxItem::opacityChanged() + + This signal is emitted when the opacity of the item changes. + + \sa opacity(), setOpacity() +*/ + +/*! + \fn void QFxItem::parentChanged() + + This signal is emitted when the parent of the item changes. + + \sa setItemParent() +*/ + +/*! + \fn void QFxItem::focusChanged() + + This signal is emitted when the item's focus state changes. + + \sa setFocus() +*/ + +/*! + \fn QFxItem::QFxItem(QFxItem *parent) + + Constructs a QFxItem with the given \a parent. +*/ +QFxItem::QFxItem(QFxItem* parent) + : QSimpleCanvasItem(*(new QFxItemPrivate), parent) +{ + Q_D(QFxItem); + d->init(parent); +} + +/*! \internal +*/ +QFxItem::QFxItem(QFxItemPrivate &dd, QFxItem *parent) + : QSimpleCanvasItem(dd, parent) +{ + Q_D(QFxItem); + d->init(parent); +} + +/*! \internal +*/ +void QFxItem::doUpdate() +{ + update(); +} + +/*! + Destroys the QFxItem. +*/ +QFxItem::~QFxItem() +{ + Q_D(QFxItem); + delete d->_anchorLines; d->_anchorLines = 0; +} + +/*! + \qmlproperty enum Item::transformOrigin + This property holds the origin point around which scale and rotation transform. + + Nine transform origins are available, as shown in the image below. + + \image declarative-transformorigin.png + + This example scales an image about its center. + \code + <Image src="myimage.png" transformOrigin="Center" scale="4" /> + \endcode + + The default transform origin is \c TopLeft. +*/ +/*! + \qmlproperty Item Item::parent + This property holds the parent of the item. +*/ +void QFxItem::setItemParent(QFxItem *parent) +{ + setParent(parent); +} + +/*! + XXX Playing around with view2view transitions. + */ +void QFxItem::moveToParent(QFxItem *parent) +{ + if(parent && itemParent()) { + QPointF me = itemParent()->mapToScene(QPointF(0,0)); + QPointF them = parent->mapToScene(QPointF(0,0)); + + QPointF themx = parent->mapToScene(QPointF(1,0)); + QPointF themy = parent->mapToScene(QPointF(0,1)); + + themx -= them; + themy -= them; + + setItemParent(parent); + + // XXX - this is silly and will only work in a few cases + + /* + xDiff = rx * themx_x + ry * themy_x + yDiff = rx * themx_y + ry * themy_y + */ + + qreal rx = 0; + qreal ry = 0; + qreal xDiff = them.x() - me.x(); + qreal yDiff = them.y() - me.y(); + + + if(themx.x() == 0.) { + ry = xDiff / themy.x(); + rx = (yDiff - ry * themy.y()) / themx.y(); + } else if(themy.x() == 0.) { + rx = xDiff / themx.x(); + ry = (yDiff - rx * themx.y()) / themy.y(); + } else if(themx.y() == 0.) { + ry = yDiff / themy.y(); + rx = (xDiff - ry * themy.x()) / themx.x(); + } else if(themy.y() == 0.) { + rx = yDiff / themx.y(); + ry = (xDiff - rx * themx.x()) / themy.x(); + } else { + qreal div = (themy.x() * themx.y() - themy.y() * themx.x()); + + if(div != 0.) + rx = (themx.y() * xDiff - themx.x() * yDiff) / div; + + if(themy.y() != 0.) ry = (yDiff - rx * themx.y()) / themy.y(); + } + + setX(x() - rx); + setY(y() - ry); + } else { + setItemParent(parent); + } +} + +/*! + Returns the QFxItem parent of this item. +*/ +QFxItem *QFxItem::itemParent() const +{ + return qobject_cast<QFxItem *>(QObject::parent()); +} + +/*! + \qmlproperty list<Item> Item::children + \qmlproperty list<Object> Item::resources + + The children property contains the list of visual children of this element. + The resources property contains non-visual resources that you want to + reference by name. + + Generally you can rely on Item's default property to handle all this for + you, but it can come in handy in some cases. + + \qml + <Item> + <children> + <Text /> + <Rect /> + </children> + <resources> + <Component id="myComponent"> + <Text /> + </Component> + </resources> + </Item> + \endqml +*/ + +/*! + Returns true if all of the attributes set via QML have been set; + otherwise returns false. + + \sa classComplete() +*/ +bool QFxItem::isClassComplete() const +{ + Q_D(const QFxItem); + return d->_classComplete; +} + +/*! + Returns true if construction of the QML component is complete; otherwise + returns false. + + It is often desireable to delay some processing until the component is + completed. + + \sa componentComplete(). +*/ +bool QFxItem::isComponentComplete() const +{ + Q_D(const QFxItem); + return d->_componentComplete; +} + +/*! + \property QFxItem::anchors + \brief The anchors (alignments) used by the item. +*/ +QFxAnchors *QFxItem::anchors() +{ + Q_D(QFxItem); + return d->anchors(); +} + +void QFxItemPrivate::data_removeAt(int) +{ + // ### +} + +int QFxItemPrivate::data_count() const +{ + // ### + return 0; +} + +void QFxItemPrivate::data_append(QObject *o) +{ + Q_Q(QFxItem); + QFxItem *i = qobject_cast<QFxItem *>(o); + if(i) + q->children()->append(i); + else + resources_append(o); +} + +void QFxItemPrivate::data_insert(int, QObject *) +{ + // ### +} + +QObject *QFxItemPrivate::data_at(int) const +{ + // ### + return 0; +} + +void QFxItemPrivate::data_clear() +{ + // ### +} + +void QFxItemPrivate::resources_removeAt(int) +{ + // ### +} + +int QFxItemPrivate::resources_count() const +{ + // ### + return 0; +} + +void QFxItemPrivate::resources_append(QObject *o) +{ + Q_Q(QFxItem); + o->setParent(q); +} + +void QFxItemPrivate::resources_insert(int, QObject *) +{ + // ### +} + +QObject *QFxItemPrivate::resources_at(int) const +{ + // ### + return 0; +} + +void QFxItemPrivate::resources_clear() +{ + // ### +} + +void QFxItemPrivate::children_removeAt(int) +{ + // ### +} + +int QFxItemPrivate::children_count() const +{ + // ### + return 0; +} + +void QFxItemPrivate::children_append(QFxItem *i) +{ + Q_Q(QFxItem); + i->setParent(q); +} + +void QFxItemPrivate::children_insert(int, QFxItem *) +{ + // ### +} + +QFxItem *QFxItemPrivate::children_at(int) const +{ + // ### + return 0; +} + +void QFxItemPrivate::children_clear() +{ + // ### +} + +/*! + \qmlproperty list<Object> Item::data + \default + + The data property is allows you to freely mix visual children and resources + of an element. If you assign a visual element to the data list it becomes + a child and if you assign any other object type, it is added as a resource. + + So you can write: + \qml + <Item> + <Text /> + <Rect /> + <Script /> + </Item> + \endqml + + instead of: + \qml + <Item> + <children> + <Text /> + <Rect /> + </children> + <resources> + <Script/> + </resources> + </Item> + \endqml + + data is a behind-the-scenes property: you should never need to explicitly + specify it. + */ +QmlList<QObject *> *QFxItem::data() +{ + Q_D(QFxItem); + return &d->data; +} + +/*! + \property QFxItem::contents + \brief An object that knows about the size of an item's children. + + contents provides an easy way to access the (collective) width and + height of the item's children. +*/ +QFxContents *QFxItem::contents() +{ + Q_D(QFxItem); + if (!d->_contents) { + d->_contents = new QFxContents; + d->_contents->setParent(this); + d->_contents->setItem(this); + } + return d->_contents; +} + +QFxItem *QFxItem::qmlItem() const +{ + Q_D(const QFxItem); + return d->qmlItem; +} + +/*! + \qmlproperty string Item::qml + This property holds the dynamic QML for the item. + + This property is used for dynamically loading QML into the + item. Querying for the QML only has meaning if the QML has been + dynamically set; otherwise an empty string is returned. +*/ +QString QFxItem::qml() const +{ + Q_D(const QFxItem); + return d->_qml; +} + +void QFxItem::setQml(const QString &qml) +{ + Q_D(QFxItem); + if (d->_qml == qml) + return; + + if(!d->_qml.isEmpty()) { + QmlChildren::Iterator iter = d->_qmlChildren.find(d->_qml); + if(iter != d->_qmlChildren.end()) + (*iter)->setOpacity(0.); + } + + d->_qml = qml; + d->_qmlurl = itemContext()->resolvedUri(qml); + d->qmlItem = 0; + + if(d->_qml.isEmpty()) { + emit qmlChanged(); + return; + } + + QmlChildren::Iterator iter = d->_qmlChildren.find(d->_qml); + if(iter != d->_qmlChildren.end()) { + (*iter)->setOpacity(1.); + d->qmlItem = (*iter); + emit qmlChanged(); + } else { + d->_qmlcomp = + new QmlComponent(itemContext()->engine(), d->_qmlurl, this); + if(d->_qmlcomp->isReady()) + qmlLoaded(); + else + QObject::connect(d->_qmlcomp, SIGNAL(readyChanged()), + this, SLOT(qmlLoaded())); + } +} + + +void QFxItem::qmlLoaded() +{ + Q_D(QFxItem); + + { // newChild... + // ### + for (int i=0; i<d->_qmlnewloading.length(); ++i) { + QmlComponent *c = d->_qmlnewcomp.at(i); + if(!c->isReady()) + continue; + + QmlContext *ctxt = new QmlContext(itemContext()); + QObject* o = c ? c->create(ctxt):0; + QFxItem* ret = qobject_cast<QFxItem*>(o); + if (ret) { + ret->setItemParent(this); + QScriptValue v = itemContext()->engine()->scriptEngine()->newQObject(ret); + emit newChildCreated(d->_qmlnewloading.at(i).toString(),v); + } + + delete c; + d->_qmlnewloading.removeAt(i); + d->_qmlnewcomp.removeAt(i); + --i; + } + } + + // setQml... + if (d->_qmlcomp) { + QmlContext *ctxt = new QmlContext(itemContext()); + ctxt->addDefaultObject(this); + + QObject *obj = d->_qmlcomp->create(ctxt); + QFxItem *qmlChild = qobject_cast<QFxItem *>(obj); + if(qmlChild) { + qmlChild->setItemParent(this); + d->_qmlChildren.insert(d->_qml, qmlChild); + d->qmlItem = qmlChild; + } else { + delete qmlChild; + d->_qml = QString(); + } + delete d->_qmlcomp; + d->_qmlcomp = 0; + emit qmlChanged(); + } +} + +/*! + \qmlproperty Item Item::clipToItem + + Experimental clip to item support. Do not use. + + \todo complete clip to item support. + */ +QFxItem *QFxItem::clipToItem() const +{ + return 0; +} + +void QFxItem::setClipToItem(QFxItem *) +{ + qWarning() << "QFxItem: clipToItem not implemented"; +} + +/*! + \qmlproperty real Item::x + \qmlproperty real Item::y + \qmlproperty int Item::width + \qmlproperty int Item::height + + Defines the item's position and size relative to its parent. + + \qml + <Item x="100" y="100" width="100" height="100" /> + \endqml + */ + +/*! + \qmlproperty real Item::z + + Sets the stacking order of the item. By default the stacking order is 0. + + Items with a higher stacking value are drawn on top of items with a + lower stacking order. Items with the same stacking value are drawn + bottom up in the order they appear. Items with a negative stacking + value are drawn under their parent's content. + + The following example shows the various effects of stacking order. + + \table + \row + \o \image declarative-item_stacking1.png + \o Same \c z - later children above earlier children: + \qml + <Item> + <Rect color="red" width="100" height="100" /> + <Rect color="blue" x="50" y="50" width="100" height="100" /> + </Item> + \endqml + \row + \o \image declarative-item_stacking2.png + \o Higher \c z on top: + \qml + <Item> + <Rect z="1" color="red" width="100" height="100" /> + <Rect color="blue" x="50" y="50" width="100" height="100" /> + </Item> + \endqml + \row + \o \image declarative-item_stacking3.png + \o Same \c z - children above parents: + \qml + <Item> + <Rect color="red" width="100" height="100"> + <Rect color="blue" x="50" y="50" width="100" height="100" /> + </Rect> + </Item> + \endqml + \row + \o \image declarative-item_stacking4.png + \o Lower \c z below: + \qml + <Item> + <Rect color="red" width="100" height="100"> + <Rect z="-1" color="blue" x="50" y="50" width="100" height="100" /> + </Rect> + </Item> + \endqml + \endtable + */ +/*! + \property QFxItem::z + \brief The z coordinate of the item relative to its parent. + + A negative z coordinate means the item will be painted below its parent. +*/ + +void QFxItem::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QFxItem); + if(newGeometry.width() != oldGeometry.width()) { + int xoffset = oldGeometry.width() - newGeometry.width(); + d->handleWidthChange(xoffset); + } + + if(newGeometry.height() != oldGeometry.height()) { + int yoffset = oldGeometry.height() - newGeometry.height(); + d->handleHeightChange(yoffset); + } + + if(newGeometry.x() != oldGeometry.x()) { + emit leftChanged(); + emit hcenterChanged(); + emit rightChanged(); + } + + if(newGeometry.y() != oldGeometry.y()) { + emit topChanged(); + emit vcenterChanged(); + emit bottomChanged(); + } +} + +void QFxItemPrivate::handleWidthChange(int xoffset) +{ + Q_Q(QFxItem); + if(!_anchors) { + emit q->hcenterChanged(); + emit q->rightChanged(); + } else { + QFxAnchors::UsedAnchors used = anchors()->usedAnchors(); + if (used & QFxAnchors::HasHCenterAnchor) { + q->setX(q->x() + xoffset/2); + emit q->rightChanged(); + } else if ((used & QFxAnchors::HasRightAnchor) && !(used & QFxAnchors::HasLeftAnchor)) { + q->setX(q->x() + xoffset); + emit q->hcenterChanged(); + } else { + emit q->hcenterChanged(); + emit q->rightChanged(); + } + } + if(q->rotation() && q->transformOrigin() != QFxItem::TopLeft) + q->setRotation(q->rotation()); + if(q->scale() && q->transformOrigin() != QFxItem::TopLeft) + q->setScale(q->scale()); + emit q->widthChanged(); +} + +void QFxItemPrivate::handleHeightChange(int yoffset) +{ + Q_Q(QFxItem); + if(!_anchors) { + emit q->vcenterChanged(); + emit q->bottomChanged(); + emit q->baselineChanged(); + } else { + QFxAnchors::UsedAnchors used = anchors()->usedAnchors(); + if (used & QFxAnchors::HasBaselineAnchor) { + q->setY(q->y() + yoffset - q->baselineOffset()); + emit q->bottomChanged(); + emit q->vcenterChanged(); + } else if (used & QFxAnchors::HasVCenterAnchor) { + q->setY(q->y() + yoffset/2); + emit q->bottomChanged(); + } else if ((used & QFxAnchors::HasBottomAnchor) && !(used & QFxAnchors::HasTopAnchor)) { + q->setY(q->y() + yoffset); + emit q->vcenterChanged(); + } else { + emit q->vcenterChanged(); + emit q->bottomChanged(); + emit q->baselineChanged(); + } + } + if(q->rotation() && q->transformOrigin() != QFxItem::TopLeft) + q->setRotation(q->rotation()); + if(q->scale() && q->transformOrigin() != QFxItem::TopLeft) + q->setScale(q->scale()); + emit q->heightChanged(); +} + +/*! + \qmlproperty bool Item::flipVertically + \qmlproperty bool Item::flipHorizontally + + When set, the item will be displayed flipped horizontally or vertically + about its center. + */ +bool QFxItem::flipVertically() const +{ + return flip() & VerticalFlip; +} + +void QFxItem::setFlipVertically(bool v) +{ + if(v) + setFlip((QSimpleCanvasItem::Flip)(flip() | VerticalFlip)); + else + setFlip((QSimpleCanvasItem::Flip)(flip() & ~VerticalFlip)); +} + +bool QFxItem::flipHorizontally() const +{ + return flip() & HorizontalFlip; +} + +void QFxItem::setFlipHorizontally(bool v) +{ + if(v) + setFlip((QSimpleCanvasItem::Flip)(flip() | HorizontalFlip)); + else + setFlip((QSimpleCanvasItem::Flip)(flip() & ~HorizontalFlip)); +} + +class QFxKeyEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int key READ key); + Q_PROPERTY(QString text READ text); + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted); +public: + QFxKeyEvent(int key, const QString &text=QString()) : _accepted(false), _key(key), _text(text) {} + + bool isAccepted() { return _accepted; } + void setAccepted(bool accepted) { _accepted = accepted; } + + int key() const { return _key; } + + QString text() const { return _text; } + +private: + bool _accepted; + int _key; + QString _text; +}; + +/*! + \reimp +*/ +void QFxItem::keyPressEvent(QKeyEvent *event) +{ + QFxKeyEvent ke(event->key(), event->text()); + emit keyPress(&ke); + event->setAccepted(ke.isAccepted()); + if (itemParent() && !ke.isAccepted()) + itemParent()->keyPressEvent(event); +} + +/*! + \reimp +*/ +void QFxItem::keyReleaseEvent(QKeyEvent *event) +{ + QFxKeyEvent ke(event->key(), event->text()); + emit keyRelease(&ke); + event->setAccepted(ke.isAccepted()); + if (itemParent() && !ke.isAccepted()) + itemParent()->keyReleaseEvent(event); +} + +/*! + Returns the bounding rectangle of the item in scene coordinates. +*/ +QRectF QFxItem::sceneBoundingRect() const +{ + return QRectF(mapToScene(QPointF(0,0)), QSize(width(), height())); +} + +/*! + \qmlproperty string Item::id + This property holds the identifier for the item. + + The identifier can be used in bindings and other expressions to + refer to the item. For example: + + \qml + <Text id="myText" .../> + <Text text="{myText.text}"/> + \endqml + + The identifier is available throughout to the \l {components}{component} + where it is declared. Two items in the same component + with the same identifier is invalid. +*/ +QString QFxItem::id() const +{ + Q_D(const QFxItem); + return d->_id; +} + +void QFxItem::setId(const QString &id) +{ + Q_D(QFxItem); + setObjectName(id); + d->_id = id; +} + +QFxAnchorLine QFxItem::left() const +{ + Q_D(const QFxItem); + return d->anchorLines()->left; +} + +QFxAnchorLine QFxItem::right() const +{ + Q_D(const QFxItem); + return d->anchorLines()->right; +} + +QFxAnchorLine QFxItem::horizontalCenter() const +{ + Q_D(const QFxItem); + return d->anchorLines()->hCenter; +} + +QFxAnchorLine QFxItem::top() const +{ + Q_D(const QFxItem); + return d->anchorLines()->top; +} + +QFxAnchorLine QFxItem::bottom() const +{ + Q_D(const QFxItem); + return d->anchorLines()->bottom; +} + +QFxAnchorLine QFxItem::verticalCenter() const +{ + Q_D(const QFxItem); + return d->anchorLines()->vCenter; +} + +/*! + \qmlproperty AnchorLine Item::top + \qmlproperty AnchorLine Item::bottom + \qmlproperty AnchorLine Item::left + \qmlproperty AnchorLine Item::right + \qmlproperty AnchorLine Item::horizontalCenter + \qmlproperty AnchorLine Item::verticalCenter + + The anchor lines of the item. + + For more information see \l {anchor-layout}{Anchor Layouts}. +*/ + +/*! + \qmlproperty AnchorLine Item::anchors.top + \qmlproperty AnchorLine Item::anchors.bottom + \qmlproperty AnchorLine Item::anchors.left + \qmlproperty AnchorLine Item::anchors.right + \qmlproperty AnchorLine Item::anchors.horizontalCenter + \qmlproperty AnchorLine Item::anchors.verticalCenter + + \qmlproperty Item Item::anchors.fill + + \qmlproperty int Item::anchors.topMargin + \qmlproperty int Item::anchors.bottomMargin + \qmlproperty int Item::anchors.leftMargin + \qmlproperty int Item::anchors.rightMargin + \qmlproperty int Item::anchors.horizontalCenterOffset + \qmlproperty int Item::anchors.verticalCenterOffset + + Anchors provide a way to position an item by specifying its + relationship with other items. + + Margins apply to top, bottom, left, right, and fill anchors. + + Offsets apply for horizontal and vertical center anchors. + + \table + \row + \o \image declarative-anchors_example.png + \o Text anchored to Image, horizontally centered and vertically below, with a margin. + \qml + <Image id="pic" .../> + <Text id="label" anchors.horizontalCenter="{pic.horizontalCenter}" + anchors.top="{pic.bottom}" + anchors.topMargin="5" .../> + \endqml + \row + \o \image declarative-anchors_example2.png + \o + Left of Text anchored to right of Image, with a margin. The y + property of both defaults to 0. + + \qml + <Image id="pic" .../> + <Text id="label" anchors.left="{pic.right}" + anchors.leftMargin="5" .../> + \endqml + \endtable + + anchors.fill provides a convenient way for one item to have the + same geometry as another item, and is equivalent to connecting all + four directional anchors. + + \note You can only anchor an item to siblings or a parent. + + For more information see \l {anchor-layout}{Anchor Layouts}. +*/ + +/* + \property QFxItem::baseline + \brief The position of the item's baseline in global (scene) coordinates. + + The baseline of a Text item is the imaginary line on which the text + sits. Controls containing text usually set their baseline to the + baseline of their text. + + For non-text items, a default baseline offset of two-thirds of the + item's height is used to determine the baseline. +*/ + +int QFxItem::baselineOffset() const +{ + Q_D(const QFxItem); + if (!d->_baselineOffset.isValid()) { + return height()*2/3; //### default baseline is 2/3 of the way to the bottom of the item + } else + return d->_baselineOffset; +} + +void QFxItem::setBaselineOffset(int offset) +{ + Q_D(QFxItem); + if (offset == d->_baselineOffset) + return; + + d->_baselineOffset = offset; + emit baselineOffsetChanged(); + emit baselineChanged(); +} + +/*! + \qmlproperty real Item::rotation + This property holds the rotation of the item in degrees. + + This specifies how many degrees to rotate the item around its origin (0,0). + The default rotation is 0 degrees (i.e. not rotated at all). + + \table + \row + \o \image declarative-rotation.png + \o + \qml + <Rect color="blue" width="100" height="100"> + <Rect color="green" width="25" height="25"/> + <Rect color="red" width="50" height="50" x="25" y="25" rotation="30"/> + </Rect> + \endqml + \endtable +*/ +qreal QFxItem::rotation() const +{ + Q_D(const QFxItem); + return d->_rotation; +} + +void QFxItem::setRotation(qreal rotation) +{ + Q_D(QFxItem); + if (d->_rotation == rotation) + return; + d->_rotation = rotation; +#if defined(QFX_RENDER_OPENGL) + QMatrix4x4 trans; + QPointF to = transformOriginPoint(); + trans.translate(to.x(), to.y()); + trans.rotate(d->_rotation, 0, 0, 1); + trans.translate(-to.x(), -to.y()); +#else + QTransform trans; + QPointF to = transformOriginPoint(); + trans.translate(to.x(), to.y()); + trans.rotate(d->_rotation); + trans.translate(-to.x(), -to.y()); +#endif + setTransform(trans); + emit rotationChanged(); +} + +/*! + \qmlproperty real Item::scale + This property holds the scale of the item. + + A scale of less than 1 means the item will be displayed smaller than + normal, and a scale of greater than 1 means the item will be + displayed larger than normal. A negative scale means the item will + be mirrored. + + By default, items are displayed at a scale of 1 (i.e. at their + normal size). + + Scaling is from the item's origin (0,0). + + \table + \row + \o \image declarative-scale.png + \o + \qml + <Rect color="blue" width="100" height="100"> + <Rect color="green" width="25" height="25"/> + <Rect color="red" width="50" height="50" x="25" y="25" scale="1.4"/> + </Rect> + \endqml + \endtable +*/ +qreal QFxItem::scale() const +{ + return QSimpleCanvasItem::scale(); +} + +void QFxItem::setScale(qreal s) +{ + if (QSimpleCanvasItem::scale() == s) return; + QSimpleCanvasItem::setScale(s); + emit scaleChanged(); + update(); +} + +/*! + \qmlproperty real Item::opacity + + The opacity of the item. Opacity is specified as a number between 0 + (fully transparent) and 1 (fully opaque). The default is 1. + + Opacity is an \e inherited attribute. That is, the opacity is + also applied individually to child items. In almost all cases this + is what you want. If you can spot the issue in the following + example, you might need to use an opacity filter instead. + + \table + \row + \o \image declarative-item_opacity1.png + \o + \qml + <Item> + <Rect color="red" width="100" height="100"> + <Rect color="blue" x="50" y="50" width="100" height="100" /> + </Rect> + </Item> + \endqml + \row + \o \image declarative-item_opacity2.png + \o + \qml + <Item> + <Rect opacity="0.5" color="red" width="100" height="100"> + <Rect color="blue" x="50" y="50" width="100" height="100" /> + </Rect> + </Item> + \endqml + \endtable + + \todo There is no such thing as an opacity filter +*/ + +qreal QFxItem::opacity() const +{ + return QSimpleCanvasItem::visible(); +} + +void QFxItem::setOpacity(qreal v) +{ + if(v == QSimpleCanvasItem::visible()) + return; + + if(v < 0) v = 0; + else if(v > 1) v = 1; + QSimpleCanvasItem::setVisible(v); + + emit opacityChanged(); +} + +bool QFxItem::keepMouseGrab() const +{ + Q_D(const QFxItem); + return d->_keepMouse; +} + +void QFxItem::setKeepMouseGrab(bool keep) +{ + Q_D(QFxItem); + d->_keepMouse = keep; +} + +void QFxItem::activeFocusChanged(bool) +{ + emit activeFocusChanged(); +} + +void QFxItem::focusChanged(bool) +{ + emit focusChanged(); +} + +QmlList<QFxItem *> *QFxItem::children() +{ + Q_D(QFxItem); + return &(d->children); +} + +QmlList<QObject *> *QFxItem::resources() +{ + Q_D(QFxItem); + return &(d->resources); +} + +/*! + \qmlproperty list<State> Item::states + This property holds a list of states defined by the item. + + \qml + <Item> + <states> + <State .../> + <State .../> + ... + </states> + </Item> + \endqml + + \sa {states-transitions}{States and Transitions} +*/ +QmlList<QmlState *>* QFxItem::states() +{ + Q_D(QFxItem); + return d->states()->statesProperty(); +} + +/*! + \qmlproperty list<Transition> Item::transitions + This property holds a list of transitions defined by the item. + + \qml + <Item> + <transitions> + <Transition .../> + <Transition .../> + ... + </transitions> + </Item> + \endqml + + \sa {states-transitions}{States and Transitions} +*/ +QmlList<QmlTransition *>* QFxItem::transitions() +{ + Q_D(QFxItem); + return d->states()->transitionsProperty(); +} + +/*! + \qmlproperty list<Filter> Item::filter + This property holds a list of graphical filters to be applied to the item. + + \l {qmlfilter}{Filters} include things like \l {qmlblur}{blurring} + the item, or giving it a Reflection. Some + filters may not be available on all canvases; if a filter is not + available on a certain canvas, it will simply not be applied for + that canvas (but the XML will still be considered valid). + + \qml + <Item> + <filter> + <Blur .../> + <Relection .../> + ... + </filter> + </Item> + \endqml +*/ + +/*! + \qmlproperty bool Item::clip + This property holds whether clipping is enabled. + + if clipping is enabled, an item will clip its own painting, as well + as the painting of its children, to its bounding rectangle. + + Non-rectangular clipping regions are not supported for performance reasons. +*/ + +/*! + Returns the state with \a name. Returns 0 if no matching state is found. +*/ +QmlState *QFxItem::findState(const QString &name) const +{ + Q_D(const QFxItem); + if(!d->_stateGroup) + return 0; + else + return d->_stateGroup->findState(name); +} + +/*! + \qmlproperty string Item::state + + This property holds the name of the current state of the item. + + This property is often used in scripts to change between states. For + example: + + \qml + <Script> + function toggle() { + if (button.state == 'On') + button.state = 'Off'; + else + button.state = 'On'; + } + </Script> + \endqml + + If the item is in its base state (i.e. no explicit state has been + set), \c state will be a blank string. Likewise, you can return an + item to its base state by setting its current state to \c ''. + + \sa {states-transitions}{States and Transitions} +*/ +QString QFxItem::state() const +{ + Q_D(const QFxItem); + if(!d->_stateGroup) + return QString(); + else + return d->_stateGroup->state(); +} + +void QFxItem::setState(const QString &state) +{ + Q_D(QFxItem); + d->states()->setState(state); +} + +/*! + \qmlproperty list<Transform> Item::transform + This property holds the list of transformations to apply. + + For more information see \l Transform. +*/ +QList<QFxTransform *> *QFxItem::transform() +{ + Q_D(QFxItem); + return &(d->_transform); +} + +/*! + Returns true if the item is visible; otherwise returns false. + + An item is considered visible if its opacity is not 0. +*/ +bool QFxItem::isVisible() const +{ + Q_D(const QFxItem); + return d->visible; +} + +/*! + Sets the visibility of the item to \a visible. + + Setting visibility to false sets opacity to 0. Setting the + visibility to true restores the opacity to its previous value. +*/ +void QFxItem::setVisible(bool visible) +{ + Q_D(QFxItem); + if(visible == d->visible) + return; + + d->visible = visible; + if(visible) + setOpacity(d->visibleOp); + else { + d->visibleOp = opacity(); + setOpacity(0); + } + + emit visibleChanged(); +} + +/*! \internal +*/ +void QFxItem::dump(int depth) +{ + Q_D(QFxItem); + QByteArray ba(depth * 4, ' '); + qWarning() << ba.constData() << metaObject()->className() << "(" << (void *)static_cast<QFxItem*>(this) << ", " << (void *)static_cast<QSimpleCanvasItem*>(this) << "):" << x() << y() << width() << height() << (void *) itemParent(); +} + +/*! \internal +*/ +QString QFxItem::propertyInfo() const +{ + return QString(); +} + +/*! + Creates a new child of the given component \a type. The + newChildCreated() signal will be emitted when and if the child is + successfully created. + + \preliminary +*/ +void QFxItem::newChild(const QString &type) +{ + Q_D(QFxItem); + + QUrl url = itemContext()->resolvedUri(type); + if (url.isEmpty()) + return; + + d->_qmlnewloading.append(url); + d->_qmlnewcomp.append(new QmlComponent(itemContext()->engine(), url, this)); + + if(d->_qmlnewcomp.last()->isReady()) + qmlLoaded(); + else + connect(d->_qmlnewcomp.last(), SIGNAL(readyChanged()), + this, SLOT(qmlLoaded())); +} + +/*! + classBegin() is called when the item is constructed, but its + properties have not yet been set. + + \sa classComplete(), componentComplete(), isClassComplete(), isComponentComplete() +*/ +void QFxItem::classBegin() +{ + Q_D(QFxItem); + d->_classComplete = false; + d->_componentComplete = false; + if(d->_stateGroup) + d->_stateGroup->classBegin(); +} + +/*! + classComplete() is called when all properties specified in QML + have been assigned. It is sometimes desireable to delay some + processing until all property assignments are complete. +*/ +void QFxItem::classComplete() +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::ItemClassComplete> cc; +#endif + Q_D(QFxItem); + d->_classComplete = true; + if(d->_stateGroup) + d->_stateGroup->classComplete(); +} + +/*! + componentComplete() is called when all elements in the component + have been constructed. It is often desireable to delay some + processing until the component is complete an all bindings in the + component have been resolved. +*/ +void QFxItem::componentComplete() +{ + Q_D(QFxItem); + d->_componentComplete = true; + if(d->_stateGroup) + d->_stateGroup->componentComplete(); + if(d->_anchors) { + d->anchors()->connectHAnchors(); + d->anchors()->connectVAnchors(); + } + if(!d->_transform.isEmpty()) + updateTransform(); +} + +/*! \internal +*/ +void QFxItem::parentChanged(QSimpleCanvasItem *, QSimpleCanvasItem *) +{ + emit parentChanged(); +} + +/*! \internal +*/ +void QFxItem::reparentItems() +{ + qFatal("EEK"); +} + +void QFxItem::updateTransform() +{ + Q_D(QFxItem); + QSimpleCanvas::Matrix trans; + for(int ii = d->_transform.count() - 1; ii >= 0; --ii) { + QFxTransform *a = d->_transform.at(ii); + if(!a->isIdentity()) + trans = a->transform() * trans; + } + + setTransform(trans); + transformChanged(trans); +} + +void QFxItem::transformChanged(const QSimpleCanvas::Matrix &) +{ +} + +/*! + Returns the current QML context for this item. +*/ +QmlContext *QFxItem::itemContext() const +{ + Q_D(const QFxItem); + return d->_ctxt; +} + +QmlStateGroup *QFxItemPrivate::states() +{ + Q_Q(QFxItem); + if(!_stateGroup) { + _stateGroup = new QmlStateGroup(q); + if(!_classComplete) + _stateGroup->classBegin(); + QObject::connect(_stateGroup, SIGNAL(stateChanged(QString)), + q, SIGNAL(stateChanged(QString))); + } + + return _stateGroup; +} + +QFxItemPrivate::AnchorLines::AnchorLines(QFxItem *q) +{ + left.item = q; + left.anchorLine = QFxAnchorLine::Left; + right.item = q; + right.anchorLine = QFxAnchorLine::Right; + hCenter.item = q; + hCenter.anchorLine = QFxAnchorLine::HCenter; + top.item = q; + top.anchorLine = QFxAnchorLine::Top; + bottom.item = q; + bottom.anchorLine = QFxAnchorLine::Bottom; + vCenter.item = q; + vCenter.anchorLine = QFxAnchorLine::VCenter; +} + +#include "qfxitem.moc" +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxitem.h b/src/declarative/fx/qfxitem.h new file mode 100644 index 0000000..d3b9899 --- /dev/null +++ b/src/declarative/fx/qfxitem.h @@ -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 QtDeclarative 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 QFXITEM_H +#define QFXITEM_H + +#include <QObject> +#include <QtScript/qscriptvalue.h> +#include <QList> +#include <QtDeclarative/qfxanchors.h> +#include <QtDeclarative/qfxglobal.h> +#include <QtDeclarative/qml.h> +#include <QtDeclarative/qfxscalegrid.h> +#include <QtDeclarative/qsimplecanvasitem.h> +#include <QtDeclarative/qmlcomponent.h> +#include <QtDeclarative/qmlstate.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QFxContents : public QObject +{ + Q_OBJECT + Q_PROPERTY(int height READ height NOTIFY heightChanged); + Q_PROPERTY(int width READ width NOTIFY widthChanged); +public: + QFxContents(); + + int height() const; + + int width() const; + + void setItem(QFxItem *item); + +public Q_SLOTS: + void calcHeight(); + void calcWidth(); + +Q_SIGNALS: + void heightChanged(); + void widthChanged(); + +private: + QFxItem *_item; + int _height; + int _width; +}; +QML_DECLARE_TYPE(QFxContents); +Q_DECLARE_OPERATORS_FOR_FLAGS(QFxAnchors::UsedAnchors); + +class QmlContext; +class QmlState; +class QmlTransition; +class QFxTransform; +class QFxItemPrivate; +class Q_DECLARATIVE_EXPORT QFxItem : public QSimpleCanvasItem, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QFxItem * parent READ itemParent WRITE setItemParent NOTIFY parentChanged DESIGNABLE false) + Q_PROPERTY(QFxItem * moveToParent READ itemParent WRITE moveToParent NOTIFY parentChanged DESIGNABLE false) + Q_PROPERTY(QString id READ id WRITE setId) + Q_PROPERTY(QmlList<QFxItem *>* children READ children DESIGNABLE false) + Q_PROPERTY(QmlList<QObject *>* resources READ resources DESIGNABLE false) + Q_PROPERTY(QFxAnchors * anchors READ anchors DESIGNABLE false) + Q_PROPERTY(QmlList<QObject *> *data READ data DESIGNABLE false) + Q_PROPERTY(QFxContents * contents READ contents DESIGNABLE false) + Q_PROPERTY(QmlList<QmlState *>* states READ states DESIGNABLE false) + Q_PROPERTY(QmlList<QmlTransition *>* transitions READ transitions DESIGNABLE false) + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QString qml READ qml WRITE setQml NOTIFY qmlChanged) + Q_PROPERTY(QFxItem *qmlItem READ qmlItem NOTIFY qmlChanged) + Q_PROPERTY(QFxItem *clipToItem READ clipToItem WRITE setClipToItem) + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY leftChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY topChanged) + Q_PROPERTY(qreal z READ z WRITE setZ) + Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged) + Q_PROPERTY(bool flipVertically READ flipVertically WRITE setFlipVertically) + Q_PROPERTY(bool flipHorizontally READ flipHorizontally WRITE setFlipHorizontally) + Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged) + Q_PROPERTY(int baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged ) + Q_PROPERTY(QFxAnchorLine left READ left) + Q_PROPERTY(QFxAnchorLine right READ right) + Q_PROPERTY(QFxAnchorLine horizontalCenter READ horizontalCenter) + Q_PROPERTY(QFxAnchorLine top READ top) + Q_PROPERTY(QFxAnchorLine bottom READ bottom) + Q_PROPERTY(QFxAnchorLine verticalCenter READ verticalCenter) + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) + Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged) + Q_PROPERTY(QSimpleCanvasFilter *filter READ filter WRITE setFilter) + Q_PROPERTY(bool clip READ clip WRITE setClip) + Q_PROPERTY(bool focusable READ isFocusable WRITE setFocusable) + Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged) + Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged) + Q_PROPERTY(QList<QFxTransform *>* transform READ transform) + Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged) + Q_CLASSINFO("DefaultProperty", "data") + + typedef QHash<QString, QFxItem *> QmlChildren; + +public: + QFxItem(QFxItem *parent = 0); + virtual ~QFxItem(); + + QFxItem *itemParent() const; + void setItemParent(QFxItem *parent); + + void moveToParent(QFxItem *parent); + + QString id() const; + void setId(const QString &); + + QmlList<QObject *> *data(); + QmlList<QFxItem *> *children(); + QmlList<QObject *> *resources(); + + QFxAnchors *anchors(); + + QFxContents *contents(); + + QmlList<QmlState *>* states(); + QmlState *findState(const QString &name) const; + + QmlList<QmlTransition *>* transitions(); + + QString state() const; + void setState(const QString &); + + QFxItem *qmlItem() const; + QString qml() const; + void setQml(const QString &); + + QFxItem *clipToItem() const; + void setClipToItem(QFxItem *i); + + bool flipVertically() const; + void setFlipVertically(bool); + bool flipHorizontally() const; + void setFlipHorizontally(bool); + + int baselineOffset() const; + void setBaselineOffset(int); + + QFxAnchorLine left() const; + QFxAnchorLine right() const; + QFxAnchorLine horizontalCenter() const; + QFxAnchorLine top() const; + QFxAnchorLine bottom() const; + QFxAnchorLine verticalCenter() const; + + qreal rotation() const; + void setRotation(qreal); + + qreal scale() const; + void setScale(qreal); + + qreal opacity() const; + virtual void setOpacity(qreal); + + QList<QFxTransform *> *transform(); + + bool isVisible() const; + void setVisible(bool); + + virtual void dump(int depth = 0); + virtual QString propertyInfo() const; + + bool isClassComplete() const; + bool isComponentComplete() const; + + QRectF sceneBoundingRect() const; + + void updateTransform(); + + bool keepMouseGrab() const; + void setKeepMouseGrab(bool); + + QmlContext *itemContext() const; + +public Q_SLOTS: + void newChild(const QString &url); + +Q_SIGNALS: + void leftChanged(); + void rightChanged(); + void widthChanged(); + void heightChanged(); + void topChanged(); + void bottomChanged(); + void hcenterChanged(); + void vcenterChanged(); + void baselineChanged(); + void baselineOffsetChanged(); + void stateChanged(const QString &); + void focusChanged(); + void activeFocusChanged(); + void parentChanged(); + void keyPress(QObject *event); + void keyRelease(QObject *event); + void rotationChanged(); + void scaleChanged(); + void opacityChanged(); + void visibleChanged(); + void qmlChanged(); + void newChildCreated(const QString &url, QScriptValue); + +protected: + virtual void transformChanged(const QSimpleCanvas::Matrix &); + virtual void classBegin(); + virtual void classComplete(); + virtual void componentComplete(); + virtual void parentChanged(QSimpleCanvasItem *, QSimpleCanvasItem *); + virtual void reparentItems(); + virtual void focusChanged(bool); + virtual void activeFocusChanged(bool); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + +private Q_SLOTS: + void doUpdate(); + void qmlLoaded(); + +protected: + QFxItem(QFxItemPrivate &dd, QFxItem *parent = 0); + +private: + void init(QFxItem *parent); + friend class QmlStatePrivate; + Q_DISABLE_COPY(QFxItem) + Q_DECLARE_PRIVATE(QFxItem) +}; +QML_DECLARE_TYPE(QFxItem); + +QML_DECLARE_TYPE(QSimpleCanvasFilter); + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QFXITEM_H diff --git a/src/declarative/fx/qfxitem_p.h b/src/declarative/fx/qfxitem_p.h new file mode 100644 index 0000000..1266711 --- /dev/null +++ b/src/declarative/fx/qfxitem_p.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXITEM_P_H +#define QFXITEM_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 "qfxitem.h" +#include <private/qsimplecanvasitem_p.h> +#include <private/qmlnullablevalue_p.h> +#include <qml.h> +#include <qmlcontext.h> +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QNetworkReply; + +class QFxItemPrivate : public QSimpleCanvasItemPrivate +{ + Q_DECLARE_PUBLIC(QFxItem) + + typedef QHash<QString, QFxItem *> QmlChildren; + +public: + QFxItemPrivate() + : _anchors(0), _contents(0), qmlItem(0), _qmlcomp(0), + _baselineOffset(0), _rotation(0.), + _classComplete(true), _componentComplete(true), _keepMouse(false), + visible(true), _anchorLines(0), visibleOp(1), reparentedChildren(0), + _stateGroup(0) + {} + ~QFxItemPrivate() + { delete _anchors; } + + void init(QFxItem *parent) + { + Q_Q(QFxItem); + _ctxt = QmlContext::activeContext(); + + if(parent) + q->setItemParent(parent); + _baselineOffset.invalidate(); + q->setAcceptedMouseButtons(Qt::NoButton); + } + + QmlContext *_ctxt; + QString _id; + + // data property + void data_removeAt(int); + int data_count() const; + void data_append(QObject *); + void data_insert(int, QObject *); + QObject *data_at(int) const; + void data_clear(); + QML_DECLARE_LIST_PROXY(QFxItemPrivate, QObject *, data); + + // resources property + void resources_removeAt(int); + int resources_count() const; + void resources_append(QObject *); + void resources_insert(int, QObject *); + QObject *resources_at(int) const; + void resources_clear(); + QML_DECLARE_LIST_PROXY(QFxItemPrivate, QObject *, resources); + + // children property + void children_removeAt(int); + int children_count() const; + void children_append(QFxItem *); + void children_insert(int, QFxItem *); + QFxItem *children_at(int) const; + void children_clear(); + QML_DECLARE_LIST_PROXY(QFxItemPrivate, QFxItem *, children); + + QList<QFxTransform *> _transform; + QFxAnchors *anchors() { + if(!_anchors) { + Q_Q(QFxItem); + _anchors = new QFxAnchors; + _anchors->setItem(q); + } + return _anchors; + } + QFxAnchors *_anchors; + QFxContents *_contents; + QFxItem *qmlItem; + QUrl _qmlurl; + QmlComponent *_qmlcomp; + QString _qml; + QList<QUrl> _qmlnewloading; + QList<QmlComponent*> _qmlnewcomp; + + QmlNullableValue<int> _baselineOffset; + float _rotation; + + bool _classComplete:1; + bool _componentComplete:1; + bool _keepMouse:1; + bool visible:1; + + QmlChildren _qmlChildren; + + struct AnchorLines { + AnchorLines(QFxItem *); + QFxAnchorLine left; + QFxAnchorLine right; + QFxAnchorLine hCenter; + QFxAnchorLine top; + QFxAnchorLine bottom; + QFxAnchorLine vCenter; + }; + mutable AnchorLines *_anchorLines; + AnchorLines *anchorLines() const { + Q_Q(const QFxItem); + if(!_anchorLines) _anchorLines = + new AnchorLines(const_cast<QFxItem *>(q)); + return _anchorLines; + } + + float visibleOp; + + int reparentedChildren; + + QmlStateGroup *states(); + QmlStateGroup *_stateGroup; + + void handleWidthChange(int xoffset); + void handleHeightChange(int xoffset); +}; + +QT_END_NAMESPACE +#endif // QFXITEM_P_H diff --git a/src/declarative/fx/qfxkeyactions.cpp b/src/declarative/fx/qfxkeyactions.cpp new file mode 100644 index 0000000..c4ae3e3 --- /dev/null +++ b/src/declarative/fx/qfxkeyactions.cpp @@ -0,0 +1,930 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxkeyactions.h" +#include <qmlexpression.h> + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxKeyActions,KeyActions); + +class QFxKeyActionsPrivate +{ +public: + QFxKeyActionsPrivate(); + + bool enabled; + + uint keys1; + uint keys2; + + QHash<Qt::Key, QString> actions; + + bool key(Qt::Key) const; + QString action(Qt::Key) const; + void setKey(Qt::Key, bool = true); + + bool testBit(int) const; + void setBit(int, bool); + + int keyToBit(Qt::Key) const; + + QString keyExpr(Qt::Key) const; + void setKeyExpr(Qt::Key, const QString &); +}; + +QFxKeyActionsPrivate::QFxKeyActionsPrivate() +: enabled(true), keys1(0), keys2(0) +{ +} + +int QFxKeyActionsPrivate::keyToBit(Qt::Key k) const +{ + if(k >= Qt::Key_A && k <= Qt::Key_Z ) { + return k - Qt::Key_A; + } else if(k >= Qt::Key_Left && k <= Qt::Key_Down) { + return 26 + k - Qt::Key_Left; + } else if(k >= Qt::Key_0 && k <= Qt::Key_9) { + return 30 + k - Qt::Key_0; + } else if(k >= Qt::Key_Context1 && k <= Qt::Key_Flip) { + return 40 + k - Qt::Key_Context1; + } else if(k >= Qt::Key_Select && k <= Qt::Key_No) { + return 47 + k - Qt::Key_Select; + } else { + const int start = 50; + switch(k) { + case Qt::Key_Escape: + return start + 0; + case Qt::Key_Return: + return start + 1; + case Qt::Key_Enter: + return start + 2; + case Qt::Key_Delete: + return start + 3; + case Qt::Key_Space: + return start + 4; + case Qt::Key_Back: + return start + 5; + case Qt::Key_unknown: + return start + 6; + case Qt::Key_Asterisk: + return start + 7; + default: + return -1; + } + } +} + +bool QFxKeyActionsPrivate::key(Qt::Key k) const +{ + int b = keyToBit(k); + bool rv = testBit(b); + if(!rv && k != Qt::Key_Shift) + rv = testBit(keyToBit(Qt::Key_unknown)); + return rv; +} + +QString QFxKeyActionsPrivate::action(Qt::Key k) const +{ + int b = keyToBit(k); + if(b != -1 && testBit(b)) + return actions.value(k); + else + return actions.value(Qt::Key_unknown); +} + +void QFxKeyActionsPrivate::setKey(Qt::Key k, bool v) +{ + int b = keyToBit(k); + if(b == -1) + return; + + setBit(b, v); +} + +bool QFxKeyActionsPrivate::testBit(int b) const +{ + if(b < 0) + return false; + + if(b < 32) + return keys1 & (1 << b); + else + return keys2 & (1 << (b - 32)); +} + +void QFxKeyActionsPrivate::setBit(int b, bool v) +{ + if(v) { + if(b < 32) + keys1 |= (1 << b); + else + keys2 |= (1 << (b - 32)); + } else { + if(b < 32) + keys1 &= ~(1 << b); + else + keys2 &= ~(1 << (b - 32)); + } +} + + +/*! + \qmlclass KeyActions + \brief The KeyActions element enables simple key handling. + \inherits Item + + KeyActions is typically used in basic key handling scenarios where writing + JavaScript key handling routines would be unnecessarily complicated. The + KeyActions element has a collection of properties that correspond to a + selection of common keys. When a given key is pressed, the element executes + the action script assigned to the matching property. If no action has + been set the KeyActions element does nothing. + + To receive (and susequently respond to) key presses, the KeyActions class + must be in the current focus chain, just like any other element. + + For basic mouse handling, see \l MouseRegion. + + KeyActions is an invisible element: it is never painted. +*/ +QFxKeyActions::QFxKeyActions(QFxItem *parent) +: QFxItem(parent), d(new QFxKeyActionsPrivate) +{ +} + +QFxKeyActions::~QFxKeyActions() +{ + delete d; +} + +QString QFxKeyActionsPrivate::keyExpr(Qt::Key k) const +{ + if(key(k)) + return actions.value(k); + else + return QString(); +} + +void QFxKeyActionsPrivate::setKeyExpr(Qt::Key k, const QString &expr) +{ + if(expr.isEmpty()) { + if(key(k)) { + actions.remove(k); + setKey(k, false); + } + } else { + actions.insert(k, expr); + setKey(k); + } +} + +/*! + \qmlproperty bool KeyActions::enabled + + Enables or disables KeyActions' key handling. When not enabled, the + KeyActions instance does not respond to any key presses. The element is + enabled by default. +*/ +bool QFxKeyActions::enabled() const +{ + return d->enabled; +} + +void QFxKeyActions::setEnabled(bool e) +{ + if(d->enabled == e) + return; + + d->enabled = e; + emit enabledChanged(); +} + +/*! + \qmlproperty string KeyActions::keyA + \qmlproperty string KeyActions::keyB + \qmlproperty string KeyActions::keyC + \qmlproperty ... KeyActions::... + \qmlproperty string KeyActions::keyY + \qmlproperty string KeyActions::keyZ + + The action to take for the given letter. + + The following example sets actions for the 'c' and 'x' keys. + \code + <KeyActions keyC="print('c is for cookie')" keyX="print('I like cookies')" /> + \endcode +*/ +QString QFxKeyActions::key_A() const +{ + return d->keyExpr(Qt::Key_A); +} + +void QFxKeyActions::setKey_A(const QString &s) +{ + d->setKeyExpr(Qt::Key_A, s); +} + +QString QFxKeyActions::key_B() const +{ + return d->keyExpr(Qt::Key_B); +} + +void QFxKeyActions::setKey_B(const QString &s) +{ + d->setKeyExpr(Qt::Key_B, s); +} + +QString QFxKeyActions::key_C() const +{ + return d->keyExpr(Qt::Key_C); +} + +void QFxKeyActions::setKey_C(const QString &s) +{ + d->setKeyExpr(Qt::Key_C, s); +} + +QString QFxKeyActions::key_D() const +{ + return d->keyExpr(Qt::Key_D); +} + +void QFxKeyActions::setKey_D(const QString &s) +{ + d->setKeyExpr(Qt::Key_D, s); +} + +QString QFxKeyActions::key_E() const +{ + return d->keyExpr(Qt::Key_E); +} + +void QFxKeyActions::setKey_E(const QString &s) +{ + d->setKeyExpr(Qt::Key_E, s); +} + +QString QFxKeyActions::key_F() const +{ + return d->keyExpr(Qt::Key_F); +} + +void QFxKeyActions::setKey_F(const QString &s) +{ + d->setKeyExpr(Qt::Key_F, s); +} + +QString QFxKeyActions::key_G() const +{ + return d->keyExpr(Qt::Key_G); +} + +void QFxKeyActions::setKey_G(const QString &s) +{ + d->setKeyExpr(Qt::Key_G, s); +} + +QString QFxKeyActions::key_H() const +{ + return d->keyExpr(Qt::Key_H); +} + +void QFxKeyActions::setKey_H(const QString &s) +{ + d->setKeyExpr(Qt::Key_H, s); +} + +QString QFxKeyActions::key_I() const +{ + return d->keyExpr(Qt::Key_I); +} + +void QFxKeyActions::setKey_I(const QString &s) +{ + d->setKeyExpr(Qt::Key_I, s); +} + +QString QFxKeyActions::key_J() const +{ + return d->keyExpr(Qt::Key_J); +} + +void QFxKeyActions::setKey_J(const QString &s) +{ + d->setKeyExpr(Qt::Key_J, s); +} + +QString QFxKeyActions::key_K() const +{ + return d->keyExpr(Qt::Key_K); +} + +void QFxKeyActions::setKey_K(const QString &s) +{ + d->setKeyExpr(Qt::Key_K, s); +} + +QString QFxKeyActions::key_L() const +{ + return d->keyExpr(Qt::Key_L); +} + +void QFxKeyActions::setKey_L(const QString &s) +{ + d->setKeyExpr(Qt::Key_L, s); +} + +QString QFxKeyActions::key_M() const +{ + return d->keyExpr(Qt::Key_M); +} + +void QFxKeyActions::setKey_M(const QString &s) +{ + d->setKeyExpr(Qt::Key_M, s); +} + +QString QFxKeyActions::key_N() const +{ + return d->keyExpr(Qt::Key_N); +} + +void QFxKeyActions::setKey_N(const QString &s) +{ + d->setKeyExpr(Qt::Key_N, s); +} + +QString QFxKeyActions::key_O() const +{ + return d->keyExpr(Qt::Key_O); +} + +void QFxKeyActions::setKey_O(const QString &s) +{ + d->setKeyExpr(Qt::Key_O, s); +} + +QString QFxKeyActions::key_P() const +{ + return d->keyExpr(Qt::Key_P); +} + +void QFxKeyActions::setKey_P(const QString &s) +{ + d->setKeyExpr(Qt::Key_P, s); +} + +QString QFxKeyActions::key_Q() const +{ + return d->keyExpr(Qt::Key_Q); +} + +void QFxKeyActions::setKey_Q(const QString &s) +{ + d->setKeyExpr(Qt::Key_Q, s); +} + +QString QFxKeyActions::key_R() const +{ + return d->keyExpr(Qt::Key_R); +} + +void QFxKeyActions::setKey_R(const QString &s) +{ + d->setKeyExpr(Qt::Key_R, s); +} + +QString QFxKeyActions::key_S() const +{ + return d->keyExpr(Qt::Key_S); +} + +void QFxKeyActions::setKey_S(const QString &s) +{ + d->setKeyExpr(Qt::Key_S, s); +} + +QString QFxKeyActions::key_T() const +{ + return d->keyExpr(Qt::Key_T); +} + +void QFxKeyActions::setKey_T(const QString &s) +{ + d->setKeyExpr(Qt::Key_T, s); +} + +QString QFxKeyActions::key_U() const +{ + return d->keyExpr(Qt::Key_U); +} + +void QFxKeyActions::setKey_U(const QString &s) +{ + d->setKeyExpr(Qt::Key_U, s); +} + +QString QFxKeyActions::key_V() const +{ + return d->keyExpr(Qt::Key_V); +} + +void QFxKeyActions::setKey_V(const QString &s) +{ + d->setKeyExpr(Qt::Key_V, s); +} + +QString QFxKeyActions::key_W() const +{ + return d->keyExpr(Qt::Key_W); +} + +void QFxKeyActions::setKey_W(const QString &s) +{ + d->setKeyExpr(Qt::Key_W, s); +} + +QString QFxKeyActions::key_X() const +{ + return d->keyExpr(Qt::Key_X); +} + +void QFxKeyActions::setKey_X(const QString &s) +{ + d->setKeyExpr(Qt::Key_X, s); +} + +QString QFxKeyActions::key_Y() const +{ + return d->keyExpr(Qt::Key_Y); +} + +void QFxKeyActions::setKey_Y(const QString &s) +{ + d->setKeyExpr(Qt::Key_Y, s); +} + +QString QFxKeyActions::key_Z() const +{ + return d->keyExpr(Qt::Key_Z); +} + +void QFxKeyActions::setKey_Z(const QString &s) +{ + d->setKeyExpr(Qt::Key_Z, s); +} + + +/*! + \qmlproperty string KeyActions::leftArrow + \qmlproperty string KeyActions::rightArrow + \qmlproperty string KeyActions::upArrow + \qmlproperty string KeyActions::downArrow + + The action to take for the given arrow key. + + The following example sets actions for the left and right arrow keys. + \code + <KeyActions leftArrow="print('You pressed left')" rightArrow="print('You pressed right')" /> + \endcode +*/ + +QString QFxKeyActions::key_Left() const +{ + return d->keyExpr(Qt::Key_Left); +} + +void QFxKeyActions::setKey_Left(const QString &s) +{ + d->setKeyExpr(Qt::Key_Left, s); +} + +QString QFxKeyActions::key_Right() const +{ + return d->keyExpr(Qt::Key_Right); +} + +void QFxKeyActions::setKey_Right(const QString &s) +{ + d->setKeyExpr(Qt::Key_Right, s); +} + +QString QFxKeyActions::key_Up() const +{ + return d->keyExpr(Qt::Key_Up); +} + +void QFxKeyActions::setKey_Up(const QString &s) +{ + d->setKeyExpr(Qt::Key_Up, s); +} + +QString QFxKeyActions::key_Down() const +{ + return d->keyExpr(Qt::Key_Down); +} + +void QFxKeyActions::setKey_Down(const QString &s) +{ + d->setKeyExpr(Qt::Key_Down, s); +} + +/*! + \qmlproperty string KeyActions::digit0 + \qmlproperty string KeyActions::digit1 + \qmlproperty string KeyActions::digit2 + \qmlproperty ... KeyActions::... + \qmlproperty string KeyActions::digit9 + + The action to take for the given number key. + + The following example sets actions for the '5' and '6' keys. + \code + <KeyActions digit5="print('5 is a prime number')" digit6="print('6 is a composite number')" /> + \endcode +*/ + +QString QFxKeyActions::key_0() const +{ + return d->keyExpr(Qt::Key_0); +} + +void QFxKeyActions::setKey_0(const QString &s) +{ + d->setKeyExpr(Qt::Key_0, s); +} + +QString QFxKeyActions::key_1() const +{ + return d->keyExpr(Qt::Key_1); +} + +void QFxKeyActions::setKey_1(const QString &s) +{ + d->setKeyExpr(Qt::Key_1, s); +} + +QString QFxKeyActions::key_2() const +{ + return d->keyExpr(Qt::Key_2); +} + +void QFxKeyActions::setKey_2(const QString &s) +{ + d->setKeyExpr(Qt::Key_2, s); +} + +QString QFxKeyActions::key_3() const +{ + return d->keyExpr(Qt::Key_3); +} + +void QFxKeyActions::setKey_3(const QString &s) +{ + d->setKeyExpr(Qt::Key_3, s); +} + +QString QFxKeyActions::key_4() const +{ + return d->keyExpr(Qt::Key_4); +} + +void QFxKeyActions::setKey_4(const QString &s) +{ + d->setKeyExpr(Qt::Key_4, s); +} + +QString QFxKeyActions::key_5() const +{ + return d->keyExpr(Qt::Key_5); +} + +void QFxKeyActions::setKey_5(const QString &s) +{ + d->setKeyExpr(Qt::Key_5, s); +} + +QString QFxKeyActions::key_6() const +{ + return d->keyExpr(Qt::Key_6); +} + +void QFxKeyActions::setKey_6(const QString &s) +{ + d->setKeyExpr(Qt::Key_6, s); +} + +QString QFxKeyActions::key_7() const +{ + return d->keyExpr(Qt::Key_7); +} + +void QFxKeyActions::setKey_7(const QString &s) +{ + d->setKeyExpr(Qt::Key_7, s); +} + +QString QFxKeyActions::key_8() const +{ + return d->keyExpr(Qt::Key_8); +} + +void QFxKeyActions::setKey_8(const QString &s) +{ + d->setKeyExpr(Qt::Key_8, s); +} + +QString QFxKeyActions::key_9() const +{ + return d->keyExpr(Qt::Key_9); +} + +void QFxKeyActions::setKey_9(const QString &s) +{ + d->setKeyExpr(Qt::Key_9, s); +} + +QString QFxKeyActions::key_Asterisk() const +{ + return d->keyExpr(Qt::Key_Asterisk); +} + +void QFxKeyActions::setKey_Asterisk(const QString &s) +{ + d->setKeyExpr(Qt::Key_Asterisk, s); +} + +QString QFxKeyActions::key_Escape() const +{ + return d->keyExpr(Qt::Key_Escape); +} + +void QFxKeyActions::setKey_Escape(const QString &s) +{ + d->setKeyExpr(Qt::Key_Escape, s); +} + +QString QFxKeyActions::key_Return() const +{ + return d->keyExpr(Qt::Key_Return); +} + +void QFxKeyActions::setKey_Return(const QString &s) +{ + d->setKeyExpr(Qt::Key_Return, s); +} + +QString QFxKeyActions::key_Enter() const +{ + return d->keyExpr(Qt::Key_Enter); +} + +void QFxKeyActions::setKey_Enter(const QString &s) +{ + d->setKeyExpr(Qt::Key_Enter, s); +} + +QString QFxKeyActions::key_Delete() const +{ + return d->keyExpr(Qt::Key_Delete); +} + +void QFxKeyActions::setKey_Delete(const QString &s) +{ + d->setKeyExpr(Qt::Key_Delete, s); +} + +QString QFxKeyActions::key_Space() const +{ + return d->keyExpr(Qt::Key_Space); +} + +void QFxKeyActions::setKey_Space(const QString &s) +{ + d->setKeyExpr(Qt::Key_Space, s); +} + +/*! + \qmlproperty string KeyActions::escape + \qmlproperty string KeyActions::return + \qmlproperty string KeyActions::enter + \qmlproperty string KeyActions::delete + \qmlproperty string KeyActions::space + + The action to take for the given utility key. + + The following example sets an action for the space key. + \code + <KeyActions space="print('Space pressed')" /> + \endcode +*/ + +/*! + \qmlproperty string KeyActions::back + \qmlproperty string KeyActions::select + \qmlproperty string KeyActions::yes + \qmlproperty string KeyActions::no + \qmlproperty string KeyActions::context1 + \qmlproperty string KeyActions::context2 + \qmlproperty string KeyActions::context3 + \qmlproperty string KeyActions::context4 + \qmlproperty string KeyActions::call + \qmlproperty string KeyActions::hangup + \qmlproperty string KeyActions::flip + + The action to take for the given device key. + + The following example sets an action for the hangup key. + \code + <KeyActions hangup="print('Go away now')" /> + \endcode +*/ + +QString QFxKeyActions::key_Back() const +{ + return d->keyExpr(Qt::Key_Back); +} + +void QFxKeyActions::setKey_Back(const QString &s) +{ + d->setKeyExpr(Qt::Key_Back, s); +} + +QString QFxKeyActions::key_Select() const +{ + return d->keyExpr(Qt::Key_Select); +} + +void QFxKeyActions::setKey_Select(const QString &s) +{ + d->setKeyExpr(Qt::Key_Select, s); +} + +QString QFxKeyActions::key_Yes() const +{ + return d->keyExpr(Qt::Key_Yes); +} + +void QFxKeyActions::setKey_Yes(const QString &s) +{ + d->setKeyExpr(Qt::Key_Yes, s); +} + +QString QFxKeyActions::key_No() const +{ + return d->keyExpr(Qt::Key_No); +} + +void QFxKeyActions::setKey_No(const QString &s) +{ + d->setKeyExpr(Qt::Key_No, s); +} + +QString QFxKeyActions::key_Context1() const +{ + return d->keyExpr(Qt::Key_Context1); +} + +void QFxKeyActions::setKey_Context1(const QString &s) +{ + d->setKeyExpr(Qt::Key_Context1, s); +} + +QString QFxKeyActions::key_Context2() const +{ + return d->keyExpr(Qt::Key_Context2); +} + +void QFxKeyActions::setKey_Context2(const QString &s) +{ + d->setKeyExpr(Qt::Key_Context2, s); +} + +QString QFxKeyActions::key_Context3() const +{ + return d->keyExpr(Qt::Key_Context3); +} + +void QFxKeyActions::setKey_Context3(const QString &s) +{ + d->setKeyExpr(Qt::Key_Context3, s); +} + +QString QFxKeyActions::key_Context4() const +{ + return d->keyExpr(Qt::Key_Context4); +} + +void QFxKeyActions::setKey_Context4(const QString &s) +{ + d->setKeyExpr(Qt::Key_Context4, s); +} + +QString QFxKeyActions::key_Call() const +{ + return d->keyExpr(Qt::Key_Call); +} + +void QFxKeyActions::setKey_Call(const QString &s) +{ + d->setKeyExpr(Qt::Key_Call, s); +} + +QString QFxKeyActions::key_Hangup() const +{ + return d->keyExpr(Qt::Key_Hangup); +} + +void QFxKeyActions::setKey_Hangup(const QString &s) +{ + d->setKeyExpr(Qt::Key_Hangup, s); +} + +QString QFxKeyActions::key_Flip() const +{ + return d->keyExpr(Qt::Key_Flip); +} + +void QFxKeyActions::setKey_Flip(const QString &s) +{ + d->setKeyExpr(Qt::Key_Flip, s); +} + +/*! + \qmlproperty string KeyActions::any + + The action to take for any key not otherwise handled. +*/ +QString QFxKeyActions::key_Any() const +{ + return d->keyExpr(Qt::Key_unknown); +} + +void QFxKeyActions::setKey_Any(const QString &s) +{ + d->setKeyExpr(Qt::Key_unknown, s); +} + +void QFxKeyActions::keyPressEvent(QKeyEvent *event) +{ + Qt::Key key = (Qt::Key)event->key(); + if(d->enabled && d->key(key)) { + QmlExpression b(itemContext(), d->action(key), + this, false); + b.value(); + event->accept(); + } else { + QFxItem::keyPressEvent(event); + } +} + +void QFxKeyActions::keyReleaseEvent(QKeyEvent *event) +{ + Qt::Key key = (Qt::Key)event->key(); + if(d->enabled && d->key(key)) { + event->accept(); + } else { + QFxItem::keyReleaseEvent(event); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxkeyactions.h b/src/declarative/fx/qfxkeyactions.h new file mode 100644 index 0000000..7ad323a --- /dev/null +++ b/src/declarative/fx/qfxkeyactions.h @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXKEYACTIONS_H +#define QFXKEYACTIONS_H + +#include <qfxglobal.h> +#include <QObject> +#include <qml.h> +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxKeyActionsPrivate; +class Q_DECLARATIVE_EXPORT QFxKeyActions : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QString keyA READ key_A WRITE setKey_A) + Q_PROPERTY(QString keyB READ key_B WRITE setKey_B) + Q_PROPERTY(QString keyC READ key_C WRITE setKey_C) + Q_PROPERTY(QString keyD READ key_D WRITE setKey_D) + Q_PROPERTY(QString keyE READ key_E WRITE setKey_E) + Q_PROPERTY(QString keyF READ key_F WRITE setKey_F) + Q_PROPERTY(QString keyG READ key_G WRITE setKey_G) + Q_PROPERTY(QString keyH READ key_H WRITE setKey_H) + Q_PROPERTY(QString keyI READ key_I WRITE setKey_I) + Q_PROPERTY(QString keyJ READ key_J WRITE setKey_J) + Q_PROPERTY(QString keyK READ key_K WRITE setKey_K) + Q_PROPERTY(QString keyL READ key_L WRITE setKey_L) + Q_PROPERTY(QString keyM READ key_M WRITE setKey_M) + Q_PROPERTY(QString keyN READ key_N WRITE setKey_N) + Q_PROPERTY(QString keyO READ key_O WRITE setKey_O) + Q_PROPERTY(QString keyP READ key_P WRITE setKey_P) + Q_PROPERTY(QString keyQ READ key_Q WRITE setKey_Q) + Q_PROPERTY(QString keyR READ key_R WRITE setKey_R) + Q_PROPERTY(QString keyS READ key_S WRITE setKey_S) + Q_PROPERTY(QString keyT READ key_T WRITE setKey_T) + Q_PROPERTY(QString keyU READ key_U WRITE setKey_U) + Q_PROPERTY(QString keyV READ key_V WRITE setKey_V) + Q_PROPERTY(QString keyW READ key_W WRITE setKey_W) + Q_PROPERTY(QString keyX READ key_X WRITE setKey_X) + Q_PROPERTY(QString keyY READ key_Y WRITE setKey_Y) + Q_PROPERTY(QString keyZ READ key_Z WRITE setKey_Z) + Q_PROPERTY(QString leftArrow READ key_Left WRITE setKey_Left) + Q_PROPERTY(QString rightArrow READ key_Right WRITE setKey_Right) + Q_PROPERTY(QString upArrow READ key_Up WRITE setKey_Up) + Q_PROPERTY(QString downArrow READ key_Down WRITE setKey_Down) + Q_PROPERTY(QString digit0 READ key_0 WRITE setKey_0) + Q_PROPERTY(QString digit1 READ key_1 WRITE setKey_1) + Q_PROPERTY(QString digit2 READ key_2 WRITE setKey_2) + Q_PROPERTY(QString digit3 READ key_3 WRITE setKey_3) + Q_PROPERTY(QString digit4 READ key_4 WRITE setKey_4) + Q_PROPERTY(QString digit5 READ key_5 WRITE setKey_5) + Q_PROPERTY(QString digit6 READ key_6 WRITE setKey_6) + Q_PROPERTY(QString digit7 READ key_7 WRITE setKey_7) + Q_PROPERTY(QString digit8 READ key_8 WRITE setKey_8) + Q_PROPERTY(QString digit9 READ key_9 WRITE setKey_9) + Q_PROPERTY(QString asterisk READ key_Asterisk WRITE setKey_Asterisk) + Q_PROPERTY(QString escape READ key_Escape WRITE setKey_Escape) + Q_PROPERTY(QString return READ key_Return WRITE setKey_Return) + Q_PROPERTY(QString enter READ key_Enter WRITE setKey_Enter) + Q_PROPERTY(QString delete READ key_Delete WRITE setKey_Delete) + Q_PROPERTY(QString space READ key_Space WRITE setKey_Space) + Q_PROPERTY(QString back READ key_Back WRITE setKey_Back) + Q_PROPERTY(QString select READ key_Select WRITE setKey_Select) + Q_PROPERTY(QString yes READ key_Yes WRITE setKey_Yes) + Q_PROPERTY(QString no READ key_No WRITE setKey_No) + Q_PROPERTY(QString context1 READ key_Context1 WRITE setKey_Context1) + Q_PROPERTY(QString context2 READ key_Context2 WRITE setKey_Context2) + Q_PROPERTY(QString context3 READ key_Context3 WRITE setKey_Context3) + Q_PROPERTY(QString context4 READ key_Context4 WRITE setKey_Context4) + Q_PROPERTY(QString call READ key_Call WRITE setKey_Call) + Q_PROPERTY(QString hangup READ key_Hangup WRITE setKey_Hangup) + Q_PROPERTY(QString flip READ key_Flip WRITE setKey_Flip) + Q_PROPERTY(QString any READ key_Any WRITE setKey_Any) + +public: + QFxKeyActions(QFxItem *parent=0); + virtual ~QFxKeyActions(); + + bool enabled() const; + void setEnabled(bool); + + QString key_A() const; + void setKey_A(const QString &); + + QString key_B() const; + void setKey_B(const QString &); + + QString key_C() const; + void setKey_C(const QString &); + + QString key_D() const; + void setKey_D(const QString &); + + QString key_E() const; + void setKey_E(const QString &); + + QString key_F() const; + void setKey_F(const QString &); + + QString key_G() const; + void setKey_G(const QString &); + + QString key_H() const; + void setKey_H(const QString &); + + QString key_I() const; + void setKey_I(const QString &); + + QString key_J() const; + void setKey_J(const QString &); + + QString key_K() const; + void setKey_K(const QString &); + + QString key_L() const; + void setKey_L(const QString &); + + QString key_M() const; + void setKey_M(const QString &); + + QString key_N() const; + void setKey_N(const QString &); + + QString key_O() const; + void setKey_O(const QString &); + + QString key_P() const; + void setKey_P(const QString &); + + QString key_Q() const; + void setKey_Q(const QString &); + + QString key_R() const; + void setKey_R(const QString &); + + QString key_S() const; + void setKey_S(const QString &); + + QString key_T() const; + void setKey_T(const QString &); + + QString key_U() const; + void setKey_U(const QString &); + + QString key_V() const; + void setKey_V(const QString &); + + QString key_W() const; + void setKey_W(const QString &); + + QString key_X() const; + void setKey_X(const QString &); + + QString key_Y() const; + void setKey_Y(const QString &); + + QString key_Z() const; + void setKey_Z(const QString &); + + QString key_Left() const; + void setKey_Left(const QString &); + + QString key_Right() const; + void setKey_Right(const QString &); + + QString key_Up() const; + void setKey_Up(const QString &); + + QString key_Down() const; + void setKey_Down(const QString &); + + QString key_0() const; + void setKey_0(const QString &); + + QString key_1() const; + void setKey_1(const QString &); + + QString key_2() const; + void setKey_2(const QString &); + + QString key_3() const; + void setKey_3(const QString &); + + QString key_4() const; + void setKey_4(const QString &); + + QString key_5() const; + void setKey_5(const QString &); + + QString key_6() const; + void setKey_6(const QString &); + + QString key_7() const; + void setKey_7(const QString &); + + QString key_8() const; + void setKey_8(const QString &); + + QString key_9() const; + void setKey_9(const QString &); + + QString key_Asterisk() const; + void setKey_Asterisk(const QString &); + + QString key_Escape() const; + void setKey_Escape(const QString &); + + QString key_Return() const; + void setKey_Return(const QString &); + + QString key_Enter() const; + void setKey_Enter(const QString &); + + QString key_Delete() const; + void setKey_Delete(const QString &); + + QString key_Space() const; + void setKey_Space(const QString &); + + QString key_Back() const; + void setKey_Back(const QString &); + + QString key_Select() const; + void setKey_Select(const QString &); + + QString key_Yes() const; + void setKey_Yes(const QString &); + + QString key_No() const; + void setKey_No(const QString &); + + QString key_Context1() const; + void setKey_Context1(const QString &); + + QString key_Context2() const; + void setKey_Context2(const QString &); + + QString key_Context3() const; + void setKey_Context3(const QString &); + + QString key_Context4() const; + void setKey_Context4(const QString &); + + QString key_Call() const; + void setKey_Call(const QString &); + + QString key_Hangup() const; + void setKey_Hangup(const QString &); + + QString key_Flip() const; + void setKey_Flip(const QString &); + + QString key_Any() const; + void setKey_Any(const QString &); + + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + +Q_SIGNALS: + void enabledChanged(); + +private: + Q_DISABLE_COPY(QFxKeyActions) + QFxKeyActionsPrivate *d; +}; + +QML_DECLARE_TYPE(QFxKeyActions); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXKEYACTIONS_H diff --git a/src/declarative/fx/qfxkeyproxy.cpp b/src/declarative/fx/qfxkeyproxy.cpp new file mode 100644 index 0000000..8598ad6 --- /dev/null +++ b/src/declarative/fx/qfxkeyproxy.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxkeyproxy.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxKeyProxy,KeyProxy); + +/*! + \qmlclass KeyProxy + \brief The KeyProxy element proxies key presses to a number of other elements. + \inherits Item + +*/ + +/*! + \internal + \class QFxKeyProxy + \brief The QFxKeyProxy class proxies key presses to a number of other elements. + \ingroup utility +*/ + +class QFxKeyProxyPrivate +{ +public: + QList<QFxItem *> targets; +}; + +QFxKeyProxy::QFxKeyProxy(QFxItem *parent) +: QFxItem(parent), d(new QFxKeyProxyPrivate) +{ +} + +QFxKeyProxy::~QFxKeyProxy() +{ + delete d; d = 0; +} + +/*! + \qmlproperty list<Item> KeyProxy::targets + + The proxy targets. +*/ + +/*! + \property QFxKeyProxy::targets + \brief the proxy targets. +*/ + +QList<QFxItem *> *QFxKeyProxy::targets() const +{ + return &d->targets; +} + +void QFxKeyProxy::keyPressEvent(QKeyEvent *e) +{ + for(int ii = 0; ii < d->targets.count(); ++ii) { + QSimpleCanvasItem *i = d->targets.at(ii); + if(i) + i->keyPressEvent(e); + if(e->isAccepted()) + return; + } +} + +void QFxKeyProxy::keyReleaseEvent(QKeyEvent *e) +{ + for(int ii = 0; ii < d->targets.count(); ++ii) { + QSimpleCanvasItem *i = d->targets.at(ii); + if(i) + i->keyReleaseEvent(e); + if(e->isAccepted()) + return; + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxkeyproxy.h b/src/declarative/fx/qfxkeyproxy.h new file mode 100644 index 0000000..ae5fce4 --- /dev/null +++ b/src/declarative/fx/qfxkeyproxy.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 QtDeclarative 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 QFXKEYPROXY_H +#define QFXKEYPROXY_H + +#include <qfxitem.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxKeyProxyPrivate; +class Q_DECLARATIVE_EXPORT QFxKeyProxy : public QFxItem +{ + Q_OBJECT + Q_PROPERTY(QList<QFxItem *> *targets READ targets) +public: + QFxKeyProxy(QFxItem *parent=0); + virtual ~QFxKeyProxy(); + + QList<QFxItem *> *targets() const; + +protected: + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + +private: + Q_DISABLE_COPY(QFxKeyProxy) + QFxKeyProxyPrivate *d; +}; + +QML_DECLARE_TYPE(QFxKeyProxy); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXKEYPROXY_H diff --git a/src/declarative/fx/qfxlayouts.cpp b/src/declarative/fx/qfxlayouts.cpp new file mode 100644 index 0000000..455b8a5 --- /dev/null +++ b/src/declarative/fx/qfxlayouts.cpp @@ -0,0 +1,1027 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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> +#include <QCoreApplication> +#include "qml.h" +#include "qmlstate.h" +#include "qmlstategroup.h" +#include "qmlstateoperations.h" +#include "qfxperf.h" +#include "qfxlayouts.h" +#include "qfxlayouts_p.h" + + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QFxBaseLayout + \brief The QFxBaseLayout class provides a base for QFx layouts. + + To create a QFx Layout, simple subclass QFxBaseLayout and implement + doLayout(), which is automatically called when the layout might need + updating. + + It is strongly recommended that in your implementation of doLayout() + that you use the move, remove and add transitions when those conditions + arise. + + \ingroup layouts +*/ +QFxBaseLayout::QFxBaseLayout(AutoUpdateType at, QFxItem *parent) + : QFxItem(*(new QFxBaseLayoutPrivate), parent) +{ + Q_D(QFxBaseLayout); + d->init(at); +} + +QFxBaseLayout::QFxBaseLayout(QFxBaseLayoutPrivate &dd, AutoUpdateType at, QFxItem *parent) + : QFxItem(dd, parent) +{ + Q_D(QFxBaseLayout); + d->init(at); +} + +/*! + \property QFxBaseLayout::spacing + \brief the amount of spacing between items (in px) + + Note that the subclass is repsonsible for ensuring this. +*/ +int QFxBaseLayout::spacing() const +{ + Q_D(const QFxBaseLayout); + return d->_spacing; +} + +void QFxBaseLayout::setSpacing(int s) +{ + Q_D(QFxBaseLayout); + if(s==d->_spacing) + return; + d->_spacing = s; + preLayout(); +} + +/*! + \property QFxBaseLayout::margin + \brief the size of the margin around all items (in px) + +*/ +int QFxBaseLayout::margin() const +{ + Q_D(const QFxBaseLayout); + return d->_margin; +} + +void QFxBaseLayout::setMargin(int s) +{ + Q_D(QFxBaseLayout); + if(s==d->_margin) + return; + d->_margin = s; + preLayout(); +} + + +/*! + \property QFxBaseLayout::move + \brief the transition when moving an item. + + \code + <BaseLayout id="layout" y="0"> + <move> + <Transition> + <NumericAnimation properties="y" ease="easeOutBounce" /> + </Transition> + </move> + </BaseLayout> + \endcode +*/ +QmlTransition *QFxBaseLayout::move() const +{ + Q_D(const QFxBaseLayout); + return d->moveTransition; +} + +void QFxBaseLayout::setMove(QmlTransition *mt) +{ + Q_D(QFxBaseLayout); + d->moveTransition = mt; +} + +/*! + \property QFxBaseLayout::add + \brief the transition when adding an item. + + \code + <BaseLayout id="layout" y="0"> + <add> + <Transition > + <NumericAnimation target="{layout.item}" properties="opacity" from="0" to="1" duration="500" /> + </Transition> + </add> + </BaseLayout> + \endcode +*/ +QmlTransition *QFxBaseLayout::add() const +{ + Q_D(const QFxBaseLayout); + return d->addTransition; +} + +void QFxBaseLayout::setAdd(QmlTransition *add) +{ + Q_D(QFxBaseLayout); + d->addTransition = add; +} + + +/*! + \property QFxBaseLayout::remove + \brief the transition when removing an item. + + Note that the item may be 'removed' because its opacity is zero. This can make certain + transitions difficult to see. + \code + <BaseLayout id="layout" y="0"> + <remove> + <Transition > + <NumericAnimation target="{layout.item}" properties="opacity" from="1" to="0" duration="500" /> + </Transition> + </remove> + </BaseLayout> + \endcode +*/ +QmlTransition *QFxBaseLayout::remove() const +{ + Q_D(const QFxBaseLayout); + return d->removeTransition; +} + +void QFxBaseLayout::setRemove(QmlTransition *remove) +{ + Q_D(QFxBaseLayout); + d->removeTransition = remove; +} + +/*! + \property QFxBaseLayout::item + + The item that is currently being laid out. Used to target transitions that apply + only to the item being laid out, such as in the add transition. +*/ + +QFxItem *QFxBaseLayout::layoutItem() const +{ + Q_D(const QFxBaseLayout); + return d->_layoutItem; +} + +/*! + \internal +*/ +void QFxBaseLayout::setLayoutItem(QFxItem *li) +{ + Q_D(QFxBaseLayout); + if(li == d->_layoutItem) + return; + d->_layoutItem = li; + emit layoutItemChanged(); +} + +void QFxBaseLayout::componentComplete() +{ + QFxItem::componentComplete(); +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BaseLayoutComponentComplete> cc; +#endif + preLayout(); +} + +void QFxBaseLayout::childrenChanged() +{ + preLayout(); +} + +bool QFxBaseLayout::event(QEvent *e) +{ + Q_D(QFxBaseLayout); + if(e->type() == QEvent::User) { + d->_ep = false; + d->_stableItems += d->_newItems; + d->_leavingItems.clear(); + d->_newItems.clear(); + return true; + } + return QFxItem::event(e); +} + +/*! + Items that have just been added to the layout. This includes invisible items + that have turned visible. +*/ +QSet<QFxItem *>* QFxBaseLayout::newItems() +{ + Q_D(QFxBaseLayout); + return &d->_newItems; +} + +/*! + Items that are visible in the layout, not including ones that have just been added. +*/ +QSet<QFxItem *>* QFxBaseLayout::items() +{ + Q_D(QFxBaseLayout); + return &d->_stableItems; +} + +/*! + Items that have just left the layout. This includes visible items + that have turned invisible. +*/ +QSet<QFxItem *>* QFxBaseLayout::leavingItems() +{ + Q_D(QFxBaseLayout); + return &d->_leavingItems; +} + +void QFxBaseLayout::preLayout() +{ + Q_D(QFxBaseLayout); + if(!isComponentComplete() || d->_movingItem) + return; + + if(!d->_ep) { + d->_ep = true; + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + } + if(d->stateGroup) { + delete d->stateGroup; d->stateGroup = 0; + } + QSet<QFxItem *> allItems; + for(int ii = 0; ii < this->QSimpleCanvasItem::children().count(); ++ii) { + QFxItem *child = qobject_cast<QFxItem *>(this->QSimpleCanvasItem::children().at(ii)); + if(!child) + continue; + if(!d->_items.contains(child)){ + QObject::connect(child, SIGNAL(visibleChanged()), + this, SLOT(preLayout())); + QObject::connect(child, SIGNAL(heightChanged()), + this, SLOT(preLayout())); + QObject::connect(child, SIGNAL(widthChanged()), + this, SLOT(preLayout())); + d->_items += child; + } + if(!child->isVisible()){ + if(d->_stableItems.contains(child)){ + d->_leavingItems += child; + d->_stableItems -= child; + } + }else if(!d->_stableItems.contains(child)){ + d->_newItems+=child; + } + allItems += child; + } + QSet<QFxItem *> deletedItems = d->_items - allItems; + foreach(QFxItem *child, d->_items){ + if(!allItems.contains(child)){ + if(!deletedItems.contains(child)) { + QObject::disconnect(child, SIGNAL(visibleChanged()), + this, SLOT(preLayout())); + QObject::disconnect(child, SIGNAL(heightChanged()), + this, SLOT(preLayout())); + QObject::disconnect(child, SIGNAL(widthChanged()), + this, SLOT(preLayout())); + } + d->_items -= child; + } + } + d->_animated.clear(); + doLayout(); + //Set the layout's size to be the rect containing all children + //Also set the margin + qreal width=0; + qreal height=0; + foreach(QFxItem *item, d->_items){ + if(item->isVisible()){ + if(!d->_animated.contains(item)){ + setMovingItem(item); + item->setPos(QPointF(item->x()+d->_margin, item->y()+d->_margin)); + setMovingItem(0); + } + width = qMax(width, item->x() + item->width()); + height = qMax(height, item->y() + item->height()); + } + } + width += d->_margin; + height+= d->_margin; + + if(d->aut & Horizontal) + setWidth(int(width)); + else + setImplicitWidth(itemParent()->width()); + if(d->aut & Vertical) + setHeight(int(height)); + else + setImplicitHeight(itemParent()->height()); + setLayoutItem(0); +} + +//###This should be considered to move more centrally, as it seems useful +void QFxBaseLayout::applyTransition(const QList<QPair<QString, QVariant> >& changes, + QFxItem* target, QmlTransition* trans) +{ + Q_D(QFxBaseLayout); + if(!trans||!target)//TODO: if !trans, just apply changes + return; + setLayoutItem(target); + if(d->stateGroup) + delete d->stateGroup; + d->stateGroup = new QmlStateGroup(this); + + QmlState *state = new QmlState; + *(d->stateGroup->statesProperty()) << state; + for(int ii=0; ii<changes.size(); ++ii){ + QmlSetProperty *sp = new QmlSetProperty(state); + sp->setObject(target); + QVariant val = changes[ii].second; + if(d->_margin && + (changes[ii].first == QLatin1String("x") || changes[ii].first == QLatin1String("y"))){ + val = QVariant(val.toInt() + d->_margin); + } + sp->setValue(val); + sp->setProperty(changes[ii].first); + *state << sp; + } + state->apply(d->stateGroup, trans, 0); + d->_animated << target; +} + +void QFxBaseLayout::setMovingItem(QFxItem *i) +{ + Q_D(QFxBaseLayout); + d->_movingItem = i; +} + +/*! + Applies the layout's add transition to the \a target item.\a changes is a list of property,value + pairs which will be changed on the target using the add transition. +*/ +void QFxBaseLayout::applyAdd(const QList<QPair<QString, QVariant> >& changes, QFxItem* target) +{ + applyTransition(changes,target, add()); +} + +/*! + Applies the layout's move transition to the \a target.\a changes is a list of property,value pairs + which will be changed on the target using the move transition. +*/ +void QFxBaseLayout::applyMove(const QList<QPair<QString, QVariant> >& changes, QFxItem* target) +{ + applyTransition(changes,target, move()); +} + +/*! + Applies the layout's remove transition to the \a target item.\a changes is a list of + property,value pairs which will be changed on the target using the remove transition. +*/ +void QFxBaseLayout::applyRemove(const QList<QPair<QString, QVariant> >& changes, QFxItem* target) +{ + applyTransition(changes,target, remove()); +} + +QML_DEFINE_TYPE(QFxVerticalLayout, VerticalLayout); +/*! + \qmlclass VerticalLayout + \brief The VerticalLayout element arranges its children in a vertical layout. + \inherits Item + + The VerticalLayout element arranges its child elements so that they are vertically + aligned and not overlapping. Spacing between items can be added, as can a margin around all the items. + + The below example lays out differently shaped rectangles using a VerticalLayout. + \table + \row + \o \image verticalLayout_example.png + \o + \code + <VerticalLayout spacing="2"> + <Rect color="red" width="50" height="50"/> + <Rect color="green" width="20" height="50"/> + <Rect color="blue" width="50" height="20"/> + </VerticalLayout> + \endcode + \endtable + + VerticalLayout also provides for transitions to be set when items are added, moved, + or removed in the layout. Adding and removing apply both to elements which are deleted + or have their position in the document changed so as to no longer be children of the layout, + as well as to elements which have their opacity set to or from zero so as to appear or disappear. + + \table + \row + \o \image verticalLayout_transition.gif + \o + \code + <VerticalLayout spacing="2"> + ... + <remove>...</remove> + <add>...</add> + <move>...</move> + </VerticalLayout> + \endcode + \endtable + + +*/ +/*! + \qmlproperty Transition VerticalLayout::remove + This property holds the transition to apply when removing an item from the layout. + + Removed can mean that either the object has been deleted or reparented, and thus is now longer a child of the layout, or that the object has had its opacity set to zero, and thus is no longer visible. + + Note that if the item counts as removed because its opacity is zero it will not be visible during the transition unless you set the opacity in the transition, like in the below example. + + \table + \row + \o \image layout-remove.gif + \o + \code + <VerticalLayout id="layout"> + <remove> + <Transition > + <NumericAnimation target="{layout.item}" + properties="opacity" from="1" to="0" + duration="500" /> + </Transition> + </remove> + </VerticalLayout> + \endcode + \endtable + +*/ +/*! + \qmlproperty Transition VerticalLayout::add + This property holds the transition to be applied when adding an item to the layout. + + Added can mean that either the object has been created or reparented, and thus is now a child or the layout, or that the object has had its opacity increased from zero, and thus is now visible. + + \table + \row + \o \image layout-add.gif + \o + \code + <VerticalLayout id="layout"> + <add> + <Transition > + <NumericAnimation target="{layout.item}" + properties="opacity" from="0" to="1" + duration="500" /> + </Transition> + </add> + </VerticalLayout> + \endcode + \endtable + +*/ +/*! + \qmlproperty Transition VerticalLayout::move + This property holds the transition to apply when moving an item within the layout. + + This can happen when other items are added or removed from the layout, or when items resize themselves. + + \table + \row + \o \image layout-move.gif + \o + \code + <VerticalLayout id="layout"> + <move> + <Transition> + <NumericAnimation properties="y" ease="easeOutBounce" /> + </Transition> + </move> + </VerticalLayout> + \endcode + \endtable +*/ +/*! + \qmlproperty Item VerticalLayout::item + + The item that is currently being laid out. Used to target transitions that apply + only to the item being laid out, such as in the add transition. + +*/ +/*! + \qmlproperty int VerticalLayout::spacing + \qmlproperty int VerticalLayout::margin + + spacing and margin allow you to control the empty space surrounding + items in layouts. + + spacing is the amount in pixels left empty between each adjacent + item. margin is the amount in pixels which will be left empty + around the inside edge of the layout. Both default to 0. + + The below example places a GridLayout containing a red, a blue and a + green rectangle on a gray background. The area the grid layout + occupies is colored white. The top layout has a spacing of 2 and a + margin of 5, the bottom layout has the defaults of no margin or + spacing. + + \image spacing_a.png + \image spacing_b.png + +*/ +/*! + \internal + \class QFxVerticalLayout + \brief The QFxVerticalLayout class lines up items vertically. + \ingroup layouts +*/ +QFxVerticalLayout::QFxVerticalLayout(QFxItem *parent) +: QFxBaseLayout(Vertical, parent) +{ +} + +void QFxVerticalLayout::doLayout() +{ + int voffset = 0; + + foreach(QFxItem* item, *leavingItems()){ + if(remove()){ + QList<QPair<QString,QVariant> > changes; + applyRemove(changes, item); + } + } + for(int ii = 0; ii < this->QSimpleCanvasItem::children().count(); ++ii) { + QFxItem *child = qobject_cast<QFxItem *>(this->QSimpleCanvasItem::children().at(ii)); + if(!child || !child->isVisible()) + continue; + + bool needMove = (child->y() != voffset || child->x()); + + QList<QPair<QString, QVariant> > changes; + changes << qMakePair(QString(QLatin1String("y")),QVariant(voffset)); + changes << qMakePair(QString(QLatin1String("x")),QVariant(0)); + if(needMove && items()->contains(child) && move()) { + applyMove(changes,child); + } else if(!items()->contains(child) && add()) { + applyAdd(changes,child); + } else if(needMove) { + setMovingItem(child); + child->setY(voffset); + setMovingItem(0); + child->setX(0); + } + voffset += child->height(); + voffset += spacing(); + } + setMovingItem(this); + setHeight(voffset); + setMovingItem(0); +} + +QML_DEFINE_TYPE(QFxHorizontalLayout,HorizontalLayout); +/*! + \qmlclass HorizontalLayout + \brief The HorizontalLayout element arranges its children in a horizontal layout. + \inherits Item + + The HorizontalLayout element arranges its child elements so that they are horizontally aligned and not overlapping. Spacing can be added between the items, and a margin around all items can also be added. It also provides for transitions to be set when items are added, moved, or removed in the layout. Adding and removing apply both to elements which are deleted or have their position in the document changed so as to no longer be children of the layout, as well as to elements which have their opacity set to or from zero so as to appear or disappear. + + The below example lays out differently shaped rectangles using a HorizontalLayout. + \code + <HorizontalLayout spacing="2"> + <Rect color="red" width="50" height="50"/> + <Rect color="green" width="20" height="50"/> + <Rect color="blue" width="50" height="20"/> + </HorizontalLayout> + \endcode + \image horizontalLayout_example.png + +*/ +/*! + \qmlproperty Transition HorizontalLayout::remove + This property holds the transition to apply when removing an item from the layout. + + Removed can mean that either the object has been deleted or reparented, and thus is now longer a child of the layout, or that the object has had its opacity set to zero, and thus is no longer visible. + + Note that if the item counts as removed because its opacity is zero it will not be visible during the transition unless you set the opacity in the transition, like in the below example. + + \code + <HorizontalLayout id="layout"> + <remove> + <Transition > + <NumericAnimation target="{layout.item}" properties="opacity" from="1" to="0" duration="500" /> + </Transition> + </remove> + </HorizontalLayout> + \endcode + +*/ +/*! + \qmlproperty Transition HorizontalLayout::add + This property holds the transition to apply when adding an item to the layout. + + Added can mean that either the object has been created or reparented, and thus is now a child or the layout, or that the object has had its opacity increased from zero, and thus is now visible. + + \code + <HorizontalLayout id="layout"> + <add> + <Transition > + <NumericAnimation target="{layout.item}" properties="opacity" from="0" to="1" duration="500" /> + </Transition> + </add> + </HorizontalLayout> + \endcode + +*/ +/*! + \qmlproperty Transition HorizontalLayout::move + This property holds the transition to apply when moving an item within the layout. + + This can happen when other items are added or removed from the layout, or when items resize themselves. + + \code + <HorizontalLayout id="layout"> + <move> + <Transition> + <NumericAnimation properties="x" ease="easeOutBounce" /> + </Transition> + </move> + </HorizontalLayout> + \endcode + +*/ +/*! + \qmlproperty Item HorizontalLayout::item + + The item that is currently being laid out. Used to target transitions that apply + only to the item being laid out, such as in the add transition. + +*/ +/*! + \qmlproperty int HorizontalLayout::spacing + + The spacing, in pixels, left empty between each adjacent item. +*/ +/*! + \qmlproperty int HorizontalLayout::margin + + The margin size, in pixels, which will be left empty around the inside edge of the layout. +*/ +/*! + \qmlproperty int HorizontalLayout::spacing + \qmlproperty int HorizontalLayout::margin + + spacing and margin allow you to control the empty space surrounding items in layouts. + + spacing is the amount in pixels left empty between each adjacent item. + margin is the amount in pixels which will be left empty around the inside edge of the layout. + Both default to 0. + + The below example places a GridLayout containing a red, a blue and a green rectangle on a gray background. The area the grid layout occupies is colored white. The top layout has a spacing of 2 and a margin of 5, the bottom layout has the defaults of no margin or spacing. + + \image spacing_a.png + \image spacing_b.png + +*/ +/*! + \internal + \class QFxHorizontalLayout + \brief The QFxHorizontalLayout class lines up items horizontally. + \ingroup layouts +*/ +QFxHorizontalLayout::QFxHorizontalLayout(QFxItem *parent) +: QFxBaseLayout(Horizontal, parent) +{ +} + +void QFxHorizontalLayout::doLayout() +{ + int hoffset = 0; + + foreach(QFxItem* item, *leavingItems()){ + if(remove()){ + QList<QPair<QString,QVariant> > changes; + applyRemove(changes, item); + } + } + for(int ii = 0; ii < this->QSimpleCanvasItem::children().count(); ++ii) { + QFxItem *child = qobject_cast<QFxItem *>(this->QSimpleCanvasItem::children().at(ii)); + if(!child || !child->isVisible()) + continue; + + bool needMove = (child->x() != hoffset || child->y()); + + QList<QPair<QString, QVariant> > changes; + changes << qMakePair(QString(QLatin1String("x")),QVariant(hoffset)); + changes << qMakePair(QString(QLatin1String("y")),QVariant(0)); + if(needMove && items()->contains(child) && move()) { + applyMove(changes,child); + } else if(!items()->contains(child) && add()) { + applyAdd(changes,child); + } else if(needMove) { + setMovingItem(child); + child->setX(hoffset); + setMovingItem(0); + child->setY(0); + } + hoffset += child->width(); + hoffset += spacing(); + } + setWidth(hoffset); +} + +QML_DEFINE_TYPE(QFxGridLayout,GridLayout); + +/*! + \qmlclass GridLayout QFxGridLayout + \brief The GridLayout element arranges its children in a grid layout. + \inherits Item + + The GridLayout element arranges its child elements so that they are + aligned in a grid and are not overlapping. Spacing can be added + between the items, and a margin around all the items can also be + defined. It also provides for transitions to be set when items are + added, moved, or removed in the layout. Adding and removing apply + both to elements which are deleted or have their position in the + document changed so as to no longer be children of the layout, as + well as to elements which have their opacity set to or from zero so + as to appear or disappear. + + The GridLayout defaults to using four columns, and as many rows as + are necessary to fit all the child elements. The number of rows + and/or the number of columns can be constrained by setting the rows + or columns properties. The grid layout calculates a grid with + rectangular cells of sufficient size to hold all items, and then + places the items in the cells, going across then down, and + positioning each item at the (0,0) corner of the cell. The below + example demonstrates this. + + \table + \row + \o \image gridLayout_example.png + \o + \code + <GridLayout columns="3" spacing="2"> + <Rect color="red" width="50" height="50"/> + <Rect color="green" width="20" height="50"/> + <Rect color="blue" width="50" height="20"/> + <Rect color="cyan" width="50" height="50"/> + <Rect color="magenta" width="10" height="10"/> + </GridLayout> + \endcode + \endtable +*/ +/*! + \qmlproperty Transition GridLayout::remove + This property holds the transition to apply when removing an item from the layout. + + Removed can mean that either the object has been deleted or + reparented, and thus is now longer a child of the layout, or that + the object has had its opacity set to zero, and thus is no longer + visible. + + Note that if the item counts as removed because its opacity is + zero it will not be visible during the transition unless you set + the opacity in the transition, like in the below example. + + \code + <GridLayout id="layout"> + <remove> + <Transition > + <NumericAnimation target="{layout.item}" properties="opacity" from="1" to="0" duration="500" /> + </Transition> + </remove> + </GridLayout> + \endcode + +*/ +/*! + \qmlproperty Transition GridLayout::add + This property holds the transition to apply when adding an item to the layout. + + Added can mean that either the object has been created or + reparented, and thus is now a child or the layout, or that the + object has had its opacity increased from zero, and thus is now + visible. + + \code + <GridLayout id="layout"> + <add> + <Transition > + <NumericAnimation target="{layout.item}" properties="opacity" from="0" to="1" duration="500" /> + </Transition> + </add> + </GridLayout> + \endcode + +*/ +/*! + \qmlproperty Transition GridLayout::move + This property holds the transition to apply when moving an item within the layout. + + This can happen when other items are added or removed from the layout, or + when items resize themselves. + + \code + <GridLayout id="layout"> + <move> + <Transition> + <NumericAnimation properties="x,y" ease="easeOutBounce" /> + </Transition> + </move> + </GridLayout> + \endcode + +*/ +/*! + \qmlproperty Item GridLayout::item + + The item that is currently being laid out. Used to target + transitions that apply only to the item being laid out, such as in + the add transition. + +*/ +/*! + \qmlproperty int GridLayout::spacing + \qmlproperty int GridLayout::margin + + spacing and margin allow you to control the empty space surrounding + items in layouts. + + spacing is the amount in pixels left empty between each adjacent + item. margin is the amount in pixels which will be left empty + around the inside edge of the layout. Both default to 0. + + The below example places a GridLayout containing a red, a blue and a + green rectangle on a gray background. The area the grid layout + occupies is colored white. The top layout has a spacing of 2 and a + margin of 5, the bottom layout has the defaults of no margin or + spacing. + + \image spacing_a.png + \image spacing_b.png + +*/ +/*! + \internal + \class QFxGridLayout + \brief The QFxGridLayout class lays out items in a grid. + \ingroup layouts + +*/ +QFxGridLayout::QFxGridLayout(QFxItem *parent) : + QFxBaseLayout(Both, parent) +{ + _columns=-1; + _rows=-1; +} + +/*! + \qmlproperty int GridLayout::columns + This property holds the number of columns in the grid. + + When the columns property is set the GridLayout will always have + that many columns. Note that if you do not have enough items to + fill this many columns some columns will be of zero width. +*/ + +/*! + \qmlproperty int GridLayout::rows + This property holds the number of rows in the grid. + + When the rows property is set the GridLayout will always have that + many rows. Note that if you do not have enough items to fill this + many rows some rows will be of zero width. +*/ + +/*! + \property QFxGridLayout::columns + \brief the number of columns in the grid. +*/ + +/*! + \property QFxGridLayout::rows + \brief the number of rows in the grid. +*/ + +void QFxGridLayout::doLayout() +{ + int c=_columns,r=_rows;//Actual number of rows/columns + int numVisible = items()->size() + newItems()->size(); + if(_columns==-1 && _rows==-1){ + c = 4; + r = (numVisible+2)/3; + }else if(_rows==-1){ + r = (numVisible+(_columns-1))/_columns; + }else if(_columns==-1){ + c = (numVisible+(_rows-1))/_rows; + } + + QList<int> maxColWidth; + QList<int> maxRowHeight; + int childIndex =0; + for(int i=0; i<r; i++){ + for(int j=0; j<c; j++){ + if(j==0) + maxRowHeight << 0; + if(i==0) + maxColWidth << 0; + if(childIndex == this->QSimpleCanvasItem::children().count()) + continue; + QFxItem *child = qobject_cast<QFxItem *>(this->QSimpleCanvasItem::children().at(childIndex++)); + if(!child || !child->isVisible()) + continue; + if(child->width() > maxColWidth[j]) + maxColWidth[j] = child->width(); + if(child->height() > maxRowHeight[i]) + maxRowHeight[i] = child->height(); + } + } + + int xoffset=0; + int yoffset=0; + int curRow =0; + int curCol =0; + foreach(QFxItem* item, *leavingItems()){ + if(remove()){ + QList<QPair<QString,QVariant> > changes; + applyRemove(changes, item); + } + } + foreach(QSimpleCanvasItem* schild, this->QSimpleCanvasItem::children()){ + QFxItem *child = qobject_cast<QFxItem *>(schild); + if(!child || !child->isVisible()) + continue; + bool needMove = (child->x()!=xoffset)||(child->y()!=yoffset); + QList<QPair<QString, QVariant> > changes; + changes << qMakePair(QString(QLatin1String("x")),QVariant(xoffset)); + changes << qMakePair(QString(QLatin1String("y")),QVariant(yoffset)); + if(newItems()->contains(child) && add()) { + applyAdd(changes,child); + } else if(needMove) { + if(move()){ + applyMove(changes,child); + }else{ + setMovingItem(child); + child->setPos(QPointF(xoffset, yoffset)); + setMovingItem(0); + } + } + xoffset+=maxColWidth[curCol]+spacing(); + curCol++; + curCol%=c; + if(!curCol){ + yoffset+=maxRowHeight[curRow]+spacing(); + xoffset=0; + curRow++; + if(curRow>=r) + return; + } + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxlayouts.h b/src/declarative/fx/qfxlayouts.h new file mode 100644 index 0000000..acfc0c4 --- /dev/null +++ b/src/declarative/fx/qfxlayouts.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXLAYOUTS_H +#define QFXLAYOUTS_H + +#include <qfxitem.h> +#include <QObject> +#include <QString> +#include <qmlstate.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxBaseLayoutPrivate; + +class Q_DECLARATIVE_EXPORT QFxBaseLayout : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(int spacing READ spacing WRITE setSpacing) + Q_PROPERTY(int margin READ margin WRITE setMargin) + Q_PROPERTY(QmlTransition *move READ move WRITE setMove) + Q_PROPERTY(QmlTransition *add READ add WRITE setAdd) + Q_PROPERTY(QmlTransition *remove READ remove WRITE setRemove) + Q_PROPERTY(QFxItem *item READ layoutItem NOTIFY layoutItemChanged) +public: + enum AutoUpdateType { None = 0x0, Horizontal = 0x1, Vertical = 0x2, Both = 0x3 }; + QFxBaseLayout(AutoUpdateType, QFxItem *parent); + + int spacing() const; + void setSpacing(int); + + int margin() const; + void setMargin(int); + + QmlTransition *move() const; + void setMove(QmlTransition *); + + QmlTransition *add() const; + void setAdd(QmlTransition *); + + QmlTransition *remove() const; + void setRemove(QmlTransition *); + + QFxItem *layoutItem() const; + +protected: + virtual void componentComplete(); + virtual void childrenChanged(); + virtual bool event(QEvent *); + QSet<QFxItem *>* newItems(); + QSet<QFxItem *>* leavingItems(); + QSet<QFxItem *>* items(); + void applyAdd(const QList<QPair<QString, QVariant> >& changes, QFxItem* target); + void applyMove(const QList<QPair<QString, QVariant> >& changes, QFxItem* target); + void applyRemove(const QList<QPair<QString, QVariant> >& changes, QFxItem* target); + +Q_SIGNALS: + void layoutItemChanged(); + +protected Q_SLOTS: + virtual void doLayout()=0; + void setLayoutItem(QFxItem *); + +private Q_SLOTS: + void preLayout(); + +protected: + QFxBaseLayout(QFxBaseLayoutPrivate &dd, AutoUpdateType at, QFxItem *parent); + void setMovingItem(QFxItem *); + +private: + void applyTransition(const QList<QPair<QString, QVariant> >& changes, QFxItem* target, + QmlTransition* transition); + Q_DISABLE_COPY(QFxBaseLayout) + Q_DECLARE_PRIVATE(QFxBaseLayout) +}; + +class Q_DECLARATIVE_EXPORT QFxVerticalLayout : public QFxBaseLayout +{ + Q_OBJECT +public: + QFxVerticalLayout(QFxItem *parent=0); +protected Q_SLOTS: + virtual void doLayout(); +private: + Q_DISABLE_COPY(QFxVerticalLayout) +}; +QML_DECLARE_TYPE(QFxVerticalLayout); + +class Q_DECLARATIVE_EXPORT QFxHorizontalLayout: public QFxBaseLayout +{ + Q_OBJECT +public: + QFxHorizontalLayout(QFxItem *parent=0); +protected Q_SLOTS: + virtual void doLayout(); +private: + Q_DISABLE_COPY(QFxHorizontalLayout) +}; +QML_DECLARE_TYPE(QFxHorizontalLayout); + +class Q_DECLARATIVE_EXPORT QFxGridLayout : public QFxBaseLayout +{ + Q_OBJECT + Q_PROPERTY(int rows READ rows WRITE setRows) + Q_PROPERTY(int columns READ columns WRITE setcolumns) +public: + QFxGridLayout(QFxItem *parent=0); + + int rows() const {return _rows;} + void setRows(const int rows){_rows = rows;} + + int columns() const {return _columns;} + void setcolumns(const int columns){_columns = columns;} +protected Q_SLOTS: + virtual void doLayout(); + +private: + int _rows; + int _columns; + Q_DISABLE_COPY(QFxGridLayout) +}; +QML_DECLARE_TYPE(QFxGridLayout); + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/fx/qfxlayouts_p.h b/src/declarative/fx/qfxlayouts_p.h new file mode 100644 index 0000000..859482f --- /dev/null +++ b/src/declarative/fx/qfxlayouts_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXLAYOUTS_P_H +#define QFXLAYOUTS_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 "qfxlayouts.h" +#include "qfxitem_p.h" +#include <QObject> +#include <QString> +#include <qmlstate.h> + + +QT_BEGIN_NAMESPACE +class QFxBaseLayoutPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxBaseLayout) + +public: + QFxBaseLayoutPrivate() + : _ep(false), _componentComplete(false), _spacing(0), + _margin(0), aut(QFxBaseLayout::None), moveTransition(0), addTransition(0), + removeTransition(0), _layoutItem(0), stateGroup(0), _movingItem(0) + { + } + + void init(QFxBaseLayout::AutoUpdateType at) + { + aut = at; + } + + bool _ep; + bool _componentComplete; + int _spacing; + int _margin; + QFxBaseLayout::AutoUpdateType aut; + QmlTransition *moveTransition; + QmlTransition *addTransition; + QmlTransition *removeTransition; + QSet<QFxItem *> _items; + QSet<QFxItem *> _leavingItems; + QSet<QFxItem *> _stableItems; + QSet<QFxItem *> _newItems; + QSet<QFxItem *> _animated; + QFxItem *_layoutItem; + QmlStateGroup *stateGroup; + QFxItem *_movingItem; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/fx/qfxlistview.cpp b/src/declarative/fx/qfxlistview.cpp new file mode 100644 index 0000000..3c8c12c --- /dev/null +++ b/src/declarative/fx/qfxlistview.cpp @@ -0,0 +1,1680 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "private/qfxflickable_p.h" +#include "qmlfollow.h" +#include "qlistmodelinterface.h" +#include "qfxvisualitemmodel.h" +#include "gfxeasing.h" +#include "qfxlistview.h" +#include <qmlexpression.h> + + +QT_BEGIN_NAMESPACE +class QFxListViewAttached : public QObject +{ + Q_OBJECT +public: + QFxListViewAttached(QObject *parent) + : QObject(parent), m_isCurrent(false), m_delayRemove(false) {} + ~QFxListViewAttached() { + attachedProperties.remove(parent()); + } + + Q_PROPERTY(QFxListView *view READ view); + QFxListView *view() { return m_view; } + + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged); + bool isCurrentItem() const { return m_isCurrent; } + void setIsCurrentItem(bool c) { + if (m_isCurrent != c) { + m_isCurrent = c; + emit currentItemChanged(); + } + } + + Q_PROPERTY(QString prevSection READ prevSection NOTIFY prevSectionChanged); + QString prevSection() const { return m_prevSection; } + void setPrevSection(const QString §) { + if (m_prevSection != sect) { + m_prevSection = sect; + emit prevSectionChanged(); + } + } + + Q_PROPERTY(QString section READ section NOTIFY sectionChanged); + QString section() const { return m_section; } + void setSection(const QString §) { + if (m_section != sect) { + m_section = sect; + emit sectionChanged(); + } + } + + Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged); + bool delayRemove() const { return m_delayRemove; } + void setDelayRemove(bool delay) { + if (m_delayRemove != delay) { + m_delayRemove = delay; + emit delayRemoveChanged(); + } + } + + static QFxListViewAttached *properties(QObject *obj) { + if(!attachedProperties.contains(obj)) { + QFxListViewAttached *rv = new QFxListViewAttached(obj); + attachedProperties.insert(obj, rv); + return rv; + } + return attachedProperties.value(obj); + } + + void emitAdd() { emit add(); } + void emitRemove() { emit remove(); } + +signals: + void currentItemChanged(); + void sectionChanged(); + void prevSectionChanged(); + void delayRemoveChanged(); + void add(); + void remove(); + +public: + QFxListView *m_view; + bool m_isCurrent; + mutable QString m_section; + QString m_prevSection; + bool m_delayRemove; + + static QHash<QObject*, QFxListViewAttached*> attachedProperties; +}; + +QHash<QObject*, QFxListViewAttached*> QFxListViewAttached::attachedProperties; + +//---------------------------------------------------------------------------- + +class FxListItem +{ +public: + FxListItem(QFxItem *i, QFxListView *v) : item(i), view(v) { + attached = QFxListViewAttached::properties(item); + attached->m_view = view; + } + ~FxListItem() {} + + qreal position() const { return (view->orientation() == Qt::Vertical ? item->y() : item->x()); } + int size() const { return (view->orientation() == Qt::Vertical ? item->height() : item->width()); } + qreal endPosition() const { + return (view->orientation() == Qt::Vertical + ? item->y() + (item->height() > 0 ? item->height() : 1) + : item->x() + (item->width() > 0 ? item->width() : 1)) - 1; + } + void setPosition(qreal pos) { + if (view->orientation() == Qt::Vertical) { + item->setY(pos); + } else { + item->setX(pos); + } + } + + QFxItem *item; + QFxListView *view; + QFxListViewAttached *attached; + int index; +}; + +//---------------------------------------------------------------------------- + +class QFxListViewPrivate : public QFxFlickablePrivate +{ + Q_DECLARE_PUBLIC(QFxListView); + +public: + QFxListViewPrivate() + : model(0), currentItem(0), tmpCurrent(0), orient(Qt::Vertical) + , visiblePos(0), visibleIndex(0) + , averageSize(100), currentIndex(-1), currItemMode(QFxListView::Free) + , snapPos(0), highlightComponent(0), highlight(0), trackedItem(0) + , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0) + , keyPressed(false), ownModel(false), wrap(false), autoHighlight(true) + , fixCurrentVisibility(false) {} + + void init(); + void clear(); + FxListItem *getItem(int modelIndex); + FxListItem *createItem(int modelIndex); + void releaseItem(FxListItem *item); + + FxListItem *visibleItem(int modelIndex) const { + if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { + for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { + FxListItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return item; + } + } + return 0; + } + + qreal position() const { + Q_Q(const QFxListView); + return orient == Qt::Vertical ? q->yPosition() : q->xPosition(); + } + void setPosition(qreal pos) { + Q_Q(QFxListView); + if (orient == Qt::Vertical) + q->setYPosition(pos); + else + q->setXPosition(pos); + } + int size() const { + Q_Q(const QFxListView); + return orient == Qt::Vertical ? q->height() : q->width(); + } + + qreal startPosition() const { + qreal pos = 0; + if (!visibleItems.isEmpty()) + pos = visibleItems.first()->position() - visibleIndex * averageSize; + return pos; + } + + qreal endPosition() const { + qreal pos = 0; + if (!visibleItems.isEmpty()) { + int invisibleCount = visibleItems.count() - visibleIndex; + for (int i = visibleItems.count()-1; i >= 0; --i) { + if (visibleItems.at(i)->index != -1) { + invisibleCount = model->count() - visibleItems.at(i)->index - 1; + break; + } + } + pos = visibleItems.last()->endPosition() + invisibleCount * averageSize; + } + return pos; + } + + qreal positionAt(int modelIndex) const { + if (FxListItem *item = visibleItem(modelIndex)) + return item->position(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = visibleIndex - modelIndex; + return visibleItems.first()->position() - count * averageSize; + } else { + int idx = visibleItems.count() - 1; + while (idx >= 0 && visibleItems.at(idx)->index == -1) + --idx; + if (idx < 0) + idx = visibleIndex; + else + idx = visibleItems.at(idx)->index; + int count = modelIndex - idx - 1; + return visibleItems.last()->endPosition() + count * averageSize + 1; + } + } + return 0; + } + + QString sectionAt(int modelIndex) { + Q_Q(QFxListView); + if (FxListItem *item = visibleItem(modelIndex)) + return item->attached->section(); + QString section; + if (!sectionExpression.isEmpty()) + section = model->evaluate(modelIndex, sectionExpression, q).toString(); + return section; + } + + bool isValid() const { + return model && model->count() && (!ownModel || model->delegate()); + } + + int snapIndex() { + qreal pos = position(); + for (int i = 0; i < visibleItems.count(); ++i) { + qreal itemTop = visibleItems[i]->position() - pos; + if (itemTop >= snapPos-averageSize/2 && itemTop < snapPos+averageSize/2) + return visibleItems[i]->index; + } + return -1; + } + + // map a model index to visibleItems index. + // These may differ if removed items are still present in the visible list, + // e.g. doing a removal animation + int mapFromModel(int modelIndex) const { + if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) + return -1; + for (int i = 0; i < visibleItems.count(); ++i) { + FxListItem *listItem = visibleItems.at(i); + if (listItem->index == modelIndex) + return i + visibleIndex; + if (listItem->index > modelIndex) + return -1; + } + return -1; // Not in visibleList + } + + // for debugging only + void checkVisible() const { + int skip = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxListItem *listItem = visibleItems.at(i); + if (listItem->index == -1) { + ++skip; + } else if (listItem->index != visibleIndex + i - skip) { + qDebug() << "index" << visibleIndex << i << listItem->index; + abort(); + } + } + } + + void refill(qreal from, qreal to); + void layout(); + void updateTrackedItem(); + void createHighlight(); + void updateHighlight(); + void updateSections(); + void updateCurrentSection(); + void updateCurrent(int); + void updateAverage(); + void fixupPosition(); + virtual void fixupY(); + virtual void fixupX(); + + QFxVisualItemModel *model; + QVariant modelVariant; + QList<FxListItem*> visibleItems; + FxListItem *currentItem; + QFxItem *tmpCurrent; + Qt::Orientation orient; + int visiblePos; + int visibleIndex; + qreal averageSize; + int currentIndex; + QFxListView::CurrentItemPositioning currItemMode; + int snapPos; + QmlComponent *highlightComponent; + FxListItem *highlight; + FxListItem *trackedItem; + QFxItem *activeItem; //XXX fix + enum MovementReason { Other, Key, Mouse }; + MovementReason moveReason; + int buffer; + QmlFollow *highlightPosAnimator; + QmlFollow *highlightSizeAnimator; + QString sectionExpression; + QString currentSection; + + int keyPressed : 1; + int ownModel : 1; + int wrap : 1; + int autoHighlight : 1; + int fixCurrentVisibility : 1; +}; + +void QFxListViewPrivate::init() +{ + Q_Q(QFxListView); + q->setOptions(QFxListView::IsFocusRealm); +} + +void QFxListViewPrivate::clear() +{ + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + visiblePos = 0; + visibleIndex = 0; + if (currentItem) { + FxListItem *tmpItem = currentItem; + currentItem = 0; + currentIndex = -1; + releaseItem(tmpItem); + } + createHighlight(); + trackedItem = 0; +} + +FxListItem *QFxListViewPrivate::getItem(int modelIndex) +{ + if (currentItem && modelIndex == currentIndex) + return currentItem; + if (FxListItem *listItem = visibleItem(modelIndex)) + return listItem; + return createItem(modelIndex); +} + +FxListItem *QFxListViewPrivate::createItem(int modelIndex) +{ + Q_Q(QFxListView); + // create object + FxListItem *listItem = 0; + if (QFxItem *item = model->item(modelIndex, false)) { + listItem = new FxListItem(item, q); + listItem->index = modelIndex; + // initialise attached properties + if (!sectionExpression.isEmpty()) { + QmlExpression e(listItem->item->itemContext(), sectionExpression, q); + e.setTrackChange(false); + listItem->attached->m_section = e.value().toString(); + if (modelIndex > 0) { + if (FxListItem *item = visibleItem(modelIndex-1)) + listItem->attached->m_prevSection = item->attached->section(); + else + listItem->attached->m_prevSection = sectionAt(modelIndex-1); + } + } + // complete + model->completeItem(); + listItem->item->setZ(modelIndex + 1); + listItem->item->setParent(q->viewport()); + if (orient == Qt::Vertical) + QObject::connect(listItem->item, SIGNAL(heightChanged()), q, SLOT(itemResized())); + else + QObject::connect(listItem->item, SIGNAL(widthChanged()), q, SLOT(itemResized())); + } + return listItem; +} + +void QFxListViewPrivate::releaseItem(FxListItem *item) +{ + Q_Q(QFxListView); + if (item != currentItem) { + if (orient == Qt::Vertical) + QObject::disconnect(item->item, SIGNAL(heightChanged()), q, SLOT(itemResized())); + else + QObject::disconnect(item->item, SIGNAL(widthChanged()), q, SLOT(itemResized())); + if (trackedItem == item) { + const char *notifier1 = orient == Qt::Vertical ? SIGNAL(topChanged()) : SIGNAL(leftChanged()); + const char *notifier2 = orient == Qt::Vertical ? SIGNAL(heightChanged()) : SIGNAL(widthChanged()); + QObject::disconnect(trackedItem->item, notifier1, q, SLOT(trackedPositionChanged())); + QObject::disconnect(trackedItem->item, notifier2, q, SLOT(trackedPositionChanged())); + trackedItem = 0; + } + model->release(item->item); + delete item; + } +} + +void QFxListViewPrivate::refill(qreal from, qreal to) +{ + Q_Q(QFxListView); + if (!isValid() || !q->isComponentComplete()) + return; + from -= buffer; + to += buffer; + int modelIndex = 0; + qreal itemEnd = visiblePos-1; + if (!visibleItems.isEmpty()) { + visiblePos = visibleItems.first()->position(); + itemEnd = visibleItems.last()->endPosition(); + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + modelIndex = visibleItems.at(i)->index + 1; + } + + bool changed = false; + FxListItem *item = 0; + int pos = itemEnd + 1; + while (modelIndex < model->count() && pos <= to) { + //qDebug() << "refill: append item" << modelIndex; + item = getItem(modelIndex); + item->setPosition(pos); + pos += item->size(); + visibleItems.append(item); + ++modelIndex; + changed = true; + } + while (visibleIndex > 0 && visiblePos > from) { + //qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; + item = getItem(visibleIndex-1); + --visibleIndex; + visiblePos -= item->size(); + item->setPosition(visiblePos); + visibleItems.prepend(item); + changed = true; + } + + while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < from) { + if (item->attached->delayRemove()) + break; + //qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition(); + if (item->index != -1) + visibleIndex++; + visibleItems.removeFirst(); + releaseItem(item); + changed = true; + } + while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > to) { + if (item->attached->delayRemove()) + break; + //qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; + visibleItems.removeLast(); + releaseItem(item); + changed = true; + } + if (changed) { + if (visibleItems.count()) + visiblePos = visibleItems.first()->position(); + updateAverage(); + if (!sectionExpression.isEmpty()) + updateCurrentSection(); + if (orient == Qt::Vertical) + q->setViewportHeight(endPosition() - startPosition()); + else + q->setViewportWidth(endPosition() - startPosition()); + } +} + +void QFxListViewPrivate::layout() +{ + Q_Q(QFxListView); + if (!visibleItems.isEmpty()) { + int oldEnd = visibleItems.last()->endPosition(); + int pos = visibleItems.first()->endPosition() + 1; + for (int i=1; i < visibleItems.count(); ++i) { + FxListItem *item = visibleItems.at(i); + item->setPosition(pos); + pos += item->size(); + } + // move current item if it is after the visible items. + if (currentItem && currentIndex > visibleItems.last()->index) + currentItem->setPosition(currentItem->position() + (visibleItems.last()->endPosition() - oldEnd)); + } + if (!isValid()) + return; + q->refill(); + q->trackedPositionChanged(); + updateHighlight(); + if (orient == Qt::Vertical) { + fixupY(); + q->setViewportHeight(endPosition() - startPosition()); + } else { + fixupX(); + q->setViewportWidth(endPosition() - startPosition()); + } +} + +void QFxListViewPrivate::updateTrackedItem() +{ + Q_Q(QFxListView); + FxListItem *item = currentItem; + if (highlight) + item = highlight; + + const char *notifier1 = orient == Qt::Vertical ? SIGNAL(topChanged()) : SIGNAL(leftChanged()); + const char *notifier2 = orient == Qt::Vertical ? SIGNAL(heightChanged()) : SIGNAL(widthChanged()); + + if (trackedItem && item != trackedItem) { + QObject::disconnect(trackedItem->item, notifier1, q, SLOT(trackedPositionChanged())); + QObject::disconnect(trackedItem->item, notifier2, q, SLOT(trackedPositionChanged())); + trackedItem = 0; + } + + if (!trackedItem && item) { + trackedItem = item; + QObject::connect(trackedItem->item, notifier1, q, SLOT(trackedPositionChanged())); + QObject::connect(trackedItem->item, notifier2, q, SLOT(trackedPositionChanged())); + q->trackedPositionChanged(); + } + if (trackedItem) + q->trackedPositionChanged(); +} + +void QFxListViewPrivate::createHighlight() +{ + Q_Q(QFxListView); + if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; + delete highlight->item; + delete highlight; + highlight = 0; + delete highlightPosAnimator; + delete highlightSizeAnimator; + highlightPosAnimator = 0; + highlightSizeAnimator = 0; + } + + if (currentItem) { + QFxItem *item = 0; + if (highlightComponent) { + QmlContext *highlightContext = new QmlContext(q->itemContext()); + QObject *nobj = highlightComponent->create(highlightContext); + if (nobj) { + highlightContext->setParent(nobj); + item = qobject_cast<QFxItem *>(nobj); + if(!item) { + delete nobj; + } else { + item->setParent(q->viewport()); + } + } else { + delete highlightContext; + } + } else { + item = new QFxItem; + item->setParent(q->viewport()); + } + if (item) { + highlight = new FxListItem(item, q); + const QLatin1String posProp(orient == Qt::Vertical ? "y" : "x"); + highlightPosAnimator = new QmlFollow(q); + highlightPosAnimator->setTarget(QmlMetaProperty(highlight->item, posProp)); + highlightPosAnimator->setSpring(3); + highlightPosAnimator->setDamping(0.3); + highlightPosAnimator->setEnabled(autoHighlight); + const QLatin1String sizeProp(orient == Qt::Vertical ? "height" : "width"); + highlightSizeAnimator = new QmlFollow(q); + highlightSizeAnimator->setTarget(QmlMetaProperty(highlight->item, sizeProp)); + highlightSizeAnimator->setEnabled(autoHighlight); + } + } +} + +void QFxListViewPrivate::updateHighlight() +{ + if ((!currentItem && highlight) || (currentItem && !highlight)) + createHighlight(); + updateTrackedItem(); + if (currentItem && autoHighlight && highlight) { + // auto-update highlight + highlightPosAnimator->setSourceValue(currentItem->position()); + highlightSizeAnimator->setSourceValue(currentItem->size()); + if (orient == Qt::Vertical) { + if (highlight->item->width() == 0) + highlight->item->setWidth(currentItem->item->width()); + } else { + if (highlight->item->height() == 0) + highlight->item->setHeight(currentItem->item->height()); + } + } +} + +void QFxListViewPrivate::updateSections() +{ + if (!sectionExpression.isEmpty()) { + QString prevSection; + if (visibleIndex > 0) + prevSection = sectionAt(visibleIndex-1); + for (int i = 0; i < visibleItems.count(); ++i) { + QFxListViewAttached *attached = visibleItems.at(i)->attached; + attached->setPrevSection(prevSection); + prevSection = attached->section(); + } + } +} + +void QFxListViewPrivate::updateCurrentSection() +{ + if (sectionExpression.isEmpty() || visibleItems.isEmpty()) { + currentSection = QString(); + return; + } + int index = 0; + while (visibleItems.at(index)->endPosition() < position() && index < visibleItems.count()) + ++index; + + if (index < visibleItems.count()) + currentSection = visibleItems.at(index)->attached->section(); + else + currentSection = visibleItems.first()->attached->section(); +} + +void QFxListViewPrivate::updateCurrent(int modelIndex) +{ + Q_Q(QFxListView); + if (!isValid() || modelIndex < 0 || modelIndex >= model->count()) { + if (currentItem) { + FxListItem *item = currentItem; + int index = currentIndex; + currentItem = 0; + currentIndex = 0; + updateHighlight(); + if (!visibleItem(index)) + releaseItem(item); + emit q->currentIndexChanged(); + } + return; + } + + if (currentItem && currentIndex == modelIndex) { + updateHighlight(); + return; + } + + if (tmpCurrent) { + delete tmpCurrent; + tmpCurrent = 0; + } + int oldCurrentIndex = currentIndex; + FxListItem *oldCurrentItem = currentItem; + currentIndex = -1; + currentItem = visibleItem(modelIndex); + if (!currentItem) { + currentItem = getItem(modelIndex); + if (modelIndex == visibleIndex - 1) { + // We can calculate exact postion in this case + currentItem->setPosition(visibleItems.first()->position() - currentItem->size()); + } else { + // Create current item now and position as best we can. + // Its position will be corrected when it becomes visible. + currentItem->setPosition(positionAt(modelIndex)); + } + } + currentIndex = modelIndex; + fixCurrentVisibility = true; + if (oldCurrentItem && oldCurrentItem->item != currentItem->item) + oldCurrentItem->attached->setIsCurrentItem(false); + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + updateHighlight(); + emit q->currentIndexChanged(); + // Release the old current item + if (oldCurrentItem && !visibleItem(oldCurrentIndex)) { + if (oldCurrentItem->item == currentItem->item) + delete oldCurrentItem; + else + releaseItem(oldCurrentItem); + } +} + +void QFxListViewPrivate::updateAverage() +{ + if (!visibleItems.count()) + return; + qreal sum = 0.0; + for (int i = 0; i < visibleItems.count(); ++i) + sum += visibleItems.at(i)->size(); + averageSize = sum / visibleItems.count(); +} + +void QFxListViewPrivate::fixupPosition() +{ + if (orient == Qt::Vertical) + fixupY(); + else + fixupX(); +} + +void QFxListViewPrivate::fixupY() +{ + Q_Q(QFxListView); + QFxFlickablePrivate::fixupY(); + if (orient == Qt::Horizontal) + return; + if (currItemMode == QFxListView::SnapAuto) { + if (currentItem) { + moveReason = Mouse; + _tl.clear(); + _tl.move(_moveY, -(currentItem->position() - snapPos), GfxEasing(GfxEasing::InOutQuad), 200); + } + } else if (currItemMode == QFxListView::Snap) { + moveReason = Mouse; + int idx = snapIndex(); + if (FxListItem *snapItem = visibleItem(idx)) { + int pos = snapItem->position() - snapPos; + if (pos > -q->maxYExtent()) + pos = -q->maxYExtent(); + else if (pos < -q->minYExtent()) + pos = -q->minYExtent(); + _tl.clear(); + _tl.move(_moveY, -(pos), GfxEasing(GfxEasing::InOutQuad), 200); + } + } +} + +void QFxListViewPrivate::fixupX() +{ + Q_Q(QFxListView); + QFxFlickablePrivate::fixupX(); + if (orient == Qt::Vertical) + return; + if (currItemMode == QFxListView::SnapAuto) { + moveReason = Mouse; + _tl.clear(); + _tl.move(_moveX, -(currentItem->position() - snapPos), GfxEasing(GfxEasing::InOutQuad), 200); + } else if (currItemMode == QFxListView::Snap) { + moveReason = Mouse; + int idx = snapIndex(); + if (FxListItem *snapItem = visibleItem(idx)) { + int pos = snapItem->position() - snapPos; + if (pos > -q->maxXExtent()) + pos = -q->maxXExtent(); + else if (pos < -q->minXExtent()) + pos = -q->minXExtent(); + _tl.clear(); + _tl.move(_moveX, -(pos), GfxEasing(GfxEasing::InOutQuad), 200); + } + } +} + +//---------------------------------------------------------------------------- + +/*! + \qmlclass ListView + \inherits Flickable + \brief The ListView element provides a list view of items provided by a model. + + The model is typically provided by a QAbstractListModel "C++ model object", but can also be created directly in XML. + + The items are laid out vertically or horizontally and may be flicked to scroll. + + The below example creates a very simple vertical list, using an XML model. + \image trivialListView.png + \code + <resources> + <ListModel id="contactModel"> + <Contact> + <firstName>John</firstName> + <lastName>Smith</lastName> + </Contact> + <Contact> + <firstName>Bill</firstName> + <lastName>Jones</lastName> + </Contact> + <Contact> + <firstName>Jane</firstName> + <lastName>Doe</lastName> + </Contact> + </ListModel> + <Component id="contactDelegate"> + <Rect pen.color="blue" z="-1" height="20" width="80" color="white" radius="2"> + <Text id="name" text="{firstName + ' ' + lastName}" font.size="11"/> + </Rect> + </Component> + </resources> + + <ListView id="list" width="320" height="240" clip="true" + model="{contactModel}" delegate="{contactDelegate}"/> + \endcode +*/ + +QFxListView::QFxListView(QFxItem *parent) + : QFxFlickable(*(new QFxListViewPrivate), parent) +{ + Q_D(QFxListView); + d->init(); +} + +QFxListView::~QFxListView() +{ + Q_D(QFxListView); + if (d->ownModel) + delete d->model; +} + +/*! + \qmlproperty model ListView::model + This property holds the model providing data for the list. + + The model provides a set of data that is used to create the items for the view. + For large or dynamic datasets the model is usually provided by a C++ model object. + The C++ model object must be a \l QListModelInterface subclass, a \l VisualModel, + or a simple list. + + Models can also be created directly in XML, using the \l ListModel element. For example: + \code + <ListModel id="contactModel"> + <Contact> + <firstName>John</firstName> + <lastName>Smith</lastName> + </Contact> + <Contact> + <firstName>Bill</firstName> + <lastName>Jones</lastName> + </Contact> + <Contact> + <firstName>Jane</firstName> + <lastName>Doe</lastName> + </Contact> + </ListModel> + + <ListView model="{contactModel}" .../> + \endcode +*/ +QVariant QFxListView::model() const +{ + Q_D(const QFxListView); + return d->modelVariant; +} + +void QFxListView::setModel(const QVariant &model) +{ + Q_D(QFxListView); + if (d->model) { + disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + } + d->clear(); + d->modelVariant = model; + QObject *object = qvariant_cast<QObject*>(model); + QFxVisualItemModel *vim = 0; + if (object && (vim = qobject_cast<QFxVisualItemModel *>(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QFxVisualItemModel(itemContext()); + d->ownModel = true; + } + d->model->setModel(model); + } + if (d->model) { + if (d->currentIndex >= d->model->count() || d->currentIndex < 0) + setCurrentIndex(0); + else + d->updateCurrent(d->currentIndex); + connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); + connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); + refill(); + emit countChanged(); + } +} + +/*! + \qmlproperty component ListView::delegate + + The delegate provides a template describing what each item in the view should look and act like. + + Here is an example delegate: + \code + <Component id="contactDelegate"> + <Item id="wrapper"> + <Image id="pic" width="100" height="100" file="{portrait}"/> + <Text id="name" text="{firstName + ' ' + lastName}" + anchors.left="{pic.right}" anchors.leftMargin="5"/> + </Item> + </Component> + ... + <ListView delegate="{contactDelegate}" .../> + \endcode +*/ +QmlComponent *QFxListView::delegate() const +{ + Q_D(const QFxListView); + return d->model ? d->model->delegate() : 0; +} + +void QFxListView::setDelegate(QmlComponent *delegate) +{ + Q_D(QFxListView); + if (!d->ownModel) { + d->model = new QFxVisualItemModel(itemContext()); + d->ownModel = true; + } + d->model->setDelegate(delegate); + d->updateCurrent(d->currentIndex); + refill(); +} + +/*! + \qmlproperty int ListView::currentIndex + \qmlproperty Item ListView::current + + \c currentIndex holds the index of the current item. + \c current is the current item. Note that the position of the current item + may only be approximate until it becomes visible in the view. +*/ +int QFxListView::currentIndex() const +{ + Q_D(const QFxListView); + return d->currentIndex; +} + +void QFxListView::setCurrentIndex(int index) +{ + Q_D(QFxListView); + d->moveReason = QFxListViewPrivate::Other; + if (d->isValid() && index != d->currentIndex && index < d->model->count() && index >= 0) + d->updateCurrent(index); + else + d->currentIndex = index; +} + +QFxItem *QFxListView::currentItem() +{ + Q_D(QFxListView); + if (!d->currentItem) { + // Always return something valid + if (!d->tmpCurrent) { + d->tmpCurrent = new QFxItem; + d->tmpCurrent->setParent(viewport()); + } + return d->tmpCurrent; + } + return d->currentItem->item; +} + +/*! + \qmlproperty int ListView::count + This property holds the number of items in the view. +*/ +int QFxListView::count() const +{ + Q_D(const QFxListView); + if (d->model) + return d->model->count(); + return 0; +} + +/*! + \qmlproperty component ListView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component will be created for each list. + The geometry of the resultant component instance will be managed by the list + so as to stay with the current item, unless the autoHighlight property is false. + + The below example demonstrates how to make a simple highlight + for a vertical list. + \code + <Component id="ListHighlight"> + <Rect color="lightsteelblue" radius="4"/> + </Component> + <ListView highlight="{ListHighlight}"> + \endcode + \image ListViewHighlight.png + + \sa autoHighlight +*/ +QmlComponent *QFxListView::highlight() const +{ + Q_D(const QFxListView); + return d->highlightComponent; +} + +void QFxListView::setHighlight(QmlComponent *highlight) +{ + Q_D(QFxListView); + delete d->highlightComponent; + d->highlightComponent = highlight; + d->updateCurrent(d->currentIndex); +} + +/*! + \qmlproperty bool ListView::autoHighlight + This property holds whether the highlight is managed by the view. + + If autoHighlight is true, the highlight will be moved smoothly + to follow the current item. If autoHighlight is false, the + highlight will not be moved by the view, and must be implemented + by the highlight, for example: + + \code + <Component id="Highlight"> + <Rect id="Wrapper" color="#242424" radius="4" width="320" height="60" > + <y> + <Follow source="{Wrapper.ListView.view.current.y}" spring="3" damping="0.2"/> + </y> + </Rect> + </Component> + \endcode +*/ +bool QFxListView::autoHighlight() const +{ + Q_D(const QFxListView); + return d->autoHighlight; +} + +void QFxListView::setAutoHighlight(bool autoHighlight) +{ + Q_D(QFxListView); + d->autoHighlight = autoHighlight; + if (d->highlightPosAnimator) { + d->highlightPosAnimator->setEnabled(d->autoHighlight); + d->highlightSizeAnimator->setEnabled(d->autoHighlight); + } + d->updateHighlight(); +} + +/*! + \qmlproperty enumeration ListView::currentItemPositioning + This property determines the current item positioning and selection characteristics. + + The modes supported are: + \list + \i Free - For Mouse, the current item may be positioned anywhere, + whether within the visible area, or outside. during Keyboard interaction, + the current item can move within the visible area, and the view will + scroll to keep the highlight visible. + \i Snap - + \i SnapAuto - + \endlist +*/ +QFxListView::CurrentItemPositioning QFxListView::currentItemPositioning() const +{ + Q_D(const QFxListView); + return d->currItemMode; +} + +void QFxListView::setCurrentItemPositioning(CurrentItemPositioning mode) +{ + Q_D(QFxListView); + d->currItemMode = mode; +} + +/*! + \qmlproperty int ListView::snapPosition + + When currentItemPositioning is set to Snap or SnapAuto, the + \c snapPosition determines where the top of the items will + snap to. +*/ +int QFxListView::snapPosition() const +{ + Q_D(const QFxListView); + return d->snapPos; +} + +void QFxListView::setSnapPosition(int pos) +{ + Q_D(QFxListView); + d->snapPos = pos; +} + +/*! + \qmlproperty enumeration ListView::orientation + This property holds the orientation of the list. + + Possible values are \c Qt::Vertical (default) and \c Qt::Horizontal. + + Vertical Example: + \image ListViewVertical.png + Horizontal Example: + \image ListViewHorizontal.png +*/ +Qt::Orientation QFxListView::orientation() const +{ + Q_D(const QFxListView); + return d->orient; +} + +void QFxListView::setOrientation(Qt::Orientation orientation) +{ + Q_D(QFxListView); + if (d->orient != orientation) { + d->orient = orientation; + if (d->orient == Qt::Vertical) + setViewportWidth(-1); + else + setViewportHeight(-1); + d->clear(); + refill(); + d->updateCurrent(d->currentIndex); + } +} + +/*! + \qmlproperty bool ListView::wrap + This property holds whether the list wraps key navigation + + If this property is true then key presses to move off of one end of the list will cause the + selection to jump to the other side. +*/ +bool QFxListView::isWrapEnabled() const +{ + Q_D(const QFxListView); + return d->wrap; +} + +void QFxListView::setWrapEnabled(bool wrap) +{ + Q_D(QFxListView); + d->wrap = wrap; +} + +/*! + \qmlproperty int ListView::cacheBuffer + This property holds the number of off-screen pixels to cache. + + This property determines the number of pixels above the top of the list + and below the bottom of the list to cache. Setting this value can make + scrolling the list smoother at the expense of additional memory usage. +*/ + +/*! + \property QFxListView::cacheBuffer + \brief sets the number of off-screen pixels to cache. + + This property determines the number of pixels above the top of the list + and below the bottom of the list to cache. Setting this value can make + scrolling the list smoother at the expense of additional memory usage. +*/ +int QFxListView::cacheBuffer() const +{ + Q_D(const QFxListView); + return d->buffer; +} + +void QFxListView::setCacheBuffer(int b) +{ + Q_D(QFxListView); + if(d->buffer != b) { + d->buffer = b; + if (isComponentComplete()) + refill(); + } +} + +/*! + \qmlproperty string ListView::sectionExpression + This property holds the expression to be evaluated for the section attached property. + + Each item in the list has attached properties named \c section and + \c prevSection. These may be used to place a section header for + related items. The example below assumes that the model is alphabetically + sorted. The section expression is the first character of the \c description + property. If \c section and \c prevSection differ, the item will + display a section header. + + \code + <Component id="Delegate"> + <Item id="wrapper" x="0" width="240" height="{Separator.height + Desc.height}"> + <Item id="Separator" height="{wrapper.ListView.prevSection != wrapper.ListView.section ? 10 : 0}" width="240"> + <Text text="{wrapper.ListView.section}" anchors.fill="{parent}"/> + </Item> + <Text id="Desc" text="{description} width="{parent.width}" height="30"/> + </Item> + </Component> + <ListView anchors.fill="{parent}" sectionExpression="{String(description).charAt(0)}" delegate="{Delegate}" model="{Model}"/> + \endcode +*/ +QString QFxListView::sectionExpression() const +{ + Q_D(const QFxListView); + return d->sectionExpression; +} + +void QFxListView::setSectionExpression(const QString &expression) +{ + Q_D(QFxListView); + if(d->sectionExpression != expression) { + d->sectionExpression = expression; + emit sectionExpressionChanged(); + } +} + +QString QFxListView::currentSection() const +{ + Q_D(const QFxListView); + return d->currentSection; +} + +/*! + \reimp +*/ +void QFxListView::setHeight(int height) +{ + Q_D(QFxListView); + QFxFlickable::setHeight(height); + if (d->orient == Qt::Vertical && isComponentComplete()) + refill(); +} + +/*! + \reimp +*/ +void QFxListView::setWidth(int width) +{ + Q_D(QFxListView); + QFxFlickable::setWidth(width); + if (d->orient == Qt::Horizontal && isComponentComplete()) + refill(); +} + +void QFxListView::viewportMoved() +{ + Q_D(QFxListView); + QFxFlickable::viewportMoved(); + refill(); + if (isFlicking() || d->pressed) + d->moveReason = QFxListViewPrivate::Mouse; + if (d->currItemMode == SnapAuto && d->moveReason == QFxListViewPrivate::Mouse) { + // Update current index + int idx = d->snapIndex(); + if (idx >= 0 && idx != d->currentIndex) + d->updateCurrent(idx); + } +} + +/*! + \reimp +*/ +qreal QFxListView::minYExtent() const +{ + Q_D(const QFxListView); + if (d->orient == Qt::Horizontal) + return QFxFlickable::minYExtent(); + qreal extent = -d->startPosition(); + if (d->currItemMode == SnapAuto) + extent += d->snapPos; + + return extent; +} + +/*! + \reimp +*/ +qreal QFxListView::maxYExtent() const +{ + Q_D(const QFxListView); + if (d->orient == Qt::Horizontal) + return QFxFlickable::maxYExtent(); + qreal extent; + if (d->currItemMode == SnapAuto) + extent = -(d->positionAt(count()-1) - d->snapPos); + else + extent = -(d->endPosition() - height()); + if (extent > 0) + extent = 0; + return extent; +} + +/*! + \reimp +*/ +qreal QFxListView::minXExtent() const +{ + Q_D(const QFxListView); + if (d->orient == Qt::Vertical) + return QFxFlickable::minXExtent(); + qreal extent = -d->startPosition(); + if (d->currItemMode == SnapAuto) + extent += d->snapPos; + + return extent; +} + +/*! + \reimp +*/ +qreal QFxListView::maxXExtent() const +{ + Q_D(const QFxListView); + if (d->orient == Qt::Vertical) + return QFxFlickable::maxXExtent(); + qreal extent; + if (d->currItemMode == SnapAuto) + extent = -(d->positionAt(count()-1) - d->snapPos); + else + extent = -(d->endPosition() - width()); + if (extent > 0) + extent = 0; + return extent; +} + +void QFxListView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QFxListView); + if (d->model && d->model->count() && !d->locked) { + if ((d->orient == Qt::Horizontal && event->key() == Qt::Key_Left) + || (d->orient == Qt::Vertical && event->key() == Qt::Key_Up)) { + if (currentIndex() > 0 || d->wrap) { + d->keyPressed = true; + d->moveReason = QFxListViewPrivate::Key; + int index = currentIndex()-1; + d->updateCurrent(index >= 0 ? index : d->model->count()-1); + event->accept(); + } + return; + } else if ((d->orient == Qt::Horizontal && event->key() == Qt::Key_Right) + || (d->orient == Qt::Vertical && event->key() == Qt::Key_Down)) { + if (currentIndex() < d->model->count() - 1 || d->wrap) { + d->keyPressed = true; + d->moveReason = QFxListViewPrivate::Key; + int index = currentIndex()+1; + d->updateCurrent(index < d->model->count() ? index : 0); + event->accept(); + } + return; + } + } + d->moveReason = QFxListViewPrivate::Other; + QFxFlickable::keyPressEvent(event); +} + +void QFxListView::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QFxListView); + d->keyPressed = false; + QFxFlickable::keyReleaseEvent(event); +} + +void QFxListView::componentComplete() +{ + Q_D(QFxListView); + QFxFlickable::componentComplete(); + if (d->currentIndex < 0) + d->updateCurrent(0); + refill(); + d->fixupPosition(); +} + +void QFxListView::refill() +{ + Q_D(QFxListView); + d->refill(d->position(), d->position()+d->size()-1); +} + +void QFxListView::trackedPositionChanged() +{ + Q_D(QFxListView); + if (!d->trackedItem) + return; + if (!isFlicking() && !d->pressed && d->moveReason != QFxListViewPrivate::Mouse) { + switch (d->currItemMode) { + case Free: + if (d->trackedItem->position() < d->position()) { + d->setPosition(d->trackedItem->position()); + } else if (d->trackedItem->endPosition() > d->position() + d->size()) { + qreal pos = d->trackedItem->endPosition() - d->size(); + if (d->trackedItem->size() > d->size()) + pos = d->trackedItem->position(); + d->setPosition(pos); + } + d->fixupPosition(); + break; + case Snap: + if (d->trackedItem->position() < d->startPosition() + d->snapPos) + d->setPosition(d->startPosition()); + else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + d->snapPos + d->trackedItem->size()) + d->setPosition(d->endPosition() - d->size()); + else + d->setPosition(d->trackedItem->position() - d->snapPos); + break; + case SnapAuto: + d->setPosition(d->trackedItem->position() - d->snapPos); + break; + } + } else if (d->fixCurrentVisibility && d->currentItem && !d->pressed) { + /* + if (d->trackedItem->position() < d->position() + && d->currentItem->position() < d->position()) { + d->setPosition(d->trackedItem->position()); + } else if (d->size() && d->trackedItem->size() <= d->size() + && d->trackedItem->endPosition() > d->position() + d->size() + && d->currentItem->endPosition() > d->position() + d->size()) { + d->setPosition(d->trackedItem->endPosition() - d->size()); + } + if (d->trackedItem->position() == d->currentItem->position()) + d->fixCurrentVisibility = false; + */ + } +} + +void QFxListView::itemResized() +{ + Q_D(QFxListView); + QFxItem *item = qobject_cast<QFxItem*>(sender()); + if (item) { + d->activeItem = item; // Ick - don't delete the sender + d->layout(); + d->activeItem = 0; + d->fixupPosition(); + } +} + +void QFxListView::itemsInserted(int modelIndex, int count) +{ + Q_D(QFxListView); + if (!d->visibleItems.count() || d->model->count() <= 1) { + refill(); + d->updateCurrent(qMax(0, qMin(d->currentIndex, d->model->count()-1))); + emit countChanged(); + return; + } + + int index = d->mapFromModel(modelIndex); + if (index == -1) { + int i = d->visibleItems.count() - 1; + while (i > 0 && d->visibleItems.at(i)->index == -1) + --i; + if (d->visibleItems.at(i)->index + 1 == modelIndex) { + // Special case of appending an item to the model. + index = d->visibleIndex + d->visibleItems.count(); + } else { + if (modelIndex + count - 1 < d->visibleIndex) { + // Insert before visible items + d->visibleIndex += count; + for (int i = 0; i < d->visibleItems.count(); ++i) { + FxListItem *listItem = d->visibleItems.at(i); + if (listItem->index != -1 && listItem != d->currentItem) + listItem->index += count; + } + } + if (d->currentIndex >= modelIndex) { + // adjust current item index + d->currentIndex += count; + if (d->currentItem) + d->currentItem->index = d->currentIndex; + } + emit countChanged(); + return; + } + } + + // At least some of the added items will be visible + int insertCount = count; + if (index < d->visibleIndex) { + insertCount -= d->visibleIndex - index; + index = d->visibleIndex; + modelIndex = d->visibleIndex; + } + + index -= d->visibleIndex; + int to = d->buffer+d->position()+d->size()-1; + // index can be the next item past the end of the visible items list (i.e. appended) + int pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position() + : d->visibleItems.at(index-1)->endPosition()+1; + int initialPos = pos; + QList<FxListItem*> added; + for (int i = 0; i < insertCount && pos <= to; ++i) { + FxListItem *item = d->createItem(modelIndex + i); + d->visibleItems.insert(index, item); + item->setPosition(pos); + added.append(item); + pos += item->size(); + ++index; + } + if (d->currentIndex >= modelIndex) { + // adjust current item index + d->currentIndex += count; + if (d->currentItem) { + d->currentItem->index = d->currentIndex; + d->currentItem->setPosition(d->currentItem->position() + (pos - initialPos)); + } + } + if (pos > to) { + // We didn't insert all our new items, which means anything + // beyond the current index is not visible - remove it. + while (d->visibleItems.count() > index) + d->releaseItem(d->visibleItems.takeLast()); + } else { + // Update the indexes of the following visible items. + for (; index < d->visibleItems.count(); ++index) { + FxListItem *listItem = d->visibleItems.at(index); + if (listItem != d->currentItem) { + listItem->setPosition(listItem->position() + (pos - initialPos)); + if (listItem->index != -1) + listItem->index += count; + } + } + } + // everything is in order now - emit add() signal + foreach(FxListItem *item, added) + item->attached->emitAdd(); + + emit countChanged(); +} + +void QFxListView::itemsRemoved(int modelIndex, int count) +{ + Q_D(QFxListView); + int index = d->mapFromModel(modelIndex); + if (index == -1) { + if (modelIndex + count - 1 < d->visibleIndex) { + // Items removed before our visible items. + d->visibleIndex -= count; + for (int i = 0; i < d->visibleItems.count(); ++i) { + FxListItem *listItem = d->visibleItems.at(i); + if (listItem->index != -1 && listItem != d->currentItem) + listItem->index -= count; + } + } + if (d->currentIndex >= modelIndex + count) { + d->currentIndex -= count; + if (d->currentItem) + d->currentItem->index -= count; + } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) { + // current item has been removed. + if (d->currentItem) { + FxListItem *item = d->currentItem; + d->currentItem = 0; + d->releaseItem(item); + } + d->currentIndex = -1; + d->updateCurrent(qMin(modelIndex, d->model->count()-1)); + } + emit countChanged(); + return; + } + + // Remove the items from the visible list, skipping anything already marked for removal + QList<FxListItem*>::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxListItem *item = *it; + if (item->index == -1 || item->index < modelIndex) { + // already removed, or before removed items + ++it; + } else if (item->index >= modelIndex + count) { + // after removed items + if (item != d->currentItem) + item->index -= count; + ++it; + } else { + // removed item + item->attached->emitRemove(); + if (item->attached->delayRemove()) { + item->index = -1; + connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection); + ++it; + } else { + it = d->visibleItems.erase(it); + d->releaseItem(item); + } + } + } + + // fix current + if (d->currentIndex >= modelIndex + count) { + d->currentIndex -= count; + if (d->currentItem) + d->currentItem->index -= count; + } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) { + // current item has been removed. + if (d->currentItem && !d->currentItem->attached->delayRemove()) { + FxListItem *item = d->currentItem; + d->currentItem = 0; + d->releaseItem(item); + } + d->currentItem = 0; + d->currentIndex = -1; + d->updateCurrent(qMin(modelIndex, d->model->count()-1)); + } + + // update visibleIndex + for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) { + if ((*it)->index != -1) { + d->visibleIndex = (*it)->index; + break; + } + } + + if (d->visibleItems.isEmpty()) { + d->visibleIndex = 0; + d->setPosition(0); + if (d->model->count() == 0) + update(); + else + refill(); + } else { + // Correct the positioning of the items + d->layout(); + } + + emit countChanged(); +} + +void QFxListView::destroyRemoved() +{ + Q_D(QFxListView); + for (QList<FxListItem*>::Iterator it = d->visibleItems.begin(); + it != d->visibleItems.end();) { + FxListItem *listItem = *it; + if (listItem->index == -1 && listItem->attached->delayRemove() == false) { + d->releaseItem(listItem); + it = d->visibleItems.erase(it); + } else { + ++it; + } + } + + // Correct the positioning of the items + d->layout(); +} + +QObject *QFxListView::qmlAttachedProperties(QObject *obj) +{ + return QFxListViewAttached::properties(obj); +} + +QML_DEFINE_TYPE(QFxListView,ListView); + +QT_END_NAMESPACE +#include "qfxlistview.moc" diff --git a/src/declarative/fx/qfxlistview.h b/src/declarative/fx/qfxlistview.h new file mode 100644 index 0000000..f15db0c --- /dev/null +++ b/src/declarative/fx/qfxlistview.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXLISTVIEW_H +#define QFXLISTVIEW_H + +#include <qfxflickable.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxVisualItemModel; +class QFxListViewPrivate; +class Q_DECLARATIVE_EXPORT QFxListView : public QFxFlickable +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QFxListView); + + Q_ENUMS(CurrentItemPositioning); + Q_PROPERTY(QVariant model READ model WRITE setModel); + Q_CLASSINFO("DefaultProperty", "delegate"); + Q_PROPERTY(QmlComponent *delegate READ delegate WRITE setDelegate); + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged); + Q_PROPERTY(QFxItem *current READ currentItem NOTIFY currentIndexChanged); + Q_PROPERTY(int count READ count NOTIFY countChanged); + Q_PROPERTY(QmlComponent *highlight READ highlight WRITE setHighlight); + Q_PROPERTY(bool autoHighlight READ autoHighlight WRITE setAutoHighlight); + Q_PROPERTY(CurrentItemPositioning currentItemPositioning READ currentItemPositioning WRITE setCurrentItemPositioning); + Q_PROPERTY(int snapPosition READ snapPosition WRITE setSnapPosition); + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation); + Q_PROPERTY(bool wrap READ isWrapEnabled WRITE setWrapEnabled); + Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer); + Q_PROPERTY(QString sectionExpression READ sectionExpression WRITE setSectionExpression NOTIFY sectionExpressionChanged); + Q_PROPERTY(QString currentSection READ currentSection NOTIFY currentSectionChanged); + +public: + QFxListView(QFxItem *parent=0); + ~QFxListView(); + + QVariant model() const; + void setModel(const QVariant &); + + QmlComponent *delegate() const; + void setDelegate(QmlComponent *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + QFxItem *currentItem(); + int count() const; + + QmlComponent *highlight() const; + void setHighlight(QmlComponent *highlight); + + bool autoHighlight() const; + void setAutoHighlight(bool); + + enum CurrentItemPositioning { Free, Snap, SnapAuto }; + CurrentItemPositioning currentItemPositioning() const; + void setCurrentItemPositioning(CurrentItemPositioning mode); + + int snapPosition() const; + void setSnapPosition(int pos); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation); + + bool isWrapEnabled() const; + void setWrapEnabled(bool); + + int cacheBuffer() const; + void setCacheBuffer(int); + + QString sectionExpression() const; + void setSectionExpression(const QString &); + QString currentSection() const; + + virtual void setHeight(int height); + virtual void setWidth(int width); + + static QObject *qmlAttachedProperties(QObject *); + +Q_SIGNALS: + void countChanged(); + void currentIndexChanged(); + void currentSectionChanged(); + void sectionExpressionChanged(); + +protected: + virtual void viewportMoved(); + virtual qreal minYExtent() const; + virtual qreal maxYExtent() const; + virtual qreal minXExtent() const; + virtual qreal maxXExtent() const; + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + virtual void componentComplete(); + +private: + void refill(); + +private Q_SLOTS: + void trackedPositionChanged(); + void itemResized(); + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void destroyRemoved(); +}; + +QML_DECLARE_TYPE(QFxListView); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/fx/qfxmouseregion.cpp b/src/declarative/fx/qfxmouseregion.cpp new file mode 100644 index 0000000..2bf7aa7 --- /dev/null +++ b/src/declarative/fx/qfxmouseregion.cpp @@ -0,0 +1,589 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxmouseregion.h" +#include "qfxmouseregion_p.h" +#include <QGraphicsSceneMouseEvent> + + +QT_BEGIN_NAMESPACE +static const int DragThreshold = 5; +static const int PressAndHoldDelay = 800; + +QML_DEFINE_TYPE(QFxDrag,Drag); +QFxDrag::QFxDrag(QObject *parent) +: QObject(parent), _target(0), _xmin(0), _xmax(0), _ymin(0), _ymax(0) +{ +} + +QFxDrag::~QFxDrag() +{ +} + +QFxItem *QFxDrag::target() const +{ + return _target; +} + +void QFxDrag::setTarget(QFxItem *t) +{ + _target = t; +} + +QString QFxDrag::axis() const +{ + return _axis; +} + +void QFxDrag::setAxis(const QString &a) +{ + _axis = a; +} + +/*! + \property QFxDrag::xmin + \brief the minimum x position for the target + + If x-axis dragging is enabled, xmin limits how far to the left the target can be dragged. If x-axis dragging is not enabled, this property has no effect. +*/ +int QFxDrag::xmin() const +{ + return _xmin; +} + +void QFxDrag::setXmin(int m) +{ + _xmin = m; +} + +/*! + \property QFxDrag::xmax + \brief the maximum x position for the target + + If x-axis dragging is enabled, xmax limits how far to the right the target can be dragged. If x-axis dragging is not enabled, this property has no effect. +*/ +int QFxDrag::xmax() const +{ + return _xmax; +} + +void QFxDrag::setXmax(int m) +{ + _xmax = m; +} + +/*! + \property QFxDrag::ymin + \brief the minimum y position for the target + + If y-axis dragging is enabled, ymin limits how far up the target can be dragged. If y-axis dragging is not enabled, this property has no effect. +*/ +int QFxDrag::ymin() const +{ + return _ymin; +} + +void QFxDrag::setYmin(int m) +{ + _ymin = m; +} + +/*! + \property QFxDrag::ymax + \brief the maximum y position for the target + + If y-axis dragging is enabled, ymax limits how far down the target can be dragged. If y-axis dragging is not enabled, this property has no effect. +*/ +int QFxDrag::ymax() const +{ + return _ymax; +} + +void QFxDrag::setYmax(int m) +{ + _ymax = m; +} + +/*! + \qmlclass MouseRegion + \brief The MouseRegion element enables simple mouse handling. + \inherits Item + + A MouseRegion is typically used in conjunction with a visible element, where the MouseRegion effectively 'proxies' mouse handling for that element. For example, we can put a MouseRegion in a Rect that changes the Rect color to red when clicked: + \code + <Rect width="100" height="100"> + <MouseRegion anchors.fill="{parent}" onClick="parent.color = 'red';"/> + </Rect> + \endcode + + For the mouse handlers the variable mouseButton is set to be one of 'Left', 'Right', 'Middle', + or 'None'. This allows you to distinguish left and right clicking. Below we have the previous + example extended so as to give a different color when you right click. + \code + <Rect width="100" height="100"> + <MouseRegion anchors.fill="{parent}" onClick="if(mouseButton=='Right') { parent.color='blue';} else { parent.color = 'red';}"/> + </Rect> + \endcode + + For basic key handling, see \l KeyActions. + + MouseRegion is an invisible element: it is never painted. +*/ + +/*! + \qmlsignal MouseRegion::onEntered + + This handler is called when the mouse enters the mouse region. + + \warning This handler is not yet implemented. +*/ + +/*! + \qmlsignal MouseRegion::onExited + + This handler is called when the mouse exists the mouse region. + + \warning This handler is not yet implemented. +*/ + +/*! + \qmlsignal MouseRegion::onReenteredWhilePressed + + This handler is called when the mouse reenters the mouse region while pressed. +*/ + +/*! + \qmlsignal MouseRegion::onExitedWhilePressed + + This handler is called when the mouse exists the mouse region while pressed. +*/ + +/*! + \qmlsignal MouseRegion::onClicked + + This handler is called when there is a click. A click is defined as a press followed by a release, + both inside the MouseRegion (pressing, moving outside the MouseRegion, and then moving back inside and + releasing is also considered a click). + The x and y parameters tell you the position of the release of the click. The followsPressAndHold parameter tells + you whether or not the release portion of the click followed a long press. +*/ + +/*! + \qmlsignal MouseRegion::onPressed + + This handler is called when there is a press. + The x and y parameters tell you the position of the press. +*/ + +/*! + \qmlsignal MouseRegion::onReleased + + This handler is called when there is a release. + The x and y parameters tell you the position of the release. The isClick parameter tells you whether + or not the release is part of a click. The followsPressAndHold parameter tells you whether or not the + release followed a long press. +*/ + +/*! + \qmlsignal MouseRegion::onPressAndHold + + This handler is called when there is a long press (currently 800ms). + The x and y parameters tell you the position of the long press. +*/ + +/*! + \qmlsignal MouseRegion::onDoubleClicked + + This handler is called when there is a double-click (a press followed by a release followed by a press). + The x and y parameters tell you the position of the double-click. +*/ + +QML_DEFINE_TYPE(QFxMouseRegion,MouseRegion); +/*! + \internal + \class QFxMouseRegion + \brief The QFxMouseRegion class provides a simple mouse handling abstraction for use within Qml. + + \ingroup coreitems + + All QFxItem derived classes can do mouse handling but the QFxMouseRegion class exposes mouse + handling data as properties and tracks flicking and dragging of the mouse. + + A QFxMouseRegion object can be instantiated in Qml using the tag \l MouseRegion. + */ +QFxMouseRegion::QFxMouseRegion(QFxItem *parent) + : QFxItem(*(new QFxMouseRegionPrivate), parent) +{ + Q_D(QFxMouseRegion); + d->init(); +} + +QFxMouseRegion::QFxMouseRegion(QFxMouseRegionPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + Q_D(QFxMouseRegion); + d->init(); +} + +QFxMouseRegion::~QFxMouseRegion() +{ +} + +/*! + \qmlproperty int MouseRegion::mouseX + \qmlproperty int MouseRegion::mouseY + + The coordinates of the mouse while pressed. The coordinates are relative to the item that was pressed. +*/ +int QFxMouseRegion::mouseX() const +{ + Q_D(const QFxMouseRegion); + return int(d->lastPos.x()); +} + +int QFxMouseRegion::mouseY() const +{ + Q_D(const QFxMouseRegion); + return int(d->lastPos.y()); +} + +/*! + \qmlproperty bool MouseRegion::enabled + This property holds whether the item accepts mouse events. +*/ +bool QFxMouseRegion::isEnabled() const +{ + Q_D(const QFxMouseRegion); + return d->absorb; +} + +void QFxMouseRegion::setEnabled(bool a) +{ + Q_D(QFxMouseRegion); + d->absorb = a; +} + +void QFxMouseRegionPrivate::bindButtonValue(Qt::MouseButton b) +{ + Q_Q(QFxMouseRegion); + QString bString; + switch(b){ + case Qt::LeftButton: + bString = QLatin1String("Left"); break; + case Qt::RightButton: + bString = QLatin1String("Right"); break; + case Qt::MidButton: + bString = QLatin1String("Middle"); break; + default: + bString = QLatin1String("None"); break; + } + q->itemContext()->setContextProperty(QLatin1String("mouseButton"), bString); +} + +void QFxMouseRegion::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxMouseRegion); + d->moved = false; + if(!d->absorb) + QFxItem::mousePressEvent(event); + else { + if (!d->inside) { + d->inside = true; + emit hoveredChanged(); + } + d->longPress = false; + d->lastPos = event->pos(); + d->dragX = drag()->axis().contains(QLatin1String("x")); + d->dragY = drag()->axis().contains(QLatin1String("y")); + d->dragged = false; + d->start = event->pos(); + d->startScene = event->scenePos(); + // ### we should only start timer if pressAndHold is connected to (but connectNotify doesn't work) + d->pressAndHoldTimer.start(PressAndHoldDelay, this); + setKeepMouseGrab(false); + d->bindButtonValue(event->button()); + setPressed(true); + emit positionChanged(); + event->accept(); + } +} + +void QFxMouseRegion::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxMouseRegion); + if(!d->absorb) { + QFxItem::mouseMoveEvent(event); + return; + } + + d->lastPos = event->pos(); + + // ### we should skip this if these signals aren't used + const QRect &bounds = itemBoundingRect(); + bool contains = bounds.contains(d->lastPos.toPoint()); + if (d->inside && !contains) { + d->inside = false; + emit hoveredChanged(); + emit exitedWhilePressed(); + } else if (!d->inside && contains) { + d->inside = true; + emit hoveredChanged(); + emit reenteredWhilePressed(); + } + + if(drag()->target()) { + if(!d->moved) { + if(d->dragX) d->startX = int(drag()->target()->x()); //### change startX and startY to qreal? + if(d->dragY) d->startY = int(drag()->target()->y()); + } + + QPointF startLocalPos; + QPointF curLocalPos; + if (drag()->target()->parent()) { + startLocalPos = drag()->target()->parent()->mapFromScene(d->startScene); + curLocalPos = drag()->target()->parent()->mapFromScene(event->scenePos()); + } else { + startLocalPos = d->startScene; + curLocalPos = event->scenePos(); + } + + int dx = int(qAbs(curLocalPos.x() - startLocalPos.x())); + int dy = int(qAbs(curLocalPos.y() - startLocalPos.y())); + if ((d->dragX && !(dx < DragThreshold)) || (d->dragY && !(dy < DragThreshold))) + d->dragged = true; + if (!keepMouseGrab()) { + if ((!d->dragY && dy < DragThreshold && d->dragX && dx > DragThreshold) + || (!d->dragX && dx < DragThreshold && d->dragY && dy > DragThreshold) + || (d->dragX && d->dragY)) { + setKeepMouseGrab(true); + } + } + + if(d->dragX) { + qreal x = (curLocalPos.x() - startLocalPos.x()) + d->startX; + if (x < drag()->xmin()) + x = drag()->xmin(); + else if (x > drag()->xmax()) + x = drag()->xmax(); + drag()->target()->setX(x); + } + if(d->dragY) { + qreal y = (curLocalPos.y() - startLocalPos.y()) + d->startY; + if (y < drag()->ymin()) + y = drag()->ymin(); + else if (y > drag()->ymax()) + y = drag()->ymax(); + drag()->target()->setY(y); + } + } + d->moved = true; + emit positionChanged(); + event->accept(); +} + + +void QFxMouseRegion::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxMouseRegion); + if(!d->absorb) + QFxItem::mouseReleaseEvent(event); + else { + setPressed(false); + //d->inside = false; + //emit hoveredChanged(); + event->accept(); + } +} + +void QFxMouseRegion::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxMouseRegion); + if(!d->absorb) + QFxItem::mouseDoubleClickEvent(event); + else { + //d->inside = true; + //emit hoveredChanged(); + setPressed(true); + emit this->doubleClicked(d->lastPos.x(), d->lastPos.y()); + event->accept(); + } +} + +void QFxMouseRegion::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + Q_D(QFxMouseRegion); + if(!d->absorb) + QFxItem::hoverEnterEvent(event); + else { + setHovered(true); + emit entered(); + } +} + +void QFxMouseRegion::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_D(QFxMouseRegion); + if(!d->absorb) + QFxItem::hoverLeaveEvent(event); + else { + setHovered(false); + emit exited(); + } +} + +void QFxMouseRegion::mouseUngrabEvent() +{ + Q_D(QFxMouseRegion); + if (d->pressed) { + // if our mouse grab has been removed (probably by Flickable), fix our + // state + d->pressed = false; + //d->inside = false; + setKeepMouseGrab(false); + emit pressedChanged(); + //emit hoveredChanged(); + } +} + +void QFxMouseRegion::timerEvent(QTimerEvent *event) +{ + Q_D(QFxMouseRegion); + if (event->timerId() == d->pressAndHoldTimer.timerId()) { + d->pressAndHoldTimer.stop(); + if (d->pressed && d->dragged == false && d->inside == true) { + d->longPress = true; + emit pressAndHold(d->lastPos.x(), d->lastPos.y()); + } + } +} + +/*! + \qmlproperty bool MouseRegion::containsMouse + This property holds whether the mouse is currently inside the mouse region. + + \warning This property is only partially implemented -- it is only valid when the mouse is pressed, and not for hover events. +*/ +bool QFxMouseRegion::hovered() +{ + Q_D(QFxMouseRegion); + return d->hovered || d->inside; +} + +/*! + \qmlproperty bool MouseRegion::pressed + This property holds whether the mouse region is currently pressed. +*/ +bool QFxMouseRegion::pressed() +{ + Q_D(QFxMouseRegion); + return d->pressed; +} + +void QFxMouseRegion::setHovered(bool h) +{ + Q_D(QFxMouseRegion); + if(d->hovered != h) { + d->hovered = h; + emit hoveredChanged(); + } +} + +void QFxMouseRegion::setPressed(bool p) +{ + Q_D(QFxMouseRegion); + bool isclick = d->pressed == true && p == false && d->dragged == false && d->inside == true; + + if(d->pressed != p) { + d->pressed = p; + if(d->pressed) + emit pressed(d->lastPos.x(), d->lastPos.y()); + else { + emit released(d->lastPos.x(), d->lastPos.y(), isclick, d->longPress); + if (isclick) + emit clicked(d->lastPos.x(), d->lastPos.y(), d->longPress); + } + + emit pressedChanged(); + } +} + +/*! + \property QFxMouseRegion::drag + \brief The current drag being performed on the Mouse Region. +*/ +QFxDrag *QFxMouseRegion::drag() +{ + Q_D(QFxMouseRegion); + return &(d->drag); +} + +/*! + \qmlproperty Item MouseRegion::drag.target + \qmlproperty string MouseRegion::drag.axis + \qmlproperty int MouseRegion::drag.xmin + \qmlproperty int MouseRegion::drag.xmax + \qmlproperty int MouseRegion::drag.ymin + \qmlproperty int MouseRegion::drag.ymax + + drag provides a convenient way to make an item draggable. + + \list + \i \c target specifies the item to drag. + \i \c axis specifies whether dragging can be done horizontally (x), vertically (y), or both (x,y) + \i the min and max properties limit how far the target can be dragged along the corresponding axes. + \endlist + + The following example uses drag to blur an image as it moves to the right: + \code + <Item id="blurtest" width="600" height="200"> + <Image id="pic" file="pic.png" anchors.verticalCenter="{parent.verticalCenter}" > + <filter><Blur radius="{pic.x/10}"/></filter> + <MouseRegion anchors.fill="{parent}" + drag.target="{pic}" + drag.axis="x" + drag.xmin="0" + drag.xmax="{blurtest.width-pic.width}" /> + </Image> + </Item> + \endcode +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxmouseregion.h b/src/declarative/fx/qfxmouseregion.h new file mode 100644 index 0000000..ee8b577 --- /dev/null +++ b/src/declarative/fx/qfxmouseregion.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXMOUSEREGION_H +#define QFXMOUSEREGION_H + +#include <qfxitem.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QFxDrag : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QFxItem *target READ target WRITE setTarget) + Q_PROPERTY(QString axis READ axis WRITE setAxis) + Q_PROPERTY(int xmin READ xmin WRITE setXmin) + Q_PROPERTY(int xmax READ xmax WRITE setXmax) + Q_PROPERTY(int ymin READ ymin WRITE setYmin) + Q_PROPERTY(int ymax READ ymax WRITE setYmax) +public: + QFxDrag(QObject *parent=0); + ~QFxDrag(); + + QFxItem *target() const; + void setTarget(QFxItem *); + QString axis() const; + void setAxis(const QString &); + int xmin() const; + void setXmin(int); + int xmax() const; + void setXmax(int); + int ymin() const; + void setYmin(int); + int ymax() const; + void setYmax(int); + +private: + QFxItem *_target; + QString _axis; + int _xmin; + int _xmax; + int _ymin; + int _ymax; + Q_DISABLE_COPY(QFxDrag) +}; +QML_DECLARE_TYPE(QFxDrag); + +class QFxMouseRegionPrivate; +class Q_DECLARATIVE_EXPORT QFxMouseRegion : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(int mouseX READ mouseX NOTIFY positionChanged) + Q_PROPERTY(int mouseY READ mouseY NOTIFY positionChanged) + Q_PROPERTY(bool containsMouse READ hovered NOTIFY hoveredChanged) + Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) + Q_PROPERTY(QFxDrag *drag READ drag) +public: + QFxMouseRegion(QFxItem *parent=0); + ~QFxMouseRegion(); + + int mouseX() const; + int mouseY() const; + + bool isEnabled() const; + void setEnabled(bool); + + bool hovered(); + bool pressed(); + + void setHovered(bool); + void setPressed(bool); + + QFxDrag *drag(); + +Q_SIGNALS: + void hoveredChanged(); + void pressedChanged(); + void positionChanged(); + + void pressed(int x, int y); + void pressAndHold(int x, int y); + void released(int x, int y, bool isClick, bool followsPressAndHold); + void clicked(int x, int y, bool followsPressAndHold); + void doubleClicked(int x, int y); + void entered(); + void exited(); + void exitedWhilePressed(); + void reenteredWhilePressed(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + void mouseUngrabEvent(); + void timerEvent(QTimerEvent *event); + +private: + void handlePress(); + void handleRelease(); + +protected: + QFxMouseRegion(QFxMouseRegionPrivate &dd, QFxItem *parent); + +private: + Q_DISABLE_COPY(QFxMouseRegion) + Q_DECLARE_PRIVATE(QFxMouseRegion) +}; +QML_DECLARE_TYPE(QFxMouseRegion); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXMOUSEREGION_H diff --git a/src/declarative/fx/qfxmouseregion_p.h b/src/declarative/fx/qfxmouseregion_p.h new file mode 100644 index 0000000..09e1b98 --- /dev/null +++ b/src/declarative/fx/qfxmouseregion_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXMOUSEREGION_P_H +#define QFXMOUSEREGION_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 "qdatetime.h" +#include "qbasictimer.h" +#include "qfxitem_p.h" + +QT_BEGIN_NAMESPACE + +class QFxMouseRegionPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxMouseRegion) + +public: + QFxMouseRegionPrivate() + : absorb(true), hovered(false), inside(true), pressed(false), longPress(0), drag(0) + { + } + + void init() + { + Q_Q(QFxMouseRegion); + q->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); + q->setOptions(QSimpleCanvasItem::HoverEvents | QSimpleCanvasItem::MouseEvents); + } + + void bindButtonValue(Qt::MouseButton); + + bool absorb; + bool hovered; + bool inside; + bool pressed; + bool longPress; + QFxDrag drag; + bool moved; + bool dragX; + bool dragY; + bool dragged; + QPointF start; + QPointF startScene; + int startX; + int startY; + QPointF lastPos; + QBasicTimer pressAndHoldTimer; +}; + +QT_END_NAMESPACE + +#endif // QFXMOUSEREGION_P_H diff --git a/src/declarative/fx/qfxpainted.cpp b/src/declarative/fx/qfxpainted.cpp new file mode 100644 index 0000000..a1eec69 --- /dev/null +++ b/src/declarative/fx/qfxpainted.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxpainted.h" +#include "qfxpainted_p.h" + + +QT_BEGIN_NAMESPACE +/*! + \class QFxPainted + \brief QFxPainted is an abstract base class for QFxView items that paint using QPainter. + + \ingroup coreitems + + This is a convenience class allowing easy use of QPainter within a custom item. + The contents of the item are cached behind the scenes. Any time you change the contents + you should call markDirty to make sure the cache is refreshed the next time painting occurs. + + \code + class GradientRect : public QFxPainted + { + Q_OBJECT + public: + GradientRect() : QFxPainted() + { + connect(this, SIGNAL(widthChanged()), this, SLOT(markDirty())); + connect(this, SIGNAL(heightChanged()), this, SLOT(markDirty())); + } + + void paint(QPainter *painter) + { + painter->fillRect(0, 0, width(), height(), QBrush(QLinearGradient(0,0,width(),height()))); + } + }; + \endcode + + \warning Dirty is only ever automatically set by QFxPainted at construction. Any other changes (including resizes) to the item need to be handled by the subclass (as in the example above). + \warning Frequent calls to markDirty will result in sub-optimal painting performance. +*/ + +/*! + Constructs the painted item. +*/ + +//### what options do we need to set? +QFxPainted::QFxPainted(QFxItem *parent) + : QFxItem(*(new QFxPaintedPrivate), parent) +{ + setOptions(HasContents, true); +} + +/*! + Destroys the item. +*/ +QFxPainted::~QFxPainted() +{ +} + +/*! \internal +*/ +QFxPainted::QFxPainted(QFxPaintedPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + setOptions(HasContents, true); +} + +/*! + \fn QFxPainted::paint(QPainter *painter) + + Implement this method to paint the item. The painting will be cached and + paint() will only be called again if \l markDirty() has been called. + + \sa markDirty() +*/ + +/*! + The contents of the item are cached behind the scenes. Any time you change the contents + you should call markDirty to make sure the cache is refreshed the next time painting occurs. +*/ +void QFxPainted::markDirty() +{ + Q_D(QFxPainted); + if (d->dirty) + return; + d->dirty = true; + + // release cache memory (will be reallocated upon paintContents, if visible) +#if defined(QFX_RENDER_QPAINTER) + d->cachedImage = QImage(); +#elif defined(QFX_RENDER_OPENGL) + d->cachedTexture.clear(); +#endif + + update(); +} + +#if defined(QFX_RENDER_QPAINTER) +void QFxPainted::paintContents(QPainter &p) +{ + Q_D(QFxPainted); + if (d->dirty) { + d->cachedImage = QImage(width(), height(), QImage::Format_ARGB32_Premultiplied); + d->cachedImage.fill(0); + QPainter painter(&(d->cachedImage)); + paint(&painter); + d->dirty = false; + } + p.drawImage(0, 0, d->cachedImage); +} +#elif defined(QFX_RENDER_OPENGL2) +void QFxPainted::paintGLContents(GLPainter &painter) +{ + Q_D(QFxPainted); + if (d->dirty) { + QImage img = QImage(width(), height(), QImage::Format_ARGB32); + img.fill(0); + QPainter p(&(img)); + paint(&p); + d->cachedTexture.setImage(img); + d->dirty = false; + } + + //### mainly copied from QFxImage code + QGLShaderProgram *shader = painter.useTextureShader(); + + GLfloat vertices[8]; + GLfloat texVertices[8]; + GLTexture *tex = &d->cachedTexture; + + float widthV = d->cachedTexture.width(); + float heightV = d->cachedTexture.height(); + + vertices[0] = 0; vertices[1] = heightV; + vertices[2] = widthV; vertices[3] = heightV; + vertices[4] = 0; vertices[5] = 0; + vertices[6] = widthV; vertices[7] = 0; + + texVertices[0] = 0; texVertices[1] = 0; + texVertices[2] = 1; texVertices[3] = 0; + texVertices[4] = 0; texVertices[5] = 1; + texVertices[6] = 1; texVertices[7] = 1; + + shader->setAttributeArray(SingleTextureShader::Vertices, vertices, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, texVertices, 2); + + glBindTexture(GL_TEXTURE_2D, tex->texture()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + shader->disableAttributeArray(SingleTextureShader::Vertices); + shader->disableAttributeArray(SingleTextureShader::TextureCoords); +} + +#endif + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxpainted.h b/src/declarative/fx/qfxpainted.h new file mode 100644 index 0000000..32f5dcb --- /dev/null +++ b/src/declarative/fx/qfxpainted.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 QtDeclarative 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 QFXPAINTED_H +#define QFXPAINTED_H + +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxPaintedPrivate; +class Q_DECLARATIVE_EXPORT QFxPainted : public QFxItem +{ +Q_OBJECT +public: + QFxPainted(QFxItem *parent); + ~QFxPainted(); + + virtual void paint(QPainter *painter) = 0; + +#if defined(QFX_RENDER_QPAINTER) + void paintContents(QPainter &painter); +#elif defined(QFX_RENDER_OPENGL2) + void paintGLContents(GLPainter &); +#endif + +protected Q_SLOTS: + void markDirty(); + +protected: + QFxPainted(QFxPaintedPrivate &dd, QFxItem *parent); + +private: + Q_DISABLE_COPY(QFxPainted) + Q_DECLARE_PRIVATE(QFxPainted) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXPAINTED_H diff --git a/src/declarative/fx/qfxpainted_p.h b/src/declarative/fx/qfxpainted_p.h new file mode 100644 index 0000000..68ac83e --- /dev/null +++ b/src/declarative/fx/qfxpainted_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 QtDeclarative 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 QFXPAINTED_P_H +#define QFXPAINTED_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 "qfxitem_p.h" +#if defined(QFX_RENDER_OPENGL) +#include "gltexture.h" +#endif + +QT_BEGIN_NAMESPACE + +class QFxPaintedPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxPainted) + +public: + QFxPaintedPrivate() + : dirty(true) + { + } + + bool dirty; + +#if defined(QFX_RENDER_QPAINTER) + QSimpleCanvasConfig::Image cachedImage; +#elif defined(QFX_RENDER_OPENGL) + GLTexture cachedTexture; +#endif +}; + +QT_END_NAMESPACE + +#endif // QFXPAINTED_P_H diff --git a/src/declarative/fx/qfxparticles.cpp b/src/declarative/fx/qfxparticles.cpp new file mode 100644 index 0000000..3526541 --- /dev/null +++ b/src/declarative/fx/qfxparticles.cpp @@ -0,0 +1,1063 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "gfxtimeline.h" +#include "qfxitem_p.h" +#if defined(QFX_RENDER_OPENGL) +#include "gltexture.h" +#endif + +#include <stdlib.h> +#include <math.h> +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#define M_PI_2 (M_PI / 2.) +#endif +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif +#include <qfxpixmap.h> +#include <qfxperf.h> +#include <private/qmlanimation_p.h> + +#include "qfxparticles.h" + + +QT_BEGIN_NAMESPACE +#define PI_SQR 9.8696044 +// parabolic approximation +inline qreal fastSin(qreal theta) +{ + const qreal b = 4 / M_PI; + const qreal c = -4 / PI_SQR; + + qreal y = b * theta + c * theta * qAbs(theta); + return y; +} + +inline qreal fastCos(qreal theta) +{ + theta += M_PI_2; + if (theta > M_PI) + theta -= 2 * M_PI; + + return fastSin(theta); +} + +class QFxParticle +{ +public: + QFxParticle(int time) : lifeSpan(1000), fadeOutAge(800) + , opacity(0), birthTime(time), x_velocity(0), y_velocity(0) + , state(FadeIn), data(0) + { + } + + int lifeSpan; + int fadeOutAge; + qreal x; + qreal y; + qreal opacity; + int birthTime; + qreal x_velocity; + qreal y_velocity; + enum State { FadeIn, Solid, FadeOut }; + State state; + void *data; +}; + +//--------------------------------------------------------------------------- + +QML_DEFINE_TYPE(QFxParticleMotion,ParticleMotion); + +/*! + \class QFxParticleMotion + \ingroup effects + \brief The QFxParticleMotion class is the base class for particle motion. + + This class causes the particles to remain static. + + \sa QFxParticles +*/ +QFxParticleMotion::QFxParticleMotion(QObject *parent) : + QObject(parent) +{ +} + +/*! + Move the \a particle to its new position. \a interval is the number of + milliseconds elapsed since it was last moved. +*/ +void QFxParticleMotion::advance(QFxParticle &particle, int interval) +{ + Q_UNUSED(particle); + Q_UNUSED(interval); +} + +/*! + The \a particle has just been created. Some motion strategies require + additional state information. This can be allocated by this function. +*/ +void QFxParticleMotion::created(QFxParticle &particle) +{ + Q_UNUSED(particle); +} + +/*! + The \a particle is about to be destroyed. Any additional memory + that has been allocated for the particle should be freed. +*/ +void QFxParticleMotion::destroy(QFxParticle &particle) +{ + Q_UNUSED(particle); +} + +/*! + \qmlclass ParticleMotionLinear + \brief The ParticleMotionLinear element moves particles linearly. + + \sa Particles +*/ + +/*! + \internal + \class QFxParticleMotionLinear + \ingroup effects + \brief The QFxParticleMotionLinear class moves the particles linearly. + + \sa QFxParticles +*/ + +QML_DEFINE_TYPE(QFxParticleMotionLinear,ParticleMotionLinear); + +void QFxParticleMotionLinear::advance(QFxParticle &p, int interval) +{ + p.x += interval * p.x_velocity; + p.y += interval * p.y_velocity; +} + +/*! + \qmlclass ParticleMotionGravity + \brief The ParticleMotionGravity element moves particles towards a point. + + \sa Particles +*/ + +/*! + \internal + \class QFxParticleMotionGravity + \ingroup effects + \brief The QFxParticleMotionGravity class moves the particles towards a point. + + \sa QFxParticles +*/ + +QML_DEFINE_TYPE(QFxParticleMotionGravity,ParticleMotionGravity); + +/*! + \qmlproperty int ParticleMotionGravity::xattractor + \qmlproperty int ParticleMotionGravity::yattractor + These properties hold the x and y coordinates of the point attracting the particles. +*/ + +/*! + \qmlproperty int ParticleMotionGravity::acceleration + This property holds the acceleration to apply to the particles. +*/ + +/*! + \property QFxParticleMotionGravity::xattractor + \brief the x coordinate of the point attracting the particles. +*/ + +/*! + \property QFxParticleMotionGravity::yattractor + \brief the y coordinate of the point attracting the particles. +*/ + +/*! + \property QFxParticleMotionGravity::acceleration + \brief the acceleration to apply to the particles. +*/ + +void QFxParticleMotionGravity::advance(QFxParticle &p, int interval) +{ + qreal xdiff = p.x - _xAttr; + qreal ydiff = p.y - _yAttr; + + qreal xcomp = xdiff / (xdiff + ydiff); + qreal ycomp = ydiff / (xdiff + ydiff); + + p.x_velocity += xcomp * _accel * interval; + p.y_velocity += ycomp * _accel * interval; + + p.x += interval * p.x_velocity; + p.y += interval * p.y_velocity; +} + +/*! + \qmlclass ParticleMotionWander + \brief The ParticleMotionWander element moves particles in a somewhat random fashion. + + The particles will continue roughly in the original direction, however will randomly + drift to each side. + + The code below produces an effect similar to falling snow. + + \code + <Rect width="240" height="320" color="black"> + <Particles y="0" width="{parent.width}" height="30" + src="star.png" lifeSpan="5000" count="50" + angle="70" angleDeviation="36" + velocity="30" velocityDeviation="10"> + <ParticleMotionWander xvariance="30" pace="100"/> + </Particles> + </Rect> + \endcode + + \sa Particles +*/ + +/*! + \internal + \class QFxParticleMotionWander + \ingroup effects + \brief The QFxParticleMotionWander class moves particles in a somewhat random fashion. + + The particles will continue roughly in the original direction, however will randomly + drift to each side. + + \sa QFxParticles +*/ + +/*! + \qmlproperty QFxParticleMotionWander::xvariance + \qmlproperty QFxParticleMotionWander::yvariance + + These properties set the amount to wander in the x and y directions. +*/ + +/*! + \qmlproperty QFxParticleMotionWander::pace + This property holds how quickly the paricles will move from side to side. +*/ + +QML_DEFINE_TYPE(QFxParticleMotionWander,ParticleMotionWander); + +void QFxParticleMotionWander::advance(QFxParticle &p, int interval) +{ + if (!particles) + particles = qobject_cast<QFxParticles*>(parent()); + if (particles) { + Data *d = (Data*)p.data; + if (_xvariance != 0.) { + qreal xdiff = p.x_velocity - d->x_targetV; + if ((xdiff > d->x_peak && d->x_var > 0.0) || (xdiff < -d->x_peak && d->x_var < 0.0)) { + d->x_var = -d->x_var; + d->x_peak = _xvariance + _xvariance * qreal(rand()) / RAND_MAX; + } + p.x_velocity += d->x_var * interval; + } + p.x += interval * p.x_velocity; + + if (_yvariance != 0.) { + qreal ydiff = p.y_velocity - d->y_targetV; + if ((ydiff > d->y_peak && d->y_var > 0.0) || (ydiff < -d->y_peak && d->y_var < 0.0)) { + d->y_var = -d->y_var; + d->y_peak = _yvariance + _yvariance * qreal(rand()) / RAND_MAX; + } + p.y_velocity += d->y_var * interval; + } + p.y += interval * p.y_velocity; + } +} + +void QFxParticleMotionWander::created(QFxParticle &p) +{ + if (!p.data) { + Data *d = new Data; + p.data = (void*)d; + d->x_targetV = p.x_velocity; + d->y_targetV = p.y_velocity; + d->x_peak = _xvariance; + d->y_peak = _yvariance; + d->x_var = _pace * qreal(rand()) / RAND_MAX / 1000.0; + d->y_var = _pace * qreal(rand()) / RAND_MAX / 1000.0; + } +} + +void QFxParticleMotionWander::destroy(QFxParticle &p) +{ + if (p.data) + delete (Data*)p.data; +} + +//--------------------------------------------------------------------------- +class QFxParticlesPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxParticles) +public: + QFxParticlesPrivate() + : count(1), lifeSpan(1000), lifeSpanDev(1000), fadeInDur(200), fadeOutDur(300) + , angle(0), angleDev(0), velocity(0), velocityDev(0) + , addParticleTime(0), addParticleCount(0), lastAdvTime(0), stream(false), streamDelay(0) + , emitting(true), motion(0), clock(this) +#if defined(QFX_RENDER_OPENGL) + , texDirty(true) +#endif + { + } + + ~QFxParticlesPrivate() + { + } + + void init() {} + void tick(int time); + void createParticle(int time); + void updateOpacity(QFxParticle &p, int age); + + QString source; + QUrl url; + QSimpleCanvasConfig::Image image; + int count; + int lifeSpan; + int lifeSpanDev; + int fadeInDur; + int fadeOutDur; + qreal angle; + qreal angleDev; + qreal velocity; + qreal velocityDev; + int addParticleTime; + int addParticleCount; + int lastAdvTime; + bool stream; + int streamDelay; + bool emitting; + QFxParticleMotion *motion; + + QList<QFxParticle> particles; + QTickAnimationProxy<QFxParticlesPrivate, &QFxParticlesPrivate::tick> clock; + +#if defined(QFX_RENDER_OPENGL) + bool texDirty; + GLTexture tex; +#endif +}; + +//TODO: Stop the clock if no visible particles and not emitting (restart on emittingChanged) +void QFxParticlesPrivate::tick(int time) +{ + Q_Q(QFxParticles); + if (!motion) + motion = new QFxParticleMotionLinear(q); + + int oldCount = particles.count(); + int removed = 0; + int interval = time - lastAdvTime; + for (int i = 0; i < particles.count(); ) { + QFxParticle &particle = particles[i]; + int age = time - particle.birthTime; + if (age >= particle.lifeSpan) { + QFxParticle part = particles.takeAt(i); + motion->destroy(part); + ++removed; + } else { + updateOpacity(particle, age); + motion->advance(particle, interval); + ++i; + } + } + + while(removed-- && particles.count() < count && emitting) + createParticle(time); + + if (!addParticleTime) + addParticleTime = time; + + if(particles.count() < count && emitting) { + qreal perc = (lifeSpanDev <= 0)?(1.):(qreal(time - addParticleTime) / qreal(lifeSpanDev)); + int percCount = addParticleCount + (int)perc * (count - addParticleCount); + int streamWidth = -1; + if(stream){ + if(streamDelay > time){ + streamWidth = 0; + }else{ + int missed = time - streamDelay; + qreal streamWidthReal = qreal(count)/qreal(lifeSpan); + if(streamWidthReal < 1){ + streamDelay = time + (int)(1.0/streamWidthReal); + streamWidth = 1; + streamWidth += missed/streamDelay; + }else{ + streamWidth = qRound(streamWidthReal * (time-lastAdvTime)); + } + } + } + while(particles.count() < count && particles.count() < percCount && streamWidth--) + createParticle(time); + } + + lastAdvTime = time; + if (oldCount || particles.count()) { + if (q->itemParent()) + q->itemParent()->update(); + else + q->update(); + } else if (!count) { + lastAdvTime = 0; + clock.stop(); + } +} + +void QFxParticlesPrivate::createParticle(int time) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::CreateParticle> x; +#endif + Q_Q(QFxParticles); + QFxParticle p(time); + p.x = q->x() + q->width() * qreal(rand()) / RAND_MAX - image.width()/2.0; + p.y = q->y() + q->height() * qreal(rand()) / RAND_MAX - image.height()/2.0; + p.lifeSpan = lifeSpan; + if (lifeSpanDev) + p.lifeSpan += int(lifeSpanDev/2 - lifeSpanDev * qreal(rand()) / RAND_MAX); + p.fadeOutAge = p.lifeSpan - fadeOutDur; + if (fadeInDur == 0.) { + p.state= QFxParticle::Solid; + p.opacity = 1.0; + } + qreal a = angle; + if (angleDev) + a += angleDev/2 - angleDev * qreal(rand()) / RAND_MAX; + if (a > M_PI) + a = a - 2 * M_PI; + qreal v = velocity; + if (velocityDev) + v += velocityDev/2 - velocityDev * qreal(rand()) / RAND_MAX; + p.x_velocity = v * fastCos(a); + p.y_velocity = v * fastSin(a); + particles.append(p); + motion->created(particles.last()); +} + +void QFxParticlesPrivate::updateOpacity(QFxParticle &p, int age) +{ + switch (p.state) { + case QFxParticle::FadeIn: + if (age <= fadeInDur) { + p.opacity = qreal(age) / fadeInDur; + break; + } else { + p.opacity = 1.0; + p.state = QFxParticle::Solid; + // Fall through + } + case QFxParticle::Solid: + if (age <= p.fadeOutAge) { + break; + } else { + p.state = QFxParticle::FadeOut; + // Fall through + } + case QFxParticle::FadeOut: + p.opacity = qreal(p.lifeSpan - age) / fadeOutDur; + break; + } +} + +QML_DEFINE_TYPE(QFxParticles,Particles); + +/*! + \qmlclass Particles + \brief The Particles element generates and moves particles. + \inherits Item + + The particles created by this element cannot be dealt with directly, they can only be controlled through the parameters of the Particles element. The particles are all the same pixmap, specified by the user. + + The particles are painted relative to the parent of the Particles element. Moving the + Particles element will not move the particles already emitted. + + The below example creates two differently behaving particle sources. + The top one has particles falling from the top like snow, + the lower one has particles expelled up like a fountain. + + \code + <Rect width="240" height="320" color="black"> + <Particles y="0" width="{parent.width}" height="30" + src="star.png" lifeSpan="5000" count="50" + angle="70" angleDeviation="36" + velocity="30" velocityDeviation="10"> + <ParticleMotionWander xvariance="30" pace="100"/> + </Particles> + <Particles y="300" x="120" width="1" height="1" + src="star.png" lifeSpan="5000" count="200" + angle="270" angleDeviation="45" + velocity="50" velocityDeviation="30"> + <ParticleMotionGravity yattractor="1000" + xattractor="0" acceleration="25"/> + </Particles> + </Rect> + \endcode + \image particles.gif +*/ + +/*! + \internal + \class QFxParticles + \ingroup effects + \brief The QFxParticles class generates and moves particles. +*/ + +QFxParticles::QFxParticles(QFxItem *parent) + : QFxItem(*(new QFxParticlesPrivate), parent) +{ + Q_D(QFxParticles); + d->init(); + setOptions(HasContents); +} + +QFxParticles::QFxParticles(QFxParticlesPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + Q_D(QFxParticles); + d->init(); + setOptions(HasContents); +} + +QFxParticles::~QFxParticles() +{ +} + +/*! + \qmlproperty string Particles::src + This property holds the URL of the particle image. +*/ + +/*! + \property QFxParticles::src + \brief the URL of the particle image. +*/ +QString QFxParticles::url() const +{ + Q_D(const QFxParticles); + return d->source; +} + +void QFxParticles::imageLoaded() +{ + Q_D(QFxParticles); + d->image = QFxPixmap(d->url); +#if defined(QFX_RENDER_OPENGL) + d->texDirty = true; + d->tex.clear(); +#endif + update(); +} + +void QFxParticles::setUrl(const QString &name) +{ + Q_D(QFxParticles); + + if (name == d->source) + return; + + if (!d->source.isEmpty()) + QFxPixmap::cancelGet(d->url, this, SLOT(imageLoaded())); + if (name.isEmpty()) { + d->source = name; + d->url = QUrl(); + d->image = QSimpleCanvasConfig::Image(); +#if defined(QFX_RENDER_OPENGL) + d->texDirty = true; + d->tex.clear(); +#endif + update(); + } else { + d->source = name; + d->url = itemContext()->resolvedUrl(name); + QFxPixmap::get(itemContext()->engine(), d->url, this, SLOT(imageLoaded())); + } +} + +/*! + \qmlproperty int Particles::count + This property holds the target number of particles +*/ + +/*! + \property QFxParticles::count + \brief the target number of particles +*/ +int QFxParticles::count() const +{ + Q_D(const QFxParticles); + return d->count; +} + +void QFxParticles::setCount(int cnt) +{ + Q_D(QFxParticles); + if (cnt != d->count) { + if (!d->count && d->clock.state() != QAbstractAnimation::Running) + d->clock.start(); // infinity?? + d->count = cnt; + d->addParticleTime = 0; + d->addParticleCount = d->particles.count(); + update(); + } +} + +/*! + \qmlproperty int Particles::lifeSpan + \qmlproperty int Particles::lifeSpanDeviation + + These properties describe the life span of each particle. + + The default lifespan for a particle is 1000ms. + + lifeSpanDeviation randomly varies the lifeSpan up to the specified variation. For + example, the following creates particles whose lifeSpan will vary + from 150ms to 250ms: + + \code + <Particles src="star.png" lifeSpan="200" lifeSpanDeviation="100"/> + \endcode +*/ + +/*! + \property QFxParticles::lifeSpan + \brief the life span of each particle. + + Default value is 1000ms. + + \sa QFxParticles::lifeSpanDeviation +*/ +int QFxParticles::lifeSpan() const +{ + Q_D(const QFxParticles); + return d->lifeSpan; +} + +void QFxParticles::setLifeSpan(int ls) +{ + Q_D(QFxParticles); + d->lifeSpan = ls; +} + +/*! + \property QFxParticles::lifeSpanDeviation + \brief the maximum possible deviation from the set lifeSpan. + + Randomly varies the lifeSpan up to the specified variation. For + example, the following creates particles whose lifeSpan will vary + from 150ms to 250ms: + +\code +<Particles src="star.png" lifeSpan="200" lifeSpanDeviation="100"/> +\endcode + + \sa QFxParticles::lifeSpan +*/ +int QFxParticles::lifeSpanDeviation() const +{ + Q_D(const QFxParticles); + return d->lifeSpanDev; +} + +void QFxParticles::setLifeSpanDeviation(int dev) +{ + Q_D(QFxParticles); + d->lifeSpanDev = dev; +} + +/*! + \qmlproperty int Particles::fadeInDuration + \qmlproperty int Particles::fadeOutDuration + These properties hold the time taken to fade the particles in and out. + + By default fade in is 200ms and fade out is 300ms. +*/ + +/*! + \property QFxParticles::fadeInDuration + \brief the time taken to fade in the particles. + + Default value is 200ms. +*/ +int QFxParticles::fadeInDuration() const +{ + Q_D(const QFxParticles); + return d->fadeInDur; +} + +void QFxParticles::setFadeInDuration(int dur) +{ + Q_D(QFxParticles); + if (dur >= 0.0) + d->fadeInDur = dur; +} + +/*! + \property QFxParticles::fadeOutDuration + \brief the time taken to fade out the particles. + + Default value is 300ms. +*/ +int QFxParticles::fadeOutDuration() const +{ + Q_D(const QFxParticles); + return d->fadeOutDur; +} + +void QFxParticles::setFadeOutDuration(int dur) +{ + Q_D(QFxParticles); + if (dur >= 0.0) + d->fadeOutDur = dur; +} + +/*! + \qmlproperty real Particles::angle + \qmlproperty real Particles::angleDeviation + + These properties control particle direction. + + angleDeviation randomly varies the direction up to the specified variation. For + example, the following creates particles whose initial direction will + vary from 15 degrees to 105 degrees: + + \code + <Particles src="star.png" angle="60" angleDeviation="90"/> + \endcode +*/ + +/*! + \property QFxParticles::angle + \brief the initial angle of direction. + + \sa QFxParticles::angleDeviation +*/ +qreal QFxParticles::angle() const +{ + Q_D(const QFxParticles); + return d->angle * 180.0 / M_PI; +} + +void QFxParticles::setAngle(qreal angle) +{ + Q_D(QFxParticles); + d->angle = angle * M_PI / 180.0; +} + +/*! + \property QFxParticles::angleDeviation + \brief the maximum possible deviation from the set angle. + + Randomly varies the direction up to the specified variation. For + example, the following creates particles whose initial direction will + vary from 15 degrees to 105 degrees: + +\code +<Particles src="star.png" angle="60" angleDeviation="90"/> +\endcode + + \sa QFxParticles::angle +*/ +qreal QFxParticles::angleDeviation() const +{ + Q_D(const QFxParticles); + return d->angleDev * 180.0 / M_PI; +} + +void QFxParticles::setAngleDeviation(qreal dev) +{ + Q_D(QFxParticles); + d->angleDev = dev * M_PI / 180.0;; +} + +/*! + \qmlproperty real Particles::velocity + \qmlproperty real Particles::velocityDeviation + + These properties control the velocity of the particles. + + velocityDeviation randomly varies the velocity up to the specified variation. For + example, the following creates particles whose initial velocity will + vary from 40 to 60. + + \code + <Particles src="star.png" velocity="50" velocityDeviation="20"/> + \endcode +*/ + +/*! + \property QFxParticles::velocity + \brief the initial velocity of the particles. + + \sa QFxParticles::velocityDeviation +*/ +qreal QFxParticles::velocity() const +{ + Q_D(const QFxParticles); + return d->velocity * 1000.0; +} + +void QFxParticles::setVelocity(qreal velocity) +{ + Q_D(QFxParticles); + d->velocity = velocity / 1000.0; +} + +/*! + \property QFxParticles::velocityDeviation + \brief the maximum possible deviation from the set velocity. + + Randomly varies the velocity up to the specified variation. For + example, the following creates particles whose initial velocity will + vary from 40 to 60. + +\code +<Particles src="star.png" velocity="50" velocityDeviation="20"/> +\endcode + + \sa QFxParticles::velocity +*/ +qreal QFxParticles::velocityDeviation() const +{ + Q_D(const QFxParticles); + return d->velocityDev * 1000.0; +} + +void QFxParticles::setVelocityDeviation(qreal velocity) +{ + Q_D(QFxParticles); + d->velocityDev = velocity / 1000.0; +} + +/*! + \qmlproperty bool Particles::streamIn + This property determines whether the particles stream in at a constant rate + + When stream is set to true the particles will stream in at a constant rate. + Otherwise the particles will appear as a clump. Note that this only affects the + start of the particle effect, variables such as lifespan deviation can cause the + particles to unclump over time. +*/ +/*! + \property QFxParticles::streamIn + \brief determines whether the particles stream in at a constant rate + + When stream is set to true the particles will stream in at a constant rate. + Otherwise the particles will appear as a clump. Note that this only affects the + start of the particle effect, variables such as lifespan deviation can cause the + +*/ +//The name may need a rethink +bool QFxParticles::streamIn() const +{ + Q_D(const QFxParticles); + return d->stream; +} + +void QFxParticles::setStreamIn(bool b) +{ + Q_D(QFxParticles); + d->stream = b; +} + +/*! + \qmlproperty bool Particles::emitting + This property determines whether new particles are created + + If emitting is set to false no new particles will be created. This means that + when a particle reaches the end of its lifespan it is not replaced. This + property can be used to turn particles on and off with a more natural look. + + The default setting is true. Note that if it initialized to false no particles + will be produced until it is set to true. +*/ +/*! + \property bool Particles::emitting + If emitting is set to false no new particles will be created. This means that + when a particle reaches the end of its lifespan it is not replaced. This + property can be used to turn particles on and off with a more natural look. + + The default setting is true. Note that if it initialized to false no particles + will be produced until it is set to true. +*/ +bool QFxParticles::emitting() const +{ + Q_D(const QFxParticles); + return d->emitting; +} + +void QFxParticles::setEmitting(bool r) +{ + Q_D(QFxParticles); + d->emitting = r; +} +/*! + \qmlproperty ParticleMotion Particles::motion + This property sets the type of motion to apply to the particles. + + When a particle is created it will have an initial direction and velocity. + The motion of the particle during its lifeSpan is then influenced by the + motion property. + + Default motion is ParticleMotionLinear. +*/ + +/*! + \property QFxParticles::motion + \brief sets the type of motion to apply to the particles. + + When a particle is created it will have an initial direction and velocity. + The motion of the particle during its lifeSpan is then influenced by the + motion property. + + Default motion is QFxParticleMotionLinear. +*/ +QFxParticleMotion *QFxParticles::motion() const +{ + Q_D(const QFxParticles); + return d->motion; +} + +void QFxParticles::setMotion(QFxParticleMotion *motion) +{ + Q_D(QFxParticles); + d->motion = motion; +} + +void QFxParticles::dump(int depth) +{ + Q_D(QFxParticles); + QByteArray ba(depth * 4, ' '); + qWarning() << ba.constData() << "URL:" << d->url << "Count:" << d->count; + QFxItem::dump(depth); +} + +QString QFxParticles::propertyInfo() const +{ + Q_D(const QFxParticles); + return d->url.toString(); +} + +#if defined(QFX_RENDER_QPAINTER) +void QFxParticles::paintContents(QPainter &p) +{ + Q_D(QFxParticles); + if (d->image.isNull()) + return; + + const int myX = x(); + const int myY = y(); + for (int i = 0; i < d->particles.count(); ++i) { + const QFxParticle &particle = d->particles.at(i); + p.setOpacity(particle.opacity); + p.drawImage(particle.x-myX, particle.y-myY, d->image); + } +} +#elif defined(QFX_RENDER_OPENGL2) +void QFxParticles::paintGLContents(GLPainter &p) +{ + Q_D(QFxParticles); + if (d->image.isNull()) + return; + + if (d->texDirty && !d->image.isNull()) { + d->tex.setImage(d->image); + d->tex.setHorizontalWrap(GLTexture::Repeat); + d->tex.setVerticalWrap(GLTexture::Repeat); + } + d->texDirty = false; + + SingleTextureOpacityShader *shader = basicShaders()->singleTextureOpacity(); + shader->enable(); + shader->setTransform(p.activeTransform); + + glBindTexture(GL_TEXTURE_2D, d->tex.texture()); + + const int myX = (int)x(); + const int myY = (int)y(); + float widthV = d->image.width(); + float heightV = d->image.height(); + for (int i = 0; i < d->particles.count(); ++i) { + const QFxParticle &particle = d->particles.at(i); + float left = particle.x - myX; + float right = particle.x - myX + widthV; + float top = particle.y - myY; + float bottom = particle.y - myY + heightV; + + GLfloat vertices[] = { left, bottom, + right, bottom, + left, top, + right, top }; + + GLfloat texVertices[] = { 0, 0, + 1, 0, + 0, 1, + 1, 1 }; + + shader->setAttributeArray(SingleTextureShader::Vertices, vertices, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, texVertices, 2); + shader->setOpacity(particle.opacity * p.activeOpacity); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + + shader->disableAttributeArray(SingleTextureShader::Vertices); + shader->disableAttributeArray(SingleTextureShader::TextureCoords); +} +#endif + +void QFxParticles::componentComplete() +{ + Q_D(QFxParticles); + QFxItem::componentComplete(); + if (d->count) + d->clock.start(); // infinity?? + if (d->lifeSpanDev > d->lifeSpan) + d->lifeSpanDev = d->lifeSpan; +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxparticles.h b/src/declarative/fx/qfxparticles.h new file mode 100644 index 0000000..d9c810d --- /dev/null +++ b/src/declarative/fx/qfxparticles.h @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXPARTICLES_H +#define QFXPARTICLES_H + +#include <qfxitem.h> + +#if defined(QFX_RENDER_OPENGL) +#include "gltexture.h" +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QFxParticle; +class QFxParticles; +class QFxParticleMotion : public QObject +{ + Q_OBJECT +public: + QFxParticleMotion(QObject *parent=0); + + virtual void advance(QFxParticle &, int interval); + virtual void created(QFxParticle &); + virtual void destroy(QFxParticle &); +}; +QML_DECLARE_TYPE(QFxParticleMotion); + +class QFxParticleMotionLinear : public QFxParticleMotion +{ + Q_OBJECT +public: + QFxParticleMotionLinear(QObject *parent=0) + : QFxParticleMotion(parent) {} + + virtual void advance(QFxParticle &, int interval); +}; +QML_DECLARE_TYPE(QFxParticleMotionLinear); + +class QFxParticleMotionGravity : public QFxParticleMotion +{ + Q_OBJECT + + Q_PROPERTY(int xattractor READ xAttractor WRITE setXAttractor) + Q_PROPERTY(int yattractor READ yAttractor WRITE setYAttractor) + Q_PROPERTY(int acceleration READ acceleration WRITE setAcceleration) +public: + QFxParticleMotionGravity(QObject *parent=0) + : QFxParticleMotion(parent), _xAttr(0), _yAttr(0), _accel(0.00005) {} + + int xAttractor() const { return _xAttr; } + void setXAttractor(int x) { _xAttr = x; } + + int yAttractor() const { return _yAttr; } + void setYAttractor(int y) { _yAttr = y; } + + int acceleration() const { return int(_accel * 1000000); } + void setAcceleration(int accel) { _accel = qreal(accel)/1000000.0; } + + virtual void advance(QFxParticle &, int interval); + +private: + int _xAttr; + int _yAttr; + qreal _accel; +}; +QML_DECLARE_TYPE(QFxParticleMotionGravity); + +class QFxParticleMotionWander : public QFxParticleMotion +{ + Q_OBJECT +public: + QFxParticleMotionWander() + : QFxParticleMotion(), particles(0), _xvariance(0), _yvariance(0) {} + + virtual void advance(QFxParticle &, int interval); + virtual void created(QFxParticle &); + virtual void destroy(QFxParticle &); + + struct Data { + qreal x_targetV; + qreal y_targetV; + qreal x_peak; + qreal y_peak; + qreal x_var; + qreal y_var; + }; + + Q_PROPERTY(int xvariance READ xVariance WRITE setXVariance); + int xVariance() const { return int(_xvariance * 1000); } + void setXVariance(int var) { _xvariance = var / 1000.0; } + + Q_PROPERTY(int yvariance READ yVariance WRITE setYVariance); + int yVariance() const { return int(_yvariance * 1000); } + void setYVariance(int var) { _yvariance = var / 1000.0; } + + Q_PROPERTY(int pace READ pace WRITE setPace); + int pace() const { return int(_pace * 1000); } + void setPace(int pace) { _pace = pace / 1000.0; } + +private: + QFxParticles *particles; + qreal _xvariance; + qreal _yvariance; + qreal _pace; +}; +QML_DECLARE_TYPE(QFxParticleMotionWander); + +class QFxParticlesPrivate; +class Q_DECLARATIVE_EXPORT QFxParticles : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(QString src READ url WRITE setUrl); + Q_PROPERTY(int count READ count WRITE setCount); + Q_PROPERTY(int lifeSpan READ lifeSpan WRITE setLifeSpan); + Q_PROPERTY(int lifeSpanDeviation READ lifeSpanDeviation WRITE setLifeSpanDeviation); + Q_PROPERTY(int fadeInDuration READ fadeInDuration WRITE setFadeInDuration); + Q_PROPERTY(int fadeOutDuration READ fadeOutDuration WRITE setFadeOutDuration); + Q_PROPERTY(qreal angle READ angle WRITE setAngle); + Q_PROPERTY(qreal angleDeviation READ angleDeviation WRITE setAngleDeviation); + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity); + Q_PROPERTY(qreal velocityDeviation READ velocityDeviation WRITE setVelocityDeviation); + Q_PROPERTY(bool streamIn READ streamIn WRITE setStreamIn); + Q_PROPERTY(bool emitting READ emitting WRITE setEmitting); + Q_PROPERTY(QFxParticleMotion *motion READ motion WRITE setMotion); + Q_CLASSINFO("DefaultProperty", "motion"); + +public: + QFxParticles(QFxItem *parent=0); + ~QFxParticles(); + + QString url() const; + void setUrl(const QString &); + + int count() const; + void setCount(int cnt); + + int lifeSpan() const; + void setLifeSpan(int); + + int lifeSpanDeviation() const; + void setLifeSpanDeviation(int); + + int fadeInDuration() const; + void setFadeInDuration(int); + + int fadeOutDuration() const; + void setFadeOutDuration(int); + + qreal angle() const; + void setAngle(qreal); + + qreal angleDeviation() const; + void setAngleDeviation(qreal); + + qreal velocity() const; + void setVelocity(qreal); + + qreal velocityDeviation() const; + void setVelocityDeviation(qreal); + + bool streamIn() const; + void setStreamIn(bool); + + bool emitting() const; + void setEmitting(bool); + + QFxParticleMotion *motion() const; + void setMotion(QFxParticleMotion *); + + virtual void dump(int depth); + virtual QString propertyInfo() const; + +#if defined(QFX_RENDER_QPAINTER) + void paintContents(QPainter &p); +#elif defined(QFX_RENDER_OPENGL2) + void paintGLContents(GLPainter &); +#endif + +protected: + virtual void componentComplete(); + QFxParticles(QFxParticlesPrivate &dd, QFxItem *parent); + +private Q_SLOTS: + void imageLoaded(); + +private: + Q_DISABLE_COPY(QFxParticles) + Q_DECLARE_PRIVATE(QFxParticles) +}; +QML_DECLARE_TYPE(QFxParticles); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/fx/qfxpath.cpp b/src/declarative/fx/qfxpath.cpp new file mode 100644 index 0000000..199f5fb --- /dev/null +++ b/src/declarative/fx/qfxpath.cpp @@ -0,0 +1,745 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxpath.h" +#include "qfxpath_p.h" +#include <qfxperf.h> +#include <private/qbezier_p.h> + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxPath,Path); +QML_DEFINE_TYPE(QFxPathElement,PathElement); +QML_DEFINE_TYPE(QFxCurve,Curve); +QML_DEFINE_TYPE(QFxPathAttribute,PathAttribute); +QML_DEFINE_TYPE(QFxPathPercent,PathPercent); +QML_DEFINE_TYPE(QFxPathLine,PathLine); +QML_DEFINE_TYPE(QFxPathQuad,PathQuad); +QML_DEFINE_TYPE(QFxPathCubic,PathCubic); + +/*! + \qmlclass Path + \brief The Path element defines a path for use by \l PathView. + + \sa PathElement, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic +*/ + +/*! + \internal + \class QFxPath + \ingroup utility + \brief The QFxPath class defines a path. + \sa QFxPathView +*/ +QFxPath::QFxPath(QObject *parent) + : QObject(*(new QFxPathPrivate), parent) +{ +} + +QFxPath::QFxPath(QFxPathPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +QFxPath::~QFxPath() +{ +} + +/*! + \qmlproperty int Path::startX + This property holds the starting x position of the path. +*/ + +/*! + \property QFxPath::startX + \brief the starting x position of the path. +*/ + +int QFxPath::startX() const +{ + Q_D(const QFxPath); + return d->startX; +} + +void QFxPath::setStartX(int x) +{ + Q_D(QFxPath); + d->startX = x; +} + +/*! + \qmlproperty int Path::startY + This property holds the starting y position of the path. +*/ + +/*! + \property QFxPath::startY + \brief the starting y position of the path. +*/ + +int QFxPath::startY() const +{ + Q_D(const QFxPath); + return d->startY; +} + +void QFxPath::setStartY(int y) +{ + Q_D(QFxPath); + d->startY = y; +} + +/*! + \qmlproperty list<PathElement> Path::pathElements + This property holds the elements composing the path. + + \default + + A path can contain the following path elements: + \list + \i \l PathLine - a straight line to a given position. + \i \l PathQuad - a quadratic Bezier curve to a given position with a control point. + \i \l PathCubic - a cubic Bezier curve to a given position with two control points. + \i \l PathAttribute - an attribute at a given position in the path. + \i \l PathPercent - a way to spread out items along various segments of the path. + \endlist + + \code + <Path startX="240" startY="350"> + <PathAttribute name="scale" value="1.0"/> + <PathAttribute name="opacity" value="1"/> + <PathQuad x="240" y="150" controlX="660" controlY="250"/> + <PathAttribute name="scale" value="0.1"/> + <PathAttribute name="opacity" value="-0.5"/> + <PathCubic x="240" y="350" control1X="-180" control1Y="250" control2X="0" control2Y="25"/> + </Path> + \endcode +*/ + +QList<QFxPathElement *>* QFxPath::pathElements() +{ + Q_D(QFxPath); + return &(d->_pathElements); +} + +void QFxPath::interpolate(int idx, const QString &name, qreal value) +{ + Q_D(QFxPath); + if(!idx) + return; + + qreal lastValue = 0; + qreal lastPercent = 0; + int search = idx - 1; + while(search >= 0) { + const AttributePoint &point = d->_attributePoints.at(search); + if(point.values.contains(name)) { + lastValue = point.values.value(name); + lastPercent = point.origpercent; + break; + } + --search; + } + + ++search; + + const AttributePoint &curPoint = d->_attributePoints.at(idx); + + for(int ii = search; ii < idx; ++ii) { + AttributePoint &point = d->_attributePoints[ii]; + + qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent); + point.values.insert(name, val); + } +} + +void QFxPath::endpoint(const QString &name) +{ + Q_D(QFxPath); + const AttributePoint &first = d->_attributePoints.first(); + qreal val = first.values.value(name); + for(int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) { + const AttributePoint &point = d->_attributePoints.at(ii); + if(point.values.contains(name)) { + for(int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) { + AttributePoint &setPoint = d->_attributePoints[jj]; + setPoint.values.insert(name, val); + } + return; + } + } +} + +void QFxPath::processPath() +{ + Q_D(QFxPath); + + d->_pointCache.clear(); + d->_attributePoints.clear(); + d->_path = QPainterPath(); + + AttributePoint first; + for(int ii = 0; ii < d->_attributes.count(); ++ii) + first.values[d->_attributes.at(ii)] = 0; + d->_attributePoints << first; + + d->_path.moveTo(d->startX, d->startY); + + foreach (QFxPathElement *pathElement, d->_pathElements) { + if(QFxCurve *curve = qobject_cast<QFxCurve *>(pathElement)) { + curve->addToPath(d->_path); + AttributePoint p; + p.origpercent = d->_path.length(); + d->_attributePoints << p; + } else if(QFxPathAttribute *attribute = qobject_cast<QFxPathAttribute *>(pathElement)) { + AttributePoint &point = d->_attributePoints.last(); + point.values[attribute->name()] = attribute->value(); + interpolate(d->_attributePoints.count() - 1, attribute->name(), attribute->value()); + } else if(QFxPathPercent *percent = qobject_cast<QFxPathPercent *>(pathElement)) { + AttributePoint &point = d->_attributePoints.last(); + point.values[QLatin1String("_qfx_percent")] = percent->value(); + interpolate(d->_attributePoints.count() - 1, QLatin1String("_qfx_percent"), percent->value()); + } + } + + // Fixup end points + const AttributePoint &last = d->_attributePoints.last(); + for(int ii = 0; ii < d->_attributes.count(); ++ii) { + if(!last.values.contains(d->_attributes.at(ii))) + endpoint(d->_attributes.at(ii)); + } + + // Adjust percent + qreal length = d->_path.length(); + qreal prevpercent = 0; + qreal prevorigpercent = 0; + for(int ii = 0; ii < d->_attributePoints.count(); ++ii) { + const AttributePoint &point = d->_attributePoints.at(ii); + if(point.values.contains(QLatin1String("_qfx_percent"))) { //special string for QFxPathPercent + if ( ii > 0) { + qreal scale = (d->_attributePoints[ii].origpercent/length - prevorigpercent) / + (point.values.value(QLatin1String("_qfx_percent"))-prevpercent); + d->_attributePoints[ii].scale = scale; + } + d->_attributePoints[ii].origpercent /= length; + d->_attributePoints[ii].percent = point.values.value(QLatin1String("_qfx_percent")); + prevorigpercent = d->_attributePoints[ii].origpercent; + prevpercent = d->_attributePoints[ii].percent; + } else { + d->_attributePoints[ii].origpercent /= length; + d->_attributePoints[ii].percent = d->_attributePoints[ii].origpercent; + } + } + + emit changed(); +} + +void QFxPath::componentComplete() +{ + Q_D(QFxPath); + QSet<QString> attrs; + // First gather up all the attributes + foreach (QFxPathElement *pathElement, d->_pathElements) { + if(QFxPathAttribute *attribute = + qobject_cast<QFxPathAttribute *>(pathElement)) + attrs.insert(attribute->name()); + } + d->_attributes = attrs.toList(); + + processPath(); + + foreach (QFxPathElement *pathElement, d->_pathElements) + connect(pathElement, SIGNAL(changed()), this, SLOT(processPath())); +} + +QPainterPath QFxPath::path() const +{ + Q_D(const QFxPath); + return d->_path; +} + +QStringList QFxPath::attributes() const +{ + Q_D(const QFxPath); + return d->_attributes; +} +#include <QTime> + +static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength) +{ + const int lastElement = path.elementCount() - 1; + for (int i=*from; i <= lastElement; ++i) { + const QPainterPath::Element &e = path.elementAt(i); + + switch (e.type) { + case QPainterPath::MoveToElement: + break; + case QPainterPath::LineToElement: + { + QLineF line(path.elementAt(i-1), e); + *bezLength = line.length(); + QPointF a = path.elementAt(i-1); + QPointF delta = e - a; + *from = i+1; + return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e); + } + case QPainterPath::CurveToElement: + { + QBezier b = QBezier::fromPoints(path.elementAt(i-1), + e, + path.elementAt(i+1), + path.elementAt(i+2)); + *bezLength = b.length(); + *from = i+3; + return b; + } + default: + break; + } + } + *from = lastElement; + *bezLength = 0; + return QBezier(); +} + +void QFxPath::createPointCache() const +{ + Q_D(const QFxPath); +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::PathCache> pc; +#endif + qreal pathLength = d->_path.length(); + const int points = int(pathLength*2); + const int lastElement = d->_path.elementCount() - 1; + d->_pointCache.resize(points+1); + + int currElement = 0; + qreal bezLength = 0; + QBezier currBez = nextBezier(d->_path, &currElement, &bezLength); + qreal currLength = bezLength; + qreal epc = currLength / pathLength; + + for (int i = 0; i < d->_pointCache.size(); i++) { + //find which set we are in + qreal prevPercent = 0; + qreal prevOrigPercent = 0; + for(int ii = 0; ii < d->_attributePoints.count(); ++ii) { + qreal percent = qreal(i)/points; + const AttributePoint &point = d->_attributePoints.at(ii); + if(percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item + qreal elementPercent = (percent - prevPercent); + + qreal spc = prevOrigPercent + elementPercent * point.scale; + + while (spc > epc) { + if (currElement > lastElement) + break; + currBez = nextBezier(d->_path, &currElement, &bezLength); + if (bezLength == 0.0) { + currLength = pathLength; + epc = 1.0; + break; + } + currLength += bezLength; + epc = currLength / pathLength; + } + qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength; + d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1))); + break; + } + prevOrigPercent = point.origpercent; + prevPercent = point.percent; + } + } +} + +QPointF QFxPath::pointAt(qreal p) const +{ + Q_D(const QFxPath); + if (d->_pointCache.isEmpty()) { + createPointCache(); + } + int idx = qRound(p*d->_pointCache.size()); + if (idx >= d->_pointCache.size()) + idx = d->_pointCache.size() - 1; + else if (idx < 0) + idx = 0; + return d->_pointCache.at(idx); +} + +qreal QFxPath::attributeAt(const QString &name, qreal percent) const +{ + Q_D(const QFxPath); + if(percent < 0 || percent > 1) + return 0; + + for(int ii = 0; ii < d->_attributePoints.count(); ++ii) { + const AttributePoint &point = d->_attributePoints.at(ii); + + if(point.percent == percent) { + return point.values.value(name); + } else if(point.percent > percent) { + qreal lastValue = + ii?(d->_attributePoints.at(ii - 1).values.value(name)):0; + qreal lastPercent = + ii?(d->_attributePoints.at(ii - 1).percent):0; + qreal curValue = point.values.value(name); + qreal curPercent = point.percent; + + return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent); + } + } + + return 0; +} + +/****************************************************************************/ + +int QFxCurve::x() const +{ + return _x; +} + +void QFxCurve::setX(int x) +{ + if (_x != x) { + _x = x; + emit changed(); + } +} + +int QFxCurve::y() const +{ + return _y; +} + +void QFxCurve::setY(int y) +{ + if (_y != y) { + _y = y; + emit changed(); + } +} + +/****************************************************************************/ + +/*! + \qmlclass PathAttribute + \brief The PathAttribute allows to set an attribute at a given position in the path. +*/ + +/*! + \internal + \class QFxPathAttribute + \ingroup utility + \brief The QFxPathAttribute class allows to set the value of an attribute at a given position in the path. + + \sa QFxPath +*/ + + +/*! + \qmlproperty string PathAttribute::name + the name of the attribute to change. +*/ + +/*! + the name of the attribute to change. +*/ + +QString QFxPathAttribute::name() const +{ + return _name; +} + +void QFxPathAttribute::setName(const QString &name) +{ + _name = name; +} + +/*! + \qmlproperty string PathAttribute::value + the new value of the attribute. +*/ + +/*! + the new value of the attribute. +*/ +qreal QFxPathAttribute::value() const +{ + return _value; +} + +void QFxPathAttribute::setValue(qreal value) +{ + if (_value != value) { + _value = value; + emit changed(); + } +} + +/****************************************************************************/ + +/*! + \qmlclass PathLine + \brief The PathLine defines a straight line. + +*/ + +/*! + \internal + \class QFxPathLine + \ingroup utility + \brief The QFxPathLine class defines a straight line. + + \sa QFxPath +*/ + +void QFxPathLine::addToPath(QPainterPath &path) +{ + path.lineTo(x(), y()); +} + +/****************************************************************************/ + +/*! + \qmlclass PathQuad + \brief The PathQuad defines a quadratic Bezier curve with a control point. + +*/ + +/*! + \internal + \class QFxPathQuad + \ingroup utility + \brief The QFxPathQuad class defines a quadratic Bezier curve with a control point. + + \sa QFxPath +*/ + +/*! + \qmlproperty string PathQuad::controlX + the x position of the control point. +*/ + +/*! + the x position of the control point. +*/ +int QFxPathQuad::controlX() const +{ + return _controlX; +} + +void QFxPathQuad::setControlX(int x) +{ + if (_controlX != x) { + _controlX = x; + emit changed(); + } +} + +/*! + \qmlproperty string PathQuad::controlY + the y position of the control point. +*/ + +/*! + the y position of the control point. +*/ +int QFxPathQuad::controlY() const +{ + return _controlY; +} + +void QFxPathQuad::setControlY(int y) +{ + if (_controlY != y) { + _controlY = y; + emit changed(); + } +} + +void QFxPathQuad::addToPath(QPainterPath &path) +{ + path.quadTo(controlX(), controlY(), x(), y()); +} + +/****************************************************************************/ + +/*! + \qmlclass PathCubic + \brief The PathCubic defines a cubic Bezier curve with two control points. + +*/ + +/*! + \internal + \class QFxPathCubic + \ingroup utility + \brief The QFxPathCubic class defines a cubic Bezier curve with two control points. + + \sa QFxPath +*/ + +/*! + \qmlproperty string PathCubic::control1X + the x position of the first control point. +*/ + +/*! + \property QFxPathCubic::control1X + \brief the x position of the first control point. +*/ +int QFxPathCubic::control1X() const +{ + return _control1X; +} + +void QFxPathCubic::setControl1X(int x) +{ + if (_control1X != x) { + _control1X = x; + emit changed(); + } +} + +/*! + \qmlproperty string PathCubic::control1Y + the y position of the first control point. +*/ + +/*! + \property QFxPathCubic::control1Y + \brief the y position of the first control point. +*/ +int QFxPathCubic::control1Y() const +{ + return _control1Y; +} + +void QFxPathCubic::setControl1Y(int y) +{ + if (_control1Y != y) { + _control1Y = y; + emit changed(); + } +} + +/*! + \qmlproperty string PathCubic::control2X + the x position of the second control point. +*/ + +/*! + \property QFxPathCubic::control2X + \brief the x position of the second control point. +*/ +int QFxPathCubic::control2X() const +{ + return _control2X; +} + +void QFxPathCubic::setControl2X(int x) +{ + if (_control2X != x) { + _control2X = x; + emit changed(); + } +} + +/*! + \qmlproperty string PathCubic::control2Y + the y position of the second control point. +*/ + +/*! + \property QFxPathCubic::control2Y + \brief the y position of the second control point. +*/ +int QFxPathCubic::control2Y() const +{ + return _control2Y; +} + +void QFxPathCubic::setControl2Y(int y) +{ + if (_control2Y != y) { + _control2Y = y; + emit changed(); + } +} + +void QFxPathCubic::addToPath(QPainterPath &path) +{ + path.cubicTo(control1X(), control1Y(), control2X(), control2Y(), x(), y()); +} + +/****************************************************************************/ + +/*! + \qmlclass PathPercent + \brief The PathPercent manipulates the way a path is interpreted. + +*/ + +/*! + \internal + \class QFxPathPercent + \ingroup utility + \brief The QFxPathPercent class manipulates the way a path is interpreted. + + QFxPathPercent allows you to bunch up items (or spread out items) along various + segments of a QFxPathView's path. + + \sa QFxPath + +*/ + +qreal QFxPathPercent::value() const +{ + return _value; +} + +void QFxPathPercent::setValue(qreal value) +{ + _value = value; +} +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxpath.h b/src/declarative/fx/qfxpath.h new file mode 100644 index 0000000..a9c48ed --- /dev/null +++ b/src/declarative/fx/qfxpath.h @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXPATH_H +#define QFXPATH_H + +#include <QObject> +#include <QPainterPath> +#include <qml.h> +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_EXPORT QFxPathElement : public QObject +{ + Q_OBJECT +public: + QFxPathElement(QObject *parent=0) : QObject(parent) {} +Q_SIGNALS: + void changed(); +}; +QML_DECLARE_TYPE(QFxPathElement); + +class Q_DECLARATIVE_EXPORT QFxPathAttribute : public QFxPathElement +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY changed) +public: + QFxPathAttribute(QObject *parent=0) : QFxPathElement(parent), _value(0) {} + + + QString name() const; + void setName(const QString &name); + + qreal value() const; + void setValue(qreal value); + +private: + QString _name; + qreal _value; +}; +QML_DECLARE_TYPE(QFxPathAttribute); + +class Q_DECLARATIVE_EXPORT QFxCurve : public QFxPathElement +{ + Q_OBJECT + + Q_PROPERTY(int x READ x WRITE setX NOTIFY changed) + Q_PROPERTY(int y READ y WRITE setY NOTIFY changed) +public: + QFxCurve(QObject *parent=0) : QFxPathElement(parent), _x(0), _y(0) {} + + int x() const; + void setX(int x); + + int y() const; + void setY(int y); + + virtual void addToPath(QPainterPath &) {} + +private: + int _x; + int _y; +}; +QML_DECLARE_TYPE(QFxCurve); + +class Q_DECLARATIVE_EXPORT QFxPathLine : public QFxCurve +{ + Q_OBJECT +public: + QFxPathLine(QObject *parent=0) : QFxCurve(parent) {} + + void addToPath(QPainterPath &path); +}; +QML_DECLARE_TYPE(QFxPathLine); + +class Q_DECLARATIVE_EXPORT QFxPathQuad : public QFxCurve +{ + Q_OBJECT + + Q_PROPERTY(int controlX READ controlX WRITE setControlX NOTIFY changed) + Q_PROPERTY(int controlY READ controlY WRITE setControlY NOTIFY changed) +public: + QFxPathQuad(QObject *parent=0) : QFxCurve(parent), _controlX(0), _controlY(0) {} + + int controlX() const; + void setControlX(int x); + + int controlY() const; + void setControlY(int y); + + void addToPath(QPainterPath &path); + +private: + int _controlX; + int _controlY; +}; +QML_DECLARE_TYPE(QFxPathQuad); + +class Q_DECLARATIVE_EXPORT QFxPathCubic : public QFxCurve +{ + Q_OBJECT + + Q_PROPERTY(int control1X READ control1X WRITE setControl1X NOTIFY changed) + Q_PROPERTY(int control1Y READ control1Y WRITE setControl1Y NOTIFY changed) + Q_PROPERTY(int control2X READ control2X WRITE setControl2X NOTIFY changed) + Q_PROPERTY(int control2Y READ control2Y WRITE setControl2Y NOTIFY changed) +public: + QFxPathCubic(QObject *parent=0) : QFxCurve(parent), _control1X(0), _control1Y(0), _control2X(0), _control2Y(0) {} + + int control1X() const; + void setControl1X(int x); + + int control1Y() const; + void setControl1Y(int y); + + int control2X() const; + void setControl2X(int x); + + int control2Y() const; + void setControl2Y(int y); + + void addToPath(QPainterPath &path); + +private: + int _control1X; + int _control1Y; + int _control2X; + int _control2Y; +}; +QML_DECLARE_TYPE(QFxPathCubic); + +class Q_DECLARATIVE_EXPORT QFxPathPercent : public QFxPathElement +{ + Q_OBJECT + Q_PROPERTY(qreal value READ value WRITE setValue) +public: + QFxPathPercent(QObject *parent=0) : QFxPathElement(parent) {} + + qreal value() const; + void setValue(qreal value); + +private: + qreal _value; +}; +QML_DECLARE_TYPE(QFxPathPercent); + +class QFxPathPrivate; +class Q_DECLARATIVE_EXPORT QFxPath : public QObject, public QmlParserStatus +{ + Q_OBJECT + + Q_INTERFACES(QmlParserStatus); + Q_PROPERTY(QList<QFxPathElement *>* pathElements READ pathElements) + Q_PROPERTY(int startX READ startX WRITE setStartX) + Q_PROPERTY(int startY READ startY WRITE setStartY) + Q_CLASSINFO("DefaultProperty", "pathElements") + Q_INTERFACES(QmlParserStatus) +public: + QFxPath(QObject *parent=0); + ~QFxPath(); + + QList<QFxPathElement *>* pathElements(); + + int startX() const; + void setStartX(int x); + + int startY() const; + void setStartY(int y); + + QPainterPath path() const; + QStringList attributes() const; + qreal attributeAt(const QString &, qreal) const; + QPointF pointAt(qreal) const; + +Q_SIGNALS: + void changed(); + +protected: + virtual void componentComplete(); + QFxPath(QFxPathPrivate &dd, QObject *parent); + +private Q_SLOTS: + void processPath(); + +private: + struct AttributePoint { + AttributePoint() : percent(0), scale(1), origpercent(0) {} + AttributePoint(const AttributePoint &other) + : percent(other.percent), scale(other.scale), origpercent(other.origpercent), values(other.values) {} + AttributePoint &operator=(const AttributePoint &other) { + percent = other.percent; scale = other.scale; origpercent = other.origpercent; values = other.values; return *this; + } + qreal percent; //massaged percent along the painter path + qreal scale; + qreal origpercent; //'real' percent along the painter path + QHash<QString, qreal> values; + }; + + void interpolate(int idx, const QString &name, qreal value); + void endpoint(const QString &name); + void createPointCache() const; + +private: + Q_DISABLE_COPY(QFxPath) + Q_DECLARE_PRIVATE(QFxPath) +}; +QML_DECLARE_TYPE(QFxPath); + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QFXPATH_H diff --git a/src/declarative/fx/qfxpath_p.h b/src/declarative/fx/qfxpath_p.h new file mode 100644 index 0000000..d4a419a --- /dev/null +++ b/src/declarative/fx/qfxpath_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 QtDeclarative 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 QFXPATH_P_H +#define QFXPATH_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 "qfxpath.h" +#include "qml.h" + + +QT_BEGIN_NAMESPACE +class QFxPathPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QFxPath) + +public: + QFxPathPrivate() : startX(0), startY(0) { } + + QPainterPath _path; + QList<QFxPathElement*> _pathElements; + mutable QVector<QPointF> _pointCache; + QList<QFxPath::AttributePoint> _attributePoints; + QStringList _attributes; + int startX; + int startY; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/fx/qfxpathview.cpp b/src/declarative/fx/qfxpathview.cpp new file mode 100644 index 0000000..79b0f8e --- /dev/null +++ b/src/declarative/fx/qfxpathview.cpp @@ -0,0 +1,841 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <math.h> +#include <QDebug> +#include <QPen> +#include <QEvent> +#include <gfxeasing.h> +#include "qmlbindablevalue.h" +#include "qmlstate.h" +#include "qlistmodelinterface.h" +#include "qmlopenmetaobject.h" + +#include "qfxpathview.h" +#include "qfxpathview_p.h" +#include <QGraphicsSceneEvent> +#include <QTimer> + +static const int FlickThreshold = 5; + +QT_BEGIN_NAMESPACE + +QML_DEFINE_TYPE(QFxPathView,PathView); + +class QFxPathViewAttached : public QObject +{ + Q_OBJECT +public: + QFxPathViewAttached(QObject *parent) + : QObject(parent), mo(new QmlOpenMetaObject(this)) + { + } + + QVariant value(const QByteArray &name) const + { + return mo->value(name); + } + void setValue(const QByteArray &name, const QVariant &val) + { + mo->setValue(name, val); + } + +private: + QmlOpenMetaObject *mo; +}; + + +/*! + \internal + \class QFxPathView + \brief The QFxPathView class lays out items provided by a model on a path. + + \ingroup views + + The model must be a \l QListModelInterface subclass. + + \sa QFxPath +*/ + +/*! + \qmlclass PathView + \brief The PathView element lays out model-provided items on a path. + \inherits Item + + The model is typically provided by a QAbstractListModel "C++ model object", but can also be created directly in XML. + + The items are laid out along a path defined by a \l Path and may be flicked to scroll. + + \code + <PathView id="pathview" delegate="{contactDelegate}"> + <resources><Component id="contactDelegate">...</Component></resources> + <model>...</model> + <path>...</path> + </PathView> + \endcode + + \image pathview.gif + + \sa Path +*/ + +QFxPathView::QFxPathView(QFxItem *parent) + : QFxItem(*(new QFxPathViewPrivate), parent) +{ + Q_D(QFxPathView); + d->init(); +} + +QFxPathView::QFxPathView(QFxPathViewPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + Q_D(QFxPathView); + d->init(); +} + +QFxPathView::~QFxPathView() +{ +} + +/*! + \qmlproperty model PathView::model + This property holds the model providing data for the view. + + The model provides a set of data that is used to create the items for the view. + For large or dynamic datasets the model is usually provided by a C++ model object. + However, models can also be created directly in XML, for example: + \code +<model> + <ListModel> + <Contact> + <portrait>pics/john.png</portrait> + <firstName>John</firstName> + <lastName>Smith</lastName> + </Contact> + <Contact> + <portrait>pics/bill.png</portrait> + <firstName>Bill</firstName> + <lastName>Jones</lastName> + </Contact> + <Contact> + <portrait>pics/jane.png</portrait> + <firstName>Jane</firstName> + <lastName>Doe</lastName> + </Contact> + </ListModel> +</model> + \endcode +*/ + +/*! + \property QFxPathView::model + \brief the model providing data for the view. + + The model must be either a \l QListModelInterface or + \l QFxVisualItemModel subclass. +*/ +QVariant QFxPathView::model() const +{ + Q_D(const QFxPathView); + return d->model ? d->model->model() : QVariant(); +} + +void QFxPathView::setModel(const QVariant &model) +{ + Q_D(QFxPathView); + if (QFxVisualItemModel *m = qvariant_cast<QFxVisualItemModel*>(model)) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = m; + } else { + if (!d->ownModel) { + d->model = new QFxVisualItemModel; + d->ownModel = true; + } + d->model->setModel(model); + } + d->regenerate(); + d->fixOffset(); +} + +/*! + \qmlproperty int PathView::count + This property holds the number of items in the model. +*/ +int QFxPathView::count() const +{ + Q_D(const QFxPathView); + return d->model ? d->model->count() : 0; +} + +/*! + \qmlproperty Path PathView::path + \default + This property holds the path used to lay out the items. + For more information see the \l Path documentation. +*/ +QFxPath *QFxPathView::path() const +{ + Q_D(const QFxPathView); + return d->path; +} + +void QFxPathView::setPath(QFxPath *path) +{ + Q_D(QFxPathView); + d->path = path; + connect(d->path, SIGNAL(changed()), this, SLOT(refill())); + d->regenerate(); +} + +/*! + \qmlproperty int PathView::currentIndex + This property holds the index of the current item. +*/ +int QFxPathView::currentIndex() const +{ + Q_D(const QFxPathView); + return d->currentIndex; +} + +void QFxPathView::setCurrentIndex(int idx) +{ + Q_D(QFxPathView); + if (d->model && d->model->count()) + idx = qAbs(idx % d->model->count()); + if (d->model && idx != d->currentIndex) { + d->currentIndex = idx; + d->snapToCurrent(); + int itemIndex = (idx - d->firstIndex + d->model->count()) % d->model->count(); + if(itemIndex < d->items.count()) + d->items.at(itemIndex)->setFocus(true); + emit currentIndexChanged(); + } +} + +/*! + \qmlproperty real PathView::offset + + The offset specifies how far along the path the items are from their initial positions. +*/ +qreal QFxPathView::offset() const +{ + Q_D(const QFxPathView); + return d->_offset; +} + +void QFxPathView::setOffset(qreal offset) +{ + Q_D(QFxPathView); + d->setOffset(offset); + d->updateCurrent(); +} + +void QFxPathViewPrivate::setOffset(qreal o) +{ + Q_Q(QFxPathView); + if (_offset != o) { + _offset = fmod(o, 100.0); + if (_offset < 0) + _offset = 100.0 + _offset; + q->refill(); + } +} + +/*! + \qmlproperty real PathView::snapPosition + This property holds the position (0-100) the current item snaps to. +*/ + +/*! + \property QFxPathView::snapPosition + \brief sets the position (0-100) the current item snaps to. + + This property determines the position the nearest item will snap to. +*/ +qreal QFxPathView::snapPosition() const +{ + Q_D(const QFxPathView); + return d->snapPos; +} + +void QFxPathView::setSnapPosition(qreal pos) +{ + Q_D(QFxPathView); + d->snapPos = pos/100; + d->fixOffset(); +} + +/*! + \qmlproperty real PathView::dragMargin + This property holds the maximum distance from the path that initiate mouse dragging. + + By default the path can only be dragged by clicking on an item. If + dragMargin is greater than zero, a drag can be initiated by clicking + within dragMargin pixels of the path. +*/ +qreal QFxPathView::dragMargin() const +{ + Q_D(const QFxPathView); + return d->dragMargin; +} + +void QFxPathView::setDragMargin(qreal dragMargin) +{ + Q_D(QFxPathView); + d->dragMargin = dragMargin; +} + +/*! + \qmlproperty component PathView::delegate + + The delegate provides a template describing what each item in the view should look and act like. + + Here is an example delegate: + \code + <PathView delegate="{contactDelegate}" ...> + <resources> + <Component id="contactDelegate"> + <Item id="wrapper" scale="{PathView.sc}" opacity="{PathView.op}" z="{PathView.z}"> + <Image id="pic" width="100" height="100" file="{portrait}" + anchors.horizontalCenter="{parent.horizontalCenter}"/> + <Text id="name" text="{firstName + ' ' + lastName}" + font.size="20" + anchors.top="{pic.bottom}" anchors.topMargin="5" + anchors.horizontalCenter="{parent.horizontalCenter}"/> + </Item> + </Component> + </resources> + ... + </PathView> + \endcode +*/ + +/*! + \property QFxPathView::delegate + \brief the component to use to render the items. + + The delegate is a component that the view will instantiate and destroy + as needed to display the items. + +*/ +QmlComponent *QFxPathView::delegate() const +{ + Q_D(const QFxPathView); + return d->model ? d->model->delegate() : 0; +} + +void QFxPathView::setDelegate(QmlComponent *c) +{ + Q_D(QFxPathView); + if (!d->ownModel) { + d->model = new QFxVisualItemModel; + d->ownModel = true; + } + d->model->setDelegate(c); + d->regenerate(); +} + +/*! + \property QFxPathView::pathItemCount + \brief the number of items visible on the path at any one time + */ +/*! + \qmlproperty int PathView::pathItemCount + This property holds the number of items visible on the path at any one time +*/ +int QFxPathView::pathItemCount() const +{ + Q_D(const QFxPathView); + return d->pathItems; +} + +void QFxPathView::setPathItemCount(int i) +{ + Q_D(QFxPathView); + if(i == d->pathItems) + return; + d->pathItems = i; + d->regenerate(); +} + +QPointF QFxPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const +{ + //XXX maybe do recursively at increasing resolution. + qreal mindist = 1e10; // big number + QPointF nearPoint = path->pointAt(0); + qreal nearPc = 0; + for (qreal i=1; i < 1000; i++) { + QPointF pt = path->pointAt(i/1000.0); + QPointF diff = pt - point; + qreal dist = diff.x()*diff.x() + diff.y()*diff.y(); + if (dist < mindist) { + nearPoint = pt; + nearPc = i; + mindist = dist; + } + } + + if (nearPercent) + *nearPercent = nearPc / 10.0; + + return nearPoint; +} + + +void QFxPathView::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxPathView); + QPointF scenePoint = mapToScene(event->pos()); + int idx = 0; + for (; idx < d->items.count(); ++idx) { + QRectF rect = d->items.at(idx)->boundingRect(); + rect = d->items.at(idx)->mapToScene(rect); + if (rect.contains(scenePoint)) + break; + } + if (idx == d->items.count() && d->dragMargin == 0.) // didn't click on an item + return; + + d->startPoint = d->pointNear(event->pos(), &d->startPc); + if (idx == d->items.count()) { + qreal distance = qAbs(event->pos().x() - d->startPoint.x()) + qAbs(event->pos().y() - d->startPoint.y()); + if (distance > d->dragMargin) + return; + } + + d->stealMouse = false; + d->lastElapsed = 0; + d->lastDist = 0; + d->lastPosTime.start(); + d->tl.clear(); +} + +void QFxPathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxPathView); + if (d->lastPosTime.isNull()) + return; + + if (!d->stealMouse) { + QPointF delta = event->pos() - d->startPoint; + if (qAbs(delta.x()) > FlickThreshold && qAbs(delta.y()) > FlickThreshold) + d->stealMouse = true; + } + + if (d->stealMouse) { + d->moveReason = QFxPathViewPrivate::Mouse; + qreal newPc; + d->pointNear(event->pos(), &newPc); + qreal diff = newPc - d->startPc; + if (diff) { + setOffset(d->_offset + diff); + + if (diff > 50) + diff -= 100; + else if (diff < -50) + diff += 100; + + d->lastElapsed = d->lastPosTime.restart(); + d->lastDist = diff; + d->startPc = newPc; + } + } +} + +void QFxPathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *) +{ + Q_D(QFxPathView); + if (d->lastPosTime.isNull()) + return; + + qreal elapsed = qreal(d->lastElapsed + d->lastPosTime.elapsed()) / 1000.; + qreal velocity = elapsed > 0. ? d->lastDist / elapsed : 0; + if (d->model && d->model->count() && qAbs(velocity) > 5) { + if (velocity > 100) + velocity = 100; + else if (velocity < -100) + velocity = -100; + qreal inc = fmod(d->_offset - d->snapPos, 100.0 / d->model->count()); + qreal dist = qAbs(velocity/2 - fmod(velocity/2, 100.0 / d->model->count()) - inc); + d->moveOffset.setValue(d->_offset); + d->tl.accel(d->moveOffset, velocity, 10, dist); + d->tl.execute(d->fixupOffsetEvent); + } else { + d->fixOffset(); + } + + d->lastPosTime = QTime(); + d->stealMouse = false; +} + +bool QFxPathView::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxPathView); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())); + QFxItem *grabber = static_cast<QFxItem*>(mouseGrabberItem()); + if ((d->stealMouse || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) { + mouseEvent.setAccepted(false); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button))); + } + } + mouseEvent.setScenePos(event->scenePos()); + mouseEvent.setLastScenePos(event->lastScenePos()); + mouseEvent.setPos(mapFromScene(event->scenePos())); + mouseEvent.setLastPos(mapFromScene(event->lastScenePos())); + + switch(mouseEvent.type()) { + case QEvent::GraphicsSceneMouseMove: + mouseMoveEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMousePress: + mousePressEvent(&mouseEvent); + break; + case QEvent::GraphicsSceneMouseRelease: + mouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = static_cast<QFxItem*>(mouseGrabberItem()); + if (grabber && d->stealMouse && !grabber->keepMouseGrab()) + mouseGrabberItem()->ungrabMouse(); + + return d->stealMouse; + } else if (!d->lastPosTime.isNull()) { + d->lastPosTime = QTime(); + } + return false; +} + +bool QFxPathView::mouseFilter(QGraphicsSceneMouseEvent *e) +{ + if(!isVisible()) + return false; + + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + { + bool ret = sendMouseEvent(e); + if (e->type() == QEvent::GraphicsSceneMouseRelease) + return ret; + break; + } + default: + break; + } + + return false; +} + +void QFxPathViewPrivate::regenerate() +{ + Q_Q(QFxPathView); + if (!model || model->count() <= 0 || !model->delegate() || !path) + return; + + for(int i=0; i<items.count(); i++){ + QFxItem *p = items[i]; + q->attachedProperties.remove(p); + model->release(p); + } + items.clear(); + + firstIndex = 0; + pathOffset = 0; + + int numItems = (pathItems>=0 ? pathItems : model->count()); + qreal minDiff = 1e9; + int minI = -1; + for(int i=0; i<numItems; i++){ + QFxItem *item = model->item(i); + items.append(item); + item->setZ(i); + item->setParent(q); + qreal percent = i * (100.0 / (numItems)); + percent /= 100.0; + updateItem(items.last(), percent); + qreal diff = qAbs(percent - snapPos); + if(diff < minDiff){ + minDiff = diff; + minI = i; + } + } + q->setCurrentIndex(minI); + + q->refill(); +} + +void QFxPathViewPrivate::updateItem(QFxItem *item, qreal percent) +{ + if(QObject *obj = QFxPathView::attachedProperties.value(item)) { + foreach(QString attr, path->attributes()) + static_cast<QFxPathViewAttached *>(obj)->setValue(attr.toLatin1(), path->attributeAt(attr, percent)); + } + + QPointF pf = path->pointAt(percent); + item->setX(pf.x() - item->width()*item->scale()/2); + item->setY(pf.y() - item->height()*item->scale()/2); +} + +void QFxPathView::refill() +{ + Q_D(QFxPathView); + if (!d->model || d->model->count() <= 0) + return; + + QList<qreal> positions; + for(int i=0; i<d->items.count(); i++){ + qreal percent = i * (100. / d->items.count()); + percent = percent + d->_offset; + percent = fmod(percent,100.); + positions << qAbs(percent/100.0); + } + + if(d->pathItems==-1){ + for(int i=0; i<positions.count(); i++){ + d->updateItem(d->items.at(i), positions[i]); + } + return; + } + + QList<qreal> rotatedPositions; + for(int i=0; i<d->items.count(); i++) + rotatedPositions << positions[(i + d->pathOffset + d->items.count()) % d->items.count()]; + + int firstFind = -1; + int i; + for(i=0; i<d->items.count()-1; i++) + { + if(rotatedPositions[i] > rotatedPositions[i+1]){ + firstFind = i; + break; + } + } + if(firstFind!=-1 ){ + //A wraparound has occured + if(firstFind<(d->items.count()/2)){ + while(firstFind-- >= 0){ + QFxItem* p = d->items.takeFirst(); + attachedProperties.remove(p); + d->model->release(p); + d->firstIndex++; + d->firstIndex%=d->model->count(); + int index = (d->firstIndex + d->items.count())%d->model->count(); + d->items << d->model->item(index); + d->items.last()->setZ(i); + d->items.last()->setParent(this); + d->pathOffset++; + d->pathOffset=d->pathOffset % d->items.count(); + } + }else{ + while(firstFind++ < (d->items.count()-1)){ + QFxItem* p = d->items.takeLast(); + attachedProperties.remove(p); + d->model->release(p); + d->firstIndex--; + if(d->firstIndex<0) + d->firstIndex = d->model->count() - 1; + d->items.prepend(d->model->item(d->firstIndex)); + d->items.first()->setZ(d->firstIndex); + d->items.first()->setParent(this); + d->pathOffset--; + if(d->pathOffset<0) + d->pathOffset = d->items.count() - 1; + } + } + for(int i=0; i<d->items.count(); i++) + rotatedPositions[i] = positions[(i + d->pathOffset + d->items.count()) + % d->items.count()]; + } + for(int i=0; i<d->items.count(); i++){ + d->updateItem(d->items.at(i), rotatedPositions[i]); + } +} + +void QFxPathView::ticked() +{ + Q_D(QFxPathView); + d->updateCurrent(); +} + +// find the item closest to the snap position +int QFxPathViewPrivate::calcCurrentIndex() +{ + int current = -1; + if (model && items.count()) { + _offset = fmod(_offset, 100.0); + if(_offset < 0) + _offset += 100.0; + + if(pathItems == -1){ + qreal delta = fmod(_offset - snapPos, 100.0); + if (delta < 0) + delta = 100.0 + delta; + int ii = model->count() - qRound(delta * model->count() / 100); + if(ii < 0) + ii = 0; + current = ii; + }else{ + qreal bestDiff=1e9; + int bestI=-1; + for(int i=0; i<items.count(); i++){ + qreal percent = i * (100. / items.count()); + percent = percent + _offset; + percent = fmod(percent,100.); + qreal diff = qAbs(snapPos - (percent/100.0)); + if(diff < bestDiff){ + bestDiff = diff; + bestI = i; + } + } + int modelIndex = (bestI - pathOffset + items.count())%items.count(); + modelIndex += firstIndex; + current = modelIndex; + } + current = qAbs(current % model->count()); + } + + return current; +} + +void QFxPathViewPrivate::updateCurrent() +{ + Q_Q(QFxPathView); + if (moveReason != Mouse) + return; + int idx = calcCurrentIndex(); + if (model && idx != currentIndex) { + currentIndex = idx; + int itemIndex = (idx - firstIndex + model->count()) % model->count(); + if(itemIndex < items.count()) + items.at(itemIndex)->setFocus(true); + emit q->currentIndexChanged(); + } +} + +void QFxPathViewPrivate::fixOffset() +{ + Q_Q(QFxPathView); + if (model && items.count()) { + int curr = calcCurrentIndex(); + if (curr != currentIndex) + q->setCurrentIndex(curr); + else + snapToCurrent(); + } +} + +void QFxPathViewPrivate::snapToCurrent() +{ + if (!model || model->count() <= 0) + return; + + int itemIndex = (currentIndex - firstIndex + model->count())%model->count(); + + //Rounds is the number of times round to make the current item visible + int rounds = itemIndex / items.count(); + int otherWayRounds = (model->count() - (itemIndex))/items.count() + 1; + if(otherWayRounds < rounds) + rounds = -otherWayRounds; + + itemIndex += pathOffset; + itemIndex %= items.count(); + qreal targetOffset = fmod(100 + (snapPos*100) - 100.0 * itemIndex / items.count(), 100); + + if (targetOffset < 0) + targetOffset = 100.0 + targetOffset; + if (targetOffset == _offset && rounds==0) + return; + + moveReason = Other; + tl.clear(); + moveOffset.setValue(_offset); + + if(rounds!=0){ + //Compensate if the targetOffset would bring the target it from off the screen + qreal distance = targetOffset - _offset; + if(distance <= -50) + rounds--; + if(distance > 50) + rounds++; + tl.move(moveOffset, targetOffset + 100.0*(-rounds), GfxEasing(GfxEasing::InOutQuad), + int(100*items.count()*qMax((qreal)(2.0/items.count()),(qreal)qAbs(rounds)))); + tl.execute(fixupOffsetEvent); + return; + } + + if (targetOffset - _offset > 50.0) { + qreal distance = 100 - targetOffset + _offset; + tl.move(moveOffset, 0.0, GfxEasing(GfxEasing::OutQuad), int(200 * _offset / distance)); + tl.set(moveOffset, 100.0); + tl.move(moveOffset, targetOffset, GfxEasing(GfxEasing::InQuad), int(200 * (100-targetOffset) / distance)); + } else if (targetOffset - _offset <= -50.0) { + qreal distance = 100 - _offset + targetOffset; + tl.move(moveOffset, 100.0, GfxEasing(GfxEasing::OutQuad), int(200 * (100-_offset) / distance)); + tl.set(moveOffset, 0.0); + tl.move(moveOffset, targetOffset, GfxEasing(GfxEasing::InQuad), int(200 * targetOffset / distance)); + } else { + tl.move(moveOffset, targetOffset, GfxEasing(GfxEasing::InOutQuad), 200); + } +} + +QHash<QObject*, QObject*> QFxPathView::attachedProperties; +QObject *QFxPathView::qmlAttachedProperties(QObject *obj) +{ + if(!attachedProperties.contains(obj)) { + QFxPathViewAttached *rv = new QFxPathViewAttached(obj); + attachedProperties.insert(obj, rv); + } + return attachedProperties.value(obj); +} + +QT_END_NAMESPACE + +#include "qfxpathview.moc" diff --git a/src/declarative/fx/qfxpathview.h b/src/declarative/fx/qfxpathview.h new file mode 100644 index 0000000..6d4280d --- /dev/null +++ b/src/declarative/fx/qfxpathview.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXPATHVIEW_H +#define QFXPATHVIEW_H + +#include <qfxitem.h> +#include <qfxpath.h> +#include <gfxvalueproxy.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QListModelInterface; +class QFxPathViewPrivate; +class Q_DECLARATIVE_EXPORT QFxPathView : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel) + Q_PROPERTY(QFxPath *path READ path WRITE setPath) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(qreal offset READ offset WRITE setOffset NOTIFY offsetChanged) + Q_PROPERTY(qreal snapPosition READ snapPosition WRITE setSnapPosition) + Q_PROPERTY(qreal dragMargin READ dragMargin WRITE setDragMargin) + Q_PROPERTY(int count READ count) + Q_PROPERTY(QmlComponent *delegate READ delegate WRITE setDelegate) + Q_PROPERTY(int pathItemCount READ pathItemCount WRITE setPathItemCount) + Q_CLASSINFO("DefaultProperty", "delegate") +public: + QFxPathView(QFxItem *parent=0); + virtual ~QFxPathView(); + + QVariant model() const; + void setModel(const QVariant &); + + QFxPath *path() const; + void setPath(QFxPath *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + qreal offset() const; + void setOffset(qreal offset); + + qreal snapPosition() const; + void setSnapPosition(qreal pos); + + qreal dragMargin() const; + void setDragMargin(qreal margin); + + int count() const; + + QmlComponent *delegate() const; + void setDelegate(QmlComponent *); + + int pathItemCount() const; + void setPathItemCount(int); + + static QObject *qmlAttachedProperties(QObject *); + +Q_SIGNALS: + void currentIndexChanged(); + void offsetChanged(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + bool mouseFilter(QGraphicsSceneMouseEvent *e); + +private Q_SLOTS: + void refill(); + void ticked(); + +protected: + QFxPathView(QFxPathViewPrivate &dd, QFxItem *parent); + +private: + static QHash<QObject*, QObject*> attachedProperties; + Q_DISABLE_COPY(QFxPathView) + Q_DECLARE_PRIVATE(QFxPathView) +}; +QML_DECLARE_TYPE(QFxPathView); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXPATHVIEW_H diff --git a/src/declarative/fx/qfxpathview_p.h b/src/declarative/fx/qfxpathview_p.h new file mode 100644 index 0000000..31933c0 --- /dev/null +++ b/src/declarative/fx/qfxpathview_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXPATHVIEW_P_H +#define QFXPATHVIEW_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 "qdatetime.h" +#include "qfxpathview.h" +#include "qfxitem_p.h" +#include "qfxvisualitemmodel.h" +#include "qml.h" +#include "private/qmlanimation_p.h" + +QT_BEGIN_NAMESPACE + +typedef struct PathViewItem{ + int index; + QFxItem* item; +}PathViewItem; + +class QFxPathViewPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxPathView) + +public: + QFxPathViewPrivate() + : path(0), currentIndex(0), startPc(0), lastDist(0) + , lastElapsed(0), stealMouse(false), ownModel(false), activeItem(0) + , snapPos(0), dragMargin(0), moveOffset(this, &QFxPathViewPrivate::setOffset) + , firstIndex(0), pathItems(-1), pathOffset(0), model(0) + , moveReason(Other) + { + fixupOffsetEvent = GfxEvent::gfxEvent<QFxPathViewPrivate, &QFxPathViewPrivate::fixOffset>(&moveOffset, this); + } + + void init() + { + Q_Q(QFxPathView); + _offset = 0; + q->setAcceptedMouseButtons(Qt::NoButton); + q->setOptions(QSimpleCanvasItem::MouseFilter | QSimpleCanvasItem::MouseEvents | QSimpleCanvasItem::IsFocusRealm); + q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked())); + } + + int calcCurrentIndex(); + void updateCurrent(); + void fixOffset(); + void setOffset(qreal offset); + void regenerate(); + void updateItem(QFxItem *, qreal); + void snapToCurrent(); + QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const; + + QFxPath *path; + int currentIndex; + qreal startPc; + QPointF startPoint; + qreal lastDist; + int lastElapsed; + qreal _offset; + int stealMouse : 1; + int ownModel : 1; + QTime lastPosTime; + QPointF lastPos; + QFxItem *activeItem; + qreal snapPos; + qreal dragMargin; + QmlTimeLine tl; + GfxValueProxy<QFxPathViewPrivate> moveOffset; + GfxEvent fixupOffsetEvent; + int firstIndex; + int pathItems; + int pathOffset; + QList<QFxItem *> items; + QFxVisualItemModel *model; + QVariant modelVariant; + enum MovementReason { Other, Key, Mouse }; + MovementReason moveReason; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/fx/qfxpixmap.cpp b/src/declarative/fx/qfxpixmap.cpp new file mode 100644 index 0000000..883dbdf --- /dev/null +++ b/src/declarative/fx/qfxpixmap.cpp @@ -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 QtDeclarative 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 "qfxpixmap.h" +#include <QHash> +#include <QNetworkReply> +#include <qfxperf.h> +#include <QtDeclarative/qmlengine.h> +#include <QFile> + + +QT_BEGIN_NAMESPACE +class QFxPixmapCacheItem; +typedef QHash<QString, QFxPixmapCacheItem *> QFxPixmapCache; +static QFxPixmapCache qfxPixmapCache; + +class QFxPixmapCacheItem +{ +public: + QFxPixmapCacheItem() : reply(0), refCount(1) {} + QString key; + QNetworkReply *reply; +#if defined(QFX_RENDER_OPENGL) + QImage image; +#else + QImage image; + QImage opaqueImage; +#endif + + int refCount; + void addRef() { ++refCount; } + void release() { Q_ASSERT(refCount > 0); --refCount; if(refCount == 0) { qfxPixmapCache.remove(key); delete this; } } +}; + +static QFxPixmapCacheItem qfxPixmapCacheDummyItem; + +class QFxPixmapPrivate +{ +public: + QFxPixmapPrivate() + : opaque(false), pixmap(&qfxPixmapCacheDummyItem) { pixmap->addRef(); } + + bool opaque; + QFxPixmapCacheItem *pixmap; +}; + +/*! + \internal + \class QFxPixmap + \ingroup utility + \brief Enacapsultes a pixmap for QFx items. + + This class is NOT reentrant. + The pixmap cache will grow indefinately. + */ +QFxPixmap::QFxPixmap() +: d(new QFxPixmapPrivate) +{ +} + +QFxPixmap::QFxPixmap(const QUrl &url) +: d(new QFxPixmapPrivate) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::PixmapLoad> perf; +#endif + QString key = url.toString(); + QFxPixmapCache::Iterator iter = qfxPixmapCache.find(key); + if(iter == qfxPixmapCache.end()) { + qWarning() << "QFxPixmap: URL not loaded" << url; + } else { + QNetworkReply *reply = (*iter)->reply; + if (reply) { + if (reply->error()) { + qWarning() << "Error loading" << url << reply->errorString(); + } else { + (*iter)->image.load(reply, 0); + } + reply->deleteLater(); + (*iter)->reply = 0; + } + (*iter)->addRef(); + } + + d->pixmap = *iter; +} + +QFxPixmap::QFxPixmap(const QFxPixmap &o) +: d(new QFxPixmapPrivate) +{ + d->opaque = o.d->opaque; + o.d->pixmap->addRef(); + d->pixmap->release(); + d->pixmap = o.d->pixmap; +} + +QFxPixmap::~QFxPixmap() +{ + d->pixmap->release(); + delete d; +} + +QFxPixmap &QFxPixmap::operator=(const QFxPixmap &o) +{ + d->opaque = o.d->opaque; + o.d->pixmap->addRef(); + d->pixmap->release(); + d->pixmap = o.d->pixmap; + return *this; +} + +bool QFxPixmap::isNull() const +{ + return d->pixmap->image.isNull(); +} + +bool QFxPixmap::opaque() const +{ + return d->opaque; +} + +void QFxPixmap::setOpaque(bool o) +{ + d->opaque = o; +} + +int QFxPixmap::width() const +{ + return d->pixmap->image.width(); +} + +int QFxPixmap::height() const +{ + return d->pixmap->image.height(); +} + +QPixmap QFxPixmap::pixmap() const +{ + return QPixmap::fromImage(d->pixmap->image); +} + +void QFxPixmap::setPixmap(const QPixmap &pix) +{ + QFxPixmapCache::Iterator iter = qfxPixmapCache.find(QString::number(pix.cacheKey())); + if(iter == qfxPixmapCache.end()) { + QFxPixmapCacheItem *item = new QFxPixmapCacheItem; + item->key = QString::number(pix.cacheKey()); + if(d->pixmap) + d->pixmap->release(); + d->pixmap = item; + d->pixmap->image = pix.toImage(); + qfxPixmapCache.insert(QString::number(pix.cacheKey()), item); + } else { + (*iter)->addRef(); + d->pixmap = *iter; + } + +#if 0 + int size = 0; + for(QFxPixmapCache::Iterator iter = qfxPixmapCache.begin(); iter != qfxPixmapCache.end(); ++iter) { + size += (*iter)->image.width() * (*iter)->image.height(); + } + qWarning() << qfxPixmapCache.count() << size; +#endif +} + +QFxPixmap::operator const QSimpleCanvasConfig::Image &() const +{ +#if defined(QFX_RENDER_OPENGL) + return d->pixmap->image; +#else + if(d->opaque) { + if(!d->pixmap->image.isNull() && d->pixmap->opaqueImage.isNull()) { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::PixmapLoad> perf; +#endif + d->pixmap->opaqueImage = d->pixmap->image.convertToFormat(QPixmap::defaultDepth() == 16 ? + QImage::Format_RGB16 : + QImage::Format_RGB32); + } + return d->pixmap->opaqueImage; + } else { + if(!d->pixmap->image.isNull() && d->pixmap->image.format() != QImage::Format_ARGB32_Premultiplied) { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::PixmapLoad> perf; +#endif + d->pixmap->image = d->pixmap->image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + } + return d->pixmap->image; + } +#endif +} + +/*! + Starts a network request to load \a url. When the URL is loaded, + the given slot is invoked. Note that if the image is already cached, + the slot may be invoked immediately. +*/ +void QFxPixmap::get(QmlEngine *engine, const QUrl& url, QObject* obj, const char* slot) +{ + QString key = url.toString(); + QFxPixmapCache::Iterator iter = qfxPixmapCache.find(key); + if(iter == qfxPixmapCache.end()) { + QFxPixmapCacheItem *item = new QFxPixmapCacheItem; + item->addRef(); // XXX - will never get deleted. Need to revisit caching + item->key = key; +#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML + if (url.scheme()==QLatin1String("file")) { + item->image.load(url.toLocalFile(), 0); + } else +#endif + { + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + item->reply = engine->networkAccessManager()->get(req); + } + iter = qfxPixmapCache.insert(item->key, item); + } else { + (*iter)->addRef(); + } + if ((*iter)->reply) { + // still loading + QObject::connect((*iter)->reply, SIGNAL(finished()), obj, slot); + } else { + // already loaded + QObject dummy; + QObject::connect(&dummy, SIGNAL(destroyed()), obj, slot); + } +} + +/*! + Stops the given slot being invoked if the given url finishes loading. + May also cancel loading (eg. if no other pending request). +*/ +void QFxPixmap::cancelGet(const QUrl& url, QObject* obj, const char* slot) +{ + QString key = url.toString(); + QFxPixmapCache::Iterator iter = qfxPixmapCache.find(key); + if(iter == qfxPixmapCache.end()) + return; + if ((*iter)->reply) + QObject::disconnect((*iter)->reply, SIGNAL(finished()), obj, slot); + // XXX - loading not cancelled. Need to revisit caching +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxpixmap.h b/src/declarative/fx/qfxpixmap.h new file mode 100644 index 0000000..9a3ba4e --- /dev/null +++ b/src/declarative/fx/qfxpixmap.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 QtDeclarative 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 QFXPIXMAP_H +#define QFXPIXMAP_H + +#include <QString> +#include <qsimplecanvas.h> +#include <qfxglobal.h> +#include <QPixmap> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlEngine; +class QFxPixmapPrivate; +class Q_DECLARATIVE_EXPORT QFxPixmap +{ +public: + QFxPixmap(); + QFxPixmap(const QUrl& url); // url must have been passed to QFxPixmap::get, and finished. + QFxPixmap(const QFxPixmap &); + virtual ~QFxPixmap(); + + QFxPixmap &operator=(const QFxPixmap &); + + static void get(QmlEngine *, const QUrl& url, QObject*, const char* slot); + static void cancelGet(const QUrl& url, QObject* obj, const char* slot); + + bool isNull() const; + + bool opaque() const; + void setOpaque(bool); + + int width() const; + int height() const; + + QPixmap pixmap() const; + void setPixmap(const QPixmap &pix); + + operator const QSimpleCanvasConfig::Image &() const; + +private: + QFxPixmapPrivate *d; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QFXPIXMAP_H diff --git a/src/declarative/fx/qfxrect.cpp b/src/declarative/fx/qfxrect.cpp new file mode 100644 index 0000000..718dc76 --- /dev/null +++ b/src/declarative/fx/qfxrect.cpp @@ -0,0 +1,851 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxrect.h" +#include "qfxrect_p.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxPen,Pen); + +/*! + \internal + \class QFxPen + \ingroup utility + \brief The QFxPen class provides a pen used for drawing rect borders on a QFxView. + + Example: + \code + <Rect pen.width="2" pen.color="red".../> + \endcode +*/ + +/*! \property QFxPen::width + \brief the width of the pen. + + The default width is 1. If the width is less than 1 the pen is considered invalid + and won't be used. +*/ + +/*! + \property QFxPen::color + \brief the color of the pen. + + color is most commonly specified in hexidecimal notation (#RRGGBB) + or as an + \htmlonly + <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords"> SVG color keyword name</a> + \endhtmlonly + (as defined by the World Wide Web Consortium). For example: + \code + <!-- rect with green border using hexidecimal notation --> + <Rect pen.color="#00FF00" .../> + + <!-- rect with steelblue border using SVG color name--> + <Rect pen.color="steelblue" .../> + \endcode + + For the full set of ways to specify color, see Qt's QColor::setNamedColor documentation. +*/ + +void QFxPen::setColor(const QColor &c) +{ + _color = c; + emit updated(); + _valid = _color.alpha() ? true : false; +} + + + +QML_DEFINE_TYPE(QFxRect,Rect); + +/*! + \qmlclass Rect QFxRect + \brief The Rect element allows you to add rectangles to a scene. + \inherits Item + + A Rect is painted having a solid fill (color) and an optional border (pen). You can also create rounded rectangles using the radius property. + + \code + <Rect width="100" height="100" color="red" pen.color="black" pen.width="5" radius="10"/> + \endcode + + \image declarative-rect.png +*/ + +/*! + \internal + \class QFxRect + \brief The QFxRect class provides a rect item that you can add to a QFxView. + + A Rect is painted having a solid fill (color) and an optional border (pen). You can also create rounded rectangles using the radius property. + + \code + <Rect width="100" height="100" color="red" pen.color="black" pen.width="5" radius="10"/> + \endcode + + \image declarative-rect.png + + A QFxRect object can be instantiated in Qml using the tag \l Rect. + + \ingroup coreitems +*/ +QFxRect::QFxRect(QFxItem *parent) + : QFxItem(*(new QFxRectPrivate), parent) +{ + Q_D(QFxRect); + d->init(); + setOptions(HasContents, true); +} + +QFxRect::QFxRect(QFxRectPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + Q_D(QFxRect); + d->init(); + setOptions(HasContents, true); +} + +void QFxRect::doUpdate() +{ +#if defined(QFX_RENDER_QPAINTER) + Q_D(QFxRect); + d->_rectImage = QSimpleCanvasConfig::Image(); +#endif +#if defined(QFX_RENDER_OPENGL) + Q_D(QFxRect); + d->_rectTexture.clear(); +#endif + update(); +} + +/*! + \qmlproperty int Rect::pen.width + \qmlproperty color Rect::pen.color + + The pen used to draw the border of the rect. +*/ +/*! + \property QFxRect::pen + \brief the pen used to draw the border of the rect. +*/ +QFxPen *QFxRect::pen() +{ + Q_D(QFxRect); + return d->pen(); +} + +/*! + \qmlproperty real Rect::radius + This property holds the corner radius used to draw a rounded rect. + + If radius is non-zero, the rect will be painted as a rounded rectangle, otherwise it will be + painted as a normal rectangle. The same radius is used by all 4 corners; there is currently + no way to specify different radii for different corners. +*/ + +/*! + \property QFxRect::radius + \brief the corner radius used to draw a rounded rect. +*/ +qreal QFxRect::radius() const +{ + Q_D(const QFxRect); + return d->_radius; +} + +void QFxRect::setRadius(qreal radius) +{ + Q_D(QFxRect); + if (d->_radius == radius) + return; + + d->_radius = radius; +#if defined(QFX_RENDER_QPAINTER) + d->_rectImage = QSimpleCanvasConfig::Image(); +#elif defined(QFX_RENDER_OPENGL) + d->_rectTexture.clear(); +#endif + update(); +} + +void QFxRect::dump(int depth) +{ + Q_D(QFxRect); + QByteArray ba(depth * 4, ' '); + qWarning() << ba.constData() << "QFxRect:" << d->_color; + QFxItem::dump(depth); +} + +/*! + \qmlproperty color Rect::color + This property holds the color used to fill the rect. + + \code + <!-- green rect using hexidecimal notation --> + <Rect color="#00FF00" .../> + + <!-- steelblue rect using SVG color name--> + <Rect color="steelblue" .../> + \endcode +*/ + +/*! + \property QFxRect::color + \brief the color used to fill the rect. +*/ +QColor QFxRect::color() const +{ + Q_D(const QFxRect); + return d->_color; +} + +void QFxRect::setColor(const QColor &c) +{ + Q_D(QFxRect); + if(d->_color == c) + return; + + d->_color = c; +#if defined(QFX_RENDER_QPAINTER) + d->_rectImage = QSimpleCanvasConfig::Image(); +#endif +#if defined(QFX_RENDER_OPENGL) + d->_rectTexture.clear(); +#endif + update(); +} + + + +/*! + \qmlproperty color Rect::tintColor + This property holds The color to tint the rectangle. + + This color will be drawn over the rect's color when the rect is painted. The tint color should usually be mostly transparent, or you will not be able to see the underlying color. The below example provides a slight red tint by having the tint color be pure red which is only 1/16th opaque. + + \code + <Rect x="0" width="80" height="80" color="lightsteelblue"/> + <Rect x="100" width="80" height="80" color="lightsteelblue" tintColor="#10FF0000"/> + \endcode + \image declarative-rect_tint.png + + This attribute is not intended to be used with a single color over the lifetime of an user interface. It is most useful when a subtle change is intended to be conveyed due to some event; you can then use the tint color to more effectively tune the visible color. +*/ + +/*! + \property QFxRect::tintColor + \brief The color to tint the rectangle. +*/ +QColor QFxRect::tintColor() const +{ + Q_D(const QFxRect); + return d->_tintColor; +} + +void QFxRect::setTintColor(const QColor &c) +{ + Q_D(QFxRect); + if(d->_tintColor == c) + return; + + d->_tintColor = c; + update(); +} + +QColor QFxRectPrivate::getColor() +{ + if(_tintColor.isValid()) { + int a = _tintColor.alpha(); + if(a == 0xFF) + return _tintColor; + else if(a == 0x00) + return _color; + else { + uint src = _tintColor.rgba(); + uint dest = _color.rgba(); + + uint res = (((a * (src & 0xFF00FF)) + + ((0xFF - a) * (dest & 0xFF00FF))) >> 8) & 0xFF00FF; + res |= (((a * ((src >> 8) & 0xFF00FF)) + + ((0xFF - a) * ((dest >> 8) & 0xFF00FF)))) & 0xFF00FF00; + if((src & 0xFF000000) == 0xFF000000) + res |= 0xFF000000; + + return QColor::fromRgba(res); + } + } else { + return _color; + } +} + +/*! + \qmlproperty color Rect::gradientColor + This property holds the color to use at the base of the rectangle and blend upwards. + + This property allows for the easy construction of simple horizontal gradients. Other gradients may by formed by adding rotation to the rect. The gradient will blend linearly from the rect's main color to the color specified for gradient color. + + \code + <Rect y="0" width="80" height="80" color="lightsteelblue"/> + <Rect y="100" width="80" height="80" color="lightsteelblue" gradientColor="blue"/> + <Rect rotation="90" x="80" y="200" width="80" height="80" color="lightsteelblue" + gradientColor="blue"/> + <!-- The x offset is needed because the rotation is from the top left corner --> + \endcode + \image declarative-rect_gradient.png +*/ + +/*! + \property QFxRect::gradientColor + \brief The color to use at the base of the rectangle and blend upwards. +*/ + +QColor QFxRect::gradientColor() const +{ + Q_D(const QFxRect); + return d->_gradcolor; +} + +void QFxRect::setGradientColor(const QColor &c) +{ + Q_D(QFxRect); + if(d->_gradcolor == c) + return; + + d->_gradcolor = c; + update(); +} + +#if defined(QFX_RENDER_QPAINTER) +void QFxRect::generateRoundedRect() +{ + Q_D(QFxRect); + if (d->_rectImage.isNull()) { + d->_rectImage = QImage(d->_radius*2 + 1, d->_radius*2 + 1, QImage::Format_ARGB32_Premultiplied); + d->_rectImage.fill(0); + QPainter p(&(d->_rectImage)); + QPen pn(QColor(pen()->color()), pen()->width()); + p.setRenderHint(QPainter::Antialiasing); + p.setPen(pn); + p.setBrush(d->_color); + p.drawRoundedRect(0, 0, d->_rectImage.width(), d->_rectImage.height(), d->_radius, d->_radius); + } +} + +void QFxRect::generateBorderedRect() +{ + Q_D(QFxRect); + if (d->_rectImage.isNull()) { + d->_rectImage = QImage(d->pen()->width()*2 + 1, d->pen()->width()*2 + 1, QImage::Format_ARGB32_Premultiplied); + d->_rectImage.fill(0); + QPainter p(&(d->_rectImage)); + QPen pn(QColor(pen()->color()), pen()->width()); + p.setRenderHint(QPainter::Antialiasing); + p.setPen(pn); + p.setBrush(d->_color); + p.drawRect(0, 0, d->_rectImage.width(), d->_rectImage.height()); + } +} +#elif defined(QFX_RENDER_OPENGL) +void QFxRect::generateRoundedRect() +{ + Q_D(QFxRect); + if (d->_rectTexture.isNull()) { + QImage roundRect(int(d->_radius*2 + 1), int(d->_radius*2 + 1), QImage::Format_ARGB32); + roundRect.fill(0); + QPainter p(&roundRect); + QPen pn(QColor(pen()->color()), pen()->width()); + p.setRenderHint(QPainter::Antialiasing); + p.setPen(pn); + p.setBrush(d->_color); + p.drawRoundedRect(0, 0, roundRect.width(), roundRect.height(), d->_radius, d->_radius); + d->_rectTexture.setImage(roundRect); + } +} + +void QFxRect::generateBorderedRect() +{ + Q_D(QFxRect); + if (d->_rectTexture.isNull()) { + QImage borderedRect(d->pen()->width()*2 + 1, d->pen()->width()*2 + 1, QImage::Format_ARGB32_Premultiplied); + borderedRect.fill(0); + QPainter p(&(borderedRect)); + QPen pn(QColor(pen()->color()), pen()->width()); + p.setRenderHint(QPainter::Antialiasing); + p.setPen(pn); + p.setBrush(d->_color); + p.drawRect(0, 0, borderedRect.width(), borderedRect.height()); + d->_rectTexture.setImage(borderedRect); + } +} +#endif + + +#if defined(QFX_RENDER_QPAINTER) +void QFxRect::paintContents(QPainter &p) +{ + Q_D(QFxRect); + if (d->_radius > 0 || (d->_pen && d->_pen->isValid()) + || d->_gradcolor.isValid()) + drawRect(p); + /* + QLinearGradient grad(0, 0, 0, height()); + grad.setColorAt(0, d->_color); + grad.setColorAt(1, d->_gradcolor); + p.setBrush(grad); + p.drawRect(0, 0, width(), height()); + p.setBrush(QBrush()); + */ + else + p.fillRect(QRect(0, 0, width(), height()), d->getColor()); +} + +void QFxRect::drawRect(QPainter &p) +{ + Q_D(QFxRect); + if(d->_gradcolor.isValid() /*|| p.usingQt() */) { + // XXX This path is still slower than the image path + // Image path won't work for gradients though + p.save(); + QPen pn(QColor(pen()->color()), pen()->width()); + p.setRenderHint(QPainter::Antialiasing); + p.setPen(pn); + if(d->_gradcolor.isValid()){ + QLinearGradient grad(0, 0, 0, height()); + grad.setColorAt(0, d->_color); + grad.setColorAt(1, d->_gradcolor); + p.setBrush(grad); + }else{ + p.setBrush(d->_color); + } + if(d->_radius) + p.drawRoundedRect(0, 0, width(), height(), d->_radius, d->_radius); + else + p.drawRect(0, 0, width(), height()); + p.restore(); + } else { + int offset = 0; + if (d->_radius > 0) { + generateRoundedRect(); + //### implicit conversion to int + offset = d->_radius; + } else { + generateBorderedRect(); + offset = d->pen()->width(); + } + + //basically same code as QFxImage uses to paint sci images + int xSide = offset * 2; + int ySide = offset * 2; + + // Upper left + p.drawImage(QRect(0, 0, offset, offset), d->_rectImage, QRect(0, 0, offset, offset)); + + // Upper middle + if(d->_rectImage.width() - xSide) + p.drawImage(QRect(offset, 0, width() - xSide, offset), d->_rectImage, + QRect(offset, 0, d->_rectImage.width() - xSide, offset)); + // Upper right + if(d->_rectImage.width() - offset) { + p.drawImage(QPoint(width()-offset, 0), d->_rectImage, + QRect(d->_rectImage.width()-offset, 0, offset, offset)); + } + // Middle left + if(d->_rectImage.height() - ySide) + p.drawImage(QRect(0, offset, offset, height() - ySide), d->_rectImage, + QRect(0, offset, offset, d->_rectImage.height() - ySide)); + + // Middle + if(d->_rectImage.width() - xSide && d->_rectImage.height() - ySide) + p.drawImage(QRect(offset, offset, width() - xSide, height() - ySide), d->_rectImage, + QRect(offset, offset, d->_rectImage.width() - xSide, d->_rectImage.height() - ySide)); + // Midlle right + if(d->_rectImage.height() - ySide) + p.drawImage(QRect(width()-offset, offset, offset, height() - ySide), d->_rectImage, + QRect(d->_rectImage.width()-offset, offset, offset, d->_rectImage.height() - ySide)); + // Lower left + p.drawImage(QPoint(0, height() - offset), d->_rectImage, QRect(0, d->_rectImage.height() - offset, offset, offset)); + + // Lower Middle + if(d->_rectImage.width() - xSide) + p.drawImage(QRect(offset, height() - offset, width() - xSide, offset), d->_rectImage, + QRect(offset, d->_rectImage.height() - offset, d->_rectImage.width() - xSide, offset)); + // Lower Right + if(d->_rectImage.width() - offset) + p.drawImage(QPoint(width()-offset, height() - offset), d->_rectImage, + QRect(d->_rectImage.width()-offset, d->_rectImage.height() - offset, offset, offset)); + } +} +#endif + +#if defined(QFX_RENDER_OPENGL2) +#include "glbasicshaders.h" + +void QFxRect::paintGLContents(GLPainter &p) +{ + Q_D(QFxRect); + if(d->_radius == 0 && (!d->_pen || !d->_pen->isValid())) { + if(d->_gradcolor.isValid()) { + float widthV = width(); + float heightV = height(); + + GLfloat vertices[] = { 0, heightV, + widthV, heightV, + 0, 0, + widthV, 0 }; + + float r = d->_color.redF(); + float g = d->_color.greenF(); + float b = d->_color.blueF(); + float a = d->_color.alphaF() * p.activeOpacity; + + float r2 = d->_gradcolor.redF(); + float g2 = d->_gradcolor.greenF(); + float b2 = d->_gradcolor.blueF(); + float a2 = d->_gradcolor.alphaF() * p.activeOpacity; + + GLfloat colors[] = { r2, g2, b2, a2, + r2, g2, b2, a2, + r, g, b, a, + r, g, b, a }; + + ColorShader *shader = basicShaders()->color(); + shader->enable(); + shader->setTransform(p.activeTransform); + + shader->setAttributeArray(ColorShader::Vertices, vertices, 2); + shader->setAttributeArray(ColorShader::Colors, colors, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(ColorShader::Vertices); + shader->disableAttributeArray(ColorShader::Colors); + } else { + QGLShaderProgram *shader = p.useColorShader(d->getColor()); + + float widthV = width(); + float heightV = height(); + + GLfloat vertices[] = { 0, heightV, + widthV, heightV, + 0, 0, + widthV, 0 }; + + shader->setAttributeArray(ConstantColorShader::Vertices, vertices, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(ConstantColorShader::Vertices); + } + } else { + qreal offset = 0; + if (d->_radius > 0) { + generateRoundedRect(); + offset = d->_radius; + } else { + generateBorderedRect(); + offset = d->pen()->width(); + } + + QGLShaderProgram *shader = p.useTextureShader(); + + float imgWidth = d->_rectTexture.width(); + float imgHeight = d->_rectTexture.height(); + if(!imgWidth || !imgHeight) + return; + + float widthV = width(); + float heightV = height(); + + float texleft = 0; + float texright = 1; + float textop = 1; + float texbottom = 0; + float imgleft = 0; + float imgright = widthV; + float imgtop = 0; + float imgbottom = heightV; + + texleft = float(offset) / imgWidth; + imgleft = offset; + texright = 1. - float(offset) / imgWidth; + imgright = widthV - offset; + textop = 1. - float(offset) / imgHeight; + imgtop = offset; + texbottom = float(offset) / imgHeight; + imgbottom = heightV - offset; + + //Bug 231768: Inappropriate interpolation was occuring on 3x3 textures + if(offset==1) + texleft=texright=textop=texbottom=0.5; + + float vert1[] = { 0, 0, + 0, imgtop, + imgleft, 0, + imgleft, imgtop, + imgright, 0, + imgright, imgtop, + widthV, 0, + widthV, imgtop }; + float tex1[] = { 0, 0, + 0, textop, + texleft, 0, + texleft, textop, + texright, 0, + texright, textop, + 1, 0, + 1, textop }; + float vert2[] = { 0, imgtop, + 0, imgbottom, + imgleft, imgtop, + imgleft, imgbottom, + imgright, imgtop, + imgright, imgbottom, + widthV, imgtop, + widthV, imgbottom }; + float tex2[] = { 0, textop, + 0, texbottom, + texleft, textop, + texleft, texbottom, + texright, textop, + texright, texbottom, + 1, textop, + 1, texbottom }; + float vert3[] = { 0, imgbottom, + 0, heightV, + imgleft, imgbottom, + imgleft, heightV, + imgright, imgbottom, + imgright, heightV, + widthV, imgbottom, + widthV, heightV }; + float tex3[] = { 0, texbottom, + 0, 0, + texleft, texbottom, + texleft, 0, + texright, texbottom, + texright, 0, + 1, texbottom, + 1, 0 }; + + glBindTexture(GL_TEXTURE_2D, d->_rectTexture.texture()); + + shader->setAttributeArray(SingleTextureShader::Vertices, vert1, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, tex1, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + shader->setAttributeArray(SingleTextureShader::Vertices, vert2, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, tex2, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + shader->setAttributeArray(SingleTextureShader::Vertices, vert3, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, tex3, 2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + + shader->disableAttributeArray(SingleTextureShader::Vertices); + shader->disableAttributeArray(SingleTextureShader::TextureCoords); + } +} +#elif defined(QFX_RENDER_OPENGL1) +void QFxRect::paintGLContents(GLPainter &p) +{ + Q_D(QFxRect); + + float widthV = width(); + float heightV = height(); + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(p.activeTransform.data()); + + if(d->_radius == 0 && (!d->_pen || !d->_pen->isValid())) { + GLfloat vertices[] = { 0, heightV, + widthV, heightV, + 0, 0, + widthV, 0 }; + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2,GL_FLOAT,0,vertices); + + QColor c; + if(d->_gradcolor.isValid()) + c = d->_color; + else + c = d->getColor(); + float r = c.redF(); + float g = c.greenF(); + float b = c.blueF(); + float a = c.alphaF() * p.activeOpacity; + + float r2 = r; float g2 = g; float b2 = b; float a2 = a; + + if(d->_gradcolor.isValid()) { + r2 = d->_gradcolor.redF(); + g2 = d->_gradcolor.greenF(); + b2 = d->_gradcolor.blueF(); + a2 = d->_gradcolor.alphaF() * p.activeOpacity; + } + + GLfloat colors[] = { r2, g2, b2, a2, + r2, g2, b2, a2, + r, g, b, a, + r, g, b, a }; + + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4,GL_FLOAT,0,colors); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } else { + qreal offset = 0; + if (d->_radius > 0) { + generateRoundedRect(); + offset = d->_radius; + } else { + generateBorderedRect(); + offset = d->pen()->width(); + } + + if(p.activeOpacity == 1.) { + GLint i = GL_REPLACE; + glTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &i); + } else { + GLint i = GL_MODULATE; + glTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &i); + glColor4f(1, 1, 1, p.activeOpacity); + } + + float imgWidth = d->_rectTexture.width(); + float imgHeight = d->_rectTexture.height(); + if(!imgWidth || !imgHeight) + return; + + float widthV = width(); + float heightV = height(); + + float texleft = 0; + float texright = 1; + float textop = 1; + float texbottom = 0; + float imgleft = 0; + float imgright = widthV; + float imgtop = 0; + float imgbottom = heightV; + + texleft = float(offset) / imgWidth; + imgleft = offset; + texright = 1. - float(offset) / imgWidth; + imgright = widthV - offset; + textop = 1. - float(offset) / imgHeight; + imgtop = offset; + texbottom = float(offset) / imgHeight; + imgbottom = heightV - offset; + + float vert1[] = { 0, 0, + 0, imgtop, + imgleft, 0, + imgleft, imgtop, + imgright, 0, + imgright, imgtop, + widthV, 0, + widthV, imgtop }; + float tex1[] = { 0, 1, + 0, textop, + texleft, 1, + texleft, textop, + texright, 1, + texright, textop, + 1, 1, + 1, textop }; + float vert2[] = { 0, imgtop, + 0, imgbottom, + imgleft, imgtop, + imgleft, imgbottom, + imgright, imgtop, + imgright, imgbottom, + widthV, imgtop, + widthV, imgbottom }; + float tex2[] = { 0, textop, + 0, texbottom, + texleft, textop, + texleft, texbottom, + texright, textop, + texright, texbottom, + 1, textop, + 1, texbottom }; + float vert3[] = { 0, imgbottom, + 0, heightV, + imgleft, imgbottom, + imgleft, heightV, + imgright, imgbottom, + imgright, heightV, + widthV, imgbottom, + widthV, heightV }; + float tex3[] = { 0, texbottom, + 0, 0, + texleft, texbottom, + texleft, 0, + texright, texbottom, + texright, 0, + 1, texbottom, + 1, 0 }; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, d->_rectTexture.texture()); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, vert1); + glTexCoordPointer(2, GL_FLOAT, 0, tex1); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + + glVertexPointer(2, GL_FLOAT, 0, vert2); + glTexCoordPointer(2, GL_FLOAT, 0, tex2); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + + glVertexPointer(2, GL_FLOAT, 0, vert3); + glTexCoordPointer(2, GL_FLOAT, 0, tex3); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); + } +} +#endif + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxrect.h b/src/declarative/fx/qfxrect.h new file mode 100644 index 0000000..42e7b2f --- /dev/null +++ b/src/declarative/fx/qfxrect.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXRECT_H +#define QFXRECT_H + +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_EXPORT QFxPen : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int width READ width WRITE setWidth) + Q_PROPERTY(QColor color READ color WRITE setColor) +public: + QFxPen(QObject *parent=0) + : QObject(parent), _width(1), _color("#000000"), _valid(false) + {} + + int width() const { return _width; } + void setWidth(int w) { _width = w; emit updated(); _valid = (_width < 1) ? false : true; } + + QColor color() const { return _color; } + void setColor(const QColor &c); + + bool isValid() { return _valid; }; + +Q_SIGNALS: + void updated(); + +private: + int _width; + QColor _color; + bool _valid; +}; +QML_DECLARE_TYPE(QFxPen); + +class QFxRectPrivate; +class Q_DECLARATIVE_EXPORT QFxRect : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor) + Q_PROPERTY(QColor gradientColor READ gradientColor WRITE setGradientColor) + Q_PROPERTY(QFxPen * pen READ pen) + Q_PROPERTY(qreal radius READ radius WRITE setRadius) +public: + QFxRect(QFxItem *parent=0); + + QColor color() const; + void setColor(const QColor &); + + QColor tintColor() const; + void setTintColor(const QColor &); + + QColor gradientColor() const; + void setGradientColor(const QColor &); + + QFxPen *pen(); + + qreal radius() const; + void setRadius(qreal radius); + + virtual void dump(int depth); +#if defined(QFX_RENDER_QPAINTER) + void paintContents(QPainter &painter); +#endif + +#if defined(QFX_RENDER_OPENGL) + void paintGLContents(GLPainter &); +#endif + +private Q_SLOTS: + void doUpdate(); + +private: + void generateRoundedRect(); + void generateBorderedRect(); +#if defined(QFX_RENDER_QPAINTER) + void drawRect(QPainter &painter); +#endif +protected: + QFxRect(QFxRectPrivate &dd, QFxItem *parent); + +private: + Q_DISABLE_COPY(QFxRect) + Q_DECLARE_PRIVATE(QFxRect) +}; +QML_DECLARE_TYPE(QFxRect); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXRECT_H diff --git a/src/declarative/fx/qfxrect_p.h b/src/declarative/fx/qfxrect_p.h new file mode 100644 index 0000000..3cb46fa --- /dev/null +++ b/src/declarative/fx/qfxrect_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXRECT_P_H +#define QFXRECT_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 "qfxitem_p.h" + +#if defined(QFX_RENDER_OPENGL) +#include "gltexture.h" +#endif + +QT_BEGIN_NAMESPACE + +class QFxRectPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxRect) + +public: + QFxRectPrivate() + : _pen(0), _radius(0) + { + } + + ~QFxRectPrivate() + { + delete _pen; + } + + void init() + { + } + +#if defined(QFX_RENDER_OPENGL) + GLTexture _rectTexture; +#endif + QColor getColor(); + QColor _color; + QColor _gradcolor; + QColor _tintColor; + QFxPen *pen() { + if(!_pen) { + Q_Q(QFxRect); + _pen = new QFxPen; + QObject::connect(_pen, SIGNAL(updated()), q, SLOT(doUpdate())); + } + return _pen; + } + QFxPen *_pen; + qreal _radius; +#if defined(QFX_RENDER_QPAINTER) + QSimpleCanvasConfig::Image _rectImage; +#endif +}; + +QT_END_NAMESPACE + +#endif // QFXRECT_P_H diff --git a/src/declarative/fx/qfxreflectionfilter.cpp b/src/declarative/fx/qfxreflectionfilter.cpp new file mode 100644 index 0000000..a541083 --- /dev/null +++ b/src/declarative/fx/qfxreflectionfilter.cpp @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxreflectionfilter.h" + +#if defined(QFX_RENDER_OPENGL2) +#include <glsave.h> +#include <QtOpenGL/qglframebufferobject.h> +#include <glbasicshaders.h> +#include <gltexture.h> +#endif + +QT_BEGIN_NAMESPACE +class QFxReflectionFilterPrivate +{ +public: + QFxReflectionFilterPrivate() + : alpha(1), height(-1), offset(0), scale(1) + { + } + qreal alpha; + int height; + int offset; + qreal scale; +}; + +/*! + \qmlclass Reflection + \inherits Filter + \brief The Reflection filter reflects an item and its contents. + + Here is an example of various Reflections applied to an image. + + \code + <HorizontalLayout> + <Image src="icon.png" > + <filter> + <Reflection /> + </filter> + </Image> + <Image src="icon.png" > + <filter> + <Reflection offset="1" /> + </filter> + </Image> + <Image src="icon.png" > + <filter> + <Reflection offset="1" alpha="0.5" /> + </filter> + </Image> + <Image src="icon.png" > + <filter> + <Reflection offset="1" alpha="0.5" height="50" /> + </filter> + </Image> + <Image src="icon.png" > + <filter> + <Reflection offset="1" alpha="0.5" height="50" scale="0.5" /> + </filter> + </Image> + </HorizontalLayout> + \endcode + + \image reflection_example.png + + Reflection is only supported when Qt Declarative is compiled for OpenGL ES 2.0. + Otherwise the Reflection filter has no effect. + + \todo describe (and fix) reflection and bounding box interaction + +*/ + +/*! + \internal + \class QFxReflectionFilter + \ingroup effects + \brief The QFxReflectionFilter class allows you to add a reflection to an item. +*/ +QFxReflectionFilter::QFxReflectionFilter(QObject *parent) +: QSimpleCanvasFilter(parent), d(new QFxReflectionFilterPrivate) +{ +} + +QFxReflectionFilter::~QFxReflectionFilter() +{ + delete d; d = 0; +} + +/*! + \property QFxReflectionFilter::alpha + \brief the starting opacity of the reflection. + + The starting opacity is the opacity closest to the item. The opacity will fade + from this value to zero over the height of the reflection. +*/ +qreal QFxReflectionFilter::alpha() const +{ + return d->alpha; +} + +void QFxReflectionFilter::setAlpha(qreal a) +{ + if(d->alpha == a) return; + d->alpha = a; + emit alphaChanged(a); + update(); +} + +/*! + \qmlproperty int Reflection::height + + The height property controls how much of the item, in pixels, to reflect. + If it is set to the default value of -1, the whole item is reflected. If + it is set to 50, the bottom 50 pixels of the item are reflected. Data + binding could be used to reflect a percentage of the item. + + \code + <Image id="myImage" src="album.png"> + <filter> + <Reflection height="{myImage.height * 0.5}" /> + </filter> + </Image> + \endcode + */ +/*! + \qmlproperty int Reflection::offset + + The offset controls how far from the base of the item, in pixels, the + start of the reflection is placed. This can be used to create a nice + sliver of space between the item and its reflection or for more advanced + effects. + + The default offset is 0 pixels. +*/ + +/*! + \qmlproperty real Reflection::alpha + + The alpha value controls the starting opacity of the reflected item. If + set to the default value of 1, the reflected item starts completely opaque + and gradually fades to completely transparent. If set to less than one, the + reflection starts out partially transparent as though the item was sitting + on a visually less reflective surface. + + Valid values are from 0 (which would be silly, but is allowed) to 1. +*/ +/*! + \qmlproperty real Reflection::scale + + When set to the default value of 1, the reflection is a 1:1 reflection of + the item. That is, each horizontal pixel in the item corresponds to one + horizontal pixel in the reflection. + + When set a value other than 1, the reflection is scaled acordingly - less + than 1 scales it down and greater than 1 scales it up. The scale is applied + after the height parameter and does not effect the reflection offset. +*/ + +/*! + \property QFxReflectionFilter::height + \brief the height of the reflection, in pixels. +*/ +int QFxReflectionFilter::height() const +{ + return d->height; +} + +void QFxReflectionFilter::setHeight(int h) +{ + if(d->height == h) return; + d->height = h; + emit heightChanged(h); + update(); +} + +/*! + \property QFxReflectionFilter::offset + \brief the distance of the reflection from the item, in pixels. +*/ +int QFxReflectionFilter::offset() +{ + return d->offset; +} + +void QFxReflectionFilter::setOffset(int o) +{ + if(d->offset == o) return; + d->offset = o; + emit offsetChanged(o); + update(); +} + +/*! + \property QFxReflectionFilter::scale + \brief the scale of the reflection relative to the item. +*/ +qreal QFxReflectionFilter::scale() const +{ + return d->scale; +} + +void QFxReflectionFilter::setScale(qreal s) +{ + if(d->scale == s) return; + d->scale = s; + emit scaleChanged(s); + update(); +} + +static inline float min(float a, float b) +{ + return (a < b)?a:b; +} + +void QFxReflectionFilter::filterGL(QSimpleCanvasItem::GLPainter &p) +{ +#if defined(QFX_RENDER_OPENGL2) + QSimpleCanvasItem *item = this->item(); + + QRect r = item->itemBoundingRect(); + if (r.isEmpty()) + return; + float width = r.width(); + float height = r.height(); + + float refHeight = height; + if(d->height > 0) + refHeight = min(height, d->height); + + QSimpleCanvas::Matrix simpMat; + QSimpleCanvasItem *simpItem = 0; + if(isSimpleItem(&simpItem, &simpMat) && + simpItem->glSimpleItemData(0, 0, 0, 0)) { + + GLfloat vertices[8]; + GLfloat texVertices[8]; + GLTexture *texture = 0; + + simpItem->glSimpleItemData(vertices, texVertices, &texture, 8); + + GLfloat opacity[4]; + + float invRefHeight = 1. / refHeight; + for(int ii = 0; ii < 4; ++ii) { + float vertex = vertices[ii * 2 + 1]; + float o = (1. - (height - vertex) * invRefHeight); + opacity[ii] = o * d->alpha * p.activeOpacity; + } + + QSimpleCanvas::Matrix trans = p.activeTransform; + trans.rotate(180, 1, 0, 0); + trans.translate(0, -r.height() - d->offset); + if(d->scale != 1) + trans.scale(1, d->scale, 1); + trans.translate(0, -r.height()); + trans *= simpMat; + + glBindTexture(GL_TEXTURE_2D, texture->texture()); + + SingleTextureVertexOpacityShader *shader = + item->basicShaders()->singleTextureVertexOpacity(); + shader->enable(); + shader->setTransform(trans); + shader->setAttributeArray(SingleTextureVertexOpacityShader::Vertices, vertices, 2); + shader->setAttributeArray(SingleTextureVertexOpacityShader::TextureCoords, texVertices, 2); + shader->setAttributeArray(SingleTextureVertexOpacityShader::OpacityCoords, opacity, 1); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(SingleTextureVertexOpacityShader::Vertices); + shader->disableAttributeArray(SingleTextureVertexOpacityShader::TextureCoords); + shader->disableAttributeArray(SingleTextureVertexOpacityShader::OpacityCoords); + + } else { + QGLFramebufferObject *fbo = renderToFBO(); + + float texWidth = width / float(fbo->width()); + float texHeight = refHeight / float(fbo->height()); + + GLfloat invVertices[] = { width, height + d->scale * refHeight + d->offset, + 0, height + d->scale * refHeight + d->offset, + width, height + d->offset, + 0, height + d->offset }; + GLfloat invTexVertices[] = { texWidth, texHeight, + 0, texHeight, + texWidth, 0, + 0, 0 }; + GLfloat invOpacity[] = { 0, 0, d->alpha * p.activeOpacity, d->alpha * p.activeOpacity}; + + glBindTexture(GL_TEXTURE_2D, fbo->texture()); + + SingleTextureVertexOpacityShader *shader = + item->basicShaders()->singleTextureVertexOpacity(); + shader->enable(); + shader->setTransform(p.activeTransform); + shader->setAttributeArray(SingleTextureVertexOpacityShader::Vertices, invVertices, 2); + shader->setAttributeArray(SingleTextureVertexOpacityShader::TextureCoords, invTexVertices, 2); + shader->setAttributeArray(SingleTextureVertexOpacityShader::OpacityCoords, invOpacity, 1); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + shader->disableAttributeArray(SingleTextureVertexOpacityShader::Vertices); + shader->disableAttributeArray(SingleTextureVertexOpacityShader::TextureCoords); + shader->disableAttributeArray(SingleTextureVertexOpacityShader::OpacityCoords); + + releaseFBO(fbo); + } + + renderToScreen(); + +#else + Q_UNUSED(p); +#endif +} + +QRectF QFxReflectionFilter::itemBoundingRect(const QRectF &r) const +{ + QRectF rv = r; + rv |= r.translated(0, r.height() + d->offset); + return rv; +} + +QML_DEFINE_TYPE(QFxReflectionFilter,Reflection); +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxreflectionfilter.h b/src/declarative/fx/qfxreflectionfilter.h new file mode 100644 index 0000000..b0cc7b2 --- /dev/null +++ b/src/declarative/fx/qfxreflectionfilter.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 QtDeclarative 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 QFXREFLECTIONFILTER_H +#define QFXREFLECTIONFILTER_H + +#include <qsimplecanvasfilter.h> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxReflectionFilterPrivate; +class Q_DECLARATIVE_EXPORT QFxReflectionFilter : public QSimpleCanvasFilter +{ + Q_OBJECT + + Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged) + Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged) + Q_PROPERTY(int offset READ offset WRITE setOffset NOTIFY offsetChanged) + Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) +public: + QFxReflectionFilter(QObject *parent=0); + virtual ~QFxReflectionFilter(); + + qreal alpha() const; + void setAlpha(qreal); + int height() const; + void setHeight(int); + int offset(); + void setOffset(int); + qreal scale() const; + void setScale(qreal); + +Q_SIGNALS: + void alphaChanged(qreal); + void heightChanged(int); + void offsetChanged(int); + void scaleChanged(qreal); + +protected: + virtual void filterGL(QSimpleCanvasItem::GLPainter &p); + virtual QRectF itemBoundingRect(const QRectF &r) const; + +private: + Q_DISABLE_COPY(QFxReflectionFilter) + QFxReflectionFilterPrivate *d; +}; +QML_DECLARE_TYPE(QFxReflectionFilter); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXREFLECTIONFILTER_H diff --git a/src/declarative/fx/qfxrepeater.cpp b/src/declarative/fx/qfxrepeater.cpp new file mode 100644 index 0000000..823932c --- /dev/null +++ b/src/declarative/fx/qfxrepeater.cpp @@ -0,0 +1,354 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxrepeater.h" +#include "qfxrepeater_p.h" +#include "qmllistaccessor.h" +#include <qlistmodelinterface.h> + + +QT_BEGIN_NAMESPACE +QFxRepeaterPrivate::QFxRepeaterPrivate() +: component(0) +{ +} + +QFxRepeaterPrivate::~QFxRepeaterPrivate() +{ +} + +QFxItem *QFxRepeaterPrivate::addItem(QmlContext *ctxt, QFxItem *lastItem) +{ + Q_Q(QFxRepeater); + QObject *nobj = component->create(ctxt); + QFxItem *item = qobject_cast<QFxItem *>(nobj); + if (item) { + item->setParent(q->itemParent()); + item->stackUnder(lastItem); + deletables << nobj; + } else { + delete nobj; + } + + return item; +} + +QML_DEFINE_TYPE(QFxRepeater,Repeater); + +/*! + \qmlclass Repeater + \inherits Item + + \brief The Repeater element allows you to repeat a component based on a data source. + + The Repeater element is used when you want to create a large number of + similar items. For each entry in the data source, an item is instantiated + in a context seeded with data from the data source. If the repeater will + be instantiating a large number of instances, it may be more efficient to + use one of Qt Declarative's \l {xmlViews}{view elements}. + + The data source may be either an object list, a string list or a Qt model. + In each case, the data element and the index is exposed to each instantiated + component. The index is always exposed as an accessible \c index property. + In the case of an object or string list, the data element (of type string + or object) is available as the \c modelData property. In the case of a Qt model, + all roles are available as named properties just like in the view classes. + + Items instantiated by the Repeater elemented are inserted, in order, as + children of the Repeater's parent. The insertion starts immediately after + the repeater's position in its parent stacking list. This is to allow + you to use a Repeater inside a layout. The following QML example shows how + the instantiated items would visually appear stacked between the red and + blue rectangles. + + \code + <Item> + <Rect width="100" height="100" color="red" /> + <Repeater ...repeater arguments... /> + <!-- Instantiated items would appear here --> + <Rect width="100" height="100" color="blue" /> + </Item> + \endcode + + The repeater instance continues to own all items it instantiates, even + if they are otherwise manipulated. It is illegal to manually remove an item + created by the Repeater. + + \todo Repeater is very conservative in how it instatiates/deletes items. + Also new model entries will not be created and old ones will not be removed. + + \todo Need an example + + */ + +/*! + \internal + \class QFxRepeater + \ingroup utility + \qmlclass Repeater + + \brief The QFxRepeater class allows you to repeat a component based on a + data source. + + The QFxRepeater class is used when you want to create a large number of + similar items. For each entry in the data source, an item is instantiated + in a context seeded with data from the data source. + + The data source may be either an object list, a string list or a Qt model. + In each case, the data element and the index is exposed to each instantiated + component. The index is always exposed as an accessible \c index property. + In the case of an object or string list, the data element (of type string + or object) is available as the \c modelData property. In the case of a Qt model, + all roles are available as named properties just like in the view classes. + + As a special case the data source can also be merely a number. In this case it will + create that many instances of the component. They will also be assigned an index + based on the order they are created. + + Items instantiated by the QFxRepeater class are inserted, in order, as + children of the repeater's parent. The insertion starts immediately after + the repeater's position in its parent stacking list. This is to allow + you to use a repeater inside a layout. The following QML example shows how + the instantiated items would visually appear stacked between the red and + blue rectangles. + + \code + <Item> + <Rect width="100" height="100" color="red" /> + <Repeater ...repeater arguments... /> + <!-- Instantiated items would appear here --> + <Rect width="100" height="100" color="blue" /> + </Item> + \endcode + + The QFxRepeater instance continues to own all items it instantiates, even + if they are otherwise manipulated. It is illegal to manually delete an item + created by the repeater. On destruction, the repeater will clean up any + items it has instantiated. + + + XXX Repeater is very conservative in how it instatiates/deletes items. Also + new model entries will not be created and old ones will not be removed. + */ + +/*! + Create a new QFxRepeater instance. + */ +QFxRepeater::QFxRepeater(QFxItem *parent) + : QFxItem(*(new QFxRepeaterPrivate), parent) +{ +} + +/*! + \internal + */ +QFxRepeater::QFxRepeater(QFxRepeaterPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ +} + +/*! + Destroy the repeater instance. All items it instantiated are also + destroyed. + */ +QFxRepeater::~QFxRepeater() +{ +} + +/*! + \qmlproperty any Repeater::dataSource + + The Repeater's data source. +*/ + +/*! + \property QFxRepeater::dataSource + \brief The source of data for the repeater. + */ +QVariant QFxRepeater::dataSource() const +{ + return QVariant(); +} + +void QFxRepeater::setDataSource(const QVariant &v) +{ + Q_D(QFxRepeater); + d->dataSource = v; + regenerate(); +} + +/*! + \qmlproperty Component Repeater::component + \default + + The component to repeat. + */ +/*! + \property QFxRepeater::component + \brief The component to repeat. + */ +QmlComponent *QFxRepeater::component() const +{ + Q_D(const QFxRepeater); + return d->component; +} + +void QFxRepeater::setComponent(QmlComponent *_c) +{ + Q_D(QFxRepeater); + d->component = _c; + regenerate(); +} + +/*! + \internal + */ +void QFxRepeater::parentChanged(QSimpleCanvasItem *o, QSimpleCanvasItem *n) +{ + QFxItem::parentChanged(o, n); + regenerate(); +} + +/*! + \internal + */ +void QFxRepeater::regenerate() +{ + Q_D(QFxRepeater); + + qDeleteAll(d->deletables); + d->deletables.clear(); + if(!d->component || !itemParent()) + return; + + QFxItem *lastItem = this; + + if(d->dataSource.type() == QVariant::StringList) { + QStringList sl = qvariant_cast<QStringList>(d->dataSource); + + for(int ii = 0; ii < sl.size(); ++ii) { + QmlContext *ctxt = new QmlContext(itemContext(), this); + d->deletables << ctxt; + + ctxt->setContextProperty(QLatin1String("index"), ii); + ctxt->setContextProperty(QLatin1String("modelData"), sl.at(ii)); + + if (QFxItem *item = d->addItem(ctxt, lastItem)) + lastItem = item; + } + } else if(QmlMetaType::isList(d->dataSource)) { + int cnt = QmlMetaType::listCount(d->dataSource); + if(cnt <= 0) + return; + + for(int ii = 0; ii < cnt; ++ii) { + QVariant v = QmlMetaType::listAt(d->dataSource, ii); + QObject *o = QmlMetaType::toQObject(v); + + QmlContext *ctxt = new QmlContext(itemContext(), this); + d->deletables << ctxt; + + ctxt->setContextProperty(QLatin1String("index"), ii); + ctxt->setContextProperty(QLatin1String("modelData"), o); + + if (QFxItem *item = d->addItem(ctxt, lastItem)) + lastItem = item; + } + } else if (QListModelInterface *model = qobject_cast<QListModelInterface*>(d->dataSource.value<QObject*>())) { + int cnt = model->count(); + if(cnt <= 0) + return; + + for(int ii = 0; ii < cnt; ++ii) { + QmlContext *ctxt = new QmlContext(itemContext(), this); + d->deletables << ctxt; + + ctxt->setContextProperty(QLatin1String("index"), ii); + + QList<int> roles = model->roles(); + QHash<int,QVariant> data = model->data(ii,roles); + for (int j = 0; j < roles.size(); ++j) { + ctxt->setContextProperty(model->toString(roles.at(j)), data.value(roles.at(j))); + } + + //for compatability with other lists, assign data if there is only a single role + if (roles.size() == 1) + ctxt->setContextProperty(QLatin1String("modelData"), data.value(roles.at(0))); + + if (QFxItem *item = d->addItem(ctxt, lastItem)) + lastItem = item; + } + } else if (QObject *object = d->dataSource.value<QObject*>()) { + // A single object (i.e. list of size 1). + // Properties are the roles (excluding objectName). + QmlContext *ctxt = new QmlContext(itemContext(), this); + d->deletables << ctxt; + + ctxt->setContextProperty(QLatin1String("index"), QVariant(0)); + for (int ii = 1; ii < object->metaObject()->propertyCount(); ++ii) { + const QMetaProperty &prop = object->metaObject()->property(ii); + ctxt->setContextProperty(QLatin1String(prop.name()), prop.read(object)); + } + + //for compatability with other lists, assign data if there is only a single role (excluding objectName) + if (object->metaObject()->propertyCount() == 2) { + const QMetaProperty &prop = object->metaObject()->property(1); + ctxt->setContextProperty(QLatin1String("modelData"), prop.read(object)); + } + + d->addItem(ctxt, lastItem); + + } else if(d->dataSource.canConvert(QVariant::Int)){ + + int count = qvariant_cast<int>(d->dataSource); + + for(int ii = 0; ii < count; ++ii) { + QmlContext *ctxt = new QmlContext(itemContext(), this); + d->deletables << ctxt; + + ctxt->setContextProperty(QLatin1String("index"), ii); + + if (QFxItem *item = d->addItem(ctxt, lastItem)) + lastItem = item; + } + } +} +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxrepeater.h b/src/declarative/fx/qfxrepeater.h new file mode 100644 index 0000000..f6c4584 --- /dev/null +++ b/src/declarative/fx/qfxrepeater.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 QtDeclarative 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 QFXREPEATER_H +#define QFXREPEATER_H + +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxRepeaterPrivate; +class Q_DECLARATIVE_EXPORT QFxRepeater : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(QVariant dataSource READ dataSource WRITE setDataSource) + Q_PROPERTY(QmlComponent *component READ component WRITE setComponent) + Q_CLASSINFO("DefaultProperty", "component") +public: + QFxRepeater(QFxItem *parent=0); + virtual ~QFxRepeater(); + + QVariant dataSource() const; + void setDataSource(const QVariant &); + + QmlComponent *component() const; + void setComponent(QmlComponent *); + +private: + void regenerate(); + +protected: + virtual void parentChanged(QSimpleCanvasItem *, QSimpleCanvasItem *); + QFxRepeater(QFxRepeaterPrivate &dd, QFxItem *parent); + +private: + Q_DISABLE_COPY(QFxRepeater) + Q_DECLARE_PRIVATE(QFxRepeater) +}; +QML_DECLARE_TYPE(QFxRepeater); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // _QFXREPEATER_H_ diff --git a/src/declarative/fx/qfxrepeater_p.h b/src/declarative/fx/qfxrepeater_p.h new file mode 100644 index 0000000..ba69658 --- /dev/null +++ b/src/declarative/fx/qfxrepeater_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXREPEATER_P_H +#define QFXREPEATER_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 "qfxitem_p.h" +#include "qfxrepeater.h" +#include <QPointer> + + +QT_BEGIN_NAMESPACE + +class QmlContext; +class QFxRepeaterPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxRepeater) + +public: + QFxRepeaterPrivate(); + ~QFxRepeaterPrivate(); + + QFxItem *addItem(QmlContext *ctxt, QFxItem *lastItem); + + QVariant dataSource; + QmlComponent *component; + + QList<QPointer<QObject> > deletables; +}; + +QT_END_NAMESPACE +#endif // QFXREPEATER_P_H diff --git a/src/declarative/fx/qfxscalegrid.cpp b/src/declarative/fx/qfxscalegrid.cpp new file mode 100644 index 0000000..d84f5e8 --- /dev/null +++ b/src/declarative/fx/qfxscalegrid.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 QtDeclarative 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 <QBuffer> +#include <qml.h> +#include "qfxscalegrid.h" + + +QT_BEGIN_NAMESPACE +/*! + \internal + \class QFxScaleGrid + \brief The QFxScaleGrid class allows you to specify a 3x3 grid to use in scaling an image. + + A scale grid uses 4 grid lines (2 horizontal and 2 vertical) to break an image into 9 sections, as shown below: + \image scalegrid.png + + When the image is scaled: + \list + \i the corners (sections 1, 3, 7, and 9) are not scaled at all + \i the middle (section 5) is scaled both horizontally and vertically + \i sections 2 and 8 are scaled horizontally + \i sections 4 and 6 are scaled vertically + \endlist + + A common way of specifying a scale grid is to create an sci file. An sci file uses a simple + text-based format that specifies each grid line, as well as the associated image file. An example of an sci file's contents: + \code + gridLeft: 10 + gridTop: 10 + gridBottom: 10 + gridRight: 10 + imageFile: picture.png + \endcode +*/ +QML_DEFINE_NOCREATE_TYPE(QFxScaleGrid); + +QFxScaleGrid::QFxScaleGrid() : QObject(), _left(0), _top(0), _right(0), _bottom(0) +{ +} + +QFxScaleGrid::~QFxScaleGrid() +{ +} + +bool QFxScaleGrid::isNull() const +{ + return !_left && !_top && !_right && !_bottom; +} + +/*! + \property QFxScaleGrid::left + \brief the position of the left grid line as an offset from the left side of the image. +*/ +void QFxScaleGrid::setLeft(int pos) +{ + _left = pos; +} + +/*! + \property QFxScaleGrid::top + \brief the position of the top grid line as an offset from the top of the image. +*/ +void QFxScaleGrid::setTop(int pos) +{ + _top = pos; +} + +/*! + \property QFxScaleGrid::right + \brief the position of the right grid line as an offset from the right side of the image. +*/ +void QFxScaleGrid::setRight(int pos) +{ + _right = pos; +} + +/*! + \property QFxScaleGrid::bottom + \brief the position of the bottom grid line as an offset from the bottom of the image. +*/ +void QFxScaleGrid::setBottom(int pos) +{ + _bottom = pos; +} + +QFxGridScaledImage::QFxGridScaledImage() +: _l(-1), _r(-1), _t(-1), _b(-1) +{ +} + +QFxGridScaledImage::QFxGridScaledImage(const QFxGridScaledImage &o) +: _l(o._l), _r(o._r), _t(o._t), _b(o._b), _pix(o._pix) +{ +} + +QFxGridScaledImage &QFxGridScaledImage::operator=(const QFxGridScaledImage &o) +{ + _l = o._l; + _r = o._r; + _t = o._t; + _b = o._b; + _pix = o._pix; + return *this; +} + +QFxGridScaledImage::QFxGridScaledImage(QIODevice *data) +: _l(-1), _r(-1), _t(-1), _b(-1) +{ + int l = -1; + int r = -1; + int t = -1; + int b = -1; + QString imgFile; + + while(!data->atEnd()) { + QString line = QString::fromUtf8(data->readLine().trimmed()); + if(line.isEmpty() || line.startsWith(QLatin1String("#"))) + continue; + + QStringList list = line.split(QLatin1Char(':')); + if(list.count() != 2) + return; + + list[0] = list[0].trimmed(); + list[1] = list[1].trimmed(); + + if(list[0] == QLatin1String("gridLeft")) + l = list[1].toInt(); + else if(list[0] == QLatin1String("gridRight")) + r = list[1].toInt(); + else if(list[0] == QLatin1String("gridTop")) + t = list[1].toInt(); + else if(list[0] == QLatin1String("gridBottom")) + b = list[1].toInt(); + else if(list[0] == QLatin1String("imageFile")) + imgFile = list[1]; + } + + if(l < 0 || r < 0 || t < 0 || b < 0 || imgFile.isEmpty()) + return; + + _l = l; _r = r; _t = t; _b = b; + + _pix = imgFile; +} + +bool QFxGridScaledImage::isValid() const +{ + return _l >= 0; +} + +int QFxGridScaledImage::gridLeft() const +{ + return _l; +} + +int QFxGridScaledImage::gridRight() const +{ + return _r; +} + +int QFxGridScaledImage::gridTop() const +{ + return _t; +} + +int QFxGridScaledImage::gridBottom() const +{ + return _b; +} + +QString QFxGridScaledImage::pixmapUrl() const +{ + return _pix; +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxscalegrid.h b/src/declarative/fx/qfxscalegrid.h new file mode 100644 index 0000000..9010ce7 --- /dev/null +++ b/src/declarative/fx/qfxscalegrid.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXSCALEGRID_H +#define QFXSCALEGRID_H + +#include <qfxglobal.h> +#include <QImage> +#include <QString> +#include <QObject> +#include <qsimplecanvas.h> +#include <qfxpixmap.h> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_EXPORT QFxScaleGrid : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int left READ left WRITE setLeft) + Q_PROPERTY(int top READ top WRITE setTop) + Q_PROPERTY(int right READ right WRITE setRight) + Q_PROPERTY(int bottom READ bottom WRITE setBottom) +public: + QFxScaleGrid(); + ~QFxScaleGrid(); + + bool isNull() const; + + int left() const { return _left; } + void setLeft(int); + + int top() const { return _top; } + void setTop(int); + + int right() const { return _right; } + void setRight(int); + + int bottom() const { return _bottom; } + void setBottom(int); + +private: + int _left; + int _top; + int _right; + int _bottom; +}; + +class Q_DECLARATIVE_EXPORT QFxGridScaledImage +{ +public: + QFxGridScaledImage(); + QFxGridScaledImage(const QFxGridScaledImage &); + QFxGridScaledImage(QIODevice*); + QFxGridScaledImage &operator=(const QFxGridScaledImage &); + bool isValid() const; + int gridLeft() const; + int gridRight() const; + int gridTop() const; + int gridBottom() const; + + QString pixmapUrl() const; + +private: + int _l; + int _r; + int _t; + int _b; + QString _pix; +}; +QML_DECLARE_TYPE(QFxScaleGrid); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QFXSCALEGRID_H diff --git a/src/declarative/fx/qfxshadowfilter.cpp b/src/declarative/fx/qfxshadowfilter.cpp new file mode 100644 index 0000000..d10899f --- /dev/null +++ b/src/declarative/fx/qfxshadowfilter.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 QtDeclarative 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 "qfxshadowfilter.h" + +#if defined(QFX_RENDER_OPENGL2) +#include <glsave.h> +#include <QtOpenGL/qglframebufferobject.h> +#include <glbasicshaders.h> +#endif + +class QFxShadowFilterPrivate + +QT_BEGIN_NAMESPACE +{ +public: + QFxShadowFilterPrivate() + : x(0), y(0) + { + } + + int x; + int y; +}; + +/*! + \qmlclass Shadow + \brief The Shadow filter casts a drop shadow. + \inherits Filter + + Shadows work on all visual elements - including transparent and masked + images. + + \table + \row + \o \image shadow_example.png + \o + \code + <Rect radius="5" color="lightsteelblue" width="100" height="100"> + <filter><Shadow yOffset="8" xOffset="8" /></filter> + </Rect> + <Image src="pics/qtlogo.png"> + <filter><Shadow yOffset="8" xOffset="8" /></filter> + </Image> + \endcode + \endtable + + Shadows are only supported when Qt Qt Declarative is compiled for OpenGL ES 2.0. + Otherwise the Shadow filter has no effect. +*/ + +/*! + \internal + \class QFxShadowFilter + \ingroup effects + \brief The QFxShadowFilter class allows you to add a shadow to an item. +*/ + +QFxShadowFilter::QFxShadowFilter(QObject *parent) +: QSimpleCanvasFilter(parent), d(new QFxShadowFilterPrivate) +{ +} + +QFxShadowFilter::~QFxShadowFilter() +{ + delete d; d = 0; +} + +/*! + \qmlproperty int Shadow::xOffset + \qmlproperty int Shadow::yOffset + + Specify the x and y offset of the shadow relative to the item. +*/ + +int QFxShadowFilter::xOffset() const +{ + return d->x; +} + +/*! + \property QFxShadowFilter::xOffset + \brief the x offset of the shadow relative to the item. +*/ +void QFxShadowFilter::setXOffset(int offset) +{ + if(d->x == offset) return; + d->x = offset; + emit offsetChanged(d->x, d->y); +} + +/*! + \property QFxShadowFilter::xOffset + \brief the y offset of the shadow relative to the item. +*/ +int QFxShadowFilter::yOffset() const +{ + return d->y; +} + +void QFxShadowFilter::setYOffset(int offset) +{ + if(d->y == offset) return; + d->y = offset; + emit offsetChanged(d->x, d->y); +} + +QRectF QFxShadowFilter::itemBoundingRect(const QRectF &r) const +{ + QRectF rv = r; + rv |= r.translated(xOffset(), yOffset()); + return rv; +} + +void QFxShadowFilter::filterGL(QSimpleCanvasItem::GLPainter &p) +{ +#if defined(QFX_RENDER_OPENGL2) + + QSimpleCanvasItem *item = this->item(); + + QRect r = item->itemBoundingRect(); + + QGLFramebufferObject *fbo = renderToFBO(); + + float width = r.width(); + float height = r.height(); + + float texWidth = width / float(fbo->width()); + float texHeight = height / float(fbo->height()); + + GLfloat vertices[] = { d->x, height + d->y, + width + d->x, height + d->y, + d->x, d->y, + d->x + width, d->y }; + GLfloat texVertices[] = { 0, 0, + texWidth, 0, + 0, texHeight, + texWidth, texHeight }; + + SingleTextureShadowShader *shader = item->basicShaders()->singleTextureShadow(); + shader->enable(); + shader->setOpacity(0.8 * p.activeOpacity); + shader->setTransform(p.activeTransform); + + shader->setAttributeArray(SingleTextureShadowShader::Vertices, vertices, 2); + shader->setAttributeArray(SingleTextureShadowShader::TextureCoords, texVertices, 2); + + glBindTexture(GL_TEXTURE_2D, fbo->texture()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + shader->disableAttributeArray(SingleTextureShadowShader::Vertices); + shader->disableAttributeArray(SingleTextureShadowShader::TextureCoords); + + releaseFBO(fbo); + + renderToScreen(); +#else + Q_UNUSED(p); +#endif +} + +QML_DEFINE_TYPE(QFxShadowFilter,Shadow); + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxshadowfilter.h b/src/declarative/fx/qfxshadowfilter.h new file mode 100644 index 0000000..9ba3b7b --- /dev/null +++ b/src/declarative/fx/qfxshadowfilter.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 QtDeclarative 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 QFXSHADOWFILTER_H +#define QFXSHADOWFILTER_H + +#include <qsimplecanvasfilter.h> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxShadowFilterPrivate; +class Q_DECLARATIVE_EXPORT QFxShadowFilter : public QSimpleCanvasFilter +{ + Q_OBJECT + Q_PROPERTY(int xOffset READ xOffset WRITE setXOffset NOTIFY offsetChanged) + Q_PROPERTY(int yOffset READ yOffset WRITE setYOffset NOTIFY offsetChanged) +public: + QFxShadowFilter(QObject *parent=0); + virtual ~QFxShadowFilter(); + + int xOffset() const; + void setXOffset(int offset); + + int yOffset() const; + void setYOffset(int offset); + +Q_SIGNALS: + void offsetChanged(int, int); + +protected: + virtual QRectF itemBoundingRect(const QRectF &) const; + virtual void filterGL(QSimpleCanvasItem::GLPainter &p); + +private: + QFxShadowFilterPrivate *d; +}; +QML_DECLARE_TYPE(QFxShadowFilter); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // _QFXSHADOWFILTER_H_ diff --git a/src/declarative/fx/qfxtext.cpp b/src/declarative/fx/qfxtext.cpp new file mode 100644 index 0000000..9cf1bb0 --- /dev/null +++ b/src/declarative/fx/qfxtext.cpp @@ -0,0 +1,958 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxtext.h" +#include "qfxtext_p.h" + +#include <private/qtextcontrol_p.h> + +#if defined(QFX_RENDER_OPENGL2) +#include "glbasicshaders.h" +#endif + +#include <qfxperf.h> +#include <QTextLayout> +#include <QTextLine> +#include <QTextDocument> +#include <QTextCursor> +#include <QGraphicsSceneMouseEvent> + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxText,Text); + +/*! + \qmlclass Text QFxText + \brief The Text element allows you to add formatted text to a scene. + \inherits Item + + It can display both plain and rich text. For example: + + \code + <Text text="Hello World!" font.family="Helvetica" font.size="24" color="red"/> + <Text> + <![CDATA[<b>Hello</b> <i>World!</i>]]]]><![CDATA[> + </Text> + \endcode + + \image declarative-text.png + + Additional examples can be found in examples/poc/text.xml + + If height and width are not explicitly set, Text will attempt to determine how + much room is needed and set it accordingly. Unless \c wrap is set, it will always + prefer width to height (all text will be placed on a single line). + + The \c elide property can alternatively be used to fit a single line of + plain text to a set width. + + Text provides read-only text. For editable text, see \l TextEdit. + + \todo explain details of auto-sizing +*/ + +/*! + \internal + \class QFxText + \qmlclass Text + \ingroup coreitems + + \brief The QFxText class provides a formatted text item that you can add to a QFxView. + + Text was designed for read-only text; it does not allow for any text editing. + It can display both plain and rich text. For example: + + \code + <Text text="Hello World!" font.family="Helvetica" font.size="24" color="red"/> + <Text> + <![CDATA[<b>Hello</b> <i>World!</i>]]> + </Text> + \endcode + + \image text.png + + Note that the 'styling' properties such as color and outline are ignored for rich text, styling + of rich text should be done within the text itself. + + Additional examples can be found in examples/poc/text.xml + + If height and width are not explicitly set, Text will attempt to determine how + much room is needed and set it accordingly. Unless \c wrap is set, it will always + prefer width to height (all text will be placed on a single line). + + The \c elideMode can alternatively be used to fit a line of plain text to a set width. + + A QFxText object can be instantiated in Qml using the tag \c <Text>. +*/ +QFxText::QFxText(QFxItem *parent) + : QFxItem(*(new QFxTextPrivate), parent) +{ + Q_D(QFxText); + d->init(); + setAcceptedMouseButtons(Qt::LeftButton); + setOptions(SimpleItem | HasContents | QFxText::MouseEvents); +} + +QFxText::QFxText(QFxTextPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + Q_D(QFxText); + d->init(); + setAcceptedMouseButtons(Qt::LeftButton); + setOptions(SimpleItem | HasContents | QFxText::MouseEvents); +} + +QFxText::~QFxText() +{ +} + +/*! + \qmlproperty font Text::font + + Set the Text's font attributes. \c font.size sets the font's point size. +*/ + +/*! + \property QFxText::font + \brief the font used to display the text. +*/ + +QmlFont *QFxText::font() +{ + Q_D(QFxText); + return d->font(); +} + +void QFxText::setText(const QString &n) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::QFxText_setText> st; +#endif + Q_D(QFxText); + if(d->text == n) + return; + + d->richText = Qt::mightBeRichText(n); // ### what's the cost? + if (d->richText) { + if (!d->doc) + { + d->control = new QTextControl(this); + d->control->setTextInteractionFlags(Qt::TextBrowserInteraction); + d->doc = d->control->document(); + d->doc->setDocumentMargin(0); + } + d->doc->setHtml(n); + } + + d->text = n; + d->imgDirty = true; + d->updateSize(); + emit textChanged(d->text); + update(); +} + +/*! + \qmlproperty string Text::text + \default + + The text to display. Text supports both plain and rich text strings. + + The item will try to automatically determine whether the text should + be treated as rich text. This determination is made using Qt::mightBeRichText(). +*/ +QString QFxText::text() const +{ + Q_D(const QFxText); + return d->text; +} + +void QFxText::setColor(const QColor &color) +{ + Q_D(QFxText); + if(d->color == color) + return; + + d->imgDirty = true; + d->color = color; + update(); +} + +/*! + \qmlproperty color Text::color + + The text color. + + \code + <!-- green text using hexadecimal notation --> + <Text color="#00FF00" .../> + + <!-- steelblue text using SVG color name--> + <Text color="steelblue" .../> + \endcode +*/ + +QColor QFxText::color() const +{ + Q_D(const QFxText); + return d->color; +} + +/*! + \qmlproperty enumeration Text::style + + Set an additional text style. + + Supported text styles are \c Normal, \c Outline, \c Raised and \c Sunken. + + \code + <HorizontalLayout> + <Text font.size="24" text="Normal" /> + <Text font.size="24" text="Raised" style="Raised" styleColor="#AAAAAA"/> + <Text font.size="24" text="Outline" style="Outline" styleColor="red"/> + <Text font.size="24" text="Sunken" style="Sunken" styleColor="#AAAAAA"/> + </HorizontalLayout> + \endcode + + \image declarative-textstyle.png +*/ + +/*! + \property QFxText::style + \brief an additional style of the text to display. + + By default, the text style is Normal. + + \note This property is used to support text styles not natively + handled by QFont or QPainter::drawText(). +*/ +QFxText::TextStyle QFxText::style() const +{ + Q_D(const QFxText); + return d->style; +} + +void QFxText::setStyle(QFxText::TextStyle style) +{ + Q_D(QFxText); + if(d->style == style) + return; + + d->imgDirty = true; + d->style = style; + update(); +} + +void QFxText::setStyleColor(const QColor &color) +{ + Q_D(QFxText); + if(d->styleColor == color) + return; + + d->imgDirty = true; + d->styleColor = color; + update(); +} + +/*! + \qmlproperty color Text::styleColor + + Defines the secondary color used by text styles. + + \c styleColor is used as the outline color for outlined text, and as the + shadow color for raised or sunken text. If no style has been set, it is not + used at all. + */ +QColor QFxText::styleColor() const +{ + Q_D(const QFxText); + return d->styleColor; +} + +/*! + \qmlproperty enumeration Text::hAlign + \qmlproperty enumeration Text::vAlign + + Sets the horizontal and vertical alignment of the text within the Text elements + width and height. By default, the text is top-left aligned. + + The valid values for \c hAlign are \c AlignLeft, \c AlignRight and + \c AlignHCenter. The valid values for \c vAlign are \c AlignTop, \c AlignBottom + and \c AlignVCenter. +*/ + +/*! + \property QFxText::hAlign + \brief the horizontal alignment of the text. + + Valid values are \c AlignLeft, \c AlignRight, and \c AlignHCenter. The default value is \c AlignLeft. +*/ +QFxText::HAlignment QFxText::hAlign() const +{ + Q_D(const QFxText); + return d->hAlign; +} + +void QFxText::setHAlign(HAlignment align) +{ + Q_D(QFxText); + d->hAlign = align; +} + +/*! + \property QFxText::vAlign + \brief the vertical alignment of the text. + + Valid values are \c AlignTop, \c AlignBottom, and \c AlignVCenter. The default value is \c AlignTop. +*/ +QFxText::VAlignment QFxText::vAlign() const +{ + Q_D(const QFxText); + return d->vAlign; +} + +void QFxText::setVAlign(VAlignment align) +{ + Q_D(QFxText); + d->vAlign = align; +} + +/*! + \qmlproperty bool Text::wrap + + Set this property to wrap the text to the Text element's width. The text will only + wrap if an explicit width has been set. + + Wrapping is done on word boundaries (i.e. it is a "word-wrap"). If the text cannot be + word-wrapped to the specified width it will be partially drawn outside of the item's bounds. + If this is undesirable then enable clipping on the item (Item::clip). + + Wrapping is off by default. +*/ +//### Future may provide choice of wrap modes, such as QTextOption::WrapAtWordBoundaryOrAnywhere +bool QFxText::wrap() const +{ + Q_D(const QFxText); + return d->wrap; +} + +void QFxText::setWrap(bool w) +{ + Q_D(QFxText); + if (w == d->wrap) + return; + + d->wrap = w; + + d->imgDirty = true; + d->updateSize(); +} + +/*! + \qmlproperty Qt::TextElideMode Text::elideMode + + Set this property to elide parts of the text fit to the Text element's width. + The text will only elide if an explicit width has been set. + + This property cannot be used with wrap enabled or with rich text. + + Eliding can be ElideNone, ElideLeft, ElideMiddle, or ElideRight. + + ElideNone is the default. +*/ +Qt::TextElideMode QFxText::elideMode() const +{ + Q_D(const QFxText); + return d->elideMode; +} + +void QFxText::setElideMode(Qt::TextElideMode mode) +{ + Q_D(QFxText); + if (mode == d->elideMode) + return; + + d->elideMode = mode; + + d->imgDirty = true; + d->updateSize(); +} + + +QString QFxText::activeLink() const +{ + Q_D(const QFxText); + return d->activeLink; +} + +void QFxText::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QFxText); + if(newGeometry.width() != oldGeometry.width()) { + if (d->wrap || d->elideMode != Qt::ElideNone) { + d->imgDirty = true; + d->updateSize(); + } + } + QFxItem::geometryChanged(newGeometry, oldGeometry); +} + + +void QFxText::fontChanged() +{ + Q_D(QFxText); + d->imgDirty = true; + d->updateSize(); + emit update(); +} + +void QFxTextPrivate::updateSize() +{ + Q_Q(QFxText); + if (q->isComponentComplete()) { + if(text.isEmpty()) { + return; + } + QFont f; if(_font) f = _font->font(); + QFontMetrics fm(f); + int dy = q->height(); + + QString tmp; + QSize size(0, 0); + + //setup instance of QTextLayout for all cases other than richtext + if (!richText) + { + tmp = text; + tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); + singleline = !tmp.contains(QChar::LineSeparator); + if (singleline && elideMode != Qt::ElideNone && q->widthValid()) + tmp = fm.elidedText(tmp,elideMode,q->width()); // XXX still worth layout...? + QTextLayout layout; + layout.setFont(f); + layout.setText(tmp); + size = setupTextLayout(&layout); + } + if (richText) { + singleline = false; // richtext can't elide or be optimized for single-line case + doc->setDefaultFont(f); + QTextOption option((Qt::Alignment)int(hAlign | vAlign)); + if (wrap) + option.setWrapMode(QTextOption::WordWrap); + else + option.setWrapMode(QTextOption::NoWrap); + doc->setDefaultTextOption(option); + if (wrap && !q->heightValid() && q->widthValid()) + doc->setTextWidth(q->width()); + else + doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug) + dy -= (int)doc->size().height(); + } else { + dy -= size.height(); + } + int yoff = 0; + + if (q->heightValid()) { + if (vAlign == QFxText::AlignBottom) + yoff = dy; + else if (vAlign == QFxText::AlignVCenter) + yoff = dy/2; + } + q->setBaselineOffset(fm.ascent() + yoff); + + if (!q->widthValid()) { + int newWidth = (richText ? (int)doc->idealWidth() : size.width()); + q->setImplicitWidth(newWidth); + } + if (!q->heightValid()) { + if (richText) { + q->setImplicitHeight((int)doc->size().height()); + } else { + q->setImplicitHeight(size.height()); + } + } + } else { + dirty = true; + } +} + +// ### text layout handling should be profiled and optimized as needed +// what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine); + +void QFxText::dump(int depth) +{ + QByteArray ba(depth * 4, ' '); + qWarning() << ba.constData() << propertyInfo(); + QFxItem::dump(depth); +} + +QString QFxText::propertyInfo() const +{ + Q_D(const QFxText); + return QChar(QLatin1Char('\"')) + d->text + QChar(QLatin1Char('\"')); +} + +void QFxTextPrivate::drawOutline() +{ + QImage img = QImage(imgCache.size(), QImage::Format_ARGB32_Premultiplied); + QPainter ppm(&img); + img.fill(qRgba(0, 0, 0, 0)); + + QPoint pos(imgCache.rect().topLeft()); + pos += QPoint(-1, 0); + ppm.drawImage(pos, imgStyleCache); + pos += QPoint(2, 0); + ppm.drawImage(pos, imgStyleCache); + pos += QPoint(-1, -1); + ppm.drawImage(pos, imgStyleCache); + pos += QPoint(0, 2); + ppm.drawImage(pos, imgStyleCache); + + pos += QPoint(0, -1); + QPainter &p = ppm; + p.drawImage(pos, imgCache); + + imgCache = QSimpleCanvasConfig::toImage(img); +} + +void QFxTextPrivate::drawOutline(int yOffset) +{ + QImage img = QImage(imgCache.size(), QImage::Format_ARGB32_Premultiplied); + QPainter ppm(&img); + img.fill(qRgba(0, 0, 0, 0)); + + QPoint pos(imgCache.rect().topLeft()); + pos += QPoint(0, yOffset); + ppm.drawImage(pos, imgStyleCache); + + pos += QPoint(0, -yOffset); + QPainter &p = ppm; + p.drawImage(pos, imgCache); + + imgCache = QSimpleCanvasConfig::toImage(img); +} + +QSize QFxTextPrivate::setupTextLayout(QTextLayout *layout) +{ + Q_Q(QFxText); + layout->setCacheEnabled(true); + + QFont f; if(_font) f = _font->font(); + QFontMetrics fm = QFontMetrics(f); + + int leading = fm.leading(); + int height = 0; + qreal widthUsed = 0; + qreal lineWidth = 0; + + //set manual width + if ((wrap || elideMode != Qt::ElideNone) && q->widthValid()) + lineWidth = q->width(); + + layout->beginLayout(); + + while (1) { + QTextLine line = layout->createLine(); + if (!line.isValid()) + break; + + if ((wrap || elideMode != Qt::ElideNone) && q->widthValid()) + line.setLineWidth(lineWidth); + } + layout->endLayout(); + + if (layout->lineCount() == 1) + height -= leading; + + for (int i = 0; i < layout->lineCount(); ++i) { + QTextLine line = layout->lineAt(i); + widthUsed = qMax(widthUsed, line.naturalTextWidth()); + line.setPosition(QPointF(0, height)); + height += int(line.height()); + } + return QSize((int)widthUsed, height); +} + +QImage QFxTextPrivate::wrappedTextImage(bool drawStyle) +{ + //do layout + Q_Q(const QFxText); + QFont f; if(_font) f = _font->font(); + QString tmp = text; + if (singleline && elideMode != Qt::ElideNone && q->widthValid()) { + QFontMetrics fm(f); + tmp = fm.elidedText(tmp,elideMode,q->width()); // XXX still worth layout...? + } + tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); + QTextLayout textLayout(tmp, f); + QSize size = setupTextLayout(&textLayout); + + int x = 0; + for (int i = 0; i < textLayout.lineCount(); ++i) { + QTextLine line = textLayout.lineAt(i); + if(hAlign == QFxText::AlignLeft) { + x = 0; + } else if(hAlign == QFxText::AlignRight) { + x = size.width() - (int)line.naturalTextWidth(); + } else if(hAlign == QFxText::AlignHCenter) { + x = (size.width() - (int)line.naturalTextWidth()) / 2; + } + line.setPosition(QPoint(x, (int)line.y())); + } + + //paint text + QImage img(size, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter p(&img); + if (drawStyle) { + p.setPen(styleColor); + } + else + p.setPen(color); + p.setFont(f); + textLayout.draw(&p, QPointF(0, 0)); + return img; +} + +QImage QFxTextPrivate::richTextImage(bool drawStyle) +{ + QSize size = doc->size().toSize(); + + //paint text + QImage img(size, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter p(&img); + + if (drawStyle) { + QPalette pal = control->palette(); + pal.setColor(QPalette::Text, styleColor); + control->setPalette(pal); + QTextOption colorOption; + colorOption.setFlags(QTextOption::SuppressColors); + doc->setDefaultTextOption(colorOption); + } else { + QPalette pal = control->palette(); + pal.setColor(QPalette::Text, color); + control->setPalette(pal); + } + control->drawContents(&p, QRectF(QPointF(0, 0), QSizeF(size))); + if (drawStyle) + doc->setDefaultTextOption(QTextOption()); + return img; +} + +void QFxTextPrivate::checkImgCache() +{ + if(!imgDirty) + return; + + bool empty = text.isEmpty(); + if (empty) { + imgCache = QSimpleCanvasConfig::Image(); + imgStyleCache = QImage(); + } else if (richText) { + imgCache = QSimpleCanvasConfig::toImage(richTextImage(false)); + if (style != QFxText::Normal) + imgStyleCache = richTextImage(true); //### should use styleColor + } else { + imgCache = QSimpleCanvasConfig::toImage(wrappedTextImage(false)); + if (style != QFxText::Normal) + imgStyleCache = wrappedTextImage(true); //### should use styleColor + } + if (!empty) + switch (style) { + case QFxText::Outline: + drawOutline(); + break; + case QFxText::Sunken: + drawOutline(-1); + break; + case QFxText::Raised: + drawOutline(1); + break; + default: + break; + } + +#if defined(QFX_RENDER_OPENGL) + tex.setImage(imgCache); +#endif + + imgDirty = false; +} + +#if defined(QFX_RENDER_QPAINTER) +void QFxText::paintContents(QPainter &p) +{ + Q_D(QFxText); + d->checkImgCache(); + if(d->imgCache.isNull()) + return; + + int w = width(); + int h = height(); + + int x = 0; + int y = 0; + + switch (d->hAlign) { + case AlignLeft: + x = 0; + break; + case AlignRight: + x = w - d->imgCache.width(); + break; + case AlignHCenter: + x = (w - d->imgCache.width()) / 2; + break; + } + + switch (d->vAlign) { + case AlignTop: + y = 0; + break; + case AlignBottom: + y = h - d->imgCache.height(); + break; + case AlignVCenter: + y = (h - d->imgCache.height()) / 2; + break; + } + + p.drawImage(x, y, d->imgCache); +} + +#elif defined(QFX_RENDER_OPENGL2) +void QFxText::paintGLContents(GLPainter &p) +{ + Q_D(QFxText); + d->checkImgCache(); + if(d->imgCache.isNull()) + return; + + int w = width(); + int h = height(); + + float x = 0; + float y = 0; + + switch (d->hAlign) { + case AlignLeft: + x = 0; + break; + case AlignRight: + x = w - d->imgCache.width(); + break; + case AlignHCenter: + x = (w - d->imgCache.width()) / 2; + break; + } + + switch (d->vAlign) { + case AlignTop: + y = 0; + break; + case AlignBottom: + y = h - d->imgCache.height(); + break; + case AlignVCenter: + y = (h - d->imgCache.height()) / 2; + break; + } + + float widthV = d->imgCache.width(); + float heightV = d->imgCache.height(); + + QGLShaderProgram *shader = p.useTextureShader(); + + GLfloat vertices[] = { x, y + heightV, + x + widthV, y + heightV, + x, y, + x + widthV, y }; + + GLfloat texVertices[] = { 0, 0, + 1, 0, + 0, 1, + 1, 1 }; + + shader->setAttributeArray(SingleTextureShader::Vertices, vertices, 2); + shader->setAttributeArray(SingleTextureShader::TextureCoords, texVertices, 2); + + glBindTexture(GL_TEXTURE_2D, d->tex.texture()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + shader->disableAttributeArray(SingleTextureShader::Vertices); + shader->disableAttributeArray(SingleTextureShader::TextureCoords); +} + +#elif defined(QFX_RENDER_OPENGL) +void QFxText::paintGLContents(GLPainter &p) +{ + Q_D(QFxText); + d->checkImgCache(); + if(d->imgCache.isNull()) + return; + + int w = width(); + int h = height(); + + float x = 0; + float y = 0; + + switch (d->hAlign) { + case AlignLeft: + x = 0; + break; + case AlignRight: + x = w - d->imgCache.width(); + break; + case AlignHCenter: + x = (w - d->imgCache.width()) / 2; + break; + } + + switch (d->vAlign) { + case AlignTop: + y = 0; + break; + case AlignBottom: + y = h - d->imgCache.height(); + break; + case AlignVCenter: + y = (h - d->imgCache.height()) / 2; + break; + } + + float widthV = d->imgCache.width(); + float heightV = d->imgCache.height(); + + GLfloat vertices[] = { x, y + heightV, + x + widthV, y + heightV, + x, y, + x + widthV, y }; + + GLfloat texVertices[] = { 0, 0, + 1, 0, + 0, 1, + 1, 1 }; + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(p.activeTransform.data()); + glEnable(GL_TEXTURE_2D); + if(p.activeOpacity == 1.) { + GLint i = GL_REPLACE; + glTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &i); + } else { + GLint i = GL_MODULATE; + glTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &i); + glColor4f(1, 1, 1, p.activeOpacity); + } + + glBindTexture(GL_TEXTURE_2D, d->tex.texture()); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, vertices); + glTexCoordPointer(2, GL_FLOAT, 0, texVertices); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); +} + +#endif + +void QFxText::componentComplete() +{ + Q_D(QFxText); +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::TextComponentComplete> cc; +#endif + QFxItem::componentComplete(); + if (d->dirty) { + d->updateSize(); + d->dirty = false; + } +} + +/*! + \overload + Handles the given mouse \a event. + */ +void QFxText::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxText); + + if (!d->richText || !d->doc || d->control->anchorAt(event->pos()).isEmpty()) { + event->setAccepted(false); + d->activeLink = QString(); + } else { + d->activeLink = d->control->anchorAt(event->pos()); + } + + // ### may malfunction if two of the same links are clicked & dragged onto each other) + + if (!event->isAccepted()) + QFxItem::mousePressEvent(event); + +} + +/*! + \overload + Handles the given mouse \a event. + */ +void QFxText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxText); + + // ### confirm the link, and send a signal out + if (d->richText && d->doc && d->activeLink == d->control->anchorAt(event->pos())) + emit linkActivated(d->activeLink); + else + event->setAccepted(false); + + if (!event->isAccepted()) + QFxItem::mouseReleaseEvent(event); +} +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxtext.h b/src/declarative/fx/qfxtext.h new file mode 100644 index 0000000..0de884b --- /dev/null +++ b/src/declarative/fx/qfxtext.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXTEXT_H +#define QFXTEXT_H + +#include <qfxitem.h> +#include <qmlfont.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxTextPrivate; +class Q_DECLARATIVE_EXPORT QFxText : public QFxItem +{ + Q_OBJECT + Q_ENUMS(HAlignment) + Q_ENUMS(VAlignment) + Q_ENUMS(TextStyle) + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QmlFont *font READ font) + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(TextStyle style READ style WRITE setStyle) + Q_PROPERTY(QColor styleColor READ styleColor WRITE setStyleColor) + Q_PROPERTY(HAlignment hAlign READ hAlign WRITE setHAlign) + Q_PROPERTY(VAlignment vAlign READ vAlign WRITE setVAlign) + Q_PROPERTY(bool wrap READ wrap WRITE setWrap) + Q_PROPERTY(Qt::TextElideMode elide READ elideMode WRITE setElideMode) + Q_PROPERTY(QString activeLink READ activeLink); + Q_CLASSINFO("DefaultProperty", "text") + +public: + QFxText(QFxItem *parent=0); + ~QFxText(); + + enum HAlignment { AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter }; + enum VAlignment { AlignTop = Qt::AlignTop, + AlignBottom = Qt::AlignBottom, + AlignVCenter = Qt::AlignVCenter }; + enum TextStyle { Normal, + Outline, + Raised, + Sunken }; + + QString text() const; + void setText(const QString &); + + QmlFont *font(); + + QColor color() const; + void setColor(const QColor &c); + + TextStyle style() const; + void setStyle(TextStyle style); + + QColor styleColor() const; + void setStyleColor(const QColor &c); + + HAlignment hAlign() const; + void setHAlign(HAlignment align); + + VAlignment vAlign() const; + void setVAlign(VAlignment align); + + bool wrap() const; + void setWrap(bool w); + + Qt::TextElideMode elideMode() const; + void setElideMode(Qt::TextElideMode); + + QString activeLink() const; + + virtual void dump(int depth); + virtual QString propertyInfo() const; + +#if defined(QFX_RENDER_QPAINTER) + void paintContents(QPainter &p); +#elif defined(QFX_RENDER_OPENGL) + void paintGLContents(GLPainter &); +#endif + + virtual void componentComplete(); + +Q_SIGNALS: + void textChanged(const QString &text); + void linkActivated(const QString &link); + +private Q_SLOTS: + //### should be in QFxTextPrivate? + void fontChanged(); + +protected: + QFxText(QFxTextPrivate &dd, QFxItem *parent); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + +private: + Q_DISABLE_COPY(QFxText) + Q_DECLARE_PRIVATE(QFxText) +}; +QML_DECLARE_TYPE(QFxText); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/fx/qfxtext_p.h b/src/declarative/fx/qfxtext_p.h new file mode 100644 index 0000000..7be4309 --- /dev/null +++ b/src/declarative/fx/qfxtext_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXTEXT_P_H +#define QFXTEXT_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 "qfxitem.h" +#include "qfxitem_p.h" +#include "qml.h" + +#if defined(QFX_RENDER_OPENGL) +#include "gltexture.h" +#endif + +QT_BEGIN_NAMESPACE + +class QTextLayout; +class QTextDocument; +class QTextControl; + +class QFxTextPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxText) +public: + QFxTextPrivate() + : _font(0), color((QRgb)0), style(QFxText::Normal), imgDirty(true), hAlign(QFxText::AlignLeft), vAlign(QFxText::AlignTop), elideMode(Qt::ElideNone), dirty(false), wrap(false), richText(false), singleline(false), control(0), doc(0) + { + } + + ~QFxTextPrivate() + { + delete _font; + } + + void init() + { + } + + void updateSize(); + void checkImgCache(); + + void drawOutline(); + void drawOutline(int yOffset); + + QImage wrappedTextImage(bool drawStyle); + QImage richTextImage(bool drawStyle); + QSize setupTextLayout(QTextLayout *layout); + + QString text; + QmlFont *font() + { + if(!_font) { + Q_Q(QFxText); + _font = new QmlFont; + QObject::connect(_font, SIGNAL(updated()), q, SLOT(fontChanged())); + } + return _font; + } + + QmlFont *_font; + QColor color; + QFxText::TextStyle style; + QColor styleColor; + QString activeLink; + bool imgDirty; +#if defined(QFX_RENDER_OPENGL) + GLTexture tex; +#endif + QSimpleCanvasConfig::Image imgCache; + QImage imgStyleCache; + QFxText::HAlignment hAlign; + QFxText::VAlignment vAlign; + Qt::TextElideMode elideMode; + bool dirty; + bool wrap; + bool richText; + bool singleline; + QTextControl *control; + QTextDocument *doc; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/fx/qfxtextedit.cpp b/src/declarative/fx/qfxtextedit.cpp new file mode 100644 index 0000000..ebfe799 --- /dev/null +++ b/src/declarative/fx/qfxtextedit.cpp @@ -0,0 +1,804 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <qfxtextedit.h> +#include "qfxtextedit_p.h" + +#include <private/qtextcontrol_p.h> + +#if defined(QFX_RENDER_OPENGL2) +#include "glbasicshaders.h" +#endif + +#include <qfxperf.h> +#include <QTextLayout> +#include <QTextLine> +#include <QTextDocument> +#include <QGraphicsSceneMouseEvent> + +#include <QDebug> + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxTextEdit, TextEdit); + +/*! + \qmlclass TextEdit + \brief The TextEdit element allows you to add editable formatted text to a scene. + \inherits ImageItem + + It can display both plain and rich text. For example: + + \code + <TextEdit id="edit" focus="true" focusable="true" + font.family="Helvetica" font.size="20" color="blue" width="240"> + <![CDATA[<b>Hello</b> <i>World!</i>]]/> + </TextEdit> + \endcode + + \image declarative-textedit.gif + + \sa Text +*/ + +/*! + \internal + \class QFxTextEdit + \qmlclass TextEdit + \ingroup coreitems + + \brief The QFxTextEdit class provides an editable formatted text item that you can add to a QFxView. + + It can display both plain and rich text. + + \image declarative-textedit.png + + A QFxTextEdit object can be instantiated in Qml using the tag \c <TextEdit>. +*/ + +/*! + Constructs a new QFxTextEdit. +*/ +QFxTextEdit::QFxTextEdit(QFxItem *parent) +: QFxImageItem(*(new QFxTextEditPrivate), parent) +{ + Q_D(QFxTextEdit); + d->init(); +} + +/*! +\internal +*/ +QFxTextEdit::QFxTextEdit(QFxTextEditPrivate &dd, QFxItem *parent) + : QFxImageItem(dd, parent) +{ + Q_D(QFxTextEdit); + d->init(); +} + +QString QFxTextEdit::text() const +{ + Q_D(const QFxTextEdit); + + if (d->richText) + return d->document->toHtml(); + else + return d->document->toPlainText(); +} + +/*! + \qmlproperty font TextEdit::font + + Set the TextEdit's font attributes. \c font.size sets the font's point size. +*/ + +/*! + \qmlproperty string TextEdit::text + \default + + The text to display. If the text format is AutoText the text edit will + automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). +*/ + +/*! + \property QFxTextEdit::text + \brief the text edit's text + + If the text format is set to AutoText the text edit will try to + automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). + + \sa textFormat +*/ + +void QFxTextEdit::setText(const QString &text) +{ + Q_D(QFxTextEdit); + d->text = text; + d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text)); + if (d->richText) { + d->control->setHtml(text); + } else { + d->control->setPlainText(text); + } + q_textChanged(); + updateSize(); +} + +/*! + \qmlproperty enumeration TextEdit::textFormat + + The way the text property should be displayed. + + Supported text formats are \c AutoText, \c PlainText and \c RichText. + + The default is AutoText. If the text format is AutoText the text edit + edit will automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). + + \table + \row + \o + \code + <VerticalLayout> + <TextEdit font.size="24"> + <![CDATA[<b>Hello</b> <i>World!</i>]]]]><![CDATA[> + </TextEdit> + <TextEdit font.size="24" textFormat="RichText"> + <![CDATA[<b>Hello</b> <i>World!</i>]]]]><![CDATA[> + </TextEdit> + <TextEdit font.size="24" textFormat="PlainText"> + <![CDATA[<b>Hello</b> <i>World!</i>]]]]><![CDATA[> + </TextEdit> + </VerticalLayout> + \endcode + \o \image declarative-textformat.png + \endtable +*/ + +/*! + \property QFxTextEdit::textFormat + \brief this property describes how the text set on the text edit + should be interpreted. + + Valid values are \c AutoText, \c PlainText and \c RichText. The + default value is \c AutoText, meaning the text edit will attempt + to guess how the text should be interpreted using the + Qt::mightBeRichText() function. + + \sa text +*/ + +QFxTextEdit::TextFormat QFxTextEdit::textFormat() const +{ + Q_D(const QFxTextEdit); + return d->format; +} + +void QFxTextEdit::setTextFormat(TextFormat format) +{ + Q_D(QFxTextEdit); + if (format == d->format) + return; + bool wasRich = d->richText; + d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text)); + + if (wasRich && !d->richText) { + d->control->setPlainText(d->text); + updateSize(); + } else if (!wasRich && d->richText) { + d->control->setHtml(d->text); + updateSize(); + } + d->format = format; +} + +/*! + \property QFxTextEdit::font + \brief the text edit's default font +*/ + +QmlFont *QFxTextEdit::font() +{ + Q_D(QFxTextEdit); + return &(d->font); +} + +/*! + \qmlproperty color TextEdit::color + + The text color. + + \code + <!-- green text using hexadecimal notation --> + <TextEdit color="#00FF00" .../> + + <!-- steelblue text using SVG color name--> + <TextEdit color="steelblue" .../> + \endcode +*/ + +/*! + \property QFxTextEdit::color + \brief the text edit's default text color +*/ +QColor QFxTextEdit::color() const +{ + Q_D(const QFxTextEdit); + return d->color; +} + +void QFxTextEdit::setColor(const QColor &color) +{ + Q_D(QFxTextEdit); + if(d->color == color) + return; + + clearCache(); + d->color = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::Text, color); + d->control->setPalette(pal); + update(); +} + +/*! + \qmlproperty enumeration TextEdit::hAlign + \qmlproperty enumeration TextEdit::vAlign + + Sets the horizontal and vertical alignment of the text within the TextEdit elements + width and height. By default, the text is top-left aligned. + + The valid values for \c hAlign are \c AlignLeft, \c AlignRight and + \c AlignHCenter. The valid values for \c vAlign are \c AlignTop, \c AlignBottom + and \c AlignVCenter. +*/ + +/*! + \property QFxText::hAlign + \brief the horizontal alignment of the text. + + Valid values are \c AlignLeft, \c AlignRight, and \c AlignHCenter. The default value is \c AlignLeft. +*/ +QFxTextEdit::HAlignment QFxTextEdit::hAlign() const +{ + Q_D(const QFxTextEdit); + return d->hAlign; +} + +void QFxTextEdit::setHAlign(QFxTextEdit::HAlignment alignment) +{ + Q_D(QFxTextEdit); + if (alignment == d->hAlign) + return; + d->hAlign = alignment; + d->updateDefaultTextOption(); + updateSize(); +} + +/*! + \property QFxText::vAlign + \brief the vertical alignment of the text. + + Valid values are \c AlignTop, \c AlignBottom, and \c AlignVCenter. The default value is \c AlignTop. +*/ +QFxTextEdit::VAlignment QFxTextEdit::vAlign() const +{ + Q_D(const QFxTextEdit); + return d->vAlign; +} + +void QFxTextEdit::setVAlign(QFxTextEdit::VAlignment alignment) +{ + Q_D(QFxTextEdit); + if (alignment == d->vAlign) + return; + d->vAlign = alignment; + d->updateDefaultTextOption(); + updateSize(); +} + +bool QFxTextEdit::wrap() const +{ + Q_D(const QFxTextEdit); + return d->wrap; +} + +/*! + \qmlproperty bool TextEdit::wrap + + Set this property to wrap the text to the TextEdit element's width. The text will only wrap if an explicit width has been set. + + Wrapping is done on word boundaries (i.e. it is a "word-wrap"). Wrapping is off by default. +*/ + +/*! + \property QFxTextEdit::wrap + \brief If true the text edit wraps text based on the width of the + text edit. +*/ +void QFxTextEdit::setWrap(bool w) +{ + Q_D(QFxTextEdit); + if (w == d->wrap) + return; + d->wrap = w; + d->updateDefaultTextOption(); + updateSize(); +} + + +void QFxTextEdit::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + if(newGeometry.width() != oldGeometry.width()) + updateSize(); + QFxImageItem::geometryChanged(newGeometry, oldGeometry); +} + +/*! + \internal +*/ +void QFxTextEdit::dump(int depth) +{ + QByteArray ba(depth * 4, ' '); + qWarning() << ba.constData() << propertyInfo(); + QFxImageItem::dump(depth); +} + +/*! + \internal +*/ +QString QFxTextEdit::propertyInfo() const +{ + Q_D(const QFxTextEdit); + return QChar(QLatin1Char('\"')) + d->text + QChar(QLatin1Char('\"')); +} + +/*! + Ensures any delayed caching or data loading the class + needs to performed is complete. +*/ +void QFxTextEdit::componentComplete() +{ + Q_D(QFxTextEdit); + QFxImageItem::componentComplete(); + if (d->dirty) { + updateSize(); + d->dirty = false; + } +} + +/*! + \qmlproperty bool TextEdit::readOnly + + Whether the user an interact with the TextEdit element. If this + property is set to true the text cannot be edited by user interaction. + + By default this property is false. +*/ + +/*! + \property QFxTextEdit::readOnly + \brief If this property is true the text can not be edited by + user interaction. + + Changing this property will modify the text interaction flags. If + you require more specific control about how user interaction + with the text edit is handled, use setTextInteractionFlags() instead. + + \sa setTextInteractionFlags() +*/ +void QFxTextEdit::setReadOnly(bool r) +{ + Q_D(QFxTextEdit); + + Qt::TextInteractionFlags flags = Qt::NoTextInteraction; + if (r) { + flags = Qt::TextSelectableByMouse; + } else { + flags = Qt::TextEditorInteraction; + } + d->control->setTextInteractionFlags(flags); + if (!r) + d->control->moveCursor(QTextCursor::End); +} + +bool QFxTextEdit::isReadOnly() const +{ + Q_D(const QFxTextEdit); + return !(d->control->textInteractionFlags() & Qt::TextEditable); +} + +/*! + Sets how the text edit should interact with user input to the given + \a flags. +*/ +void QFxTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags) +{ + Q_D(QFxTextEdit); + d->control->setTextInteractionFlags(flags); +} + +/*! + Returns the flags specifying how the text edit should interact + with user input. +*/ +Qt::TextInteractionFlags QFxTextEdit::textInteractionFlags() const +{ + Q_D(const QFxTextEdit); + return d->control->textInteractionFlags(); +} + +/*! + Returns the cursor for the point at the given \a pos on the + text edit. +*/ +QTextCursor QFxTextEdit::cursorForPosition(const QPoint &pos) const +{ + Q_D(const QFxTextEdit); + return d->control->cursorForPosition(pos); +} + +/*! + Returns the rectangle where the given text \a cursor is rendered + within the text edit. +*/ +QRect QFxTextEdit::cursorRect(const QTextCursor &cursor) const +{ + Q_D(const QFxTextEdit); + if (cursor.isNull()) + return QRect(); + + return d->control->cursorRect(cursor).toRect(); +} + +/*! + Returns the rectangle where the text cursor is rendered + within the text edit. +*/ +QRect QFxTextEdit::cursorRect() const +{ + Q_D(const QFxTextEdit); + return d->control->cursorRect().toRect(); +} + + +/*! + Sets the text cursor for the text edit to the given \a cursor. +*/ +void QFxTextEdit::setTextCursor(const QTextCursor &cursor) +{ + Q_D(QFxTextEdit); + d->control->setTextCursor(cursor); +} + +/*! + Returns the text cursor for the text edit. +*/ +QTextCursor QFxTextEdit::textCursor() const +{ + Q_D(const QFxTextEdit); + return d->control->textCursor(); +} + +/*! +Moves the cursor by performing the given \a operation. + +If \a mode is QTextCursor::KeepAnchor, the cursor selects the text it moves over. This is the same effect that the user achieves when they hold down the Shift key and move the cursor with the cursor keys. +*/ +void QFxTextEdit::moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode) +{ + Q_D(QFxTextEdit); + d->control->moveCursor(operation, mode); +} + +/*! +\overload +Handles the given key \a event. +*/ +void QFxTextEdit::keyPressEvent(QKeyEvent *event) +{ + Q_D(QFxTextEdit); + QTextCursor c = textCursor(); + QTextCursor::MoveOperation op = QTextCursor::NoMove; + if (event == QKeySequence::MoveToNextChar) { + op = QTextCursor::Right; + } else if (event == QKeySequence::MoveToPreviousChar) { + op = QTextCursor::Left; + } else if (event == QKeySequence::MoveToNextWord) { + op = QTextCursor::WordRight; + } else if (event == QKeySequence::MoveToPreviousWord) { + op = QTextCursor::WordLeft; + } else if (event == QKeySequence::MoveToNextLine) { + op = QTextCursor::Down; + } else if (event == QKeySequence::MoveToPreviousLine) { + op = QTextCursor::Up; + } + + if (op != QTextCursor::NoMove && !c.movePosition(op)) + event->ignore(); + else + d->control->processEvent(event, QPointF(0, 0)); +} + +/*! +\overload +Handles the given key \a event. +*/ +void QFxTextEdit::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QFxTextEdit); + d->control->processEvent(event, QPointF(0, 0)); +} + +/*! +\overload +Handles the given focus \a event. +*/ +void QFxTextEdit::focusInEvent(QFocusEvent *event) +{ + Q_D(QFxTextEdit); + QFxImageItem::focusInEvent(event); + d->control->processEvent(event, QPointF(0, 0)); +} + +/*! +\overload +Handles the given focus \a event. +*/ +void QFxTextEdit::focusOutEvent(QFocusEvent *event) +{ + Q_D(QFxTextEdit); + QFxImageItem::focusOutEvent(event); + d->control->processEvent(event, QPointF(0, 0)); +} + +static QMouseEvent *sceneMouseEventToMouseEvent(QGraphicsSceneMouseEvent *e) +{ + QEvent::Type t; + switch(e->type()) { + default: + case QEvent::GraphicsSceneMousePress: + t = QEvent::MouseButtonPress; + break; + case QEvent::GraphicsSceneMouseRelease: + t = QEvent::MouseButtonRelease; + break; + case QEvent::GraphicsSceneMouseMove: + t = QEvent::MouseMove; + break; + case QGraphicsSceneEvent::GraphicsSceneMouseDoubleClick: + t = QEvent::MouseButtonDblClick; + break; + } + + QMouseEvent *me = new QMouseEvent(t, e->pos().toPoint(), e->button(), e->buttons(), 0); + return me; +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QFxTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxTextEdit); + QMouseEvent *me = sceneMouseEventToMouseEvent(event); + d->control->processEvent(me, QPointF(0, 0)); + event->setAccepted(me->isAccepted()); + delete me; + if (!event->isAccepted()) + QFxImageItem::mousePressEvent(event); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QFxTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxTextEdit); + QMouseEvent *me = sceneMouseEventToMouseEvent(event); + d->control->processEvent(me, QPointF(0, 0)); + event->setAccepted(me->isAccepted()); + delete me; + if (!event->isAccepted()) + QFxImageItem::mousePressEvent(event); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QFxTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxTextEdit); + QMouseEvent *me = sceneMouseEventToMouseEvent(event); + d->control->processEvent(me, QPointF(0, 0)); + event->setAccepted(me->isAccepted()); + delete me; + if (!event->isAccepted()) + QFxImageItem::mousePressEvent(event); +} + +/*! +\overload +Handles the given input method \a event. +*/ +void QFxTextEdit::inputMethodEvent(QInputMethodEvent *event) +{ + Q_D(QFxTextEdit); + d->control->processEvent(event, QPointF(0, 0)); +} + +/*! +\overload +Returns the value of the given \a property. +*/ +QVariant QFxTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const +{ + Q_D(const QFxTextEdit); + return d->control->inputMethodQuery(property); +} + +/*! +Draws the contents of the text edit using the given \a painter within +the given \a bounds. +*/ +void QFxTextEdit::drawContents(QPainter *painter, const QRect &bounds) +{ + Q_D(QFxTextEdit); + + painter->setRenderHint(QPainter::TextAntialiasing, true); + + d->control->drawContents(painter, bounds); +} + +/*! +This signal is emitted when the font of the item changes. +*/ +void QFxTextEdit::fontChanged() +{ + Q_D(QFxTextEdit); + clearCache(); + d->document->setDefaultFont(d->font.font()); + updateSize(); + emit update(); +} + +void QFxTextEdit::updateImgCache(const QRectF &r) +{ + dirtyCache(r.toRect()); + emit update(); +} + +void QFxTextEditPrivate::init() +{ + Q_Q(QFxTextEdit); + + q->setSmooth(true); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setOptions(QFxTextEdit::AcceptsInputMethods | QFxTextEdit::SimpleItem + | QFxTextEdit::HasContents | QFxTextEdit::MouseEvents); + + QObject::connect(&font, SIGNAL(updated()), q, SLOT(fontChanged())); + + control = new QTextControl(q); + + QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateImgCache(QRectF))); + + QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged())); + QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged())); + + document = control->document(); + document->setDefaultFont(font.font()); + document->setDocumentMargin(0); + document->setUndoRedoEnabled(false); // flush undo buffer. + document->setUndoRedoEnabled(true); +} + +void QFxTextEdit::q_textChanged() +{ + emit textChanged(text()); +} + +void QFxTextEdit::updateSize() +{ + Q_D(QFxTextEdit); + if (isComponentComplete()) { + QFontMetrics fm = QFontMetrics(d->font.font()); + int dy = height(); + // ### assumes that if the width is set, the text will fill to edges + // ### (unless wrap is false, then clipping will occur) + if (widthValid()) + d->document->setTextWidth(width()); + dy -= (int)d->document->size().height(); + + int yoff = 0; + if (heightValid()) { + if (d->vAlign == AlignBottom) + yoff = dy; + else if (d->vAlign == AlignVCenter) + yoff = dy/2; + } + setBaselineOffset(fm.ascent() + yoff); + if (!widthValid()) { + int newWidth = (int)d->document->idealWidth(); + d->document->setTextWidth(newWidth); // ### QTextDoc> Alignment will not work unless textWidth is set + setImplicitWidth(newWidth); + } + if (!heightValid()) { + if (d->text.isEmpty()) { + setImplicitHeight(fm.height()); + } else { + setImplicitHeight((int)d->document->size().height()); + } + } + setContentsSize(QSize(width(), height())); + } else { + d->dirty = true; + } + emit update(); +} + +void QFxTextEditPrivate::updateDefaultTextOption() +{ + QTextDocument *doc = control->document(); + + QTextOption opt = doc->defaultTextOption(); + int oldAlignment = opt.alignment(); + opt.setAlignment((Qt::Alignment)(int)(hAlign | vAlign)); + + QTextOption::WrapMode oldWrapMode = opt.wrapMode(); + + if (wrap) + opt.setWrapMode(QTextOption::WordWrap); + else + opt.setWrapMode(QTextOption::NoWrap); + + if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment()) + return; + doc->setDefaultTextOption(opt); +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxtextedit.h b/src/declarative/fx/qfxtextedit.h new file mode 100644 index 0000000..be84a3c --- /dev/null +++ b/src/declarative/fx/qfxtextedit.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXTEXTEDIT_H +#define QFXTEXTEDIT_H + +#include <qfxtext.h> +#include <qfximageitem.h> + +#include <QtGui/qtextdocument.h> +#include <QtGui/qtextoption.h> +#include <QtGui/qtextcursor.h> +#include <QtGui/qtextformat.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +/*! +WARNING: SHORT TERM CLASS. INTENDED TO MERGE INTO QFxTextItem +*/ +class QFxTextEditPrivate; +class Q_DECLARATIVE_EXPORT QFxTextEdit : public QFxImageItem +{ + Q_OBJECT + Q_ENUMS(VAlignment) + Q_ENUMS(HAlignment) + Q_ENUMS(TextFormat) + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(QmlFont * font READ font) + Q_PROPERTY(HAlignment hAlign READ hAlign WRITE setHAlign) + Q_PROPERTY(VAlignment vAlign READ vAlign WRITE setVAlign) + Q_PROPERTY(bool wrap READ wrap WRITE setWrap) + Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + Q_CLASSINFO("DefaultProperty", "text") +public: + QFxTextEdit(QFxItem *parent=0); + + enum HAlignment { + AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter + }; + + enum VAlignment { + AlignTop = Qt::AlignTop, + AlignBottom = Qt::AlignBottom, + AlignVCenter = Qt::AlignVCenter + }; + + enum TextFormat { + AutoText, + PlainText, + RichText, + }; + + QString text() const; + void setText(const QString &); + + TextFormat textFormat() const; + void setTextFormat(TextFormat format); + + // leave in, have affect the default text + QmlFont *font(); + + QColor color() const; + void setColor(const QColor &c); + + HAlignment hAlign() const; + void setHAlign(HAlignment align); + + VAlignment vAlign() const; + void setVAlign(VAlignment align); + + bool wrap() const; + void setWrap(bool w); + + virtual void dump(int depth); + virtual QString propertyInfo() const; + + virtual void componentComplete(); + + /* FROM EDIT */ + void setReadOnly(bool); + bool isReadOnly() const; + + void setTextInteractionFlags(Qt::TextInteractionFlags flags); + Qt::TextInteractionFlags textInteractionFlags() const; + + QTextCursor cursorForPosition(const QPoint &pos) const; + QRect cursorRect(const QTextCursor &cursor) const; + QRect cursorRect() const; + + void setTextCursor(const QTextCursor &cursor); + QTextCursor textCursor() const; + + void moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor); + + QVariant inputMethodQuery(Qt::InputMethodQuery property) const; + +Q_SIGNALS: + void textChanged(const QString &); + void cursorPositionChanged(); + +private Q_SLOTS: + void fontChanged(); + void updateImgCache(const QRectF &rect); + void q_textChanged(); + +private: + void updateSize(); + +protected: + QFxTextEdit(QFxTextEditPrivate &dd, QFxItem *parent); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + + void keyPressEvent(QKeyEvent *); + void keyReleaseEvent(QKeyEvent *); + + void focusInEvent(QFocusEvent *); + void focusOutEvent(QFocusEvent *); + + // mouse filter? + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + + void inputMethodEvent(QInputMethodEvent *e); + + void drawContents(QPainter *, const QRect &); +private: + + friend class QmlFont; + Q_DISABLE_COPY(QFxTextEdit) + Q_DECLARE_PRIVATE(QFxTextEdit) +}; +QML_DECLARE_TYPE(QFxTextEdit); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/fx/qfxtextedit_p.h b/src/declarative/fx/qfxtextedit_p.h new file mode 100644 index 0000000..aa07484 --- /dev/null +++ b/src/declarative/fx/qfxtextedit_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXTEXTEDIT_P_H +#define QFXTEXTEDIT_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 "qfxitem.h" +#include "qfximageitem_p.h" +#include "qml.h" + + +QT_BEGIN_NAMESPACE +class QTextLayout; +class QTextDocument; +class QTextControl; +class QFxTextEditPrivate : public QFxImageItemPrivate +{ + Q_DECLARE_PUBLIC(QFxTextEdit) + +public: + QFxTextEditPrivate() + : font(0), color("black"), imgDirty(true), hAlign(QFxTextEdit::AlignLeft), vAlign(QFxTextEdit::AlignTop), dirty(false), wrap(false), richText(false), format(QFxTextEdit::AutoText), document(0) + { + } + + void init(); + + void updateDefaultTextOption(); + void relayoutDocument(); + + QString text; + QmlFont font; + QColor color; + QString style; + QColor styleColor; + bool imgDirty; +#if defined(QFX_RENDER_OPENGL) + GLTexture texture; +#endif + QSimpleCanvasConfig::Image imgCache; + QImage imgStyleCache; + QFxTextEdit::HAlignment hAlign; + QFxTextEdit::VAlignment vAlign; + bool dirty; + bool wrap; + bool richText; + QFxTextEdit::TextFormat format; + QTextDocument *document; + QTextControl *control; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/declarative/fx/qfxtransform.cpp b/src/declarative/fx/qfxtransform.cpp new file mode 100644 index 0000000..8d8e5b4 --- /dev/null +++ b/src/declarative/fx/qfxtransform.cpp @@ -0,0 +1,661 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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> +#include "private/qfxitem_p.h" +#include "qfxtransform.h" +#include <QtDeclarative/qmlinfo.h> + +#include <math.h> +#ifndef M_PI + +QT_BEGIN_NAMESPACE +#define M_PI 3.14159265358979323846 +#endif + +QML_DEFINE_TYPE(QFxTransform,Transform); + +/*! + \qmlclass Transform + \brief A transformation. + + \todo Document Transform. +*/ +QFxTransform::QFxTransform(QObject *parent) : + QObject(parent) +{ +} + +bool QFxTransform::isIdentity() const +{ + return true; +} + +QSimpleCanvas::Matrix QFxTransform::transform() const +{ + return QSimpleCanvas::Matrix(); +} + +void QFxTransform::update() +{ + QFxItem *item = qobject_cast<QFxItem *>(parent()); + if(item) + item->updateTransform(); +} + +QML_DEFINE_TYPE(QFxAxis,Axis); + +QFxAxis::QFxAxis(QObject *parent) +: QFxTransform(parent), _xStart(0), _yStart(0), _xEnd(0), _yEnd(0), _zEnd(0), _rotation(0), + _translation(0), _distanceToPlane(1024.), _dirty(true) +{ +} + +qreal QFxAxis::xStart() const +{ + return _xStart; +} + +void QFxAxis::setXStart(qreal x) +{ + _xStart = x; + update(); +} + +qreal QFxAxis::yStart() const +{ + return _yStart; +} + +void QFxAxis::setYStart(qreal y) +{ + _yStart = y; + update(); +} + +qreal QFxAxis::xEnd() const +{ + return _xEnd; +} + +void QFxAxis::setXEnd(qreal x) +{ + _xEnd = x; + update(); +} + +qreal QFxAxis::yEnd() const +{ + return _yEnd; +} + +void QFxAxis::setYEnd(qreal y) +{ + _yEnd = y; + update(); +} + +qreal QFxAxis::zEnd() const +{ + return _zEnd; +} + +void QFxAxis::setZEnd(qreal z) +{ +#if !defined(QFX_RENDER_OPENGL) + if(z != 0. && translation() != 0.) { + qmlInfo(this) << "QTransform cannot translate along Z-axis"; + return; + } +#endif + + _zEnd = z; + update(); +} + +qreal QFxAxis::rotation() const +{ + return _rotation; +} + +void QFxAxis::setRotation(qreal r) +{ + _rotation = r; + update(); +} + +qreal QFxAxis::translation() const +{ + return _translation; +} + +void QFxAxis::setTranslation(qreal t) +{ +#if !defined(QFX_RENDER_OPENGL) + if(zEnd() != 0. && t != 0.) { + qmlInfo(this) << "QTransform cannot translate along Z-axis"; + return; + } +#endif + + _translation = t; + update(); +} + +bool QFxAxis::isIdentity() const +{ + return (_rotation == 0. && _translation == 0.) || + (zEnd() == 0. && yEnd() == yStart() && xEnd() == xStart()); +} + +#if defined(QFX_RENDER_QPAINTER) +const qreal inv_dist_to_plane = 1. / 1024.; +QTransform QFxAxis::transform() const +{ + if(_dirty) { + _transform = QTransform(); + + if(!isIdentity()) { + if(rotation() != 0.) { + QTransform rotTrans; + rotTrans.translate(-xStart(), -yStart()); + QTransform rotTrans2; + rotTrans2.translate(xStart(), yStart()); + + qreal rad = rotation() * 2. * M_PI / 360.; + qreal c = ::cos(rad); + qreal s = ::sin(rad); + + qreal x = xEnd() - xStart(); + qreal y = yEnd() - yStart(); + qreal z = zEnd(); + + qreal idtp = inv_dist_to_plane; + if(distanceToPlane() != 1024.) + idtp = 1. / distanceToPlane(); + + qreal len = x * x + y * y + z * z; + if(len != 1.) { + len = ::sqrt(len); + x /= len; + y /= len; + z /= len; + } + + QTransform rot(x*x*(1-c)+c, x*y*(1-c)-z*s, x*z*(1-c)+y*s*idtp, + y*x*(1-c)+z*s, y*y*(1-c)+c, y*z*(1-c)-x*s*idtp, + 0, 0, 1); + + _transform *= rotTrans; + _transform *= rot; + _transform *= rotTrans2; + } + + if(translation() != 0.) { + QTransform trans; + trans.translate((xEnd() - xStart()) * translation(), + (yEnd() - yStart()) * translation()); + _transform *= trans; + } + } + + _dirty = false; + } + + return _transform; +} +#elif defined(QFX_RENDER_OPENGL) +QMatrix4x4 QFxAxis::transform() const +{ + if(_dirty) { + _dirty = false; + _transform = QMatrix4x4(); + + if(!isIdentity()) { + if(rotation() != 0.) { + qreal x = xEnd() - xStart(); + qreal y = yEnd() - yStart(); + qreal z = zEnd(); + + _transform.translate(xStart(), yStart(), 0); + _transform.rotate(rotation(), x, y, z); + _transform.translate(-xStart(), -yStart(), 0); + } + + if(translation() != 0.) + _transform.translate((xEnd() - xStart()) * translation(), + (yEnd() - yStart()) * translation(), + (zEnd()) * translation()); + } + } + + return _transform; +} +#endif + +qreal QFxAxis::distanceToPlane() const +{ + return _distanceToPlane; +} + +void QFxAxis::setDistanceToPlane(qreal d) +{ + _distanceToPlane = d; + update(); +} + +void QFxAxis::update() +{ + _dirty = true; + QFxItem *item = qobject_cast<QFxItem *>(parent()); + if(item) + item->updateTransform(); +} + +QML_DEFINE_TYPE(QFxFlipable,Flipable); + +class QFxFlipablePrivate : public QFxItemPrivate +{ +public: + QFxFlipablePrivate() : current(QFxFlipable::Front), front(0), back(0) {} + + QFxFlipable::Side current; + QFxItem *front; + QFxItem *back; +}; + +/*! + \qmlclass Flipable QFxFlipable + \brief The Flipable element provides a surface that can be flipped. + \inherits Item + + Flipable allows you to specify a front and a back and then flip between those sides. + + \code + <Flipable id="flipable" width="40" height="40"> + <transform> + <Axis id="axis" xStart="20" xEnd="20" yStart="20" yEnd="0" /> + </transform> + <front> + <Image file="front.png"/> + </front> + <back> + <Image file="back.png"/> + </back> + <states> + <State name="back"> + <SetProperty target="{axis}" property="rotation" value="180" /> + </State> + </states> + <transitions> + <Transition> + <NumericAnimation easing="easeInOutQuad" properties="rotation"/> + </Transition> + </transitions> + </Flipable> + \endcode + + \image flipable.gif + + \todo A lot needs to be done to get a fully-functioning Flipable. Should we simplify? + +*/ + +/*! + \internal + \class QFxFlipable + \brief The QFxFlipable class provides a flipable surface. + + \ingroup widgets + + QFxFlipable allows you to specify a front and a back, as well as an + axis for the flip. +*/ + +QFxFlipable::QFxFlipable(QFxItem *parent) +: QFxItem(*(new QFxFlipablePrivate), parent) +{ +} + +QFxFlipable::~QFxFlipable() +{ +} + +/*! + \qmlproperty Item Flipable::front + \qmlproperty Item Flipable::back + + The front and back sides of the flipable. +*/ + +QFxItem *QFxFlipable::front() +{ + Q_D(const QFxFlipable); + return d->front; +} + +void QFxFlipable::setFront(QFxItem *front) +{ + Q_D(QFxFlipable); + if(d->front) { + qmlInfo(this) << "front is a write-once property"; + return; + } + d->front = front; + children()->append(d->front); + if(Back == d->current) + d->front->setOpacity(0.); +} + +QFxItem *QFxFlipable::back() +{ + Q_D(const QFxFlipable); + return d->back; +} + +void QFxFlipable::setBack(QFxItem *back) +{ + Q_D(QFxFlipable); + if(d->back) { + qmlInfo(this) << "back is a write-once property"; + return; + } + d->back = back; + children()->append(d->back); + if(Front == d->current) + d->back->setOpacity(0.); +} + +/*! + \qmlproperty enumeration Flipable::side + + The side of the Flippable currently visible. Possible values are \c + Front and \c Back. +*/ +QFxFlipable::Side QFxFlipable::side() const +{ + Q_D(const QFxFlipable); + return d->current; +} + +void QFxFlipable::transformChanged(const QSimpleCanvas::Matrix &trans) +{ + Q_D(QFxFlipable); + QPointF p1(0, 0); + QPointF p2(1, 0); + QPointF p3(1, 1); + + p1 = trans.map(p1); + p2 = trans.map(p2); + p3 = trans.map(p3); + + qreal cross = (p1.x() - p2.x()) * (p3.y() - p2.y()) - + (p1.y() - p2.y()) * (p3.x() - p2.x()); + + Side newSide; + if(cross > 0) { + newSide = Back; + } else { + newSide = Front; + } + + if(newSide != d->current) { + d->current = newSide; + if (d->current==Back) { + QSimpleCanvas::Matrix mat; +#ifdef QFX_RENDER_OPENGL + mat.translate(d->back->width()/2,d->back->height()/2, 0); + if(d->back->width() && p1.x() >= p2.x()) + mat.rotate(180, 0, 1, 0); + if(d->back->height() && p2.y() >= p3.y()) + mat.rotate(180, 1, 0, 0); + mat.translate(-d->back->width()/2,-d->back->height()/2, 0); +#else + mat.translate(d->back->width()/2,d->back->height()/2); + if(d->back->width() && p1.x() >= p2.x()) + mat.rotate(180, Qt::YAxis); + if(d->back->height() && p2.y() >= p3.y()) + mat.rotate(180, Qt::XAxis); + mat.translate(-d->back->width()/2,-d->back->height()/2); +#endif + d->back->setTransform(mat); + } + if(d->front) + d->front->setOpacity((d->current==Front)?1.:0.); + if(d->back) + d->back->setOpacity((d->current==Back)?1.:0.); + emit sideChanged(); + } +} + +QML_DEFINE_TYPE(QFxPerspective,Perspective); + +QFxPerspective::QFxPerspective(QObject *parent) + : QFxTransform(parent) +{ +} + +#if defined(QFX_RENDER_OPENGL) +bool QFxPerspective::isIdentity() const +{ + return false; +} + +QMatrix4x4 QFxPerspective::transform() const +{ + QMatrix4x4 rv; + rv.translate(_x, _y); + rv.perspective(_angle, _aspect, 1, 1024 * 1024); + rv.translate(-_x, -_y, -1); + rv.scale(1, 1, 1. / _scale); + + return rv; +} +#endif + +QML_DEFINE_TYPE(QFxSquish,Squish); + +QFxSquish::QFxSquish(QObject *parent) + : QFxTransform(parent) +{ +} + +qreal QFxSquish::x() const +{ + return p.x(); +} + +void QFxSquish::setX(qreal v) +{ + p.setX(v); + update(); +} + +qreal QFxSquish::y() const +{ + return p.y(); +} + +void QFxSquish::setY(qreal v) +{ + p.setY(v); + update(); +} + +qreal QFxSquish::width() const +{ + return s.width(); +} + +void QFxSquish::setWidth(qreal v) +{ + s.setWidth(v); + update(); +} + +qreal QFxSquish::height() const +{ + return s.height(); +} + +void QFxSquish::setHeight(qreal v) +{ + s.setHeight(v); + update(); +} + +qreal QFxSquish::topLeft_x() const +{ + return p1.x(); +} + +void QFxSquish::settopLeft_x(qreal v) +{ + p1.setX(v); + update(); +} + +qreal QFxSquish::topLeft_y() const +{ + return p1.y(); +} + +void QFxSquish::settopLeft_y(qreal v) +{ + p1.setY(v); + update(); +} + +qreal QFxSquish::topRight_x() const +{ + return p2.x(); +} + +void QFxSquish::settopRight_x(qreal v) +{ + p2.setX(v); + update(); +} + +qreal QFxSquish::topRight_y() const +{ + return p2.y(); +} + +void QFxSquish::settopRight_y(qreal v) +{ + p2.setY(v); + update(); +} + +qreal QFxSquish::bottomLeft_x() const +{ + return p3.x(); +} + +void QFxSquish::setbottomLeft_x(qreal v) +{ + p3.setX(v); + update(); +} + +qreal QFxSquish::bottomLeft_y() const +{ + return p3.y(); +} + +void QFxSquish::setbottomLeft_y(qreal v) +{ + p3.setY(v); + update(); +} + +qreal QFxSquish::bottomRight_x() const +{ + return p4.x(); +} + +void QFxSquish::setbottomRight_x(qreal v) +{ + p4.setX(v); + update(); +} + +qreal QFxSquish::bottomRight_y() const +{ + return p4.y(); +} + +void QFxSquish::setbottomRight_y(qreal v) +{ + p4.setY(v); + update(); +} + +void QFxSquish::update() +{ + QFxItem *item = qobject_cast<QFxItem *>(parent()); + if(item) + item->updateTransform(); +} + +#if defined(QFX_RENDER_OPENGL) +bool QFxSquish::isIdentity() const +{ + return false; +} + +QMatrix4x4 QFxSquish::transform() const +{ + QPolygonF poly; + poly << p << QPointF(p.x() + s.width(), p.y()) << QPointF(p.x() + s.width(), p.y() + s.height()) << QPointF(p.x(), p.y() + s.height()); + QPolygonF poly2; + poly2 << p1 << p2 << p4 << p3; + + QTransform t; + QMatrix4x4 rv; + if(QTransform::quadToQuad(poly, poly2, t)) + rv = QMatrix4x4(t); + + return rv; +} +QT_END_NAMESPACE +#endif diff --git a/src/declarative/fx/qfxtransform.h b/src/declarative/fx/qfxtransform.h new file mode 100644 index 0000000..129a4b4 --- /dev/null +++ b/src/declarative/fx/qfxtransform.h @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXTRANSFORM_H +#define QFXTRANSFORM_H + +#include <QObject> +#include <QTransform> +#if defined(QFX_RENDER_OPENGL) +#include <QtGui/qmatrix4x4.h> +#endif +#include <qfxitem.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_EXPORT QFxTransform : public QObject +{ + Q_OBJECT +public: + QFxTransform(QObject *parent=0); + + void update(); + + virtual bool isIdentity() const; + virtual QSimpleCanvas::Matrix transform() const; +}; +QML_DECLARE_TYPE(QFxTransform); + +class Q_DECLARATIVE_EXPORT QFxAxis : public QFxTransform +{ + Q_OBJECT + + Q_PROPERTY(qreal xStart READ xStart WRITE setXStart) + Q_PROPERTY(qreal yStart READ yStart WRITE setYStart) + Q_PROPERTY(qreal xEnd READ xEnd WRITE setXEnd) + Q_PROPERTY(qreal yEnd READ yEnd WRITE setYEnd) + Q_PROPERTY(qreal zEnd READ zEnd WRITE setZEnd) + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation) + Q_PROPERTY(qreal translation READ translation WRITE setTranslation) + Q_PROPERTY(qreal distanceToPlane READ distanceToPlane WRITE setDistanceToPlane) +public: + QFxAxis(QObject *parent=0); + + qreal xStart() const; + void setXStart(qreal); + qreal yStart() const; + void setYStart(qreal); + + qreal xEnd() const; + void setXEnd(qreal); + qreal yEnd() const; + void setYEnd(qreal); + qreal zEnd() const; + void setZEnd(qreal); + + qreal rotation() const; + void setRotation(qreal); + qreal translation() const; + void setTranslation(qreal); + + qreal distanceToPlane() const; + void setDistanceToPlane(qreal); + + virtual bool isIdentity() const; + virtual QSimpleCanvas::Matrix transform() const; + +private: + void update(); + + qreal _xStart; + qreal _yStart; + qreal _xEnd; + qreal _yEnd; + qreal _zEnd; + qreal _rotation; + qreal _translation; + qreal _distanceToPlane; + + mutable bool _dirty; + mutable QSimpleCanvas::Matrix _transform; +}; +QML_DECLARE_TYPE(QFxAxis); + +class Q_DECLARATIVE_EXPORT QFxPerspective : public QFxTransform +{ + Q_OBJECT + + Q_PROPERTY(qreal angle READ angle WRITE setAngle) + Q_PROPERTY(qreal aspect READ aspect WRITE setAspect) + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal scale READ scale WRITE setScale) +public: + QFxPerspective(QObject *parent=0); + + qreal angle() const { return _angle; } + void setAngle(qreal v) { _angle = v; update(); } + + qreal aspect() const { return _aspect; } + void setAspect(qreal v) { _aspect = v; update(); } + + qreal x() const { return _x; } + void setX(qreal v) { _x = v; update(); } + + qreal y() const { return _y; } + void setY(qreal v) { _y = v; update(); } + + qreal scale() const { return _scale; } + void setScale(qreal v) { _scale = v; update(); } + +#if defined(QFX_RENDER_OPENGL) + virtual bool isIdentity() const; + virtual QMatrix4x4 transform() const; +#endif +private: + qreal _scale; + qreal _x; + qreal _y; + qreal _angle; + qreal _aspect; +}; +QML_DECLARE_TYPE(QFxPerspective); + +class Q_DECLARATIVE_EXPORT QFxSquish : public QFxTransform +{ + Q_OBJECT + + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal width READ width WRITE setWidth) + Q_PROPERTY(qreal height READ height WRITE setHeight) + Q_PROPERTY(qreal topLeft_x READ topLeft_x WRITE settopLeft_x) + Q_PROPERTY(qreal topLeft_y READ topLeft_y WRITE settopLeft_y) + Q_PROPERTY(qreal topRight_x READ topRight_x WRITE settopRight_x) + Q_PROPERTY(qreal topRight_y READ topRight_y WRITE settopRight_y) + Q_PROPERTY(qreal bottomLeft_x READ bottomLeft_x WRITE setbottomLeft_x) + Q_PROPERTY(qreal bottomLeft_y READ bottomLeft_y WRITE setbottomLeft_y) + Q_PROPERTY(qreal bottomRight_y READ bottomRight_y WRITE setbottomRight_y) + Q_PROPERTY(qreal bottomRight_x READ bottomRight_x WRITE setbottomRight_x) +public: + QFxSquish(QObject *parent=0); + + qreal x() const; + void setX(qreal); + + qreal y() const; + void setY(qreal); + + qreal width() const; + void setWidth(qreal); + + qreal height() const; + void setHeight(qreal); + + qreal topLeft_x() const; + void settopLeft_x(qreal); + + qreal topLeft_y() const; + void settopLeft_y(qreal); + + qreal topRight_x() const; + void settopRight_x(qreal); + + qreal topRight_y() const; + void settopRight_y(qreal); + + qreal bottomLeft_x() const; + void setbottomLeft_x(qreal); + + qreal bottomLeft_y() const; + void setbottomLeft_y(qreal); + + qreal bottomRight_y() const; + void setbottomRight_y(qreal); + + qreal bottomRight_x() const; + void setbottomRight_x(qreal); + +#if defined(QFX_RENDER_OPENGL) + virtual bool isIdentity() const; + virtual QMatrix4x4 transform() const; +#endif + +private: + void update(); + + QPointF p; + QSizeF s; + QPointF p1, p2, p3, p4; +}; + +QML_DECLARE_TYPE(QFxSquish); + +class QFxFlipablePrivate; +class Q_DECLARATIVE_EXPORT QFxFlipable : public QFxItem +{ + Q_OBJECT + + Q_ENUMS(Side); + Q_PROPERTY(QFxItem *front READ front WRITE setFront) + Q_PROPERTY(QFxItem *back READ back WRITE setBack) + Q_PROPERTY(Side side READ side NOTIFY sideChanged) +public: + QFxFlipable(QFxItem *parent=0); + ~QFxFlipable(); + + QFxItem *front(); + void setFront(QFxItem *); + + QFxItem *back(); + void setBack(QFxItem *); + + enum Side { Front, Back }; + Side side() const; + +protected: + virtual void transformChanged(const QSimpleCanvas::Matrix &); + +Q_SIGNALS: + void sideChanged(); + +private: + Q_DISABLE_COPY(QFxFlipable) + Q_DECLARE_PRIVATE(QFxFlipable) +}; +QML_DECLARE_TYPE(QFxFlipable); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // _TRANSFORM_H_ diff --git a/src/declarative/fx/qfxvisualitemmodel.cpp b/src/declarative/fx/qfxvisualitemmodel.cpp new file mode 100644 index 0000000..145e750 --- /dev/null +++ b/src/declarative/fx/qfxvisualitemmodel.cpp @@ -0,0 +1,690 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qlistmodelinterface.h" +#include "qfxitem.h" +#include <qmlcontext.h> +#include <qmlexpression.h> +#include "qmlpackage.h" +#include "qhash.h" +#include "qlist.h" +#include "private/qobject_p.h" +#include "qmlopenmetaobject.h" +#include "qmllistaccessor.h" +#include "qfxvisualitemmodel.h" + + +QT_BEGIN_NAMESPACE +QML_DECLARE_TYPE(QListModelInterface); + +class QFxVisualItemModelParts; +class QFxVisualItemModelData; +class QFxVisualItemModelPrivate : public QObjectPrivate +{ +public: + QFxVisualItemModelPrivate(QmlContext *); + + QListModelInterface *m_listModelInterface; + QAbstractItemModel *m_abstractItemModel; + QFxVisualItemModel *m_visualItemModel; + QString m_part; + + QmlComponent *m_delegate; + QmlContext *m_context; + QList<int> m_roles; + QHash<int,QString> m_roleNames; + + QHash<int, QObject *> m_cache; + QHash<QObject *, QmlPackage*> m_packaged; + QHash<QmlPackage*, int> m_packageRef; + + QFxVisualItemModelParts *m_parts; + friend class QFxVisualItemParts; + + QFxVisualItemModelData *data(QObject *item); + + QVariant m_modelVariant; + QmlListAccessor *m_modelList; + + int modelCount() const { + if(m_visualItemModel) + return m_visualItemModel->count(); + if(m_listModelInterface) + return m_listModelInterface->count(); + if(m_abstractItemModel) + return m_abstractItemModel->rowCount(); + if(m_modelList) + return m_modelList->count(); + return 0; + } +}; + +class QFxVisualItemModelDataMetaObject : public QmlOpenMetaObject +{ +public: + QFxVisualItemModelDataMetaObject(QObject *parent) + : QmlOpenMetaObject(parent) {} + + virtual QVariant propertyCreated(int, QMetaPropertyBuilder &); + virtual int createProperty(const char *, const char *); + +private: + friend class QFxVisualItemModelData; + QList<int> roles; +}; + +class QFxVisualItemModelData : public QObject +{ +Q_OBJECT +public: + QFxVisualItemModelData(int index, QFxVisualItemModelPrivate *model); + + Q_PROPERTY(int index READ index NOTIFY indexChanged); + int index() const; + void setIndex(int index); + + int count() const; + int role(int) const; + void setValue(int, const QVariant &); + +Q_SIGNALS: + void indexChanged(); + +private: + friend class QFxVisualItemModelDataMetaObject; + int m_index; + QFxVisualItemModelPrivate *m_model; + QFxVisualItemModelDataMetaObject *m_meta; +}; + +int QFxVisualItemModelData::count() const +{ + return m_meta->count(); +} + +int QFxVisualItemModelData::role(int id) const +{ + Q_ASSERT(id >= 0 && id < count()); + return m_meta->roles.at(id); +} + +void QFxVisualItemModelData::setValue(int id, const QVariant &val) +{ + m_meta->setValue(id, val); +} + +int QFxVisualItemModelDataMetaObject::createProperty(const char *name, const char *type) +{ + QFxVisualItemModelData *data = + static_cast<QFxVisualItemModelData *>(object()); + + if ((!data->m_model->m_listModelInterface || !data->m_model->m_abstractItemModel) + && data->m_model->m_modelList) { + if (!qstrcmp(name, "modelData")) + return QmlOpenMetaObject::createProperty(name, type); + } else { + const QLatin1String sname(name); + for(QHash<int, QString>::ConstIterator iter = data->m_model->m_roleNames.begin(); + iter != data->m_model->m_roleNames.end(); ++iter) { + + if(*iter == sname) + return QmlOpenMetaObject::createProperty(name, type); + } + } + return -1; +} + +QVariant +QFxVisualItemModelDataMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); + + QFxVisualItemModelData *data = + static_cast<QFxVisualItemModelData *>(object()); + QString name = QLatin1String(prop.name()); + if ((!data->m_model->m_listModelInterface || !data->m_model->m_abstractItemModel) + && data->m_model->m_modelList) { + return data->m_model->m_modelList->at(data->m_index); + } else if (data->m_model->m_listModelInterface) { + for(QHash<int, QString>::ConstIterator iter = data->m_model->m_roleNames.begin(); + iter != data->m_model->m_roleNames.end(); ++iter) { + + if(*iter == name) { + roles.append(iter.key()); + QHash<int,QVariant> values = data->m_model->m_listModelInterface->data(data->m_index, QList<int>() << iter.key()); + if (values.isEmpty()) + return QVariant(); + else + return *values.begin(); + } + } + } else if (data->m_model->m_abstractItemModel) { + for(QHash<int, QString>::ConstIterator iter = data->m_model->m_roleNames.begin(); + iter != data->m_model->m_roleNames.end(); ++iter) { + + if(*iter == name) { + roles.append(iter.key()); + QModelIndex index = data->m_model->m_abstractItemModel->index(data->m_index, 0); + return data->m_model->m_abstractItemModel->data(index, iter.key()); + } + } + } + Q_ASSERT(!"Can never be reached"); + return QVariant(); +} + +QFxVisualItemModelData::QFxVisualItemModelData(int index, + QFxVisualItemModelPrivate *model) +: m_index(index), m_model(model), + m_meta(new QFxVisualItemModelDataMetaObject(this)) +{ +} + +int QFxVisualItemModelData::index() const +{ + return m_index; +} + +// This is internal only - it should not be set from qml +void QFxVisualItemModelData::setIndex(int index) +{ + m_index = index; + emit indexChanged(); +} + +class QFxVisualItemModelPartsMetaObject : public QmlOpenMetaObject +{ +public: + QFxVisualItemModelPartsMetaObject(QObject *parent) + : QmlOpenMetaObject(parent) {} + + virtual QVariant propertyCreated(int, QMetaPropertyBuilder &); +}; + +class QFxVisualItemModelParts : public QObject +{ +Q_OBJECT +public: + QFxVisualItemModelParts(QFxVisualItemModel *parent); + +private: + friend class QFxVisualItemModelPartsMetaObject; + QFxVisualItemModel *model; +}; + +QVariant +QFxVisualItemModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); + + QFxVisualItemModel *m = new QFxVisualItemModel; + m->setParent(object()); + m->setPart(QLatin1String(prop.name())); + m->setModel(QVariant::fromValue(static_cast<QFxVisualItemModelParts *>(object())->model)); + + QVariant var = QVariant::fromValue((QObject *)m); + return var; +} + +QFxVisualItemModelParts::QFxVisualItemModelParts(QFxVisualItemModel *parent) +: QObject(parent), model(parent) +{ + new QFxVisualItemModelPartsMetaObject(this); +} + +QFxVisualItemModelPrivate::QFxVisualItemModelPrivate(QmlContext *ctxt) +: m_listModelInterface(0), m_abstractItemModel(0), m_visualItemModel(0), m_delegate(0) +, m_context(ctxt), m_parts(0), m_modelList(0) +{ +} + +QFxVisualItemModelData *QFxVisualItemModelPrivate::data(QObject *item) +{ + QList<QFxVisualItemModelData *> dataList = + item->findChildren<QFxVisualItemModelData *>(); + Q_ASSERT(dataList.count() == 1); + return dataList.first(); +} + +QFxVisualItemModel::QFxVisualItemModel() +: QObject(*(new QFxVisualItemModelPrivate(QmlContext::activeContext()))) +{ +} + +QFxVisualItemModel::QFxVisualItemModel(QmlContext *ctxt) +: QObject(*(new QFxVisualItemModelPrivate(ctxt))) +{ +} + +QFxVisualItemModel::~QFxVisualItemModel() +{ + Q_D(QFxVisualItemModel); + if(d->m_modelList) + delete d->m_modelList; +} + +QVariant QFxVisualItemModel::model() const +{ + Q_D(const QFxVisualItemModel); + return d->m_modelVariant; +} + +void QFxVisualItemModel::setModel(const QVariant &model) +{ + Q_D(QFxVisualItemModel); + d->m_modelVariant = model; + if(d->m_listModelInterface) { + // Assume caller has released all items. + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList<int>)), + this, SLOT(_q_itemsChanged(int,int,QList<int>))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), + this, SLOT(_q_itemsInserted(int,int))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), + this, SLOT(_q_itemsRemoved(int,int))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), + this, SLOT(_q_itemsMoved(int,int,int))); + d->m_listModelInterface = 0; + } else if (d->m_abstractItemModel) { + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsInserted(const QModelIndex &,int,int)), + this, SLOT(_q_rowsInserted(const QModelIndex &,int,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsRemoved(const QModelIndex &,int,int)), + this, SLOT(_q_rowsRemoved(const QModelIndex &,int,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)), + this, SLOT(_q_dataChanged(const QModelIndex&,const QModelIndex&))); + } else if(d->m_visualItemModel) { + QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsInserted(int,int)), + this, SIGNAL(itemsInserted(int,int))); + QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsRemoved(int,int)), + this, SIGNAL(itemsRemoved(int,int))); + QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsMoved(int,int,int)), + this, SIGNAL(itemsMoved(int,int,int))); + d->m_visualItemModel = 0; + } + + QObject *object = qvariant_cast<QObject *>(model); + if (object && (d->m_listModelInterface = qobject_cast<QListModelInterface *>(object))) { + d->m_roles.clear(); + d->m_roleNames.clear(); + if(d->m_listModelInterface) { + d->m_roles = d->m_listModelInterface->roles(); + for(int ii = 0; ii < d->m_roles.count(); ++ii) + d->m_roleNames.insert(d->m_roles.at(ii), + d->m_listModelInterface->toString(d->m_roles.at(ii))); + } + + QObject::connect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList<int>)), + this, SLOT(_q_itemsChanged(int,int,QList<int>))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), + this, SLOT(_q_itemsInserted(int,int))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), + this, SLOT(_q_itemsRemoved(int,int))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), + this, SLOT(_q_itemsMoved(int,int,int))); + + if(d->m_delegate && d->m_listModelInterface->count()) + emit itemsInserted(0, d->m_listModelInterface->count()); + return; + } else if (object && (d->m_abstractItemModel = qobject_cast<QAbstractItemModel *>(object))) { + d->m_roles.clear(); + d->m_roleNames.clear(); + for (QHash<int,QByteArray>::const_iterator it = d->m_abstractItemModel->roleNames().begin(); + it != d->m_abstractItemModel->roleNames().end(); ++it) { + d->m_roles.append(it.key()); + d->m_roleNames.insert(it.key(), QLatin1String(*it)); + } + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsInserted(const QModelIndex &,int,int)), + this, SLOT(_q_rowsInserted(const QModelIndex &,int,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsRemoved(const QModelIndex &,int,int)), + this, SLOT(_q_rowsRemoved(const QModelIndex &,int,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)), + this, SLOT(_q_dataChanged(const QModelIndex&,const QModelIndex&))); + return; + } + if ((d->m_visualItemModel = qvariant_cast<QFxVisualItemModel *>(model))) { + QObject::connect(d->m_visualItemModel, SIGNAL(itemsInserted(int,int)), + this, SIGNAL(itemsInserted(int,int))); + QObject::connect(d->m_visualItemModel, SIGNAL(itemsRemoved(int,int)), + this, SIGNAL(itemsRemoved(int,int))); + QObject::connect(d->m_visualItemModel, SIGNAL(itemsMoved(int,int,int)), + this, SIGNAL(itemsMoved(int,int,int))); + return; + } + if (!d->m_modelList) + d->m_modelList = new QmlListAccessor; + d->m_modelList->setList(model); + if(d->m_delegate && d->modelCount()) + emit itemsInserted(0, d->modelCount()); +} + +QmlComponent *QFxVisualItemModel::delegate() const +{ + Q_D(const QFxVisualItemModel); + return d->m_delegate; +} + +void QFxVisualItemModel::setDelegate(QmlComponent *delegate) +{ + Q_D(QFxVisualItemModel); + d->m_delegate = delegate; + + if(d->modelCount()) + emit itemsInserted(0, d->modelCount()); +} + +QString QFxVisualItemModel::part() const +{ + Q_D(const QFxVisualItemModel); + return d->m_part; +} + +void QFxVisualItemModel::setPart(const QString &part) +{ + Q_D(QFxVisualItemModel); + d->m_part = part; +} + +int QFxVisualItemModel::count() const +{ + Q_D(const QFxVisualItemModel); + return d->modelCount(); +} + +QFxItem *QFxVisualItemModel::item(int index, bool complete) +{ + Q_D(QFxVisualItemModel); + if(d->m_visualItemModel) + return d->m_visualItemModel->item(index, d->m_part.toLatin1(), complete); + return item(index, QByteArray(), complete); +} + +void QFxVisualItemModel::release(QFxItem *item) +{ + Q_D(QFxVisualItemModel); + if (d->m_visualItemModel) { + d->m_visualItemModel->release(item); + return; + } + item->setItemParent(0); + QObject *obj = item; + + if (QmlPackage *package = d->m_packaged.value(item)) { + static_cast<QObject*>(item)->setParent(package); + d->m_packaged.remove(item); + //XXX Inefficient + for (QHash<QObject *, QmlPackage *>::Iterator iter = d->m_packaged.begin(); + iter != d->m_packaged.end(); ++iter) { + if (*iter == package) + return; + } + obj = package; // fall through and delete + } + + //XXX Inefficient + for (QHash<int, QObject *>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ++iter) { + if (*iter == obj) { + delete obj; + d->m_cache.erase(iter); + return; + } + } +} + +QObject *QFxVisualItemModel::parts() +{ + Q_D(QFxVisualItemModel); + if(!d->m_parts) + d->m_parts = new QFxVisualItemModelParts(this); + return d->m_parts; +} + +QFxItem *QFxVisualItemModel::item(int index, const QByteArray &viewId, bool complete) +{ + Q_D(QFxVisualItemModel); + if(d->m_visualItemModel) + return d->m_visualItemModel->item(index, viewId, complete); + + if(d->modelCount() <= 0 || !d->m_delegate) + return 0; + + QObject *nobj = 0; + if(d->m_cache.contains(index)) { + nobj = d->m_cache[index]; + } else { + QmlContext *ctxt = new QmlContext(d->m_context); + QFxVisualItemModelData *data = new QFxVisualItemModelData(index, d); + ctxt->setContextProperty(QLatin1String("model"), data); + ctxt->addDefaultObject(data); + nobj = d->m_delegate->beginCreate(ctxt); + if (complete) + d->m_delegate->completeCreate(); + ctxt->setParent(nobj); + data->setParent(nobj); + + d->m_cache.insert(index, nobj); + } + + QFxItem *item = qobject_cast<QFxItem *>(nobj); + if (!item) { + QmlPackage *package = qobject_cast<QmlPackage *>(nobj); + if(package) { + QObject *o = package->part(QLatin1String(viewId)); + item = qobject_cast<QFxItem *>(o); + d->m_packaged[o] = package; + } + } + + return item; +} + +void QFxVisualItemModel::completeItem() +{ + Q_D(QFxVisualItemModel); + if(d->m_visualItemModel) { + d->m_visualItemModel->completeItem(); + return; + } + + d->m_delegate->completeCreate(); +} + +QVariant QFxVisualItemModel::evaluate(int index, const QString &expression, QObject *objectContext) +{ + Q_D(QFxVisualItemModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->evaluate(index, expression, objectContext); + + if((!d->m_listModelInterface && !d->m_abstractItemModel) || !d->m_delegate) + return QVariant(); + + QVariant value; + if(d->m_cache.contains(index)) { + QObject *nobj = d->m_cache[index]; + QFxItem *item = qobject_cast<QFxItem *>(nobj); + if (item) { + QmlExpression e(item->itemContext(), expression, objectContext); + e.setTrackChange(false); + value = e.value(); + } + } else { + QmlContext *ctxt = new QmlContext(d->m_context); + QFxVisualItemModelData *data = new QFxVisualItemModelData(index, d); + ctxt->addDefaultObject(data); + QmlExpression e(ctxt, expression, objectContext); + e.setTrackChange(false); + value = e.value(); + delete data; + delete ctxt; + } + + return value; +} + +void QFxVisualItemModel::_q_itemsChanged(int index, int count, + const QList<int> &roles) +{ + Q_D(QFxVisualItemModel); + // XXX - highly inefficient + for(int ii = index; ii < index + count; ++ii) { + + if(d->m_cache.contains(ii)) { + + QObject *item = d->m_cache[ii]; + QFxVisualItemModelData *data = d->data(item); + + for(int prop = 0; prop < data->count(); ++prop) { + + int role = data->role(prop); + if(roles.contains(role)) { + if (d->m_listModelInterface) { + data->setValue(prop, *d->m_listModelInterface->data(ii, QList<int>() << role).begin()); + } else if (d->m_abstractItemModel) { + QModelIndex index = d->m_abstractItemModel->index(ii, 0); + data->setValue(prop, d->m_abstractItemModel->data(index, role)); + } + } + } + } + + } +} + +void QFxVisualItemModel::_q_itemsInserted(int index, int count) +{ + Q_D(QFxVisualItemModel); + // XXX - highly inefficient + QHash<int, QObject *> items; + for(QHash<int, QObject *>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + + if(iter.key() >= index) { + QObject *item = *iter; + int index = iter.key() + count; + iter = d->m_cache.erase(iter); + + items.insert(index, item); + + QFxVisualItemModelData *data = d->data(item); + data->setIndex(index); + } else { + ++iter; + } + } + d->m_cache.unite(items); + + emit itemsInserted(index, count); +} + +void QFxVisualItemModel::_q_itemsRemoved(int index, int count) +{ + Q_D(QFxVisualItemModel); + // XXX - highly inefficient + QHash<int, QObject *> items; + for(QHash<int, QObject *>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + if(iter.key() >= index && iter.key() < index + count) { + QObject *item = *iter; + iter = d->m_cache.erase(iter); + items.insertMulti(-1, item); //XXX perhaps better to maintain separately + QFxVisualItemModelData *data = d->data(item); + data->setIndex(-1); + } else if(iter.key() >= index + count) { + QObject *item = *iter; + int index = iter.key() - count; + iter = d->m_cache.erase(iter); + items.insert(index, item); + QFxVisualItemModelData *data = d->data(item); + data->setIndex(index); + } else { + ++iter; + } + } + + d->m_cache.unite(items); + emit itemsRemoved(index, count); +} + +void QFxVisualItemModel::_q_itemsMoved(int from, int to, int count) +{ + Q_D(QFxVisualItemModel); + // XXX - highly inefficient + QHash<int, QObject *> items; + for(QHash<int, QObject *>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + + if(iter.key() >= from && iter.key() < from + count) { + QObject *item = *iter; + int index = iter.key() - from + to; + iter = d->m_cache.erase(iter); + + items.insert(index, item); + + QFxVisualItemModelData *data = d->data(item); + data->setIndex(index); + } else { + ++iter; + } + } + d->m_cache.unite(items); + + emit itemsMoved(from, to, count); +} + +void QFxVisualItemModel::_q_rowsInserted(const QModelIndex &, int begin, int end) +{ + _q_itemsInserted(begin, end - begin + 1); +} + +void QFxVisualItemModel::_q_rowsRemoved(const QModelIndex &, int begin, int end) +{ + _q_itemsRemoved(begin, end - begin + 1); +} + +void QFxVisualItemModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end) +{ + Q_D(QFxVisualItemModel); + _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, d->m_roles); +} + +QML_DEFINE_TYPE(QFxVisualItemModel,VisualModel); + +QT_END_NAMESPACE +#include "qfxvisualitemmodel.moc" diff --git a/src/declarative/fx/qfxvisualitemmodel.h b/src/declarative/fx/qfxvisualitemmodel.h new file mode 100644 index 0000000..5ec42e9 --- /dev/null +++ b/src/declarative/fx/qfxvisualitemmodel.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXVISUALITEMMODEL_H +#define QFXVISUALITEMMODEL_H + +#include <QtCore/qobject.h> +#include <QtCore/qabstractitemmodel.h> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +/***************************************************************************** + ***************************************************************************** + XXX Experimental + ***************************************************************************** +*****************************************************************************/ + +class QFxItem; +class QmlComponent; +class QFxVisualItemModelPrivate; +class QFxVisualItemModel : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QFxVisualItemModel) + + Q_PROPERTY(QVariant model READ model WRITE setModel) + Q_PROPERTY(QmlComponent *delegate READ delegate WRITE setDelegate) + Q_PROPERTY(QString part READ part WRITE setPart) + Q_PROPERTY(QObject *parts READ parts) + Q_CLASSINFO("DefaultProperty", "delegate") +public: + QFxVisualItemModel(); + QFxVisualItemModel(QmlContext *); + virtual ~QFxVisualItemModel(); + + QVariant model() const; + void setModel(const QVariant &); + + QmlComponent *delegate() const; + void setDelegate(QmlComponent *); + + QString part() const; + void setPart(const QString &); + + int count() const; + QFxItem *item(int index, bool complete=true); + QFxItem *item(int index, const QByteArray &, bool complete=true); + void release(QFxItem *item); + void completeItem(); + QVariant evaluate(int index, const QString &expression, QObject *objectContext); + + QObject *parts(); + +Q_SIGNALS: + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int from, int to, int count); + +private Q_SLOTS: + void _q_itemsChanged(int, int, const QList<int> &); + void _q_itemsInserted(int index, int count); + void _q_itemsRemoved(int index, int count); + void _q_itemsMoved(int from, int to, int count); + void _q_rowsInserted(const QModelIndex &,int,int); + void _q_rowsRemoved(const QModelIndex &,int,int); + void _q_dataChanged(const QModelIndex&,const QModelIndex&); + +private: + Q_DISABLE_COPY(QFxVisualItemModel) +}; +QML_DECLARE_TYPE(QFxVisualItemModel); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXVISUALITEMMODEL_H diff --git a/src/declarative/fx/qfxwebview.cpp b/src/declarative/fx/qfxwebview.cpp new file mode 100644 index 0000000..c4a2750 --- /dev/null +++ b/src/declarative/fx/qfxwebview.cpp @@ -0,0 +1,1079 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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> +#include <QPen> +#include <QFile> +#include <QEvent> +#include <QBasicTimer> +#include <QApplication> +#include <QGraphicsSceneMouseEvent> +#include <QtWebKit/QWebPage> +#include <QtWebKit/QWebFrame> + +#include "qml.h" +#include "qmlbindablevalue.h" +#include "qmlengine.h" +#include "qmlstate.h" +#include "qfxtransform.h" +#include "qfxscalegrid.h" +#include "qsimplecanvas.h" +#include "qlistmodelinterface.h" + +#if defined(QFX_RENDER_OPENGL2) +#include <QtOpenGL/qglframebufferobject.h> +#include <glsave.h> +#endif +#if defined(QFX_RENDER_OPENGL) +#include <gltexture.h> +#endif + +#include "qfxwebview.h" +#include <qsimplecanvasfilter.h> +#include <private/qfxitem_p.h> + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QFxWebView,WebView); + +static const int MAX_DOUBLECLICK_TIME=500; // XXX need better gesture system + +class QFxWebViewPrivate : public QFxItemPrivate +{ + Q_DECLARE_PUBLIC(QFxWebView) + +public: + QFxWebViewPrivate() + : page(0), idealwidth(0), idealheight(0), interactive(true), lastPress(0), lastRelease(0), mouseX(0), mouseY(0), + smooth(true), max_imagecache_size(100000), progress(1.0), pending(PendingNone) + { + } + + QWebPage *page; + + struct ImageCacheItem { + ImageCacheItem() : age(0) {} + ~ImageCacheItem() { } + int age; + QRect area; +#if defined(QFX_RENDER_QPAINTER) + QSimpleCanvasConfig::Image image; +#else + GLTexture image; +#endif + }; + QList<ImageCacheItem*> imagecache; + void dirtyCache(const QRect& dirt) + { + for (int i=0; i<imagecache.count(); ) { + if (imagecache[i]->area.intersects(dirt)) { + imagecache.removeAt(i); + } else { + ++i; + } + } + } + void clearCache() + { + foreach (ImageCacheItem* i, imagecache) + delete i; + imagecache.clear(); + } + + int idealwidth; + int idealheight; + bool interactive; + QMouseEvent *lastPress, *lastRelease; + int mouseX, mouseY; + bool smooth; + int max_imagecache_size; + qreal progress; + QBasicTimer dcTimer; + QString statusBarMessage; + enum { PendingNone, PendingUrl, PendingHtml, PendingContent } pending; + QUrl pending_url; + QString pending_string; + QByteArray pending_data; +}; + + +/*! + \qmlclass WebView + \brief The WebView element allows you to add web content to a canvas. + \inherits Item + + A WebView renders web content based on a URL. + + If the width and height of the element is not set, they will + dynamically adjust to a size appropriate for the content. + This width may be large (eg. 980) for typical online web pages. + + If the idealWidth is set, the width will be this amount or larger, + usually laying out the web content to fit the idealWidth. + + If the idealHeight is set, the height will be this amount or larger. + Due to WebKit limitations, the height may be more than necessary + if the idealHeight is changed after the content is loaded. + + \code + <WebView url="http://www.nokia.com" smooth="true" scale="0.5" width="490" height="400"/> + \endcode + + \image webview.png + + The element includes no scrolling, scaling, + toolbars, etc., those must be implemented around WebView. See the WebBrowser example + for a demonstration of this. +*/ + +/*! + \internal + \class QFxWebView + \brief The QFxWebView class allows you to add web content to a QFxView. + + A WebView renders web content base on a URL. + + \image webview.png + + The element includes no scrolling, scaling, + toolbars, etc., those must be implemented around WebView. See the WebBrowser example + for a demonstration of this. + + A QFxWebView object can be instantiated in Qml using the tag \l WebView. +*/ + +QFxWebView::QFxWebView(QFxItem *parent) + : QFxItem(*(new QFxWebViewPrivate), parent) +{ + init(); +} + +QFxWebView::QFxWebView(QFxWebViewPrivate &dd, QFxItem *parent) + : QFxItem(dd, parent) +{ + init(); +} + +QFxWebView::~QFxWebView() +{ + Q_D(QFxWebView); + delete d->page; +} + +void QFxWebView::init() +{ + setAcceptedMouseButtons(Qt::LeftButton); + setOptions(HasContents | MouseEvents); + setFocusable(true); + + QWebPage *wp = new QFxWebPage(this); + + // QML elements don't default to having a background, + // even though most we pages will set one anyway. + QPalette pal = QApplication::palette(); + pal.setBrush(QPalette::Base, QColor::fromRgbF(0, 0, 0, 0)); + wp->setPalette(pal); + + wp->setNetworkAccessManager(itemContext()->engine()->networkAccessManager()); + setPage(wp); + + // XXX settable from QML? + settings()->setAttribute(QWebSettings::PluginsEnabled, true); +} + +void QFxWebView::componentComplete() +{ + QFxItem::componentComplete(); + Q_D(QFxWebView); + switch (d->pending) { + case QFxWebViewPrivate::PendingUrl: + setUrl(d->pending_url.toString()); + break; + case QFxWebViewPrivate::PendingHtml: + setHtml(d->pending_string, d->pending_url); + break; + case QFxWebViewPrivate::PendingContent: + setContent(d->pending_data, d->pending_string, d->pending_url); + break; + default: + break; + } + d->pending = QFxWebViewPrivate::PendingNone; +} + +/*! + \qmlproperty real WebView::progress + This property holds the progress of loading the current URL, from 0 to 1. +*/ +/*! + \property qreal QFxWebView::progress + \brief the progress of loading the current URL, from 0 to 1. +*/ +qreal QFxWebView::progress() const +{ + Q_D(const QFxWebView); + return d->progress; +} + +void QFxWebView::doLoadProgress(int p) +{ + Q_D(QFxWebView); + if (d->progress == p/100.0) + return; + d->progress = p/100.0; + expandToWebPage(); + emit progressChanged(); +} + +void QFxWebView::doLoadFinished(bool ok) +{ + // XXX bug 232556 - pages with no title never get this signal + if (title().isEmpty()) + emit urlChanged(); + + if (ok) { + emit loadFinished(); + } else { + emit loadFailed(); + } +} + +/*! + \qmlproperty string WebView::url + This property holds the URL to the page displayed in this item. + + Note that after this property is set, it may take some time + before the change is notified, as this only happens when + loading of the URL successfully starts. +*/ +/*! + \property QString QFxWebView::url + \brief the URL to the page displayed in this item. + + \sa urlChanged() +*/ +/*! + \fn void QFxWebView::urlChanged() + + Emitted when loading of the URL successfully starts after + setUrl() is called. +*/ +QString QFxWebView::url() const +{ + Q_D(const QFxWebView); + return d->page->mainFrame()->url().toString(); +} + +void QFxWebView::setUrl(const QString &n) +{ + Q_D(QFxWebView); + if(n == d->page->mainFrame()->url().toString()) + return; + + d->page->setViewportSize(QSize( + d->idealwidth>0 ? d->idealwidth : width(), + d->idealheight>0 ? d->idealheight : height())); + + QUrl url(n); + if (url.isRelative()) + url = itemContext()->resolvedUrl(n); + + if (isComponentComplete()) + d->page->mainFrame()->load(url); + else { + d->pending = d->PendingUrl; + d->pending_url = url; + } + + // emit urlChanged() - not until actually loaded +} + +/*! + \qmlproperty int WebView::idealWidth + This property holds the ideal width for displaying the current URL. +*/ +/*! + \property int QFxWebView::idealWidth + \brief the ideal width for displaying the current URL. +*/ +int QFxWebView::idealWidth() const +{ + Q_D(const QFxWebView); + return d->idealwidth; +} + +void QFxWebView::setIdealWidth(int iw) +{ + Q_D(QFxWebView); + if(d->idealwidth == iw) return; + d->idealwidth = iw; + expandToWebPage(); + emit idealWidthChanged(); +} + +/*! + \qmlproperty int WebView::idealHeight + This property holds the ideal height for displaying the current URL. +*/ +/*! + \property int QFxWebView::idealHeight + \brief the ideal height for displaying the current URL. +*/ +int QFxWebView::idealHeight() const +{ + Q_D(const QFxWebView); + return d->idealheight; +} + +void QFxWebView::setIdealHeight(int ih) +{ + Q_D(QFxWebView); + if(d->idealheight == ih) return; + d->idealheight = ih; + expandToWebPage(); + emit idealHeightChanged(); +} + +/*! + \qmlproperty bool WebView::interactive + This property holds controls whether the item responds to mouse and key events. +*/ +/*! + \property bool QFxWebView::interactive + \brief controls whether the item responds to mouse and key events. +*/ +bool QFxWebView::interactive() const +{ + Q_D(const QFxWebView); + return d->interactive; +} + +void QFxWebView::setInteractive(bool i) +{ + Q_D(QFxWebView); + if(d->interactive == i) return; + d->interactive = i; + emit interactiveChanged(); +} + +/*! + \qmlproperty bool WebView::smooth + This property holds hints as to whether the item should be drawn anti-aliased. +*/ +/*! + \property bool QFxWebView::smooth + \brief hints as to whether the item should be drawn anti-aliased. +*/ +bool QFxWebView::smooth() const +{ + Q_D(const QFxWebView); + return d->smooth; +} + +void QFxWebView::setSmooth(bool i) +{ + Q_D(QFxWebView); + if(d->smooth == i) return; + d->smooth = i; + update(); +} + +void QFxWebView::updateCacheForVisibility() +{ + Q_D(QFxWebView); + if (!isVisible()) + d->clearCache(); +} + +void QFxWebView::expandToWebPage() +{ + Q_D(QFxWebView); + QSize cs = d->page->mainFrame()->contentsSize(); + if (cs.width() < d->idealwidth) + cs.setWidth(d->idealwidth); + if (cs.height() < d->idealheight) + cs.setHeight(d->idealheight); + if (widthValid() && cs.width() < width()) + cs.setWidth(width()); + if (heightValid() && cs.height() < height()) + cs.setHeight(height()); + if (cs != d->page->viewportSize()) { + d->page->setViewportSize(cs); + d->clearCache(); + setImplicitWidth(cs.width()); + setImplicitHeight(cs.height()); + } +} + +void QFxWebView::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + if(newGeometry.size() != oldGeometry.size()) + expandToWebPage(); + QFxItem::geometryChanged(newGeometry, oldGeometry); +} + +void QFxWebView::paintPage(const QRect& r) +{ + Q_D(QFxWebView); + d->dirtyCache(r); + update(); +} + +/*! + \qmlproperty int WebView::cacheSize + This property holds the maximum number of pixels of image cache to allow + + The default is 0.1 megapixels. + + The cache will not be larger than the (unscaled) size of the WebView. +*/ +/*! + \property int QFxWebView::cacheSize + \brief the maximum number of pixels of image cache to allow + The default is 0.1 megapixels. + + The cache will not be larger than the (unscaled) size of the QFxWebView. +*/ +int QFxWebView::cacheSize() const +{ + Q_D(const QFxWebView); + return d->max_imagecache_size; +} + +void QFxWebView::setCacheSize(int pixels) +{ + Q_D(QFxWebView); + if (pixels < d->max_imagecache_size) { + int cachesize=0; + for (int i=0; i<d->imagecache.count(); ++i) { + QRect area = d->imagecache[i]->area; + cachesize += area.width()*area.height(); + } + while (d->imagecache.count() && cachesize > pixels) { + int oldest=-1; + int age=-1; + for (int i=0; i<d->imagecache.count(); ++i) { + int a = d->imagecache[i]->age; + if (a > age) { + oldest = i; + age = a; + } + } + cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height(); + d->imagecache.removeAt(oldest); + } + } + d->max_imagecache_size = pixels; +} + +void QFxWebView::dump(int depth) +{ + QByteArray ba(depth * 4, ' '); + qWarning() << ba.constData() << "url:" << url(); + QFxItem::dump(depth); +} + +#if defined(QFX_RENDER_QPAINTER) +void QFxWebView::paintContents(QPainter &p) +#elif defined(QFX_RENDER_OPENGL) +void QFxWebView::paintGLContents(GLPainter &p) +#else +#error "What render?" +#endif +{ + Q_D(QFxWebView); + QWebFrame *frame = d->page->mainFrame(); + const QRect content(QPoint(0,0),frame->contentsSize()); + + if (content.width() <= 0 || content.height() <= 0) + return; + +#if defined(QFX_RENDER_QPAINTER) + bool wasAA = p.testRenderHint(QPainter::Antialiasing); + p.setRenderHints(QPainter::Antialiasing, d->smooth); + QRectF clipf = p.clipRegion().boundingRect(); + const QRect clip = p.clipRegion().isEmpty() ? content : clipf.toRect(); +#elif defined(QFX_RENDER_OPENGL) + const QRectF clipf = p.sceneClipRect; + const QRect clip = mapFromScene(clipf).toRect(); +#endif + + QRegion topaint(clip); + topaint &= content; + QRegion uncached(content); + + int cachesize=0; + for (int i=0; i<d->imagecache.count(); ++i) { + QRect area = d->imagecache[i]->area; + if (topaint.contains(area)) { + p.drawImage(area, d->imagecache[i]->image); + topaint -= area; + d->imagecache[i]->age=0; + } else { + d->imagecache[i]->age++; + } + cachesize += area.width()*area.height(); + uncached -= area; + } + + if (!topaint.isEmpty()) { + // Find a sensible larger area, otherwise will paint lots of tiny images. + QRect biggerrect = topaint.boundingRect().adjusted(-64,-64,128,128); + cachesize += biggerrect.width() * biggerrect.height(); + while (d->imagecache.count() && cachesize > d->max_imagecache_size) { + int oldest=-1; + int age=-1; + for (int i=0; i<d->imagecache.count(); ++i) { + int a = d->imagecache[i]->age; + if (a > age) { + oldest = i; + age = a; + } + } + cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height(); + uncached += d->imagecache[oldest]->area; + d->imagecache.removeAt(oldest); + } + const QRegion bigger = QRegion(biggerrect) & uncached; + const QVector<QRect> rects = bigger.rects(); + foreach (QRect r, rects) { + QImage img(r.size(),QImage::Format_ARGB32_Premultiplied); + img.fill(0); + { + QPainter qp(&img); + qp.translate(-r.x(),-r.y()); + frame->render(&qp,r); + } + QFxWebViewPrivate::ImageCacheItem *newitem = new QFxWebViewPrivate::ImageCacheItem; + newitem->area = r; +#if defined(QFX_RENDER_QPAINTER) + newitem->image = QSimpleCanvasConfig::Image(QSimpleCanvasConfig::toImage(img)); +#else + newitem->image.setImage(img); +#endif + d->imagecache.append(newitem); + p.drawImage(r, newitem->image); + } + } +#if defined(QFX_RENDER_QPAINTER) + p.setRenderHints(QPainter::Antialiasing, wasAA); +#endif +} + +QString QFxWebView::propertyInfo() const +{ + Q_D(const QFxWebView); + return d->page->mainFrame()->url().toString(); +} + +static QMouseEvent *sceneMouseEventToMouseEvent(QGraphicsSceneMouseEvent *e) +{ + QEvent::Type t; + switch(e->type()) { + default: + case QEvent::GraphicsSceneMousePress: + t = QEvent::MouseButtonPress; + break; + case QEvent::GraphicsSceneMouseRelease: + t = QEvent::MouseButtonRelease; + break; + case QEvent::GraphicsSceneMouseMove: + t = QEvent::MouseMove; + break; + case QGraphicsSceneEvent::GraphicsSceneMouseDoubleClick: + t = QEvent::MouseButtonDblClick; + break; + } + + QMouseEvent *me = new QMouseEvent(t, e->pos().toPoint(), e->button(), e->buttons(), 0); + return me; +} + + +void QFxWebView::timerEvent(QTimerEvent *event) +{ + Q_D(QFxWebView); + if (event->timerId() ==d->dcTimer.timerId()) { + d->dcTimer.stop(); + if (d->lastPress) { + d->page->event(d->lastPress); + delete d->lastPress; + d->lastPress = 0; + } + if (d->lastRelease) { + d->page->event(d->lastRelease); + delete d->lastRelease; + d->lastRelease = 0; + } + } +} + +int QFxWebView::mouseX() const +{ + Q_D(const QFxWebView); + if (d->lastPress) + return d->lastPress->x(); + if (d->lastRelease) + return d->lastRelease->x(); + return d->mouseX; +} + +int QFxWebView::mouseY() const +{ + Q_D(const QFxWebView); + if (d->lastPress) + return d->lastPress->y(); + if (d->lastRelease) + return d->lastRelease->y(); + return d->mouseY; +} + +void QFxWebView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + QMouseEvent *me = sceneMouseEventToMouseEvent(event); + Q_D(QFxWebView); + d->dcTimer.stop(); + delete d->lastPress; + delete d->lastRelease; + d->lastPress = 0; + d->lastRelease = 0; + d->mouseX = me->x(); + d->mouseY = me->y(); + emit doubleClick(); + d->mouseX = 0; + d->mouseY = 0; + delete me; +} + +void QFxWebView::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxWebView); + if (d->interactive) { + d->lastPress = sceneMouseEventToMouseEvent(event); + event->setAccepted(true); + } else { + event->setAccepted(false); + } + if (!event->isAccepted()) + QFxItem::mousePressEvent(event); +} + +void QFxWebView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QFxWebView); + if (d->interactive) { + d->lastRelease = sceneMouseEventToMouseEvent(event); + d->dcTimer.start(MAX_DOUBLECLICK_TIME,this); + event->setAccepted(true); + } else { + event->setAccepted(false); + } + if (!event->isAccepted()) + QFxItem::mouseReleaseEvent(event); +} + +void QFxWebView::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(const QFxWebView); + if (d->interactive && !d->dcTimer.isActive()) { + QMouseEvent *me = sceneMouseEventToMouseEvent(event); + d->page->event(me); + event->setAccepted( +#if QT_VERSION <= 0x040500 // XXX see bug 230835 + true +#else + me->isAccepted() +#endif + ); + delete me; + } else { + event->setAccepted(false); + } + if (!event->isAccepted()) + QFxItem::mouseMoveEvent(event); +} + +void QFxWebView::keyPressEvent(QKeyEvent* event) +{ + Q_D(const QFxWebView); + if (d->interactive) + d->page->event(event); + if (!event->isAccepted()) + QFxItem::keyPressEvent(event); +} + +void QFxWebView::keyReleaseEvent(QKeyEvent* event) +{ + Q_D(const QFxWebView); + if (d->interactive) + d->page->event(event); + if (!event->isAccepted()) + QFxItem::keyReleaseEvent(event); +} + +/*! + \qmlproperty action WebView::back + This property holds the action for causing the previous URL in the history to be displayed. +*/ +QAction *QFxWebView::backAction() const +{ + Q_D(const QFxWebView); + return d->page->action(QWebPage::Back); +} + +/*! + \qmlproperty action WebView::forward + This property holds the action for causing the next URL in the history to be displayed. +*/ +QAction *QFxWebView::forwardAction() const +{ + Q_D(const QFxWebView); + return d->page->action(QWebPage::Forward); +} + +/*! + \qmlproperty action WebView::reload + This property holds the action for reloading with the current URL +*/ +QAction *QFxWebView::reloadAction() const +{ + Q_D(const QFxWebView); + return d->page->action(QWebPage::Reload); +} + +/*! + \qmlproperty action WebView::stop + This property holds the action for stopping loading with the current URL +*/ +QAction *QFxWebView::stopAction() const +{ + Q_D(const QFxWebView); + return d->page->action(QWebPage::Stop); +} + +/*! + \qmlproperty real WebView::title + This property holds the title of the web page currently viewed + + By default, this property contains an empty string. +*/ +/*! + \property QFxWebView::title + This property holds the title of the web page currently viewed + + By default, this property contains an empty string. + + \sa titleChanged() +*/ +QString QFxWebView::title() const +{ + Q_D(const QFxWebView); + if (d->page) + return d->page->mainFrame()->title(); + return QString(); +} + + + +/*! + \qmlproperty pixmap WebView::icon + This property holds the icon associated with the web page currently viewed +*/ +/*! + \property QFxWebView::icon + \brief the icon associated with the web page currently viewed + + By default, this property contains a null icon. + + \sa iconChanged(), QWebSettings::iconForUrl() +*/ +QPixmap QFxWebView::icon() const +{ + Q_D(const QFxWebView); + if (d->page) + return d->page->mainFrame()->icon().pixmap(QSize(256,256)); + return QPixmap(); +} + + +/*! + \qmlproperty real WebView::textSizeMultiplier + This property holds multiplier used to scale the text in a Web page +*/ +/*! + Sets the value of the multiplier used to scale the text in a Web page to + the \a factor specified. +*/ +void QFxWebView::setTextSizeMultiplier(qreal factor) +{ + Q_D(QFxWebView); + d->page->mainFrame()->setTextSizeMultiplier(factor); +} + +/*! + Returns the value of the multiplier used to scale the text in a Web page. +*/ +qreal QFxWebView::textSizeMultiplier() const +{ + Q_D(const QFxWebView); + return d->page->mainFrame()->textSizeMultiplier(); +} + +void QFxWebView::setStatusBarMessage(const QString& s) +{ + Q_D(QFxWebView); + d->statusBarMessage = s; + emit statusChanged(); +} + +QString QFxWebView::status() const +{ + Q_D(const QFxWebView); + return d->statusBarMessage; +} + +QWebPage *QFxWebView::page() const +{ + Q_D(const QFxWebView); + return d->page; +} + +void QFxWebView::setPage(QWebPage *page) +{ + Q_D(QFxWebView); + if (d->page == page) + return; + if (d->page) { + if (d->page->parent() == this) { + delete d->page; + } else { + d->page->disconnect(this); + } + } + d->page = page; + d->page->setViewportSize(QSize( + d->idealwidth>0 ? d->idealwidth : -1, + d->idealheight>0 ? d->idealheight : -1)); + d->page->mainFrame()->setScrollBarPolicy(Qt::Horizontal,Qt::ScrollBarAlwaysOff); + d->page->mainFrame()->setScrollBarPolicy(Qt::Vertical,Qt::ScrollBarAlwaysOff); + connect(this,SIGNAL(visibleChanged()),this,SLOT(updateCacheForVisibility())); + connect(d->page,SIGNAL(repaintRequested(QRect)),this,SLOT(paintPage(QRect))); + connect(d->page->mainFrame(),SIGNAL(urlChanged(QUrl)),this,SIGNAL(urlChanged())); + connect(d->page->mainFrame(), SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged(QString))); + connect(d->page->mainFrame(), SIGNAL(iconChanged()), this, SIGNAL(iconChanged())); + + connect(d->page,SIGNAL(loadStarted()),this,SIGNAL(loadStarted())); + connect(d->page,SIGNAL(loadProgress(int)),this,SLOT(doLoadProgress(int))); + connect(d->page,SIGNAL(loadFinished(bool)),this,SLOT(doLoadFinished(bool))); + connect(d->page,SIGNAL(statusBarMessage(QString)),this,SLOT(setStatusBarMessage(QString))); +} + +void QFxWebView::load(const QNetworkRequest &request, + QNetworkAccessManager::Operation operation, + const QByteArray &body) +{ + page()->mainFrame()->load(request, operation, body); +} + +QString QFxWebView::html() const +{ + return page()->mainFrame()->toHtml(); +} + +/*! + \qmlproperty string WebView::html + This property holds HTML text set directly + + The html property can be set as a string (using CDATA for large blocks), + or as xhtml inline using the XML namespace http://www.w3.org/1999/xhtml: + + \code + <WebView> + <html xmlns="http://www.w3.org/1999/xhtml"> + <p>This is valid xHTML.</p> + </html> + </WebView> + \endcode + + \code + <WebView> + <html><CDATA[ + <p>This is just HTML. + ]]>html> + </WebView> + \endcode +*/ +void QFxWebView::setHtml(const QString &html, const QUrl &baseUrl) +{ + Q_D(QFxWebView); + d->page->setViewportSize(QSize( + d->idealwidth>0 ? d->idealwidth : width(), + d->idealheight>0 ? d->idealheight : height())); + if (isComponentComplete()) + d->page->mainFrame()->setHtml(html, baseUrl); + else { + d->pending = d->PendingHtml; + d->pending_url = baseUrl; + d->pending_string = html; + } +} + +void QFxWebView::setContent(const QByteArray &data, const QString &mimeType, const QUrl &baseUrl) +{ + Q_D(QFxWebView); + d->page->setViewportSize(QSize( + d->idealwidth>0 ? d->idealwidth : width(), + d->idealheight>0 ? d->idealheight : height())); + + if (isComponentComplete()) + d->page->mainFrame()->setContent(data,mimeType,baseUrl); + else { + d->pending = d->PendingContent; + d->pending_url = baseUrl; + d->pending_string = mimeType; + d->pending_data = data; + } +} + +QWebHistory *QFxWebView::history() const +{ + return page()->history(); +} + +QWebSettings *QFxWebView::settings() const +{ + return page()->settings(); +} + +/*! + \internal + \class QFxWebPage + \brief The QFxWebPage class is a QWebPage that can create QML plugins. + + \sa QFxWebView +*/ +QFxWebPage::QFxWebPage(QFxWebView *parent) : + QWebPage(parent) +{ +} + +QFxWebPage::~QFxWebPage() +{ +} + +/* + Qt WebKit does not understand non-QWidget plugins, so dummy widgets + are created, parented to a single dummy tool window. + + The requirements for QML object plugins are input to the Qt WebKit + non-QWidget plugin support, which will obsolete this kludge. +*/ +class QWidget_Dummy_Plugin : public QWidget +{ + Q_OBJECT +public: + static QWidget *dummy_shared_parent() + { + static QWidget *dsp = 0; + if (!dsp) { + dsp = new QWidget(0,Qt::Tool); + dsp->setGeometry(-10000,-10000,0,0); + dsp->show(); + } + return dsp; + } + QWidget_Dummy_Plugin(const QUrl& url, QFxWebView *view, const QStringList ¶mNames, const QStringList ¶mValues) : + QWidget(dummy_shared_parent()), + propertyNames(paramNames), + propertyValues(paramValues), + webview(view) + { + QmlEngine *engine = webview->itemContext()->engine(); + component = new QmlComponent(engine, url, this); + item = 0; + connect(engine, SIGNAL(readyChanged()), this, SLOT(qmlLoaded())); + } + +public Q_SLOTS: + void qmlLoaded() + { + item = qobject_cast<QFxItem*>(component->create(webview->itemContext())); + item->setParent(webview); + for (int i=0; i<propertyNames.count(); ++i) { + if (propertyNames[i] != QLatin1String("type") && propertyNames[i] != QLatin1String("data")) { + item->setProperty(propertyNames[i].toLatin1(),propertyValues[i]); + } + } + resizeEvent(0); + delete component; + component = 0; + } + void resizeEvent(QResizeEvent*) + { + if (item) { + item->setX(x()); + item->setY(y()); + item->setWidth(width()); + item->setHeight(height()); + } + } + +private: + QmlComponent *component; + QFxItem *item; + QStringList propertyNames, propertyValues; + QFxWebView *webview; +}; + +QFxWebView *QFxWebPage::view() +{ + return static_cast<QFxWebView*>(parent()); +} + +QObject *QFxWebPage::createPlugin(const QString &, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues) +{ + QUrl comp = view()->itemContext()->resolvedUri(url.toString()); + return new QWidget_Dummy_Plugin(comp,view(),paramNames,paramValues); +} + +#include "qfxwebview.moc" + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxwebview.h b/src/declarative/fx/qfxwebview.h new file mode 100644 index 0000000..463d0c4 --- /dev/null +++ b/src/declarative/fx/qfxwebview.h @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXWEBVIEW_H +#define QFXWEBVIEW_H + +#include <QAction> +#include <QUrl> +#include <qfxglobal.h> +#include <qfxitem.h> +#include <QtNetwork/qnetworkaccessmanager.h> +#include <QWebPage> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QWebHistory; +class QWebSettings; +class QFxWebViewPrivate; +class QNetworkRequest; +class QFxWebView; + +class Q_DECLARATIVE_EXPORT QFxWebPage : public QWebPage +{ + Q_OBJECT +public: + explicit QFxWebPage(QFxWebView *parent); + ~QFxWebPage(); +protected: + QObject *createPlugin(const QString &classid, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues); +private: + QFxWebView *view(); +}; + + +class Q_DECLARATIVE_EXPORT QFxWebView : public QFxItem +{ + Q_OBJECT + + Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(QPixmap icon READ icon NOTIFY iconChanged) + Q_PROPERTY(qreal textSizeMultiplier READ textSizeMultiplier WRITE setTextSizeMultiplier DESIGNABLE false) + Q_PROPERTY(QString status READ status NOTIFY statusChanged) + + Q_PROPERTY(int mouseX READ mouseX) + Q_PROPERTY(int mouseY READ mouseY) + + Q_PROPERTY(QString html READ html WRITE setHtml) + + Q_PROPERTY(int idealWidth READ idealWidth WRITE setIdealWidth NOTIFY idealWidthChanged) + Q_PROPERTY(int idealHeight READ idealHeight WRITE setIdealHeight NOTIFY idealHeightChanged) + Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(bool smooth READ smooth WRITE setSmooth) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged()) + Q_PROPERTY(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged) + Q_PROPERTY(int cacheSize READ cacheSize WRITE setCacheSize) + + Q_PROPERTY(QObject* reload READ reloadAction) + Q_PROPERTY(QObject* back READ backAction) + Q_PROPERTY(QObject* forward READ forwardAction) + Q_PROPERTY(QObject* stop READ stopAction) + +public: + QFxWebView(QFxItem *parent=0); + ~QFxWebView(); + + QString url() const; + void setUrl(const QString &); + + QString title() const; + + QPixmap icon() const; + + qreal textSizeMultiplier() const; + void setTextSizeMultiplier(qreal); + + bool interactive() const; + void setInteractive(bool); + + int cacheSize() const; + void setCacheSize(int pixels); + + int mouseX() const; + int mouseY() const; + + bool smooth() const; + void setSmooth(bool); + + int idealWidth() const; + void setIdealWidth(int); + int idealHeight() const; + void setIdealHeight(int); + + + qreal progress() const; + + QAction *reloadAction() const; + QAction *backAction() const; + QAction *forwardAction() const; + QAction *stopAction() const; + + virtual void dump(int depth); + virtual QString propertyInfo() const; +#if defined(QFX_RENDER_QPAINTER) + void paintContents(QPainter &painter); +#elif defined(QFX_RENDER_OPENGL) + void paintGLContents(GLPainter &); +#endif + + QWebPage *page() const; + void setPage(QWebPage *page); + + void load(const QNetworkRequest &request, + QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, + const QByteArray &body = QByteArray()); + + QString html() const; + + void setHtml(const QString &html, const QUrl &baseUrl = QUrl()); + void setContent(const QByteArray &data, const QString &mimeType = QString(), const QUrl &baseUrl = QUrl()); + + QWebHistory *history() const; + QWebSettings *settings() const; + + QString status() const; + +Q_SIGNALS: + void idealWidthChanged(); + void idealHeightChanged(); + void urlChanged(); + void interactiveChanged(); + void progressChanged(); + void titleChanged(const QString&); + void iconChanged(); + void statusChanged(); + + void loadStarted(); + void loadFinished(); + void loadFailed(); + + void singleClick(); + void doubleClick(); + +private Q_SLOTS: + void updateCacheForVisibility(); + void expandToWebPage(); + void paintPage(const QRect&); + void doLoadProgress(int p); + void doLoadFinished(bool ok); + void setStatusBarMessage(const QString&); + +protected: + QFxWebView(QFxWebViewPrivate &dd, QFxItem *parent); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + void keyPressEvent(QKeyEvent* event); + void keyReleaseEvent(QKeyEvent* event); + void timerEvent(QTimerEvent *event); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + +private: + void init(); + virtual void componentComplete(); + Q_DISABLE_COPY(QFxWebView) + Q_DECLARE_PRIVATE(QFxWebView) +}; +QML_DECLARE_TYPE(QFxWebView); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/fx/qfxwidgetcontainer.cpp b/src/declarative/fx/qfxwidgetcontainer.cpp new file mode 100644 index 0000000..44ccf0f --- /dev/null +++ b/src/declarative/fx/qfxwidgetcontainer.cpp @@ -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 QtDeclarative 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 "qfxwidgetcontainer.h" +#include <qsimplecanvas.h> +#include <qgraphicswidget.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass WidgetContainer QFxWidgetContainer + \brief The WidgetContainer element allows you to add QGraphicsWidgets into Fluid UI elements. +*/ + +/*! + \internal + \class QFxWidgetContainer + \brief The QFxWidgetContainer class allows you to add QGraphicsWidgets into Fluid UI applications. +*/ + +QML_DEFINE_TYPE(QFxWidgetContainer, WidgetContainer); + +QFxWidgetContainer::QFxWidgetContainer(QFxItem *parent) +: QFxItem(parent), _graphicsWidget(0) +{ +} + +QFxWidgetContainer::~QFxWidgetContainer() +{ +} + +QGraphicsWidget *QFxWidgetContainer::graphicsWidget() const +{ + return _graphicsWidget; +} + +/*! + \property QGraphicsWidget QFxWidgetContainer::graphicsWidget + The QGraphicsWidget associated with this element. +*/ +void QFxWidgetContainer::setGraphicsWidget(QGraphicsWidget *widget) +{ + if(widget == _graphicsWidget) + return; + + _graphicsWidget = widget; + + QSimpleCanvas *c = canvas(); + if(!c) + return; + + if(c->canvasMode() != QSimpleCanvas::GraphicsView) { + qWarning("QFxWidgetContainer: Cannot add a widget to a non-graphicsview canvas. You might need to set QFX_USE_GRAPHICSVIEW=1"); + return; + } + + QGraphicsItem *item = (QGraphicsItem *)(*this); + _graphicsWidget->setParentItem(item); +} + +void QFxWidgetContainer::canvasChanged() +{ + if(_graphicsWidget) { + QGraphicsWidget *w = _graphicsWidget; + _graphicsWidget = 0; + setGraphicsWidget(w); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxwidgetcontainer.h b/src/declarative/fx/qfxwidgetcontainer.h new file mode 100644 index 0000000..65e4352 --- /dev/null +++ b/src/declarative/fx/qfxwidgetcontainer.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 QtDeclarative 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 QFXWIDGETCONTAINER_H +#define QFXWIDGETCONTAINER_H + +#include <QtDeclarative/qfxitem.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGraphicsWidget; + +class Q_DECLARATIVE_EXPORT QFxWidgetContainer : public QFxItem +{ + Q_OBJECT + + Q_CLASSINFO("DefaultProperty", "graphicsWidget") + Q_PROPERTY(QGraphicsWidget *graphicsWidget READ graphicsWidget WRITE setGraphicsWidget) + +public: + QFxWidgetContainer(QFxItem *parent = 0); + ~QFxWidgetContainer(); + + QGraphicsWidget *graphicsWidget() const; + void setGraphicsWidget(QGraphicsWidget *); + +protected: + virtual void canvasChanged(); + +private: + QGraphicsWidget *_graphicsWidget; +}; +QML_DECLARE_TYPE(QFxWidgetContainer); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFXGRAPHICSWIDGET_H diff --git a/src/declarative/opengl/glbasicshaders.cpp b/src/declarative/opengl/glbasicshaders.cpp new file mode 100644 index 0000000..e95e53f --- /dev/null +++ b/src/declarative/opengl/glbasicshaders.cpp @@ -0,0 +1,707 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "glbasicshaders.h" +#include <QDebug> +#include <QColor> + + +QT_BEGIN_NAMESPACE +SingleTextureVertexOpacityShader::SingleTextureVertexOpacityShader() +{ + QGLShader vert(QGLShader::VertexShader); + QGLShader frag(QGLShader::FragmentShader); + + vert.setSourceCode("\ + attribute highp vec4 myVertex;\ + attribute lowp float myOpacity;\ + attribute mediump vec4 myUV;\ + uniform mediump mat4 myPMVMatrix;\ + varying mediump vec2 myTexCoord;\ + varying lowp float myFragOpacity;\ + void main(void)\ + {\ + gl_Position = myPMVMatrix * myVertex;\ + myTexCoord = myUV.st;\ + myFragOpacity = myOpacity;\ + }" + ); + + frag.setSourceCode("\ + uniform sampler2D sampler2d;\ + varying mediump vec2 myTexCoord;\ + varying lowp float myFragOpacity;\ + void main(void)\ + {\ + mediump vec4 frag = texture2D(sampler2d,myTexCoord);\ + gl_FragColor = vec4(frag.rgb, frag.a * myFragOpacity);\ + }" + ); + + addShader(&vert); + addShader(&frag); + + bindAttributeLocation("myVertex", Vertices); + bindAttributeLocation("myUV", TextureCoords); + bindAttributeLocation("myOpacity", OpacityCoords); +} + +bool SingleTextureVertexOpacityShader::link() +{ + if (!QGLShaderProgram::link()) + return false; + transform = uniformLocation("myPMVMatrix"); + enable(); + setUniformValue("sampler2d", 0); + disable(); + return true; +} + +void SingleTextureVertexOpacityShader::setTransform(const QMatrix4x4 &matrix) +{ + setUniformValue(transform, matrix); +} + +BlurTextureShader::BlurTextureShader() +{ + QGLShader vert(QGLShader::VertexShader); + QGLShader frag(QGLShader::FragmentShader); + + vert.setSourceCode("\ + attribute highp vec4 myVertex;\ + attribute mediump vec4 myUV;\ + uniform mediump mat4 myPMVMatrix;\ + varying mediump vec2 myTexCoord;\ + void main(void)\ + {\ + gl_Position = myPMVMatrix * myVertex;\ + myTexCoord = myUV.st;\ + }" + ); + +#if 0 + frag.setSourceCode("\ + uniform sampler2D sampler2d;\ + uniform bool horizontal; \ + uniform mediump float blurStep; \ + varying mediump vec2 myTexCoord;\ + void main(void)\ + {\ + mediump vec4 accum = vec4(0, 0, 0, 0); \ + mediump vec2 offset; \ + if(horizontal) \ + offset = vec2(blurStep, 0); \ + else \ + offset = vec2(0, blurStep); \ + accum += texture2D(sampler2d, myTexCoord + 2.0 * offset); \ + accum += 2.0 * texture2D(sampler2d, myTexCoord + 1.0 * offset); \ + accum += 4.0 * texture2D(sampler2d, myTexCoord + 0.0 * offset); \ + accum += 2.0 * texture2D(sampler2d, myTexCoord - 1.0 * offset); \ + accum += texture2D(sampler2d, myTexCoord - 2.0 * offset); \ + gl_FragColor = accum / 10.0; \ + }" + ); +#else + frag.setSourceCode("\ + uniform sampler2D sampler2d;\ + uniform bool horizontal; \ + uniform mediump float blurStep; \ + uniform int blurSteps; \ + varying mediump vec2 myTexCoord;\ + void main(void)\ + {\ + mediump vec4 accum = vec4(0, 0, 0, 0); \ + mediump vec2 offset; \ + if(horizontal) \ + offset = vec2(blurStep, 0); \ + else \ + offset = vec2(0, blurStep); \ + mediump float sum = 0.0; \ + for(int ii = 0; ii < blurSteps; ++ii) { \ + mediump float frac = float(blurSteps - ii) / float(blurSteps); \ + mediump vec2 coord = myTexCoord + -float(ii) * offset; \ + if(coord.x >= 0.0 && coord.y >= 0.0 && coord.y <= 1.0 && coord.x <=1.0) \ + accum += texture2D(sampler2d, coord) * frac; \ + sum += frac; \ + } \ + for(int ii = 1; ii < blurSteps; ++ii) { \ + mediump float frac = float(blurSteps - ii) / float(blurSteps); \ + mediump vec2 coord = myTexCoord + float(ii) * offset; \ + if(coord.x <= 1.0 && coord.y <= 1.0 && coord.x >= 0.0 && coord.y >= 0.0) \ + accum += texture2D(sampler2d, coord) * frac; \ + sum += frac; \ + } \ + gl_FragColor = accum / sum; \ + }" + ); +#endif + + addShader(&vert); + addShader(&frag); + + bindAttributeLocation("myVertex", Vertices); + bindAttributeLocation("myUV", TextureCoords); +} + +bool BlurTextureShader::link() +{ + if (!QGLShaderProgram::link()) + return false; + transform = uniformLocation("myPMVMatrix"); + mode = uniformLocation("horizontal"); + step = uniformLocation("blurStep"); + steps = uniformLocation("blurSteps"); + enable(); + setUniformValue("sampler2d", 0); + disable(); + return true; +} + +void BlurTextureShader::setStep(float f) +{ + setUniformValue(step, f); +} + +void BlurTextureShader::setSteps(int s) +{ + setUniformValue(steps, s); +} + +void BlurTextureShader::setMode(Mode m) +{ + if(m == Horizontal) + setUniformValue(mode, 1); + else + setUniformValue(mode, 0); +} + +void BlurTextureShader::setTransform(const QMatrix4x4 &matrix) +{ + setUniformValue(transform, matrix); +} + +DualTextureBlendShader::DualTextureBlendShader() +{ + QGLShader vert(QGLShader::VertexShader); + QGLShader frag(QGLShader::FragmentShader); + + vert.setSourceCode("\ + attribute highp vec4 myVertex;\ + attribute mediump vec4 myUV;\ + attribute mediump vec4 myBlendUV;\ + uniform mediump mat4 myPMVMatrix;\ + varying mediump vec2 myTexCoord;\ + varying mediump vec2 myBlendTexCoord;\ + void main(void)\ + {\ + gl_Position = myPMVMatrix * myVertex;\ + myTexCoord = myUV.st;\ + myBlendTexCoord = myBlendUV.st;\ + }" + ); + + frag.setSourceCode("\ + uniform sampler2D sampler2d;\ + uniform sampler2D sampler2dBlend;\ + uniform lowp float myOpacity;\ + uniform lowp float myBlend; \ + varying mediump vec2 myTexCoord;\ + varying mediump vec2 myBlendTexCoord;\ + void main(void)\ + {\ + mediump vec4 tex = texture2D(sampler2d,myTexCoord);\ + mediump vec4 blendtex = texture2D(sampler2dBlend, myBlendTexCoord);\ + gl_FragColor = mix(tex, blendtex, myBlend) * myOpacity; \ + }" + ); + + addShader(&vert); + addShader(&frag); + + bindAttributeLocation("myVertex", Vertices); + bindAttributeLocation("myUV", TextureCoords); + bindAttributeLocation("myBlendUV", BlendTextureCoords); +} + +bool DualTextureBlendShader::link() +{ + if (!QGLShaderProgram::link()) + return false; + transform = uniformLocation("myPMVMatrix"); + opacity = uniformLocation("myOpacity"); + blend = uniformLocation("myBlend"); + enable(); + setUniformValue("sampler2d", 0); + setUniformValue("sampler2dBlend", 1); + disable(); + return true; +} + +void DualTextureBlendShader::setOpacity(GLfloat o) +{ + setUniformValue(opacity, o); +} + +void DualTextureBlendShader::setBlend(GLfloat b) +{ + setUniformValue(blend, b); +} + +void DualTextureBlendShader::setTransform(const QMatrix4x4 &matrix) +{ + setUniformValue(transform, matrix); +} + +DualTextureAddShader::DualTextureAddShader() +{ + QGLShader vert(QGLShader::VertexShader); + QGLShader frag(QGLShader::FragmentShader); + + vert.setSourceCode("\ + attribute highp vec4 myVertex;\ + attribute mediump vec4 myUV;\ + attribute mediump vec4 myAddUV;\ + uniform mediump mat4 myPMVMatrix;\ + varying mediump vec2 myTexCoord;\ + varying mediump vec2 myAddTexCoord;\ + void main(void)\ + {\ + gl_Position = myPMVMatrix * myVertex;\ + myTexCoord = myUV.st;\ + myAddTexCoord = myAddUV.st;\ + }" + ); + + frag.setSourceCode("\ + uniform sampler2D sampler2d;\ + uniform sampler2D sampler2dAdd;\ + uniform lowp float myOpacity;\ + varying mediump vec2 myTexCoord;\ + varying mediump vec2 myAddTexCoord;\ + void main(void)\ + {\ + mediump vec4 tex = texture2D(sampler2d,myTexCoord);\ + mediump vec4 addtex = texture2D(sampler2dAdd, myAddTexCoord);\ + tex = tex + vec4(addtex.rgb * addtex.a * tex.a, 0); \ + tex = min(tex, vec4(1, 1, 1, 1)); \ + gl_FragColor = vec4(tex.rgb, tex.a) * myOpacity;\ + }" + ); + + addShader(&vert); + addShader(&frag); + + bindAttributeLocation("myVertex", Vertices); + bindAttributeLocation("myUV", TextureCoords); + bindAttributeLocation("myAddUV", AddTextureCoords); +} + +void DualTextureAddShader::setOpacity(GLfloat f) +{ + setUniformValue(opacity, f); +} + +void DualTextureAddShader::setTransform(const QMatrix4x4 &matrix) +{ + setUniformValue(transform, matrix); +} + +bool DualTextureAddShader::link() +{ + if (!QGLShaderProgram::link()) + return false; + transform = uniformLocation("myPMVMatrix"); + opacity = uniformLocation("myOpacity"); + enable(); + setUniformValue("sampler2d", 0); + setUniformValue("sampler2dAdd", 1); + disable(); + return true; +} + +SingleTextureShader::SingleTextureShader() +{ + QGLShader vert(QGLShader::VertexShader); + QGLShader frag(QGLShader::FragmentShader); + + vert.setSourceCode("\ + attribute highp vec4 myVertex;\ + attribute mediump vec4 myUV;\ + uniform mediump mat4 myPMVMatrix;\ + varying mediump vec2 myTexCoord;\ + void main(void)\ + {\ + gl_Position = myPMVMatrix * myVertex;\ + myTexCoord = myUV.st;\ + }" + ); + + frag.setSourceCode("\ + uniform sampler2D sampler2d;\ + varying mediump vec2 myTexCoord;\ + void main(void)\ + {\ + gl_FragColor = texture2D(sampler2d,myTexCoord);\ + }" + ); + + addShader(&vert); + addShader(&frag); + + bindAttributeLocation("myVertex", Vertices); + bindAttributeLocation("myUV", TextureCoords); +} + +bool SingleTextureShader::link() +{ + if (!QGLShaderProgram::link()) + return false; + transform = uniformLocation("myPMVMatrix"); + enable(); + setUniformValue("sampler2d", 0); + disable(); + return true; +} + +void SingleTextureShader::setTransform(const QMatrix4x4 &matrix) +{ + setUniformValue(transform, matrix); +} + +ConstantColorShader::ConstantColorShader() +{ + QGLShader vert(QGLShader::VertexShader); + QGLShader frag(QGLShader::FragmentShader); + + vert.setSourceCode("\ + uniform mediump mat4 myPMVMatrix;\ + attribute highp vec4 myVertex;\ + void main(void)\ + {\ + gl_Position = myPMVMatrix * myVertex; \ + }" + ); + + frag.setSourceCode("\ + uniform lowp vec4 myColor;\ + void main(void)\ + {\ + gl_FragColor = myColor;\ + }" + ); + + addShader(&vert); + addShader(&frag); + + bindAttributeLocation("myVertex", Vertices); +} + +void ConstantColorShader::setColor(const QColor &c) +{ + setUniformValue(color, c); +} + +void ConstantColorShader::setTransform(const QMatrix4x4 &matrix) +{ + setUniformValue(transform, matrix); +} + +bool ConstantColorShader::link() +{ + if (!QGLShaderProgram::link()) + return false; + transform = uniformLocation("myPMVMatrix"); + color = uniformLocation("myColor"); + return true; +} + +ColorShader::ColorShader() +{ + QGLShader vert(QGLShader::VertexShader); + QGLShader frag(QGLShader::FragmentShader); + + vert.setSourceCode("\ + uniform mediump mat4 myPMVMatrix;\ + attribute highp vec4 myVertex;\ + attribute lowp vec4 myColors;\ + varying lowp vec4 myColor;\ + void main(void)\ + {\ + gl_Position = myPMVMatrix * myVertex; \ + myColor = myColors; \ + }" + ); + + frag.setSourceCode("\ + varying lowp vec4 myColor;\ + void main(void)\ + {\ + gl_FragColor = myColor;\ + }" + ); + + addShader(&vert); + addShader(&frag); + + bindAttributeLocation("myVertex", Vertices); + bindAttributeLocation("myColors", Colors); +} + +void ColorShader::setTransform(const QMatrix4x4 &matrix) +{ + setUniformValue(transform, matrix); +} + +bool ColorShader::link() +{ + if (!QGLShaderProgram::link()) + return false; + transform = uniformLocation("myPMVMatrix"); + return true; +} + +class GLBasicShadersPrivate +{ +public: + GLBasicShadersPrivate(); + ~GLBasicShadersPrivate(); + + BlurTextureShader *blurTexture; + SingleTextureShader *singleTexture; + SingleTextureOpacityShader *singleTextureOpacity; + DualTextureAddShader *dualTextureAdd; + SingleTextureShadowShader *singleTextureShadow; + SingleTextureVertexOpacityShader *singleTextureVertexOpacity; + ConstantColorShader *constantColor; + ColorShader *color; +}; + +GLBasicShadersPrivate::GLBasicShadersPrivate() +: blurTexture(0), singleTexture(0), singleTextureOpacity(0), + dualTextureAdd(0), singleTextureShadow(0), singleTextureVertexOpacity(0), + constantColor(0), color(0) +{ +} + +GLBasicShadersPrivate::~GLBasicShadersPrivate() +{ + delete blurTexture; + delete singleTexture; + delete singleTextureOpacity; + delete dualTextureAdd; + delete singleTextureVertexOpacity; + delete singleTextureShadow; + delete constantColor; + delete color; +} + +GLBasicShaders::GLBasicShaders() +: d(new GLBasicShadersPrivate) +{ +} + +GLBasicShaders::~GLBasicShaders() +{ + delete d; +} + +BlurTextureShader *GLBasicShaders::blurTexture() +{ + if(!d->blurTexture) d->blurTexture = new BlurTextureShader(); + return d->blurTexture; +} + +SingleTextureShader *GLBasicShaders::singleTexture() +{ + if(!d->singleTexture) d->singleTexture = new SingleTextureShader(); + return d->singleTexture; +} + +SingleTextureOpacityShader *GLBasicShaders::singleTextureOpacity() +{ + if(!d->singleTextureOpacity) d->singleTextureOpacity = new SingleTextureOpacityShader(); + return d->singleTextureOpacity; +} + +DualTextureAddShader *GLBasicShaders::dualTextureAdd() +{ + if(!d->dualTextureAdd) d->dualTextureAdd = new DualTextureAddShader(); + return d->dualTextureAdd; +} + +SingleTextureVertexOpacityShader *GLBasicShaders::singleTextureVertexOpacity() +{ + if(!d->singleTextureVertexOpacity) d->singleTextureVertexOpacity = new SingleTextureVertexOpacityShader(); + return d->singleTextureVertexOpacity; +} + +SingleTextureShadowShader *GLBasicShaders::singleTextureShadow() +{ + if(!d->singleTextureShadow) d->singleTextureShadow = new SingleTextureShadowShader(); + return d->singleTextureShadow; +} + +ConstantColorShader *GLBasicShaders::constantColor() +{ + if(!d->constantColor) d->constantColor = new ConstantColorShader(); + return d->constantColor; +} + +ColorShader *GLBasicShaders::color() +{ + if(!d->color) d->color = new ColorShader(); + return d->color; +} + +SingleTextureOpacityShader::SingleTextureOpacityShader() +{ + QGLShader vert(QGLShader::VertexShader); + QGLShader frag(QGLShader::FragmentShader); + + vert.setSourceCode("\ + attribute highp vec4 myVertex;\ + attribute mediump vec4 myUV;\ + uniform mediump mat4 myPMVMatrix;\ + varying mediump vec2 myTexCoord;\ + void main(void)\ + {\ + gl_Position = myPMVMatrix * myVertex;\ + myTexCoord = myUV.st;\ + }" + ); + + frag.setSourceCode("\ + uniform sampler2D sampler2d;\ + uniform lowp float myOpacity;\ + varying mediump vec2 myTexCoord;\ + void main(void)\ + {\ + mediump vec4 tex = texture2D(sampler2d,myTexCoord);\ + gl_FragColor = vec4(tex.rgb, myOpacity * tex.a);\ + }" + ); + + addShader(&vert); + addShader(&frag); + + bindAttributeLocation("myVertex", Vertices); + bindAttributeLocation("myUV", TextureCoords); +} + +bool SingleTextureOpacityShader::link() +{ + if (!QGLShaderProgram::link()) + return false; + transform = uniformLocation("myPMVMatrix"); + opacity = uniformLocation("myOpacity"); + enable(); + setUniformValue("sampler2d", 0); + disable(); + return true; +} + +void SingleTextureOpacityShader::setTransform(const QMatrix4x4 &matrix) +{ + setUniformValue(transform, matrix); +} + +void SingleTextureOpacityShader::setOpacity(GLfloat f) +{ + setUniformValue(opacity, f); +} + +SingleTextureShadowShader::SingleTextureShadowShader() +{ + QGLShader vert(QGLShader::VertexShader); + QGLShader frag(QGLShader::FragmentShader); + + vert.setSourceCode("\ + attribute highp vec4 myVertex;\ + attribute mediump vec4 myUV;\ + uniform mediump mat4 myPMVMatrix;\ + varying mediump vec2 myTexCoord;\ + void main(void)\ + {\ + gl_Position = myPMVMatrix * myVertex;\ + myTexCoord = myUV.st;\ + }" + ); + + frag.setSourceCode("\ + uniform sampler2D sampler2d;\ + uniform lowp float myOpacity;\ + varying mediump vec2 myTexCoord;\ + void main(void)\ + {\ + mediump vec4 tex = texture2D(sampler2d,myTexCoord);\ + gl_FragColor = vec4(0, 0, 0, myOpacity * tex.a * .75);\ + }" + ); + + addShader(&vert); + addShader(&frag); + + bindAttributeLocation("myVertex", Vertices); + bindAttributeLocation("myUV", TextureCoords); +} + +bool SingleTextureShadowShader::link() +{ + if (!QGLShaderProgram::link()) + return false; + transform = uniformLocation("myPMVMatrix"); + opacity = uniformLocation("myOpacity"); + enable(); + setUniformValue("sampler2d", 0); + disable(); + return true; +} + +void SingleTextureShadowShader::setTransform(const QMatrix4x4 &matrix) +{ + setUniformValue(transform, matrix); +} + +void SingleTextureShadowShader::setOpacity(GLfloat f) +{ + setUniformValue(opacity, f); +} +QT_END_NAMESPACE diff --git a/src/declarative/opengl/glbasicshaders.h b/src/declarative/opengl/glbasicshaders.h new file mode 100644 index 0000000..7d358d8 --- /dev/null +++ b/src/declarative/opengl/glbasicshaders.h @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 _GLBASICSHADERS_H_ +#define _GLBASICSHADERS_H_ + +#include <QtOpenGL/qglshaderprogram.h> +#include <QtGui/qmatrix4x4.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class BlurTextureShader : public QGLShaderProgram +{ + Q_OBJECT +public: + BlurTextureShader(); + + enum { Vertices = 0, + TextureCoords = 1 }; + + enum Mode { Horizontal, Vertical }; + void setMode(Mode); + void setStep(float); + void setSteps(int); + void setTransform(const QMatrix4x4 &); + virtual bool link(); + +private: + GLint mode; + GLint step; + GLint steps; + GLint transform; +}; + +class SingleTextureShader : public QGLShaderProgram +{ + Q_OBJECT +public: + SingleTextureShader(); + + enum { Vertices = 0, + TextureCoords = 1 }; + + void setTransform(const QMatrix4x4 &); + virtual bool link(); + +private: + GLint transform; +}; + +class DualTextureBlendShader : public QGLShaderProgram +{ + Q_OBJECT +public: + DualTextureBlendShader(); + enum { Vertices = 0, + TextureCoords = 1, + BlendTextureCoords = 2 }; + + void setOpacity(GLfloat); + void setBlend(GLfloat); + void setTransform(const QMatrix4x4 &); + virtual bool link(); + +private: + GLint transform; + GLint opacity; + GLint blend; +}; + +class DualTextureAddShader : public QGLShaderProgram +{ + Q_OBJECT +public: + DualTextureAddShader(); + enum { Vertices = 0, + TextureCoords = 1, + AddTextureCoords = 2 }; + + void setOpacity(GLfloat); + void setTransform(const QMatrix4x4 &); + virtual bool link(); + +private: + GLint transform; + GLint opacity; +}; + +class SingleTextureOpacityShader : public QGLShaderProgram +{ + Q_OBJECT +public: + SingleTextureOpacityShader(); + + enum { Vertices = 0, + TextureCoords = 1 }; + + void setOpacity(GLfloat); + void setTransform(const QMatrix4x4 &); + virtual bool link(); + +private: + GLint transform; + GLint opacity; +}; + +class SingleTextureVertexOpacityShader : public QGLShaderProgram +{ + Q_OBJECT +public: + SingleTextureVertexOpacityShader(); + + enum { Vertices = 0, + TextureCoords = 1, + OpacityCoords = 2 }; + + void setTransform(const QMatrix4x4 &); + virtual bool link(); + +private: + GLint transform; +}; + + +class SingleTextureShadowShader : public QGLShaderProgram +{ + Q_OBJECT +public: + SingleTextureShadowShader(); + + enum { Vertices = 0, + TextureCoords = 1 }; + + void setOpacity(GLfloat); + void setTransform(const QMatrix4x4 &); + virtual bool link(); + +private: + GLint transform; + GLint opacity; +}; + + +class QColor; +class ConstantColorShader : public QGLShaderProgram +{ + Q_OBJECT +public: + ConstantColorShader(); + + enum { Vertices = 0 }; + + void setColor(const QColor &); + void setTransform(const QMatrix4x4 &); + virtual bool link(); + +private: + GLint transform; + GLint color; +}; + +class ColorShader : public QGLShaderProgram +{ + Q_OBJECT +public: + ColorShader(); + + enum { Vertices = 0, Colors = 1 }; + + void setTransform(const QMatrix4x4 &); + virtual bool link(); + +private: + GLint transform; +}; + +class GLBasicShadersPrivate; +class GLBasicShaders +{ +public: + GLBasicShaders(); + virtual ~GLBasicShaders(); + + BlurTextureShader *blurTexture(); + SingleTextureShader *singleTexture(); + SingleTextureOpacityShader *singleTextureOpacity(); + DualTextureAddShader *dualTextureAdd(); + SingleTextureVertexOpacityShader *singleTextureVertexOpacity(); + SingleTextureShadowShader *singleTextureShadow(); + ConstantColorShader *constantColor(); + ColorShader *color(); + +private: + GLBasicShadersPrivate *d; +}; + +#endif // _GLBASICSHADERS_H_ + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/opengl/glheaders.h b/src/declarative/opengl/glheaders.h new file mode 100644 index 0000000..f0f6a55 --- /dev/null +++ b/src/declarative/opengl/glheaders.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 _GLHEADERS_H_ +#define _GLHEADERS_H_ + +#include <qfxglobal.h> +#define GL_GLEXT_PROTOTYPES 1 +#include <QtOpenGL/qgl.h> + +#endif // _GLHEADERS_H_ diff --git a/src/declarative/opengl/glsave.cpp b/src/declarative/opengl/glsave.cpp new file mode 100644 index 0000000..125e81b --- /dev/null +++ b/src/declarative/opengl/glsave.cpp @@ -0,0 +1 @@ +#include "glsave.h" diff --git a/src/declarative/opengl/glsave.h b/src/declarative/opengl/glsave.h new file mode 100644 index 0000000..3a67fb0 --- /dev/null +++ b/src/declarative/opengl/glsave.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 QtDeclarative 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 _GLSAVE_H_ +#define _GLSAVE_H_ + +#include <qglobal.h> +#include <qfxglobal.h> +#include <QRect> +#include "glheaders.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class GLSaveViewport +{ +public: + GLSaveViewport() + { + glGetIntegerv(GL_VIEWPORT, viewport); + } + + ~GLSaveViewport() + { + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + } + +private: + Q_DISABLE_COPY(GLSaveViewport); + GLint viewport[4]; +}; + +class GLSaveScissor +{ +public: + GLSaveScissor() + { + enabled = glIsEnabled(GL_SCISSOR_TEST); + glGetIntegerv(GL_SCISSOR_BOX, box); + } + + ~GLSaveScissor() + { + if(enabled) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + glScissor(box[0], box[1], box[2], box[3]); + } + + bool wasEnabled() const + { + return enabled == GL_TRUE; + } + + QRect rect() const + { + return QRect(box[0], box[1], box[2], box[3]); + } + +private: + Q_DISABLE_COPY(GLSaveScissor); + GLint box[4]; + GLboolean enabled; +}; + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // _GLSAVE_H_ diff --git a/src/declarative/opengl/gltexture.cpp b/src/declarative/opengl/gltexture.cpp new file mode 100644 index 0000000..73deece --- /dev/null +++ b/src/declarative/opengl/gltexture.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "gltexture.h" +#include <QImage> + + +QT_BEGIN_NAMESPACE +/*! + \class GLTexture + \brief The GLTexture class simplifies the use of OpenGL textures. +*/ + +// Copied from QGLWidget::convertToGLFormat +static QImage QGLWidget_convertToGLFormat(const QImage& img) +{ + QImage res = img.convertToFormat(QImage::Format_ARGB32); + res = res.mirrored(); + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + // Qt has ARGB; OpenGL wants RGBA + for (int i=0; i < res.height(); i++) { + uint *p = (uint*)res.scanLine(i); + uint *end = p + res.width(); + while (p < end) { + *p = (*p << 8) | ((*p >> 24) & 0xFF); + p++; + } + } + } + else { + // Qt has ARGB; OpenGL wants ABGR (i.e. RGBA backwards) + res = res.rgbSwapped(); + } + return res; +} +class GLTexturePrivate +{ +public: + GLTexturePrivate(GLTexture *_q) + : q(_q), texture(0), width(0), height(0), + horizWrap(GLTexture::Repeat), vertWrap(GLTexture::Repeat), + minFilter(GLTexture::Linear), magFilter(GLTexture::Linear) + { + } + + GLTexture *q; + GLuint texture; + int width; + int height; + GLTexture::WrapMode horizWrap; + GLTexture::WrapMode vertWrap; + GLTexture::FilterMode minFilter; + GLTexture::FilterMode magFilter; + + void genTexture(); +}; + +void GLTexturePrivate::genTexture() +{ + if(texture) + return; + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); + q->setHorizontalWrap(horizWrap); + q->setVerticalWrap(vertWrap); +} + +GLTexture::GLTexture() +: d(new GLTexturePrivate(this)) +{ +} + +GLTexture::GLTexture(const QString &file) +: d(new GLTexturePrivate(this)) +{ + QImage img(file); + if(!img.isNull()) + setImage(img); +} + +GLTexture::GLTexture(const QImage &img) +: d(new GLTexturePrivate(this)) +{ + setImage(img); +} + +GLTexture::~GLTexture() +{ + if(d->texture) + glDeleteTextures(1, &d->texture); + delete d; + d = 0; +} + +bool GLTexture::isNull() const +{ + return d->texture == 0; +} + +void GLTexture::clear() +{ + if(d->texture) { + glDeleteTextures(1, &d->texture); + d->texture = 0; + d->width = 0; + d->height = 0; + } +} + +static inline int npot(int size) +{ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + return size; +} + +/*! + Set the texture to \a img. If the texture has already been created (either + by explicitly setting the size, or by previously setting an image), it will + be destroyed and a new texture created with \a img's contents and size. + */ +void GLTexture::setImage(const QImage &img) +{ + if(img.isNull()) + return; + + d->genTexture(); + + //QImage dataImage = img.scaled(npot(img.width()), npot(img.height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + QImage dataImage = QGLWidget_convertToGLFormat(img); + + glBindTexture(GL_TEXTURE_2D, d->texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dataImage.width(), + dataImage.height(), 0, + (dataImage.format() == QImage::Format_ARGB32)?GL_RGBA:GL_RGB, + GL_UNSIGNED_BYTE, dataImage.bits()); + d->width = dataImage.width(); + d->height = dataImage.height(); + +#if 0 + glGenerateMipmap(GL_TEXTURE_2D); + int e = glGetError(); + if(d->magFilter == Linear) + setMagFilter(MipmapLinear); + if(d->minFilter == Linear) + setMinFilter((GLTexture::FilterMode)GL_LINEAR_MIPMAP_LINEAR); +#endif +} + +void GLTexture::copyImage(const QImage &img, const QPoint &point, + const QRect &srcRect) +{ + qFatal("Not implemented"); + Q_UNUSED(img); + Q_UNUSED(point); + Q_UNUSED(srcRect); +} + +QSize GLTexture::size() const +{ + return QSize(d->width, d->height); +} + +int GLTexture::width() const +{ + return d->width; +} + +int GLTexture::height() const +{ + return d->height; +} + +/*! + Sets the \a size of the texture. This will destroy the current contents of + the texture. If an image has been assigned, it will need to be reassigned + using either setImage() or copyImage(). + + If size is invalid (width or height is less than or equal to 0) the texture + will be destroyed. This is equivalent to calling clear(). +*/ +void GLTexture::setSize(const QSize &size) +{ + if(size.width() <= 0 || size.height() <= 0) { + clear(); + return; + } + + d->genTexture(); + glBindTexture(GL_TEXTURE_2D, d->texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + d->width = size.width(); + d->height = size.height(); +} + +GLTexture::WrapMode GLTexture::horizontalWrap() const +{ + return d->horizWrap; +} + +GLTexture::WrapMode GLTexture::verticalWrap() const +{ + return d->vertWrap; +} + +void GLTexture::setHorizontalWrap(WrapMode mode) +{ + d->horizWrap = mode; + if(d->texture) { + GLint last; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last); + if(GLuint(last) != d->texture) + glBindTexture(GL_TEXTURE_2D, d->texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode); + if(GLuint(last) != d->texture) + glBindTexture(GL_TEXTURE_2D, last); + } +} + +/*! + Set the veritcal wrap mode to \a mode. + */ +void GLTexture::setVerticalWrap(WrapMode mode) +{ + d->vertWrap = mode; + if(d->texture) { + GLint last; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last); + if(GLuint(last) != d->texture) + glBindTexture(GL_TEXTURE_2D, d->texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode); + if(GLuint(last) != d->texture) + glBindTexture(GL_TEXTURE_2D, last); + } +} + +GLTexture::FilterMode GLTexture::minFilter() const +{ + return d->minFilter; +} + +GLTexture::FilterMode GLTexture::magFilter() const +{ + return d->magFilter; +} + +void GLTexture::setMinFilter(FilterMode f) +{ + if(d->minFilter == f) + return; + d->minFilter = f; + if(d->texture) { + glBindTexture(GL_TEXTURE_2D, d->texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, d->minFilter); + } +} + +void GLTexture::setMagFilter(FilterMode f) +{ + if(d->magFilter == f) + return; + d->magFilter = f; + if(d->texture) { + glBindTexture(GL_TEXTURE_2D, d->texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, d->magFilter); + } +} + +GLuint GLTexture::texture() const +{ + return d->texture; +} + +QT_END_NAMESPACE diff --git a/src/declarative/opengl/gltexture.h b/src/declarative/opengl/gltexture.h new file mode 100644 index 0000000..f920b60 --- /dev/null +++ b/src/declarative/opengl/gltexture.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 _GLTEXTURE_H_ +#define _GLTEXTURE_H_ + +#include <qfxglobal.h> + +#include <QRect> +#include <QPoint> +#include "glheaders.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QString; +class QImage; +class GLTexturePrivate; +class Q_DECLARATIVE_EXPORT GLTexture +{ +public: + GLTexture(); + GLTexture(const QString &file); + GLTexture(const QImage &img); + virtual ~GLTexture(); + + bool isNull() const; + void clear(); + + void setImage(const QImage &); + void copyImage(const QImage &, const QPoint & = QPoint(0, 0), const QRect & = QRect()); + + int width() const; + int height() const; + QSize size() const; + void setSize(const QSize &); + + enum WrapMode { + Repeat = GL_REPEAT, + ClampToEdge = GL_CLAMP_TO_EDGE, +#if defined(QFX_RENDER_OPENGL2) + MirroredRepeat = GL_MIRRORED_REPEAT, +#else + MirroredRepeat = Repeat +#endif + }; + + WrapMode horizontalWrap() const; + WrapMode verticalWrap() const; + void setHorizontalWrap(WrapMode); + void setVerticalWrap(WrapMode); + + enum FilterMode { + Nearest = GL_NEAREST, + Linear = GL_LINEAR, + MipmapLinear = GL_LINEAR_MIPMAP_LINEAR + }; + + FilterMode minFilter() const; + FilterMode magFilter() const; + void setMinFilter(FilterMode); + void setMagFilter(FilterMode); + + GLuint texture() const; +private: + Q_DISABLE_COPY(GLTexture); + GLTexturePrivate *d; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // _GLTEXTURE_H_ diff --git a/src/declarative/opengl/opengl.pri b/src/declarative/opengl/opengl.pri new file mode 100644 index 0000000..c9ccefb --- /dev/null +++ b/src/declarative/opengl/opengl.pri @@ -0,0 +1,20 @@ +DEPENDPATH += opengl +INCLUDEPATH += opengl +INCLUDEPATH += $$QMAKE_INCDIR_OPENGL +QT += opengl + +contains(QT_CONFIG, opengles1) { + SOURCES += gltexture.cpp \ + glsave.cpp + + HEADERS += gltexture.h \ + glsave.h +} else:contains(QT_CONFIG, opengles2) { + SOURCES += gltexture.cpp \ + glbasicshaders.cpp \ + glsave.cpp + + HEADERS += gltexture.h \ + glbasicshaders.h \ + glsave.h +} diff --git a/src/declarative/qml/qml.h b/src/declarative/qml/qml.h new file mode 100644 index 0000000..bf7b6bc --- /dev/null +++ b/src/declarative/qml/qml.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 QtDeclarative 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 QML_H +#define QML_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qurl.h> +#include <QtCore/qmetaobject.h> +#include <QtDeclarative/qfxglobal.h> +#include <QtDeclarative/qmlmetatype.h> +#include <QtDeclarative/qmlmetaproperty.h> +#include <QtDeclarative/qmlparserstatus.h> +#include <QtDeclarative/qmllist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +#define QML_DECLARE_TYPE(TYPE) \ + Q_DECLARE_METATYPE(TYPE *); \ + Q_DECLARE_METATYPE(QList<TYPE *> *); \ + Q_DECLARE_METATYPE(QmlList<TYPE *> *); + +#define QML_DECLARE_TYPE_HASMETATYPE(TYPE) \ + Q_DECLARE_METATYPE(QList<TYPE *> *); \ + Q_DECLARE_METATYPE(QmlList<TYPE *> *); + +#define QML_DECLARE_INTERFACE(INTERFACE) \ + QML_DECLARE_TYPE(INTERFACE) + +#define QML_DECLARE_INTERFACE_HASMETATYPE(INTERFACE) \ + QML_DECLARE_TYPE_HASMETATYPE(INTERFACE) + +#define QML_DEFINE_INTERFACE(INTERFACE) \ + template<> QmlPrivate::InstanceType QmlPrivate::Define<INTERFACE *>::instance(qmlRegisterInterface<INTERFACE>(#INTERFACE)); + +#define QML_DEFINE_EXTENDED_TYPE(TYPE, NAME, EXTENSION) \ + template<> QmlPrivate::InstanceType QmlPrivate::Define<TYPE *>::instance(qmlRegisterExtendedType<TYPE,EXTENSION>(#NAME, #TYPE)); + +#define QML_DEFINE_TYPE(TYPE, NAME) \ + template<> QmlPrivate::InstanceType QmlPrivate::Define<TYPE *>::instance(qmlRegisterType<TYPE>(#NAME, #TYPE)); + +#define QML_DEFINE_EXTENDED_NOCREATE_TYPE(TYPE, EXTENSION) \ + template<> QmlPrivate::InstanceType QmlPrivate::Define<TYPE *>::instance(qmlRegisterExtendedType<TYPE,EXTENSION>(#TYPE)); + +#define QML_DEFINE_NOCREATE_TYPE(TYPE) \ + template<> QmlPrivate::InstanceType QmlPrivate::Define<TYPE *>::instance(qmlRegisterType<TYPE>(#TYPE)); + +QML_DECLARE_TYPE(QObject); +Q_DECLARE_METATYPE(QVariant); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QML_H diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri new file mode 100644 index 0000000..65e6763 --- /dev/null +++ b/src/declarative/qml/qml.pri @@ -0,0 +1,63 @@ +SOURCES += qml/qmlparser.cpp \ + qml/qmlinstruction.cpp \ + qml/qmlvmemetaobject.cpp \ + qml/qmlengine.cpp \ + qml/qmlbindablevalue.cpp \ + qml/qmlmetaproperty.cpp \ + qml/qmlcomponent.cpp \ + qml/qmlcontext.cpp \ + qml/qmlcustomparser.cpp \ + qml/qmlpropertyvaluesource.cpp \ + qml/qmlxmlparser.cpp \ + qml/qmlproxymetaobject.cpp \ + qml/qmlvme.cpp \ + qml/qmlcompiler.cpp \ + qml/qmlcompiledcomponent.cpp \ + qml/qmlboundsignal.cpp \ + qml/qmldom.cpp \ + qml/qmlrefcount.cpp \ + qml/qmlprivate.cpp \ + qml/qmlmetatype.cpp \ + qml/qmlstringconverters.cpp \ + qml/qmlclassfactory.cpp \ + qml/qmlparserstatus.cpp \ + qml/qmlcompositetypemanager.cpp \ + qml/qmlinfo.cpp + +HEADERS += qml/qmlparser_p.h \ + qml/qmlinstruction_p.h \ + qml/qmlvmemetaobject_p.h \ + qml/qml.h \ + qml/qmlbindablevalue.h \ + qml/qmlmetaproperty.h \ + qml/qmlcomponent.h \ + qml/qmlcomponent_p.h \ + qml/qmlcustomparser.h \ + qml/qmlpropertyvaluesource.h \ + qml/qmlboundsignal_p.h \ + qml/qmlxmlparser_p.h \ + qml/qmlparserstatus.h \ + qml/qmlproxymetaobject_p.h \ + qml/qmlcompiledcomponent_p.h \ + qml/qmlvme_p.h \ + qml/qmlcompiler_p.h \ + qml/qmlengine_p.h \ + qml/qmlprivate.h \ + qml/qmldom.h \ + qml/qmldom_p.h \ + qml/qmlrefcount_p.h \ + qml/qmlmetatype.h \ + qml/qmlengine.h \ + qml/qmlcontext.h \ + qml/qmlexpression.h \ + qml/qmlstringconverters_p.h \ + qml/qmlclassfactory_p.h \ + qml/qmlinfo.h \ + qml/qmlmetaproperty_p.h \ + qml/qmlcontext_p.h \ + qml/qmlcompositetypemanager_p.h \ + qml/qmllist.h + +# for qtscript debugger +QT += scripttools +include(script/script.pri) diff --git a/src/declarative/qml/qmlbindablevalue.cpp b/src/declarative/qml/qmlbindablevalue.cpp new file mode 100644 index 0000000..423fda8 --- /dev/null +++ b/src/declarative/qml/qmlbindablevalue.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <qml.h> +#include "qmlbindablevalue.h" +#include <gfxeasing.h> +#include <qmlcontext.h> +#include <gfxtimeline.h> +#include <QVariant> +#include <qfxperf.h> +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE +DEFINE_BOOL_CONFIG_OPTION(scriptWarnings, QML_SCRIPT_WARNINGS); + +QML_DEFINE_NOCREATE_TYPE(QmlBindableValue); +QmlBindableValue::QmlBindableValue(QObject *parent) +: QmlPropertyValueSource(parent), _inited(false) +{ + qFatal("QmlBindableValue: Default constructor not supported"); +} + +QmlBindableValue::QmlBindableValue(void *data, QmlRefCount *rc, QObject *obj, QObject *parent) +: QmlPropertyValueSource(parent), QmlExpression(QmlContext::activeContext(), data, rc, obj), _inited(false) +{ +} + +QmlBindableValue::QmlBindableValue(const QString &str, QObject *obj, bool sse, QObject *parent) +: QmlPropertyValueSource(parent), QmlExpression(QmlContext::activeContext(), str, obj, sse), _inited(false) +{ +} + +QmlBindableValue::~QmlBindableValue() +{ +} + +void QmlBindableValue::setTarget(const QmlMetaProperty &prop) +{ + _property = prop; + + update(); +} + +void QmlBindableValue::init() +{ + if(_inited) + return; + _inited = true; + update(); +} + +void QmlBindableValue::setExpression(const QString &expr) +{ + QmlExpression::setExpression(expr); + update(); +} + +Q_DECLARE_METATYPE(QList<QObject *>); +void QmlBindableValue::update() +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BindableValueUpdate> bu; +#endif + if(!_inited) + return; + + if(_property.propertyCategory() == QmlMetaProperty::List) { + QVariant value = this->value(); + int listType = QmlMetaType::listType(_property.propertyType()); + + if(value.userType() == qMetaTypeId<QList<QObject *> >()) { + const QList<QObject *> &list = + qvariant_cast<QList<QObject *> >(value); + QVariant listVar = _property.read(); + QmlMetaType::clear(listVar); + for(int ii = 0; ii < list.count(); ++ii) { + QVariant v = QmlMetaType::fromObject(list.at(ii), listType); + QmlMetaType::append(listVar, v); + } + + } else if(value.type() == uint(listType) || + value.userType() == listType) { + QVariant listVar = _property.read(); + QmlMetaType::clear(listVar); + QmlMetaType::append(listVar, value); + } + } else if(_property.propertyCategory() == QmlMetaProperty::QmlList) { + // XXX - optimize! + QVariant value = this->value(); + QVariant list = _property.read(); + QmlPrivate::ListInterface *li = + *(QmlPrivate::ListInterface **)list.constData(); + + int type = li->type(); + + if (QObject *obj = QmlMetaType::toQObject(value)) { + const QMetaObject *mo = + QmlMetaType::rawMetaObjectForType(type); + + const QMetaObject *objMo = obj->metaObject(); + bool found = false; + while(!found && objMo) { + if(objMo == mo) + found = true; + else + objMo = objMo->superClass(); + } + + if(!found) { + qWarning() << "Unable to assign object to list"; + return; + } + + // NOTE: This assumes a cast to QObject does not alter + // the object pointer + void *d = (void *)&obj; + li->append(d); + } + } else if(_property.propertyCategory() == QmlMetaProperty::Bindable) { + + // NOTE: We assume that only core properties can have + // propertyType == Bindable + int idx = _property.coreIndex(); + Q_ASSERT(idx != -1); + + void *a[1]; + QmlBindableValue *t = this; + a[0] = (void *)&t; + _property.object()->qt_metacall(QMetaObject::WriteProperty, + idx, a); + + } else if(_property.propertyCategory() == QmlMetaProperty::Object) { + + QVariant value = this->value(); + if((int)value.type() != qMetaTypeId<QObject *>()) { + if(scriptWarnings()) { + if(!value.isValid()) { + qWarning() << "QmlBindableValue: Unable to assign invalid value to object property"; + } else { + qWarning() << "QmlBindableValue: Unable to assign non-object to object property"; + } + } + return; + } + + // NOTE: This assumes a cast to QObject does not alter the + // object pointer + QObject *obj = *(QObject **)value.data(); + + // NOTE: We assume that only core properties can have + // propertyType == Object + int idx = _property.coreIndex(); + Q_ASSERT(idx != -1); + + void *a[1]; + a[0] = (void *)&obj; + _property.object()->qt_metacall(QMetaObject::WriteProperty, + idx, a); + + } else if(_property.propertyCategory() == QmlMetaProperty::Normal) { + QVariant value = this->value(); + _property.write(value); + } +} + +void QmlBindableValue::valueChanged() +{ + update(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlbindablevalue.h b/src/declarative/qml/qmlbindablevalue.h new file mode 100644 index 0000000..578fc12 --- /dev/null +++ b/src/declarative/qml/qmlbindablevalue.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLBINDABLEVALUE_H +#define QMLBINDABLEVALUE_H + +#include <QObject> +#include <qfxglobal.h> +#include <qml.h> +#include <qmlpropertyvaluesource.h> +#include <QtDeclarative/qmlexpression.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlExpression; +class QmlContext; +class Q_DECLARATIVE_EXPORT QmlBindableValue : public QmlPropertyValueSource, + public QmlExpression +{ +Q_OBJECT +public: + QmlBindableValue(QObject *parent); + QmlBindableValue(const QString &, QObject *, bool = true, QObject *parent=0); + QmlBindableValue(void *, QmlRefCount *, QObject *, QObject *parent); + ~QmlBindableValue(); + + virtual void setTarget(const QmlMetaProperty &); + QmlMetaProperty property() const { return _property; } + + Q_CLASSINFO("DefaultProperty", "expression"); + Q_PROPERTY(QString expression READ expression WRITE setExpression); + virtual void setExpression(const QString &); + + void init(); + +private Q_SLOTS: + void update(); + +protected: + virtual void valueChanged(); + +private: + bool _inited; + QmlMetaProperty _property; +}; +QML_DECLARE_TYPE(QmlBindableValue); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QMLBINDABLEVALUE_H diff --git a/src/declarative/qml/qmlboundsignal.cpp b/src/declarative/qml/qmlboundsignal.cpp new file mode 100644 index 0000000..5359753 --- /dev/null +++ b/src/declarative/qml/qmlboundsignal.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlboundsignal_p.h" +#include "private/qmetaobjectbuilder_p.h" +#include "private/qmlengine_p.h" +#include "private/qmlcontext_p.h" +#include <qfxglobal.h> +#include <qmlmetatype.h> +#include <qml.h> +#include <qmlcontext.h> + +QT_BEGIN_NAMESPACE + +int QmlBoundSignal::evaluateIdx = -1; +QmlBoundSignal::QmlBoundSignal(QmlContext *ctxt, const QString &val, QObject *me, int idx, QObject *parent) +: QmlExpressionObject(ctxt, val, me, false), _idx(idx) +{ + // A cached evaluation of the QmlExpressionObject::value() slot index. + // + // This is thread safe. Although it may be updated by two threads, they + // will both set it to the same value - so the worst thing that can happen + // is that they both do the work to figure it out. Boo hoo. + if(evaluateIdx == -1) evaluateIdx = QmlExpressionObject::staticMetaObject.indexOfMethod("value()"); + + setTrackChange(false); + QFx_setParent_noEvent(this, parent); + QMetaObject::connect(me, _idx, this, evaluateIdx); +} + +QmlBoundSignalProxy::QmlBoundSignalProxy(QmlContext *ctxt, const QString &val, QObject *me, int idx, QObject *parent) +: QmlBoundSignal(ctxt, val, me, idx, parent) +{ + QMetaMethod signal = me->metaObject()->method(idx); + + params = new QmlBoundSignalParameters(signal, this); + + ctxt->d_func()->addDefaultObject(params, QmlContextPrivate::HighPriority); +} + +int QmlBoundSignalProxy::qt_metacall(QMetaObject::Call c, int id, void **a) +{ + if(c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) { + params->setValues(a); + value(); + params->clearValues(); + return -1; + } else { + return QmlBoundSignal::qt_metacall(c, id, a); + } +} + +#include <QDebug> +QmlBoundSignalParameters::QmlBoundSignalParameters(const QMetaMethod &method, + QObject *parent) +: QObject(parent), types(0), values(0) +{ + MetaObject *mo = new MetaObject(this); + + // ### Optimize! + // ### Ensure only supported types are allowed, otherwise it might crash + QMetaObjectBuilder mob; + mob.setSuperClass(&QmlBoundSignalParameters::staticMetaObject); + + QList<QByteArray> paramTypes = method.parameterTypes(); + QList<QByteArray> paramNames = method.parameterNames(); + types = new int[paramTypes.count()]; + for(int ii = 0; ii < paramTypes.count(); ++ii) { + const QByteArray &type = paramTypes.at(ii); + const QByteArray &name = paramNames.at(ii); + + if(name.isEmpty() || type.isEmpty()) { + types[ii] = 0; + continue; + } + + QVariant::Type t = (QVariant::Type)QMetaType::type(type.constData()); + if(QmlMetaType::isObject(t)) { + types[ii] = QMetaType::QObjectStar; + QMetaPropertyBuilder prop = mob.addProperty(name, "QObject*"); + prop.setWritable(false); + } else { + types[ii] = t; + QMetaPropertyBuilder prop = mob.addProperty(name, type); + prop.setWritable(false); + } + } + myMetaObject = mob.toMetaObject(); + *static_cast<QMetaObject *>(mo) = *myMetaObject; + + d_ptr->metaObject = mo; +} + +QmlBoundSignalParameters::~QmlBoundSignalParameters() +{ + delete [] types; + qFree(myMetaObject); +} + +void QmlBoundSignalParameters::setValues(void **v) +{ + values = v; +} + +void QmlBoundSignalParameters::clearValues() +{ + values = 0; +} + +int QmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a) +{ + if(c == QMetaObject::ReadProperty && id >= 1) { + QmlMetaType::copy(types[id - 1], a[0], values[id]); + return -1; + } else { + return qt_metacall(c, id, a); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlboundsignal_p.h b/src/declarative/qml/qmlboundsignal_p.h new file mode 100644 index 0000000..e84f0c1 --- /dev/null +++ b/src/declarative/qml/qmlboundsignal_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLBOUNDSIGNAL_P_H +#define QMLBOUNDSIGNAL_P_H + +#include <qmlexpression.h> +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE +class QmlBoundSignal : public QmlExpressionObject +{ +Q_OBJECT +public: + QmlBoundSignal(QmlContext *, const QString &, QObject *me, int idx, QObject *parent); + + int index() const { return _idx; } +protected: + static int evaluateIdx; +private: + int _idx; +}; + +class QmlBoundSignalParameters : public QObject +{ +Q_OBJECT +public: + QmlBoundSignalParameters(const QMetaMethod &, QObject * = 0); + ~QmlBoundSignalParameters(); + + void setValues(void **); + void clearValues(); + +private: + friend class MetaObject; + int metaCall(QMetaObject::Call, int _id, void **); + struct MetaObject : public QAbstractDynamicMetaObject { + MetaObject(QmlBoundSignalParameters *b) + : parent(b) {} + + int metaCall(QMetaObject::Call c, int id, void **a) { + return parent->metaCall(c, id, a); + } + QmlBoundSignalParameters *parent; + }; + + int *types; + void **values; + QMetaObject *myMetaObject; +}; + +class QmlBoundSignalProxy : public QmlBoundSignal +{ +public: + QmlBoundSignalProxy(QmlContext *, const QString &, QObject *me, int idx, QObject *parent); + +protected: + virtual int qt_metacall(QMetaObject::Call c, int id, void **a); +private: + QmlBoundSignalParameters *params; +}; + + +#endif // QMLBOUNDSIGNAL_P_H + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlclassfactory.cpp b/src/declarative/qml/qmlclassfactory.cpp new file mode 100644 index 0000000..7e5b929 --- /dev/null +++ b/src/declarative/qml/qmlclassfactory.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlclassfactory_p.h" + +QmlClassFactory::~QmlClassFactory() +{ +} + diff --git a/src/declarative/qml/qmlclassfactory_p.h b/src/declarative/qml/qmlclassfactory_p.h new file mode 100644 index 0000000..e3e71c9 --- /dev/null +++ b/src/declarative/qml/qmlclassfactory_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLCLASSFACTORY_P_H +#define QMLCLASSFACTORY_P_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QmlEngine; +class QByteArray; +class QUrl; +class QmlComponent; + +class QmlClassFactory +{ +public: + virtual ~QmlClassFactory(); + virtual QmlComponent *create(const QByteArray &, const QUrl& baseUrl, QmlEngine*) = 0; +}; + +QT_END_NAMESPACE + +#endif // QMLCLASSFACTORY_P_H diff --git a/src/declarative/qml/qmlcompiledcomponent.cpp b/src/declarative/qml/qmlcompiledcomponent.cpp new file mode 100644 index 0000000..a8b2be0 --- /dev/null +++ b/src/declarative/qml/qmlcompiledcomponent.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlcompiledcomponent_p.h" +#include "qmlparser_p.h" +#include <QtCore/qdebug.h> +#include <QmlComponent> +using namespace QmlParser; + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP); + +QmlCompiledComponent::QmlCompiledComponent() +: dumpStatus(NoDump) +{ +} + +QmlCompiledComponent::~QmlCompiledComponent() +{ + for(int ii = 0; ii < mos.count(); ++ii) + qFree(mos.at(ii)); +} + + +void QmlCompiledComponent::dumpInstructions() +{ + if(!compilerDump()) + return; + + if(!name.isEmpty()) + qWarning() << name; + qWarning() << "Index\tLine\tOperation\t\tData1\tData2\t\tComments"; + qWarning() << "-------------------------------------------------------------------------------"; + for(int ii = 0; ii < bytecode.count(); ++ii) { + dump(&bytecode[ii], ii); + } + qWarning() << "-------------------------------------------------------------------------------"; +} + +void QmlCompiledComponent::dump(int indent, Property *p) +{ + QByteArray ba(indent * 4, ' '); + for(int ii = 0; ii < p->values.count(); ++ii) + dump(indent, p->values.at(ii)); + if(p->value) + dump(indent, p->value); +} + +void QmlCompiledComponent::dump(int indent, Object *o) +{ + QByteArray ba(indent * 4, ' '); + if(o->type != -1) { + qWarning() << ba.constData() << "Object:" << types.at(o->type).className; + } else { + qWarning() << ba.constData() << "Object: fetched"; + } + + for(QHash<QByteArray, Property *>::ConstIterator iter = o->properties.begin(); + iter != o->properties.end(); + ++iter) { + qWarning() << ba.constData() << " Property" << iter.key(); + dump(indent + 1, *iter); + } + + if(o->defaultProperty) { + qWarning() << ba.constData() << " Default property"; + dump(indent + 1, o->defaultProperty); + } +} + +void QmlCompiledComponent::dump(int indent, Value *v) +{ + QByteArray type; + switch(v->type) { + default: + case Value::Unknown: + type = "Unknown"; + break; + case Value::Literal: + type = "Literal"; + break; + case Value::PropertyBinding: + type = "PropertyBinding"; + break; + case Value::ValueSource: + type = "ValueSource"; + break; + case Value::CreatedObject: + type = "CreatedObject"; + break; + case Value::SignalObject: + type = "SignalObject"; + break; + case Value::SignalExpression: + type = "SignalExpression"; + break; + case Value::Component: + type = "Component"; + break; + case Value::Id: + type = "Id"; + break; + }; + + QByteArray ba(indent * 4, ' '); + if(v->object) { + qWarning() << ba.constData() << "Value (" << type << "):"; + dump(indent + 1, v->object); + } else { + qWarning() << ba.constData() << "Value (" << type << "):" << v->primitive; + } +} + +void QmlCompiledComponent::dumpPre() +{ + if(!(dumpStatus & DumpPre)) { + dumpInstructions(); + dumpStatus = (DumpStatus)(dumpStatus | DumpPre); + } +} + +void QmlCompiledComponent::dumpPost() +{ + if(!(dumpStatus & DumpPost)) { + dumpInstructions(); + dumpStatus = (DumpStatus)(dumpStatus | DumpPost); + } + +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcompiledcomponent_p.h b/src/declarative/qml/qmlcompiledcomponent_p.h new file mode 100644 index 0000000..fa68eab --- /dev/null +++ b/src/declarative/qml/qmlcompiledcomponent_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLCOMPILEDCOMPONENT_P_H +#define QMLCOMPILEDCOMPONENT_P_H + +#include <qml.h> +#include <private/qmlinstruction_p.h> +#include <private/qmlcompiler_p.h> +#include <private/qmlrefcount_p.h> +class QmlXmlParser; + +QT_BEGIN_NAMESPACE +namespace QmlParser { + class Property; + class Object; + class Value; +}; + +class QmlCompiledComponent : public QmlRefCount, public QmlCompiledData +{ +public: + QmlCompiledComponent(); + ~QmlCompiledComponent(); + + void dumpPre(); + void dumpPost(); + +private: + enum DumpStatus { NoDump = 0x00, DumpPre = 0x01, DumpPost = 0x02 } dumpStatus; + void dumpInstructions(); + void dump(int indent, QmlParser::Property *p); + void dump(int indent, QmlParser::Object *o); + void dump(int indent, QmlParser::Value *v); + void dump(QmlInstruction *, int idx = -1); + friend class QmlCompiler; + friend class QmlDomDocument; +}; + + +QT_END_NAMESPACE +#endif // QMLCOMPILEDCOMPONENT_P_H diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp new file mode 100644 index 0000000..9af0a06 --- /dev/null +++ b/src/declarative/qml/qmlcompiler.cpp @@ -0,0 +1,1662 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "private/qmlcompiler_p.h" +#include <qfxperf.h> +#include "qmlparser_p.h" +#include "private/qmlxmlparser_p.h" +#include <qmlpropertyvaluesource.h> +#include <qmlcomponent.h> +#include "private/qmetaobjectbuilder_p.h" +#include <qmlbasicscript.h> +#include <QColor> +#include <QDebug> +#include <QPointF> +#include <QSizeF> +#include <QRectF> +#include <private/qmlcompiledcomponent_p.h> +#include <private/qmlstringconverters_p.h> +#include <private/qmlengine_p.h> +#include <qmlengine.h> +#include <qmlcontext.h> +#include <qmlmetatype.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE +/* + New properties and signals can be added to any QObject type from QML. + <QObject> + <properties><Property name="myProperty" /></properties> + <signals><Signal name="mySignal" /></signals> + </QObject + The special names used as magical properties (in the above case "properties" + and "signals") are defined here. +*/ +#define PROPERTIES_NAME "properties" +#define SIGNALS_NAME "signals" + +using namespace QmlParser; + +int QmlCompiledData::indexForString(const QString &data) +{ + int idx = primitives.indexOf(data); + if(idx == -1) { + idx = primitives.count(); + primitives << data; + } + return idx; +} + +int QmlCompiledData::indexForByteArray(const QByteArray &data) +{ + int idx = datas.indexOf(data); + if(idx == -1) { + idx = datas.count(); + datas << data; + } + return idx; +} + +int QmlCompiledData::indexForFloat(float *data, int count) +{ + Q_ASSERT(count > 0); + + for(int ii = 0; ii < floatData.count() - count; ++ii) { + bool found = true; + for(int jj = 0; jj < count; ++jj) { + if(floatData.at(ii + jj) != data[jj]) { + found = false; + break; + } + } + + if(found) + return ii; + } + + int idx = floatData.count(); + for(int ii = 0; ii < count; ++ii) + floatData << data[ii]; + + return idx; +} + +int QmlCompiledData::indexForInt(int *data, int count) +{ + Q_ASSERT(count > 0); + + for(int ii = 0; ii < floatData.count() - count; ++ii) { + bool found = true; + for(int jj = 0; jj < count; ++jj) { + if(intData.at(ii + jj) != data[jj]) { + found = false; + break; + } + } + + if(found) + return ii; + } + + int idx = intData.count(); + for(int ii = 0; ii < count; ++ii) + intData << data[ii]; + + return idx; +} + +QmlCompiler::QmlCompiler() +: exceptionLine(-1), output(0) +{ +} + +bool QmlCompiler::isError() const +{ + return exceptionLine != -1; +} + +qint64 QmlCompiler::errorLine() const +{ + return exceptionLine; +} + +QString QmlCompiler::errorDescription() const +{ + return exceptionDescription; +} + +bool QmlCompiler::isValidId(const QString &val) +{ + if(val.isEmpty()) + return false; + + QChar u(QLatin1Char('_')); + for(int ii = 0; ii < val.count(); ++ii) + if(val.at(ii) != u && + ((ii == 0 && !val.at(ii).isLetter()) || + (ii != 0 && !val.at(ii).isLetterOrNumber())) ) + return false; + + return true; +} + +/*! + Returns true if \a str is a valid binding string, false otherwise. + + Valid binding strings are those enclosed in braces ({}). +*/ +bool QmlCompiler::isBinding(const QString &str) +{ + return str.startsWith(QLatin1Char('{')) && str.endsWith(QLatin1Char('}')); +} + +/*! + Returns true if property name \a name refers to an attached property, false + otherwise. + + Attached property names are those that start with a capital letter. +*/ +bool QmlCompiler::isAttachedProperty(const QByteArray &name) +{ + return !name.isEmpty() && name.at(0) >= 'A' && name.at(0) <= 'Z'; +} + +QmlCompiler::StoreInstructionResult +QmlCompiler::generateStoreInstruction(QmlCompiledData &cdata, + QmlInstruction &instr, + const QMetaProperty &prop, + int coreIdx, int primitive, + const QString *string) +{ + if(!prop.isWritable()) + return ReadOnly; + if(prop.isEnumType()) { + int value; + if (prop.isFlagType()) { + value = prop.enumerator().keysToValue(string->toLatin1().constData()); + } else + value = prop.enumerator().keyToValue(string->toLatin1().constData()); + if(value == -1) + return InvalidData; + instr.type = QmlInstruction::StoreInteger; + instr.storeInteger.propertyIndex = coreIdx; + instr.storeInteger.value = value; + return Ok; + } + int type = prop.type(); + switch(type) { + case -1: + instr.type = QmlInstruction::StoreVariant; + instr.storeString.propertyIndex = coreIdx; + if(primitive == -1) + primitive = cdata.indexForString(*string); + instr.storeString.value = primitive; + break; + break; + case QVariant::String: + { + instr.type = QmlInstruction::StoreString; + instr.storeString.propertyIndex = coreIdx; + if(string->startsWith(QLatin1Char('\'')) && string->endsWith(QLatin1Char('\''))) { + QString unquotedString = string->mid(1, string->length() - 2); + primitive = cdata.indexForString(unquotedString); + } else { + if(primitive == -1) + primitive = cdata.indexForString(*string); + } + instr.storeString.value = primitive; + } + break; + case QVariant::UInt: + { + instr.type = QmlInstruction::StoreInteger; + instr.storeInteger.propertyIndex = coreIdx; + bool ok; + int value = string->toUInt(&ok); + if (!ok) + return InvalidData; + instr.storeInteger.value = value; + } + break; + case QVariant::Int: + { + instr.type = QmlInstruction::StoreInteger; + instr.storeInteger.propertyIndex = coreIdx; + bool ok; + int value = string->toInt(&ok); + if (!ok) + return InvalidData; + instr.storeInteger.value = value; + } + break; + case 135: + case QVariant::Double: + { + instr.type = QmlInstruction::StoreReal; + instr.storeReal.propertyIndex = coreIdx; + bool ok; + float value = string->toFloat(&ok); + if (!ok) + return InvalidData; + instr.storeReal.value = value; + } + break; + case QVariant::Color: + { + QColor c = QmlStringConverters::colorFromString(*string); + if (!c.isValid()) + return InvalidData; + instr.type = QmlInstruction::StoreColor; + instr.storeColor.propertyIndex = coreIdx; + instr.storeColor.value = c.rgba(); + } + break; + case QVariant::Date: + { + QDate d = QDate::fromString(*string, Qt::ISODate); + if (!d.isValid()) + return InvalidData; + instr.type = QmlInstruction::StoreDate; + instr.storeDate.propertyIndex = coreIdx; + instr.storeDate.value = d.toJulianDay(); + } + break; + case QVariant::Time: + { + QTime time = QTime::fromString(*string, Qt::ISODate); + if (!time.isValid()) + return InvalidData; + int data[] = { time.hour(), time.minute(), time.second(), time.msec() }; + int index = cdata.indexForInt(data, 4); + instr.type = QmlInstruction::StoreTime; + instr.storeTime.propertyIndex = coreIdx; + instr.storeTime.valueIndex = index; + } + break; + case QVariant::DateTime: + { + QDateTime dateTime = QDateTime::fromString(*string, Qt::ISODate); + if (!dateTime.isValid()) + return InvalidData; + int data[] = { dateTime.date().toJulianDay(), + dateTime.time().hour(), + dateTime.time().minute(), + dateTime.time().second(), + dateTime.time().msec() }; + int index = cdata.indexForInt(data, 5); + instr.type = QmlInstruction::StoreDateTime; + instr.storeDateTime.propertyIndex = coreIdx; + instr.storeDateTime.valueIndex = index; + } + break; + case QVariant::Point: + case QVariant::PointF: + { + bool ok; + QPointF point = QmlStringConverters::pointFFromString(*string, &ok); + if (!ok) + return InvalidData; + float data[] = { point.x(), point.y() }; + int index = cdata.indexForFloat(data, 2); + if (type == QVariant::PointF) + instr.type = QmlInstruction::StorePointF; + else + instr.type = QmlInstruction::StorePoint; + instr.storeRealPair.propertyIndex = coreIdx; + instr.storeRealPair.valueIndex = index; + } + break; + case QVariant::Size: + case QVariant::SizeF: + { + bool ok; + QSizeF size = QmlStringConverters::sizeFFromString(*string, &ok); + if (!ok) + return InvalidData; + float data[] = { size.width(), size.height() }; + int index = cdata.indexForFloat(data, 2); + if (type == QVariant::SizeF) + instr.type = QmlInstruction::StoreSizeF; + else + instr.type = QmlInstruction::StoreSize; + instr.storeRealPair.propertyIndex = coreIdx; + instr.storeRealPair.valueIndex = index; + } + break; + case QVariant::Rect: + case QVariant::RectF: + { + bool ok; + QRectF rect = QmlStringConverters::rectFFromString(*string, &ok); + if (!ok) + return InvalidData; + float data[] = { rect.x(), rect.y(), + rect.width(), rect.height() }; + int index = cdata.indexForFloat(data, 4); + if (type == QVariant::RectF) + instr.type = QmlInstruction::StoreRectF; + else + instr.type = QmlInstruction::StoreRect; + instr.storeRect.propertyIndex = coreIdx; + instr.storeRect.valueIndex = index; + } + break; + case QVariant::Bool: + { + bool ok; + bool b = QmlStringConverters::boolFromString(*string, &ok); + if (!ok) + return InvalidData; + instr.type = QmlInstruction::StoreBool; + instr.storeBool.propertyIndex = coreIdx; + instr.storeBool.value = b; + } + break; + default: + { + int t = prop.type(); + if(t == QVariant::UserType) + t = prop.userType(); + QmlMetaType::StringConverter converter = + QmlMetaType::customStringConverter(t); + if (converter) { + int index = cdata.customTypeData.count(); + instr.type = QmlInstruction::AssignCustomType; + instr.assignCustomType.propertyIndex = coreIdx; + instr.assignCustomType.valueIndex = index; + + QmlCompiledData::CustomTypeData data; + if(primitive == -1) + primitive = cdata.indexForString(*string); + data.index = primitive; + data.type = t; + cdata.customTypeData << data; + break; + } + } + return UnknownType; + break; + } + return Ok; +} + +void QmlCompiler::reset(QmlCompiledComponent *cc, bool deleteMemory) +{ + cc->types.clear(); + cc->primitives.clear(); + cc->floatData.clear(); + cc->intData.clear(); + cc->customTypeData.clear(); + cc->datas.clear(); + if(deleteMemory) { + for(int ii = 0; ii < cc->mos.count(); ++ii) + qFree(cc->mos.at(ii)); + } + cc->mos.clear(); + cc->bytecode.clear(); +} + +#define COMPILE_EXCEPTION(desc) \ + { \ + exceptionLine = obj->line; \ + QDebug d(&exceptionDescription); \ + d << desc; \ + return false; \ + } + +#define COMPILE_CHECK(a) \ + { \ + if (!a) return false; \ + } + +bool QmlCompiler::compile(QmlEngine *engine, + QmlCompositeTypeManager::TypeData *unit, + QmlCompiledComponent *out) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::Compile> pc; +#endif + exceptionLine = -1; + + Q_ASSERT(out); + reset(out, true); + + output = out; + + // Compile types + for(int ii = 0; ii < unit->types.count(); ++ii) { + QmlCompositeTypeManager::TypeData::TypeReference &tref = unit->types[ii]; + QmlCompiledComponent::TypeReference ref; + if(tref.type) + ref.type = tref.type; + else if(tref.unit) { + ref.component = tref.unit->toComponent(engine); + ref.ref = tref.unit; + ref.ref->addref(); + } else if(tref.parser) + ref.parser = tref.parser; + ref.className = unit->data.types().at(ii).toLatin1(); + out->types << ref; + } + + Object *root = unit->data.tree(); + if(!root) { + exceptionDescription = QLatin1String("Can't compile because of earlier errors"); + output = 0; + return false; + } + + compileTree(root); + + if(!isError()) { + out->dumpPre(); + } else { + reset(out, true); + } + + output = 0; + return !isError(); +} + +void QmlCompiler::compileTree(Object *tree) +{ + QmlInstruction init; + init.type = QmlInstruction::Init; + init.line = 0; + init.init.dataSize = 0; + output->bytecode << init; + + if(!compileObject(tree, 0)) // Compile failed + return; + + QmlInstruction def; + init.line = 0; + def.type = QmlInstruction::SetDefault; + output->bytecode << def; + + optimizeExpressions(0, output->bytecode.count() - 1, 0); +} + +bool QmlCompiler::compileObject(Object *obj, int ctxt) +{ + if(obj->type != -1) { + obj->metatype = + QmlMetaType::metaObjectForType(output->types.at(obj->type).className); + } + + if(output->types.at(obj->type).className == "Component") { + COMPILE_CHECK(compileComponent(obj, ctxt)); + return true; + } + + ctxt = 0; + + // Only use magical "properties" and "signals" properties if the type + // doesn't have already have them + bool ignoreProperties = false; + bool ignoreSignals = false; + if(obj->metatype && obj->metatype->indexOfProperty(PROPERTIES_NAME) != -1) + ignoreProperties = true; + if(obj->metatype && obj->metatype->indexOfProperty(SIGNALS_NAME) != -1) + ignoreSignals = true; + + Property *propertiesProperty = ignoreProperties?0:obj->getProperty(PROPERTIES_NAME, false); + Property *signalsProperty = ignoreSignals?0:obj->getProperty(SIGNALS_NAME, false); + + if(propertiesProperty) { + obj->dynamicPropertiesProperty = propertiesProperty; + obj->properties.remove(PROPERTIES_NAME); + } + if(signalsProperty) { + obj->dynamicSignalsProperty = signalsProperty; + obj->properties.remove(SIGNALS_NAME); + } + + if(obj->type != -1 && output->types.at(obj->type).parser) { + QByteArray data = obj->custom; + int ref = output->indexForByteArray(data); + + QmlInstruction create; + create.type = QmlInstruction::CreateCustomObject; + create.line = obj->line; + create.createCustom.type = obj->type; + create.createCustom.data = ref; + output->bytecode << create; + } else { + // Create the object + QmlInstruction create; + create.type = QmlInstruction::CreateObject; + create.line = obj->line; + create.create.type = obj->type; + output->bytecode << create; + } + + COMPILE_CHECK(compileDynamicPropertiesAndSignals(obj)); + + if(obj->type != -1) { + if(output->types.at(obj->type).component) { + QmlInstruction begin; + begin.type = QmlInstruction::TryBeginObject; + begin.line = obj->line; + output->bytecode << begin; + } else { + int cast = QmlMetaType::qmlParserStatusCast(QmlMetaType::type(output->types.at(obj->type).className)); + if(cast != -1) { + QmlInstruction begin; + begin.type = QmlInstruction::BeginObject; + begin.begin.castValue = cast; + begin.line = obj->line; + output->bytecode << begin; + } + } + } + + foreach(Property *prop, obj->properties) { + if(!ignoreProperties && prop->name == PROPERTIES_NAME) { + } else if(!ignoreSignals && prop->name == SIGNALS_NAME) { + } else if(prop->name.length() >= 3 && prop->name.startsWith("on") && + ('A' <= prop->name.at(2) && 'Z' >= prop->name.at(2))) { + COMPILE_CHECK(compileSignal(prop, obj)); + } else { + COMPILE_CHECK(compileProperty(prop, obj, ctxt)); + } + } + + if(obj->defaultProperty) + COMPILE_CHECK(compileProperty(obj->defaultProperty, obj, ctxt)); + + if(obj->type != -1) { + if(output->types.at(obj->type).component) { + QmlInstruction complete; + complete.type = QmlInstruction::TryCompleteObject; + complete.line = obj->line; + output->bytecode << complete; + } else { + int cast = QmlMetaType::qmlParserStatusCast(QmlMetaType::type(output->types.at(obj->type).className)); + if(cast != -1) { + QmlInstruction complete; + complete.type = QmlInstruction::CompleteObject; + complete.complete.castValue = cast; + complete.line = obj->line; + output->bytecode << complete; + } + } + } + + return true; +} + +bool QmlCompiler::compileComponent(Object *obj, int ctxt) +{ + Property *idProp = 0; + if(obj->properties.count() > 1 || + (obj->properties.count() == 1 && obj->properties.begin().key() != "id")) + COMPILE_EXCEPTION("Invalid component specification"); + if(obj->defaultProperty && + (obj->defaultProperty->value || obj->defaultProperty->values.count() > 1 || + (obj->defaultProperty->values.count() == 1 && !obj->defaultProperty->values.first()->object))) + COMPILE_EXCEPTION("Invalid component body specification"); + if(obj->properties.count()) + idProp = *obj->properties.begin(); + if(idProp && (idProp->value || idProp->values.count() > 1)) + COMPILE_EXCEPTION("Invalid component id specification"); + + Object *root = 0; + if(obj->defaultProperty && obj->defaultProperty->values.count()) + root = obj->defaultProperty->values.first()->object; + + COMPILE_CHECK(compileComponentFromRoot(root, ctxt)); + + if(idProp && idProp->values.count()) { + QString val = idProp->values.at(0)->primitive; + if(!isValidId(val)) + COMPILE_EXCEPTION("Invalid id property value"); + + if(ids.contains(val)) + COMPILE_EXCEPTION("id is not unique"); + ids.insert(val); + + int pref = output->indexForString(val); + QmlInstruction id; + id.type = QmlInstruction::SetId; + id.line = idProp->line; + id.setId.value = pref; + id.setId.save = -1; + output->bytecode << id; + } + + return true; +} + +bool QmlCompiler::compileComponentFromRoot(Object *obj, int ctxt) +{ + output->bytecode.push_back(QmlInstruction()); + QmlInstruction &create = output->bytecode.last(); + create.type = QmlInstruction::CreateComponent; + create.line = obj->line; + int count = output->bytecode.count(); + + QmlInstruction init; + init.type = QmlInstruction::Init; + init.init.dataSize = 0; + init.line = obj->line; + output->bytecode << init; + + QSet<QString> oldIds = ids; + ids.clear(); + if(obj) + COMPILE_CHECK(compileObject(obj, ctxt)); + ids = oldIds; + + create.createComponent.count = output->bytecode.count() - count; + + int inc = optimizeExpressions(count, count - 1 + create.createComponent.count, count); + create.createComponent.count += inc; + return true; +} + + +bool QmlCompiler::compileFetchedObject(Object *obj, int ctxt) +{ + if(obj->defaultProperty) + COMPILE_CHECK(compileProperty(obj->defaultProperty, obj, ctxt)); + + foreach(Property *prop, obj->properties) { + if(prop->name.length() >= 3 && prop->name.startsWith("on") && + ('A' <= prop->name.at(2) && 'Z' >= prop->name.at(2))) { + COMPILE_CHECK(compileSignal(prop, obj)); + } else { + COMPILE_CHECK(compileProperty(prop, obj, ctxt)); + } + } + + return true; +} + +bool QmlCompiler::compileSignal(Property *prop, Object *obj) +{ + if(prop->values.isEmpty() && !prop->value) + return true; + + if(prop->value || prop->values.count() > 1) + COMPILE_EXCEPTION("Incorrectly specified signal"); + + if(prop->values.at(0)->object) { + int pr = output->indexForByteArray(prop->name); + + bool rv = compileObject(prop->values.at(0)->object, 0); + + if(rv) { + QmlInstruction assign; + assign.type = QmlInstruction::AssignSignalObject; + assign.line = prop->values.at(0)->line; + assign.assignSignalObject.signal = pr; + + output->bytecode << assign; + + prop->values.at(0)->type = Value::SignalObject; + } + + return rv; + + } else { + QString script = prop->values.at(0)->primitive.trimmed(); + if(script.isEmpty()) + return true; + + if(isBinding(script)) + COMPILE_EXCEPTION("Cannot assign binding to signal property"); + + int idx = output->indexForString(script); + int pr = output->indexForByteArray(prop->name); + + QmlInstruction assign; + assign.type = QmlInstruction::AssignSignal; + assign.line = prop->values.at(0)->line; + assign.assignSignal.signal = pr; + assign.assignSignal.value = idx; + + output->bytecode << assign; + + prop->values.at(0)->type = Value::SignalExpression; + } + + return true; +} + +bool QmlCompiler::compileProperty(Property *prop, Object *obj, int ctxt) +{ + if(prop->values.isEmpty() && !prop->value) + return true; + + // First we're going to need a reference to this property + if(obj->type != -1) { + + const QMetaObject *mo = obj->metaObject(); + if(mo) { + if(prop->isDefault) { + QMetaProperty p = QmlMetaType::defaultProperty(mo); + // XXX + // Currently we don't handle enums in the static analysis + // so we let them drop through to generateStoreInstruction() + if(p.name() && !p.isEnumType()) { + prop->index = mo->indexOfProperty(p.name()); + prop->name = p.name(); + + int t = p.type(); + if(t == QVariant::UserType) + t = p.userType(); + + prop->type = t; + } + } else { + prop->index = mo->indexOfProperty(prop->name.constData()); + QMetaProperty p = mo->property(prop->index); + // XXX + // Currently we don't handle enums in the static analysis + // so we let them drop through to generateStoreInstruction() + if(p.name() && !p.isEnumType()) { + int t = p.type(); + if(t == QVariant::UserType) + t = p.userType(); + + prop->type = t; + } + } + } + } else { + const QMetaObject *mo = obj->metaObject(); + if(mo) { + if(prop->isDefault) { + QMetaProperty p = QmlMetaType::defaultProperty(mo); + if(p.name()) { + prop->index = mo->indexOfProperty(p.name()); + prop->name = p.name(); + } + int t = p.type(); + if(t == QVariant::UserType) + t = p.userType(); + prop->type = t; + } else { + prop->index = mo->indexOfProperty(prop->name.constData()); + QMetaProperty p = mo->property(prop->index); + int t = p.type(); + if(t == QVariant::UserType) + t = p.userType(); + prop->type = t; + } + } + } + + if(prop->name == "id") { + + COMPILE_CHECK(compileIdProperty(prop, obj)); + + } else if(isAttachedProperty(prop->name)) { + + COMPILE_CHECK(compileAttachedProperty(prop, obj, ctxt)); + + } else if(prop->value) { + + COMPILE_CHECK(compileNestedProperty(prop, ctxt)); + + } else if(QmlMetaType::isQmlList(prop->type) || + QmlMetaType::isList(prop->type)) { + + COMPILE_CHECK(compileListProperty(prop, obj, ctxt)); + + } else { + + COMPILE_CHECK(compilePropertyAssignment(prop, obj, ctxt)); + + } + + return true; +} + +bool QmlCompiler::compileIdProperty(QmlParser::Property *prop, + QmlParser::Object *obj) +{ + if(prop->value) + COMPILE_EXCEPTION("The 'id' property cannot be fetched"); + if(prop->values.count() > 1) + COMPILE_EXCEPTION("The 'id' property cannot be multiset"); + + if(prop->values.count() == 1) { + if(prop->values.at(0)->object) + COMPILE_EXCEPTION("Cannot assign an object as an id"); + QString val = prop->values.at(0)->primitive; + if(!isValidId(val)) + COMPILE_EXCEPTION(val << "is not a valid id"); + if(ids.contains(val)) + COMPILE_EXCEPTION("id is not unique"); + ids.insert(val); + + int pref = output->indexForString(val); + + if(prop->type == QVariant::String) { + QmlInstruction assign; + assign.type = QmlInstruction::StoreString; + assign.storeString.propertyIndex = prop->index; + assign.storeString.value = pref; + assign.line = prop->values.at(0)->line; + output->bytecode << assign; + + prop->values.at(0)->type = Value::Id; + } else { + prop->values.at(0)->type = Value::Literal; + } + + QmlInstruction id; + id.type = QmlInstruction::SetId; + id.line = prop->values.at(0)->line; + id.setId.value = pref; + id.setId.save = -1; + id.line = prop->values.at(0)->line; + output->bytecode << id; + + obj->id = val.toLatin1(); + } + + return true; +} + +bool QmlCompiler::compileAttachedProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + int ctxt) +{ + if(!prop->value) + COMPILE_EXCEPTION("Incorrect usage of an attached property"); + + QmlInstruction fetch; + fetch.type = QmlInstruction::FetchAttached; + fetch.line = prop->line; + int ref = output->indexForByteArray(prop->name); + fetch.fetchAttached.idx = ref; + output->bytecode << fetch; + + COMPILE_CHECK(compileFetchedObject(prop->value, ctxt + 1)); + + QmlInstruction pop; + pop.type = QmlInstruction::PopFetchedObject; + pop.line = prop->line; + output->bytecode << pop; + + return true; +} + +bool QmlCompiler::compileNestedProperty(QmlParser::Property *prop, + int ctxt) +{ + if(prop->type != 0) + prop->value->metatype = QmlMetaType::metaObjectForType(prop->type); + + QmlInstruction fetch; + if(prop->index != -1 && + QmlMetaType::isObject(prop->value->metatype)) { + fetch.type = QmlInstruction::FetchObject; + fetch.fetch.property = prop->index; + fetch.fetch.isObject = true; + } else { + fetch.type = QmlInstruction::ResolveFetchObject; + fetch.fetch.property = output->indexForByteArray(prop->name); + } + fetch.line = prop->line; + output->bytecode << fetch; + + COMPILE_CHECK(compileFetchedObject(prop->value, ctxt + 1)); + + QmlInstruction pop; + pop.type = QmlInstruction::PopFetchedObject; + pop.line = prop->line; + output->bytecode << pop; + + return true; +} + +bool QmlCompiler::compileListProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + int ctxt) +{ + int t = prop->type; + if(QmlMetaType::isQmlList(t)) { + QmlInstruction fetch; + fetch.line = prop->line; + fetch.type = QmlInstruction::FetchQmlList; + fetch.fetchQmlList.property = prop->index; + fetch.fetchQmlList.type = QmlMetaType::qmlListType(t); + output->bytecode << fetch; + + for(int ii = 0; ii < prop->values.count(); ++ii) { + Value *v = prop->values.at(ii); + if(v->object) { + v->type = Value::CreatedObject; + COMPILE_CHECK(compileObject(v->object, ctxt)); + QmlInstruction assign; + assign.type = QmlInstruction::AssignObjectList; + assign.line = prop->line; + assign.assignObject.property = output->indexForByteArray(prop->name); + assign.assignObject.castValue = 0; + output->bytecode << assign; + } else { + COMPILE_EXCEPTION("Cannot assign primitives to lists"); + } + } + + QmlInstruction pop; + pop.type = QmlInstruction::PopQList; + pop.line = prop->line; + output->bytecode << pop; + } else { + Q_ASSERT(QmlMetaType::isList(t)); + + QmlInstruction fetch; + fetch.type = QmlInstruction::FetchQList; + fetch.line = prop->line; + fetch.fetch.property = prop->index; + output->bytecode << fetch; + + bool assignedBinding = false; + for(int ii = 0; ii < prop->values.count(); ++ii) { + Value *v = prop->values.at(ii); + if(v->object) { + v->type = Value::CreatedObject; + COMPILE_CHECK(compileObject(v->object, ctxt)); + QmlInstruction assign; + assign.type = QmlInstruction::AssignObjectList; + assign.line = v->line; + assign.assignObject.property = output->indexForByteArray(prop->name); + assign.assignObject.castValue = 0; + output->bytecode << assign; + } else if(isBinding(v->primitive)) { + if(assignedBinding) + COMPILE_EXCEPTION("Can only assign one binding to lists"); + + compileBinding(v->primitive, prop, ctxt, obj->metaObject(), v->line); + v->type = Value::PropertyBinding; + } else { + COMPILE_EXCEPTION("Cannot assign primitives to lists"); + } + } + + QmlInstruction pop; + pop.line = prop->line; + pop.type = QmlInstruction::PopQList; + output->bytecode << pop; + } + return true; +} + +bool QmlCompiler::compilePropertyAssignment(QmlParser::Property *prop, + QmlParser::Object *obj, + int ctxt) +{ + for(int ii = 0; ii < prop->values.count(); ++ii) { + Value *v = prop->values.at(ii); + if(v->object) { + + COMPILE_CHECK(compilePropertyObjectAssignment(prop, obj, v, ctxt)); + + } else { + + COMPILE_CHECK(compilePropertyLiteralAssignment(prop, obj, v, ctxt)); + + } + } + + return true; +} + +bool QmlCompiler::compilePropertyObjectAssignment(QmlParser::Property *prop, + QmlParser::Object *obj, + QmlParser::Value *v, + int ctxt) +{ + if(v->object->type != -1) + v->object->metatype = QmlMetaType::metaObjectForType(output->types.at(v->object->type).className); + + if(v->object->metaObject()) { + + const QMetaObject *propmo = + QmlMetaType::rawMetaObjectForType(prop->type); + + bool isPropertyValue = false; + bool isAssignable = false; + + if(propmo) { + // We want to raw metaObject here as the raw metaobject is the + // actual property type before we applied any extensions + const QMetaObject *c = v->object->metatype; + while(propmo && c) { + isPropertyValue = isPropertyValue || (c == &QmlPropertyValueSource::staticMetaObject); + isAssignable = isAssignable || (c == propmo); + c = c->superClass(); + } + } else { + const QMetaObject *c = v->object->metatype; + while(!isPropertyValue && c) { + isPropertyValue = c == &QmlPropertyValueSource::staticMetaObject; + c = c->superClass(); + } + } + + if(!propmo && !isPropertyValue) { + COMPILE_CHECK(compileObject(v->object, ctxt)); + + QmlInstruction assign; + assign.type = QmlInstruction::AssignObject; + assign.line = v->object->line; + assign.assignObject.castValue = 0; + if(prop->isDefault) + assign.assignObject.property = -1; + else + assign.assignObject.property = + output->indexForByteArray(prop->name); + output->bytecode << assign; + + v->type = Value::CreatedObject; + } else if(isAssignable) { + COMPILE_CHECK(compileObject(v->object, ctxt)); + + QmlInstruction assign; + assign.type = QmlInstruction::StoreObject; + assign.line = v->object->line; + assign.storeObject.propertyIndex = prop->index; + // XXX - this cast may not be 0 + assign.storeObject.cast = 0; + output->bytecode << assign; + + v->type = Value::CreatedObject; + } else if(propmo == &QmlComponent::staticMetaObject) { + + COMPILE_CHECK(compileComponentFromRoot(v->object, ctxt)); + + QmlInstruction assign; + assign.type = QmlInstruction::StoreObject; + assign.line = v->object->line; + assign.storeObject.propertyIndex = prop->index; + // XXX - this cast may not be 0 + assign.storeObject.cast = 0; + output->bytecode << assign; + + v->type = Value::Component; + } else if(isPropertyValue) { + COMPILE_CHECK(compileObject(v->object, ctxt)); + + if (prop->index != -1) { + QmlInstruction assign; + assign.type = QmlInstruction::StoreValueSource; + assign.line = v->object->line; + assign.assignValueSource.property = prop->index; + output->bytecode << assign; + } else { + QmlInstruction assign; + assign.type = QmlInstruction::AssignValueSource; + assign.line = v->object->line; + assign.assignValueSource.property = output->indexForByteArray(prop->name);; + output->bytecode << assign; + } + + v->type = Value::ValueSource; + } else { + COMPILE_EXCEPTION("Unassignable object"); + } + + } else { + COMPILE_CHECK(compileObject(v->object, ctxt)); + + QmlInstruction assign; + assign.type = QmlInstruction::AssignObject; + assign.line = v->object->line; + assign.assignObject.property = output->indexForByteArray(prop->name); + assign.assignObject.castValue = 0; + output->bytecode << assign; + + v->type = Value::CreatedObject; + } + + return true; +} + +bool QmlCompiler::compilePropertyLiteralAssignment(QmlParser::Property *prop, + QmlParser::Object *obj, + QmlParser::Value *v, + int ctxt) +{ + if(isBinding(v->primitive)) { + + compileBinding(v->primitive, prop, ctxt, obj->metaObject(), v->line); + + v->type = Value::PropertyBinding; + + } else { + + QmlInstruction assign; + assign.line = v->line; + + bool doassign = true; + if(prop->index != -1) { + StoreInstructionResult r = + generateStoreInstruction(*output, assign, obj->metaObject()->property(prop->index), prop->index, -1, &v->primitive); + + if(r == Ok) { + doassign = false; + } else if(r == InvalidData) { + //### we are restricted to a rather generic message here. If we can find a way to move + // the exception into generateStoreInstruction we could potentially have better messages. + // (the problem is that both compile and run exceptions can be generated, though) + COMPILE_EXCEPTION("Cannot assign value" << v->primitive << "to property" << obj->metaObject()->property(prop->index).name()); + doassign = false; + } else if(r == ReadOnly) { + COMPILE_EXCEPTION("Cannot assign value" << v->primitive << "to the read-only property" << obj->metaObject()->property(prop->index).name()); + } else { + doassign = true; + } + } + + if(doassign) { + assign.type = QmlInstruction::AssignConstant; + if(prop->isDefault) { + assign.assignConstant.property = -1; + } else { + assign.assignConstant.property = + output->indexForByteArray(prop->name); + } + assign.assignConstant.constant = + output->indexForString(v->primitive); + } + + output->bytecode << assign; + + v->type = Value::Literal; + } + return true; +} + +bool QmlCompiler::findDynamicProperties(QmlParser::Property *prop, + QmlParser::Object *obj) +{ + QList<Object::DynamicProperty> definedProperties; + + struct TypeNameToType { + const char *name; + Object::DynamicProperty::Type type; + } propTypeNameToTypes[] = { + { "", Object::DynamicProperty::Variant }, + { "int", Object::DynamicProperty::Int }, + { "bool", Object::DynamicProperty::Bool }, + { "double", Object::DynamicProperty::Real }, + { "real", Object::DynamicProperty::Real }, + { "string", Object::DynamicProperty::String }, + { "color", Object::DynamicProperty::Color }, + { "date", Object::DynamicProperty::Date }, + { "variant", Object::DynamicProperty::Variant } + }; + const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / + sizeof(propTypeNameToTypes[0]); + + + if(prop->value) + COMPILE_EXCEPTION("Invalid property specification"); + + bool seenDefault = false; + for(int ii = 0; ii < prop->values.count(); ++ii) { + QmlParser::Value *val = prop->values.at(ii); + if(!val->object) + COMPILE_EXCEPTION("Invalid property specification"); + + QmlParser::Object *obj = val->object; + if(obj->type == -1 || output->types.at(obj->type).className != "Property") + COMPILE_EXCEPTION("Use Property tag to specify properties"); + + + enum Seen { None = 0, Name = 0x01, + Type = 0x02, Value = 0x04, + ValueChanged = 0x08, + Default = 0x10 } seen = None; + + Object::DynamicProperty propDef; + + for(QHash<QByteArray, QmlParser::Property *>::ConstIterator iter = + obj->properties.begin(); + iter != obj->properties.end(); + ++iter) { + + QmlParser::Property *property = *iter; + if(property->name == "name") { + if (seen & Name) + COMPILE_EXCEPTION("May only specify Property name once"); + seen = (Seen)(seen | Name); + + if(property->value || property->values.count() != 1 || + property->values.at(0)->object) + COMPILE_EXCEPTION("Invalid Property name"); + + propDef.name = property->values.at(0)->primitive.toLatin1(); + + } else if(property->name == "type") { + if (seen & Type) + COMPILE_EXCEPTION("May only specify Property type once"); + seen = (Seen)(seen | Type); + + if(property->value || property->values.count() != 1 || + property->values.at(0)->object) + COMPILE_EXCEPTION("Invalid Property type"); + + QString type = property->values.at(0)->primitive.toLower(); + bool found = false; + for(int ii = 0; !found && ii < propTypeNameToTypesCount; ++ii) { + if(type == QLatin1String(propTypeNameToTypes[ii].name)){ + found = true; + propDef.type = propTypeNameToTypes[ii].type; + } + + } + + if(!found) + COMPILE_EXCEPTION("Invalid Property type"); + + } else if(property->name == "value") { + if (seen & Value) + COMPILE_EXCEPTION("May only specify Property value once"); + seen = (Seen)(seen | Value); + + propDef.defaultValue = property; + } else if(property->name == "onValueChanged") { + if (seen & ValueChanged) + COMPILE_EXCEPTION("May only specify Property onValueChanged once"); + seen = (Seen)(seen | ValueChanged); + + if(property->value || property->values.count() != 1 || + property->values.at(0)->object) + COMPILE_EXCEPTION("Invalid Property onValueChanged"); + + propDef.onValueChanged = property->values.at(0)->primitive; + + } else if(property->name == "default") { + if(seen & Default) + COMPILE_EXCEPTION("May only specify Property default once"); + seen = (Seen)(seen | Default); + if(property->value || property->values.count() != 1 || + property->values.at(0)->object) + COMPILE_EXCEPTION("Invalid Property default"); + + bool defaultValue = + QmlStringConverters::boolFromString(property->values.at(0)->primitive); + propDef.isDefaultProperty = defaultValue; + if(defaultValue) { + if(seenDefault) + COMPILE_EXCEPTION("Only one property may be the default"); + seenDefault = true; + } + + } else { + COMPILE_EXCEPTION("Invalid Property property"); + } + + } + if(obj->defaultProperty) { + if(seen & Value) + COMPILE_EXCEPTION("May only specify Property value once"); + + seen = (Seen)(seen | Value); + propDef.defaultValue = obj->defaultProperty; + } + + if(!(seen & Name)) + COMPILE_EXCEPTION("Must specify Property name"); + + definedProperties << propDef; + } + + obj->dynamicProperties = definedProperties; + return true; +} + +bool QmlCompiler::findDynamicSignals(QmlParser::Property *sigs, + QmlParser::Object *obj) +{ + QList<Object::DynamicSignal> definedSignals; + + if(sigs->value) + COMPILE_EXCEPTION("Invalid signal specification"); + + for(int ii = 0; ii < sigs->values.count(); ++ii) { + QmlParser::Value *val = sigs->values.at(ii); + if(!val->object) + COMPILE_EXCEPTION("Invalid signal specification"); + + QmlParser::Object *obj = val->object; + if(obj->type == -1 || output->types.at(obj->type).className != "Signal") + COMPILE_EXCEPTION("Use Signal tag to specify signals"); + + enum Seen { None = 0, Name = 0x01 } seen = None; + Object::DynamicSignal sigDef; + + for(QHash<QByteArray, QmlParser::Property *>::ConstIterator iter = + obj->properties.begin(); + iter != obj->properties.end(); + ++iter) { + + QmlParser::Property *property = *iter; + if(property->name == "name") { + if (seen & Name) + COMPILE_EXCEPTION("May only specify Signal name once"); + seen = (Seen)(seen | Name); + + if(property->value || property->values.count() != 1 || + property->values.at(0)->object) + COMPILE_EXCEPTION("Invalid Signal name"); + + sigDef.name = property->values.at(0)->primitive.toLatin1(); + + } else { + COMPILE_EXCEPTION("Invalid Signal property"); + } + + } + + if(obj->defaultProperty) + COMPILE_EXCEPTION("Invalid Signal property"); + + if(!(seen & Name)) + COMPILE_EXCEPTION("Must specify Signal name"); + + definedSignals << sigDef; + } + + obj->dynamicSignals = definedSignals; + return true; +} + +bool QmlCompiler::compileDynamicPropertiesAndSignals(QmlParser::Object *obj) +{ + if(obj->dynamicPropertiesProperty) + findDynamicProperties(obj->dynamicPropertiesProperty, obj); + if(obj->dynamicSignalsProperty) + findDynamicSignals(obj->dynamicSignalsProperty, obj); + + if(obj->dynamicProperties.isEmpty() && obj->dynamicSignals.isEmpty()) + return true; + + QMetaObjectBuilder builder; + if(obj->metatype) + builder.setClassName(QByteArray(obj->metatype->className()) + "QML"); + + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + for(int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); + + if(p.isDefaultProperty) + builder.addClassInfo("DefaultProperty", p.name); + + QByteArray type; + switch(p.type) { + case Object::DynamicProperty::Variant: + type = "QVariant"; + break; + case Object::DynamicProperty::Int: + type = "int"; + break; + case Object::DynamicProperty::Bool: + type = "bool"; + break; + case Object::DynamicProperty::Real: + type = "double"; + break; + case Object::DynamicProperty::String: + type = "QString"; + break; + case Object::DynamicProperty::Color: + type = "QColor"; + break; + case Object::DynamicProperty::Date: + type = "QDate"; + break; + } + + builder.addSignal("qml__" + QByteArray::number(ii) + "()"); + builder.addProperty(p.name, type, ii); + } + + for(int ii = 0; ii < obj->dynamicSignals.count(); ++ii) { + const Object::DynamicSignal &s = obj->dynamicSignals.at(ii); + builder.addSignal(s.name + "()"); + } + + if(obj->metatype) + builder.setSuperClass(obj->metatype); + + obj->extObject = builder.toMetaObject(); + + output->mos << obj->extObject; + QmlInstruction store; + store.type = QmlInstruction::StoreMetaObject; + store.storeMeta.data = output->mos.count() - 1; + store.line = obj->line; + output->bytecode << store; + + for(int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); + + if(p.defaultValue) { + p.defaultValue->name = p.name; + p.defaultValue->isDefault = false; + COMPILE_CHECK(compileProperty(p.defaultValue, obj, 0)); + } + + if(!p.onValueChanged.isEmpty()) { + QmlInstruction assign; + assign.type = QmlInstruction::AssignSignal; + assign.line = obj->line; + assign.assignSignal.signal = + output->indexForByteArray("onQml__" + QByteArray::number(ii)); + assign.assignSignal.value = + output->indexForString(p.onValueChanged); + output->bytecode << assign; + } + } + + return true; +} + +void QmlCompiler::compileBinding(const QString &str, QmlParser::Property *prop, + int ctxt, const QMetaObject *mo, qint64 line) +{ + Q_ASSERT(isBinding(str)); + + QString bind = str.mid(1, str.length() - 2).trimmed(); + QmlBasicScript bs; + bs.compile(bind.toLatin1()); + + int bref; + if(bs.isValid()) { + bref = output->indexForByteArray(QByteArray(bs.compileData(), bs.compileDataSize())); + } else { + bref = output->indexForString(bind); + } + + QmlInstruction assign; + assign.assignBinding.context = ctxt; + assign.line = line; + if(prop->index != -1) { + if(bs.isValid()) + assign.type = QmlInstruction::StoreCompiledBinding; + else + assign.type = QmlInstruction::StoreBinding; + + assign.assignBinding.property = prop->index; + assign.assignBinding.value = bref; + assign.assignBinding.category = QmlMetaProperty::Unknown; + if(mo) { + //XXX we should generate an exception if the property is read-only + QMetaProperty mp = mo->property(assign.assignBinding.property); + assign.assignBinding.category = QmlMetaProperty::propertyCategory(mp); + } + } else { + if(bs.isValid()) + assign.type = QmlInstruction::AssignCompiledBinding; + else + assign.type = QmlInstruction::AssignBinding; + assign.assignBinding.property = output->indexForByteArray(prop->name); + assign.assignBinding.value = bref; + assign.assignBinding.category = QmlMetaProperty::Unknown; + } + output->bytecode << assign; +} + +int QmlCompiler::optimizeExpressions(int start, int end, int patch) +{ + QHash<QString, int> ids; + int saveCount = 0; + int newInstrs = 0; + + for(int ii = start; ii <= end; ++ii) { + const QmlInstruction &instr = output->bytecode.at(ii); + + if(instr.type == QmlInstruction::CreateComponent) { + ii += instr.createComponent.count - 1; + continue; + } + + if(instr.type == QmlInstruction::SetId) { + QString id = output->primitives.at(instr.setId.value); + ids.insert(id, ii); + } + } + + for(int ii = start; ii <= end; ++ii) { + const QmlInstruction &instr = output->bytecode.at(ii); + + if(instr.type == QmlInstruction::CreateComponent) { + ii += instr.createComponent.count - 1; + continue; + } + + if(instr.type == QmlInstruction::StoreCompiledBinding) { + QmlBasicScript s(output->datas.at(instr.assignBinding.value).constData()); + + if(s.isSingleLoad()) { + QString slt = QLatin1String(s.singleLoadTarget()); + if(!slt.at(0).isUpper()) + continue; + + if(ids.contains(slt) && + instr.assignBinding.category == QmlMetaProperty::Object) { + int id = ids[slt]; + int saveId = -1; + + if(output->bytecode.at(id).setId.save != -1) { + saveId = output->bytecode.at(id).setId.save; + } else { + output->bytecode[id].setId.save = saveCount; + saveId = saveCount; + ++saveCount; + } + + int prop = instr.assignBinding.property; + + QmlInstruction &rwinstr = output->bytecode[ii]; + rwinstr.type = QmlInstruction::PushProperty; + rwinstr.pushProperty.property = prop; + + QmlInstruction instr; + instr.type = QmlInstruction::AssignStackObject; + instr.line = 0; + instr.assignStackObject.property = newInstrs; + instr.assignStackObject.object = saveId; + output->bytecode << instr; + ++newInstrs; + } + } + } + + } + + if(saveCount) + output->bytecode[patch].init.dataSize = saveCount; + + return newInstrs; +} + +QmlCompiledData::QmlCompiledData() +{ +} + +QmlCompiledData::QmlCompiledData(const QmlCompiledData &other) +{ + *this = other; +} + +QmlCompiledData::~QmlCompiledData() +{ + for(int ii = 0; ii < types.count(); ++ii) { + if(types.at(ii).ref) + types.at(ii).ref->release(); + } +} + +QmlCompiledData &QmlCompiledData::operator=(const QmlCompiledData &other) +{ + types = other.types; + primitives = other.primitives; + floatData = other.floatData; + intData = other.intData; + customTypeData = other.customTypeData; + datas = other.datas; + mos = other.mos; + bytecode = other.bytecode; + return *this; +} + +QObject *QmlCompiledData::TypeReference::createInstance() const +{ + if(type) { + return type->create(); + } else if(component) { + return component->create(QmlContext::activeContext()); + } else { + return 0; + } +} + + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h new file mode 100644 index 0000000..732d9ea --- /dev/null +++ b/src/declarative/qml/qmlcompiler_p.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLCOMPILER_P_H +#define QMLCOMPILER_P_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qset.h> +#include <qml.h> +#include <private/qmlinstruction_p.h> +#include <private/qmlcompositetypemanager_p.h> +class QStringList; + +QT_BEGIN_NAMESPACE +class QmlXmlParser; +class QmlEngine; +class QmlComponent; +class QmlCompiledComponent; + +namespace QmlParser { + class Object; + class Property; + class Value; +}; + +class QmlCompiledData +{ +public: + QmlCompiledData(); + QmlCompiledData(const QmlCompiledData &other); + QmlCompiledData &operator=(const QmlCompiledData &other); + virtual ~QmlCompiledData(); + + QByteArray name; + QUrl url; + + struct TypeReference + { + TypeReference() + : type(0), component(0), parser(0), ref(0) {} + + QByteArray className; + QmlType *type; + QmlComponent *component; + QmlCustomParser *parser; + + QmlRefCount *ref; + QObject *createInstance() const; + }; + QList<TypeReference> types; + struct CustomTypeData + { + int index; + int type; + }; + QList<QString> primitives; + QList<float> floatData; + QList<int> intData; + QList<CustomTypeData> customTypeData; + QList<QByteArray> datas; + QList<QMetaObject *> mos; + QList<QmlInstruction> bytecode; + +private: + friend class QmlCompiler; + int indexForString(const QString &); + int indexForByteArray(const QByteArray &); + int indexForFloat(float *, int); + int indexForInt(int *, int); +}; + +class Q_DECLARATIVE_EXPORT QmlCompiler +{ +public: + QmlCompiler(); + + bool compile(QmlEngine *, QmlCompositeTypeManager::TypeData *, QmlCompiledComponent *); + + bool isError() const; + qint64 errorLine() const; + QString errorDescription() const; + + static bool isValidId(const QString &); + static bool isBinding(const QString &); + static bool isAttachedProperty(const QByteArray &); + + enum StoreInstructionResult { Ok, UnknownType, InvalidData, ReadOnly }; + static StoreInstructionResult + generateStoreInstruction(QmlCompiledData &data, + QmlInstruction &instr, + const QMetaProperty &prop, + int index, + int primitive, + const QString *string); +private: + void reset(QmlCompiledComponent *, bool); + + void compileTree(QmlParser::Object *tree); + bool compileObject(QmlParser::Object *obj, int); + bool compileComponent(QmlParser::Object *obj, int); + bool compileComponentFromRoot(QmlParser::Object *obj, int); + bool compileFetchedObject(QmlParser::Object *obj, int); + bool compileSignal(QmlParser::Property *prop, QmlParser::Object *obj); + bool compileProperty(QmlParser::Property *prop, QmlParser::Object *obj, int); + bool compileIdProperty(QmlParser::Property *prop, + QmlParser::Object *obj); + bool compileAttachedProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + int ctxt); + bool compileNestedProperty(QmlParser::Property *prop, + int ctxt); + bool compileListProperty(QmlParser::Property *prop, + QmlParser::Object *obj, + int ctxt); + bool compilePropertyAssignment(QmlParser::Property *prop, + QmlParser::Object *obj, + int ctxt); + bool compilePropertyObjectAssignment(QmlParser::Property *prop, + QmlParser::Object *obj, + QmlParser::Value *value, + int ctxt); + bool compilePropertyLiteralAssignment(QmlParser::Property *prop, + QmlParser::Object *obj, + QmlParser::Value *value, + int ctxt); + + bool findDynamicProperties(QmlParser::Property *prop, + QmlParser::Object *obj); + bool findDynamicSignals(QmlParser::Property *sigs, + QmlParser::Object *obj); + + bool compileDynamicPropertiesAndSignals(QmlParser::Object *obj); + void compileBinding(const QString &, QmlParser::Property *prop, + int ctxt, const QMetaObject *, qint64); + + int optimizeExpressions(int start, int end, int patch = -1); + + QSet<QString> ids; + qint64 exceptionLine; + QString exceptionDescription; + QmlCompiledData *output; +}; + +QT_END_NAMESPACE +#endif // QMLCOMPILER_P_H diff --git a/src/declarative/qml/qmlcomponent.cpp b/src/declarative/qml/qmlcomponent.cpp new file mode 100644 index 0000000..c863a00 --- /dev/null +++ b/src/declarative/qml/qmlcomponent.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlcomponent.h" +#include "qmlcomponent_p.h" +#include "private/qmlcontext_p.h" +#include "private/qmlengine_p.h" +#include "qmlvme_p.h" +#include "qml.h" +#include <QStack> +#include <qfxperf.h> +#include <QStringList> +#include <qmlengine.h> +#include <QFileInfo> +#include <qmlbindablevalue.h> +#include "private/qmlxmlparser_p.h" +#include "qmlcompiledcomponent_p.h" +#include <QtCore/qdebug.h> +#include <QApplication> + + +QT_BEGIN_NAMESPACE +class QByteArray; + +/*! + \class QmlComponent + \brief The QmlComponent class encapsulates a QML component description. + \mainclass +*/ +QML_DEFINE_TYPE(QmlComponent,Component); + +void QmlComponentPrivate::typeDataReady() +{ + Q_Q(QmlComponent); + + Q_ASSERT(typeData); + + fromTypeData(typeData); + typeData = 0; + + emit q->readyChanged(); +} + +void QmlComponentPrivate::fromTypeData(QmlCompositeTypeManager::TypeData *data) +{ + name = data->url; + QmlCompiledComponent *c = data->toCompiledComponent(engine); + + if(!c) { + Q_ASSERT(data->status == QmlCompositeTypeManager::TypeData::Error); + + qWarning().nospace() << "QmlComponent: " + << data->errorDescription.toLatin1().constData(); + } else { + + cc = c; + + } + + data->release(); +} + +/*! + Construct a null QmlComponent. +*/ +QmlComponent::QmlComponent(QObject *parent) + : QObject(*(new QmlComponentPrivate), parent) +{ + Q_D(QmlComponent); + d->name = QLatin1String("<unspecified file>"); +} + +/*! + Destruct the QmlComponent. +*/ +QmlComponent::~QmlComponent() +{ + Q_D(QmlComponent); + if (d->typeData) { + d->typeData->remWaiter(d); + d->typeData->release(); + } + if (d->cc) + d->cc->release(); +} + +/*! + \property QmlComponent::name + \brief the component's name. + + The component's name is used in error and warning messages. If available, + the XML source file name is used as the component's name, otherwise it is + set to "<unspecified file>". +*/ +QString QmlComponent::name() const +{ + Q_D(const QmlComponent); + return d->name; +} + +void QmlComponent::setName(const QString &name) +{ + Q_D(QmlComponent); + d->name = name; +} + +/*! + \internal +*/ +QmlComponent::QmlComponent(QmlEngine *engine, QmlCompiledComponent *cc, int start, int count, QObject *parent) + : QObject(*(new QmlComponentPrivate), parent) +{ + Q_D(QmlComponent); + d->engine = engine; + d->name = QLatin1String("<unspecified file>"); + d->cc = cc; + cc->addref(); + d->start = start; + d->count = count; +} + +QmlComponent::QmlComponent(QmlEngine *engine, const QUrl &url, QObject *parent) +: QObject(*(new QmlComponentPrivate), parent) +{ + Q_D(QmlComponent); + d->engine = engine; + d->name = url.toString(); + + QmlCompositeTypeManager::TypeData *data = + engine->d_func()->typeManager.get(url); + + if(data->status == QmlCompositeTypeManager::TypeData::Waiting) { + + d->typeData = data; + d->typeData->addWaiter(d); + + } else { + + d->fromTypeData(data); + + } +} + +/*! + Create a QmlComponent with no data. Set setData(). +*/ +QmlComponent::QmlComponent(QmlEngine *engine, QObject *parent) + : QObject(*(new QmlComponentPrivate), parent) +{ + Q_D(QmlComponent); + d->engine = engine; +} + +/*! + Create a QmlComponent from the given XML \a data. If provided, \a filename + is used to set the component name, and to provide a base path for items + resolved by this component. +*/ +QmlComponent::QmlComponent(QmlEngine *engine, const QByteArray &data, const QUrl &url, QObject *parent) + : QObject(*(new QmlComponentPrivate), parent) +{ + Q_D(QmlComponent); + d->engine = engine; + setData(data,url); +} + +/*! + Sets the QmlComponent to use the given XML \a data. If provided, \a filename + is used to set the component name, and to provide a base path for items + resolved by this component. + + Currently only supported to call once per component. +*/ +void QmlComponent::setData(const QByteArray &data, const QUrl &url) +{ + Q_D(QmlComponent); + + if(d->cc) d->cc->release(); + if(d->typeData) d->typeData->release(); + d->cc = 0; + d->typeData = 0; + + d->name = url.toString(); + + QmlCompositeTypeManager::TypeData *typeData = + d->engine->d_func()->typeManager.getImmediate(data, url); + + if(typeData->status == QmlCompositeTypeManager::TypeData::Waiting) { + + d->typeData = typeData; + d->typeData->addWaiter(d); + + } else { + + d->fromTypeData(typeData); + + } + +} + +bool QmlComponent::isReady() const +{ + Q_D(const QmlComponent); + + return d->engine && !d->typeData; +} + +/*! + \internal +*/ +QmlComponent::QmlComponent(QmlComponentPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ + Q_D(QmlComponent); + d->name = QLatin1String("<unspecified file>"); + d->cc = new QmlCompiledComponent; +} + +/*! + Create an object instance from this component. Returns 0 if creation + failed. \a context specifies the context within which to create the object + instance. + + If \a context is 0 (the default), it will create the instance in the + engine' s \l {QmlEngine::rootContext()}{root context}. +*/ +QObject *QmlComponent::create(QmlContext *context) +{ + Q_D(QmlComponent); + + if(!context) + context = d->engine->rootContext(); + + if(context->engine() != d->engine) { + qWarning("QmlComponent::create(): Must create component in context from the same QmlEngine"); + return 0; + } + + QObject *rv = beginCreate(context); + completeCreate(); + return rv; +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QmlComponent::create() to create a + component. + + Create an object instance from this component. Returns 0 if creation + failed. \a context specifies the context within which to create the object + instance. + + When QmlComponent constructs an instance, it occurs in three steps: + \list 1 + \i The object hierarchy is created, and constant values are assigned. + \i Property bindings are evaluated for the the first time. + \i If applicable, QmlParserStatus::componentComplete() is called on objects. + \endlist + QmlComponent::beginCreate() differs from QmlComponent::create() in that it + only performs step 1. QmlComponent::completeCreate() must be called to + complete steps 2 and 3. + + This breaking point is sometimes useful when using attached properties to + communicate information to an instantiated component, as it allows their + initial values to be configured before property bindings take effect. +*/ +QObject *QmlComponent::beginCreate(QmlContext *context) +{ + Q_D(QmlComponent); + + if(!context) { + qWarning("QmlComponent::beginCreate(): Cannot create a component in a null context"); + return 0; + } + + if(context->engine() != d->engine) { + qWarning("QmlComponent::beginCreate(): Must create component in context from the same QmlEngine"); + return 0; + } + + if (d->completePending) { + qWarning("QmlComponent: Cannot create new component instance before completing the previous"); + return 0; + } + if (!d->cc) { + qWarning("QmlComponent: Cannot load component data"); + return 0; + } + +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::CreateComponent> perf; +#endif + if(!d->engine->d_func()->rootComponent) + d->engine->d_func()->rootComponent = this; + + QmlContext *ctxt = + new QmlContext(context, 0); + static_cast<QmlContextPrivate*>(ctxt->d_ptr)->component = d->cc; + static_cast<QmlContextPrivate*>(ctxt->d_ptr)->component->addref(); + ctxt->activate(); + + QmlVME vme; + QObject *rv = vme.run(ctxt, d->cc, d->start, d->count); + if(vme.isError()) { + qWarning().nospace() +#ifdef QML_VERBOSEERRORS_ENABLED + << "QmlComponent: " +#endif + << vme.errorDescription().toLatin1().constData() << " @" + << d->name.toLatin1().constData() << ":" << vme.errorLine(); + } + + + ctxt->deactivate(); + + if(rv) { + QFx_setParent_noEvent(ctxt, rv); + QmlEnginePrivate *ep = d->engine->d_func(); + if(ep->rootComponent == this) { + ep->rootComponent = 0; + + d->bindValues = ep->currentBindValues; + d->parserStatus = ep->currentParserStatus; + ep->currentBindValues.clear(); + ep->currentParserStatus.clear(); + d->completePending = true; + } + } else { + delete ctxt; + } + + return rv; +} + +/*! + This method provides more advanced control over component instance creation. + In general, programmers should use QmlComponent::create() to create a + component. + + Complete a component creation begin with QmlComponent::beginCreate(). +*/ +void QmlComponent::completeCreate() +{ + Q_D(QmlComponent); + if (d->completePending) { + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BindInit> bi; +#endif + for(int ii = 0; ii < d->bindValues.count(); ++ii) + d->bindValues.at(ii)->init(); + } + QSet<QmlParserStatus *> done; + for(int ii = 0; ii < d->parserStatus.count(); ++ii) { + QmlParserStatus *ps = d->parserStatus.at(ii); + if(!done.contains(ps)) { + done.insert(ps); + ps->componentComplete(); + } + } + + d->bindValues.clear(); + d->parserStatus.clear(); + d->completePending = false; + } +} +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcomponent.h b/src/declarative/qml/qmlcomponent.h new file mode 100644 index 0000000..1a74fe9 --- /dev/null +++ b/src/declarative/qml/qmlcomponent.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLCOMPONENT_H +#define QMLCOMPONENT_H + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtDeclarative/qfxglobal.h> +#include <QtDeclarative/qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlCompiledComponent; +class QByteArray; +class QmlComponentPrivate; +class QmlEngine; +class Q_DECLARATIVE_EXPORT QmlComponent : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlComponent); + + Q_PROPERTY(QString name READ name WRITE setName); + +public: + QmlComponent(QObject *parent=0); + QmlComponent(QmlEngine *, QObject *parent=0); + QmlComponent(QmlEngine *, const QUrl &url, QObject *parent = 0); + QmlComponent(QmlEngine *, const QByteArray &, const QUrl &url=QUrl(), QObject *parent=0); + ~QmlComponent(); + + virtual QObject *create(QmlContext *context = 0); + virtual QObject *beginCreate(QmlContext *); + virtual void completeCreate(); + + QString name() const; + void setName(const QString &name); + + void setData(const QByteArray &, const QUrl &url); + + bool isReady() const; + + QmlComponent(QmlEngine *, QmlCompiledComponent *, int, int, QObject *parent); + +Q_SIGNALS: + void readyChanged(); + +protected: + QmlComponent(QmlComponentPrivate &dd, QObject* parent); + +private: + friend class QmlVME; +}; +QML_DECLARE_TYPE(QmlComponent); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLCOMPONENT_H diff --git a/src/declarative/qml/qmlcomponent_p.h b/src/declarative/qml/qmlcomponent_p.h new file mode 100644 index 0000000..e97ec67 --- /dev/null +++ b/src/declarative/qml/qmlcomponent_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 QtDeclarative 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 QMLCOMPONENT_P_H +#define QMLCOMPONENT_P_H + +#include <QString> +#include <QStringList> +#include <QList> +#include "private/qobject_p.h" +#include "private/qmlcompositetypemanager_p.h" +#include "qmlcomponent.h" +class QmlComponent; +class QmlEngine; +class QmlCompiledComponent; +#include "qml.h" + + +QT_BEGIN_NAMESPACE + +class QmlComponentPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlComponent) + +public: + QmlComponentPrivate() : typeData(0), start(-1), count(-1), cc(0), completePending(false), engine(0) {} + + QmlCompositeTypeManager::TypeData *typeData; + void typeDataReady(); + + void fromTypeData(QmlCompositeTypeManager::TypeData *data); + + QString name; + + int start; + int count; + QmlCompiledComponent *cc; + QList<QmlBindableValue *> bindValues; + QList<QmlParserStatus *> parserStatus; + bool completePending; + + QmlEngine *engine; +}; + +#endif // QMLCOMPONENT_P_H + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcompositetypemanager.cpp b/src/declarative/qml/qmlcompositetypemanager.cpp new file mode 100644 index 0000000..65596ae --- /dev/null +++ b/src/declarative/qml/qmlcompositetypemanager.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <private/qmlcompositetypemanager_p.h> +#include <private/qmlxmlparser_p.h> +#include <private/qmlcompiledcomponent_p.h> +#include <QtDeclarative/qmlengine.h> +#include <QtNetwork/qnetworkreply.h> +#include <private/qmlengine_p.h> +#include <QtCore/qdebug.h> +#include <QtCore/qfile.h> +#include <QtDeclarative/qmlcomponent.h> +#include <private/qmlcomponent_p.h> + +QmlCompositeTypeManager::TypeData::TypeData() +: status(Invalid), component(0), compiledComponent(0) +{ +} + +QmlCompositeTypeManager::TypeData::~TypeData() +{ + for(int ii = 0; ii < dependants.count(); ++ii) + dependants.at(ii)->release(); + + if(compiledComponent) + compiledComponent->release(); + + if(component) + delete component; +} + +void QmlCompositeTypeManager::TypeData::addWaiter(QmlComponentPrivate *p) +{ + waiters << p; +} + +void QmlCompositeTypeManager::TypeData::remWaiter(QmlComponentPrivate *p) +{ + waiters.removeAll(p); +} + +QmlComponent *QmlCompositeTypeManager::TypeData::toComponent(QmlEngine *engine) +{ + if(!component) { + + QmlCompiledComponent *cc = toCompiledComponent(engine); + if(cc) { + component = new QmlComponent(engine, cc, -1, -1, 0); + } else { + component = new QmlComponent(engine, 0); + } + + } + + return component; +} + +QmlCompiledComponent * +QmlCompositeTypeManager::TypeData::toCompiledComponent(QmlEngine *engine) +{ + if(status == Complete && !compiledComponent) { + + compiledComponent = new QmlCompiledComponent; + compiledComponent->url = QUrl(url); + compiledComponent->name = url.toLatin1(); // ### + + QmlCompiler compiler; + if(!compiler.compile(engine, this, compiledComponent)) { + status = Error; + errorDescription = compiler.errorDescription() + + QLatin1String("@") + + url + QLatin1String(":") + + QString::number(compiler.errorLine()); + compiledComponent->release(); + compiledComponent = 0; + } + + // Data is no longer needed once we have a compiled component + data.clear(); + } + + if(compiledComponent) + compiledComponent->addref(); + + return compiledComponent; +} + +QmlCompositeTypeManager::TypeData::TypeReference::TypeReference() +: type(0), unit(0), parser(0) +{ +} + +QmlCompositeTypeManager::QmlCompositeTypeManager(QmlEngine *e) +: engine(e) +{ +} + +QmlCompositeTypeManager::TypeData *QmlCompositeTypeManager::get(const QUrl &url) +{ + TypeData *unit = components.value(url.toString()); + + if(!unit) { + unit = new TypeData; + unit->status = TypeData::Waiting; + unit->url = url.toString(); + components.insert(url.toString(), unit); + + loadSource(unit); + } + + unit->addref(); + return unit; +} + +QmlCompositeTypeManager::TypeData * +QmlCompositeTypeManager::getImmediate(const QByteArray &data, const QUrl &url) +{ + TypeData *unit = new TypeData; + unit->status = TypeData::Waiting; + unit->url = url.toString(); + setData(unit, data, url); + return unit; +} + +void QmlCompositeTypeManager::clearCache() +{ + for(Components::Iterator iter = components.begin(); iter != components.end();) { + if((*iter)->status != TypeData::Waiting) { + (*iter)->release(); + iter = components.erase(iter); + } else { + ++iter; + } + } +} + + +void QmlCompositeTypeManager::replyFinished() +{ + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + + TypeData *unit = components.value(reply->url().toString()); + Q_ASSERT(unit); + + if(reply->error() != QNetworkReply::NoError) { + + QString errorDescription; + // ### - Fill in error + errorDescription = QLatin1String("Network error for URL ") + + reply->url().toString(); + + unit->status = TypeData::Error; + unit->errorDescription = errorDescription; + doComplete(unit); + + } else { + QByteArray data = reply->readAll(); + + setData(unit, data, reply->url()); + } + + reply->deleteLater(); +} + +void QmlCompositeTypeManager::loadSource(TypeData *unit) +{ + QUrl url(unit->url); + + if(url.scheme() == QLatin1String("file")) { + + QFile file(url.toLocalFile()); + if(file.open(QFile::ReadOnly)) { + QByteArray data = file.readAll(); + setData(unit, data, url); + } else { + QString errorDescription; + // ### - Fill in error + errorDescription = QLatin1String("File error for URL ") + url.toString(); + unit->status = TypeData::Error; + unit->errorDescription = errorDescription; + doComplete(unit); + } + + } else { + QNetworkReply *reply = + engine->networkAccessManager()->get(QNetworkRequest(url)); + QObject::connect(reply, SIGNAL(finished()), + this, SLOT(replyFinished())); + } +} + +void QmlCompositeTypeManager::setData(TypeData *unit, const QByteArray &data, + const QUrl &url) +{ + if(!unit->data.parse(data, url)) { + + unit->status = TypeData::Error; + unit->errorDescription = unit->data.errorDescription(); + doComplete(unit); + + } else { + + engine->addNameSpacePaths(unit->data.nameSpacePaths()); + compile(unit); + + } +} + +void QmlCompositeTypeManager::doComplete(TypeData *unit) +{ + for(int ii = 0; ii < unit->dependants.count(); ++ii) { + checkComplete(unit->dependants.at(ii)); + unit->dependants.at(ii)->release(); + } + unit->dependants.clear(); + + while(!unit->waiters.isEmpty()) { + QmlComponentPrivate *p = unit->waiters.takeFirst(); + p->typeDataReady(); + } +} + +void QmlCompositeTypeManager::checkComplete(TypeData *unit) +{ + if(unit->status != TypeData::Waiting) + return; + + int waiting = 0; + for(int ii = 0; ii < unit->types.count(); ++ii) { + TypeData *u = unit->types.at(ii).unit; + + if(!u) + continue; + + if(u->status == TypeData::Error) { + unit->status = TypeData::Error; + unit->errorDescription = u->errorDescription; + doComplete(unit); + return; + } else if(u->status == TypeData::Waiting) { + waiting++; + } + } + if(!waiting) { + unit->status = TypeData::Complete; + doComplete(unit); + } +} + +void QmlCompositeTypeManager::compile(TypeData *unit) +{ + QStringList typeNames = unit->data.types(); + + int waiting = 0; + for(int ii = 0; ii < typeNames.count(); ++ii) { + QByteArray type = typeNames.at(ii).toLatin1(); + + TypeData::TypeReference ref; + if (type == QByteArray("Property") || + type == QByteArray("Signal")) { + unit->types << ref; + continue; + } + + QmlCustomParser *parser = + QmlMetaType::customParser(type); + + if(parser) { + ref.parser = parser; + unit->types << ref; + continue; + } + + ref.type = QmlMetaType::qmlType(type); + if(ref.type) { + ref.parser = parser; + unit->types << ref; + continue; + } + + QUrl url = engine->componentUrl(QUrl(type + ".qml"), QUrl(unit->url)); + TypeData *urlUnit = components.value(url.toString()); + + if(!urlUnit) { + urlUnit = new TypeData; + urlUnit->status = TypeData::Waiting; + urlUnit->url = url.toString(); + components.insert(url.toString(), urlUnit); + + loadSource(urlUnit); + } + + ref.unit = urlUnit; + switch(urlUnit->status) { + case TypeData::Invalid: + case TypeData::Error: + unit->status = TypeData::Error; + unit->errorDescription = urlUnit->errorDescription; + doComplete(unit); + return; + + case TypeData::Complete: + break; + + case TypeData::Waiting: + unit->addref(); + ref.unit->dependants << unit; + waiting++; + break; + } + + unit->types << ref; + } + + if(waiting) { + unit->status = TypeData::Waiting; + } else { + unit->status = TypeData::Complete; + doComplete(unit); + } +} diff --git a/src/declarative/qml/qmlcompositetypemanager_p.h b/src/declarative/qml/qmlcompositetypemanager_p.h new file mode 100644 index 0000000..6982844 --- /dev/null +++ b/src/declarative/qml/qmlcompositetypemanager_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLCOMPOSITETYPEMANAGER_P_H +#define QMLCOMPOSITETYPEMANAGER_P_H + +#include <qglobal.h> +#include <private/qmlxmlparser_p.h> +#include <private/qmlrefcount_p.h> + +QT_BEGIN_NAMESPACE + +class QmlXmlParser; +class QmlEngine; +class QmlCompiledComponent; +class QmlComponentPrivate; +class QmlComponent; +class QmlCompositeTypeManager : public QObject +{ + Q_OBJECT +public: + QmlCompositeTypeManager(QmlEngine *); + + struct TypeData : public QmlRefCount + { + TypeData(); + virtual ~TypeData(); + + enum Status { + Invalid, + Complete, + Error, + Waiting + }; + Status status; + QString errorDescription; + + QString url; + QList<TypeData *> dependants; + + // Return a QmlComponent if the TypeData is not in the Waiting state. + // The QmlComponent is owned by the TypeData, so a reference should be + // kept to keep the QmlComponent alive. + QmlComponent *toComponent(QmlEngine *); + // Return a QmlCompiledComponent if possible, or 0 if an error + // occurs + QmlCompiledComponent *toCompiledComponent(QmlEngine *); + + struct TypeReference + { + TypeReference(); + + QmlType *type; + TypeData *unit; + QmlCustomParser *parser; + }; + + QList<TypeReference> types; + + // Add or remove p as a waiter. When the TypeData becomes ready, the + // QmlComponentPrivate::typeDataReady() method will be invoked on p. + // The waiter is automatically removed when the typeDataReady() method + // is invoked, so there is no need to call remWaiter() in this case. + void addWaiter(QmlComponentPrivate *p); + void remWaiter(QmlComponentPrivate *p); + + private: + friend class QmlCompositeTypeManager; + friend class QmlCompiler; + + QmlXmlParser data; + QList<QmlComponentPrivate *> waiters; + QmlComponent *component; + QmlCompiledComponent *compiledComponent; + }; + + // Return a TypeData for url. The TypeData may be cached. + TypeData *get(const QUrl &url); + // Return a TypeData for data, with the provided base url. The TypeData + // will not be cached. + TypeData *getImmediate(const QByteArray &data, const QUrl &url); + + // Clear cached types. Only types that aren't in the Waiting state will + // be cleared. + void clearCache(); + +private Q_SLOTS: + void replyFinished(); + +private: + void loadSource(TypeData *); + void compile(TypeData *); + void setData(TypeData *, const QByteArray &, const QUrl &); + + void doComplete(TypeData *); + void checkComplete(TypeData *); + + QmlEngine *engine; + typedef QHash<QString, TypeData *> Components; + Components components; +}; + +QT_END_NAMESPACE + +#endif // QMLCOMPOSITETYPEMANAGER_P_H + diff --git a/src/declarative/qml/qmlcontext.cpp b/src/declarative/qml/qmlcontext.cpp new file mode 100644 index 0000000..104f460 --- /dev/null +++ b/src/declarative/qml/qmlcontext.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <qmlcontext.h> +#include <private/qmlcontext_p.h> +#include <private/qmlengine_p.h> +#include <private/qmlcompiledcomponent_p.h> +#include <qmlengine.h> +#include <qscriptengine.h> + +#include <qdebug.h> + +// 6-bits +#define MAXIMUM_DEFAULT_OBJECTS 63 + +QT_BEGIN_NAMESPACE + +QmlContextPrivate::QmlContextPrivate() + : parent(0), engine(0), highPriorityCount(0), component(0) +{ +} + +void QmlContextPrivate::dump() +{ + dump(0); +} + +void QmlContextPrivate::dump(int depth) +{ + QByteArray ba(depth * 4, ' '); + qWarning() << ba << properties.keys(); + qWarning() << ba << variantProperties.keys(); + if(parent) + parent->d_func()->dump(depth + 1); +} + +void QmlContextPrivate::destroyed(QObject *obj) +{ + defaultObjects.removeAll(obj); + for(QHash<QString, QObject *>::Iterator iter = properties.begin(); + iter != properties.end(); ) { + if(*iter == obj) + iter = properties.erase(iter); + else + ++iter; + } +} + +void QmlContextPrivate::init() +{ + Q_Q(QmlContext); + //set scope chain + QScriptEngine *scriptEngine = engine->scriptEngine(); + QScriptValue scopeObj = + scriptEngine->newObject(engine->d_func()->contextClass, scriptEngine->newVariant(QVariant::fromValue((QObject*)q))); + if (!parent) + scopeChain.append(scriptEngine->globalObject()); + else + scopeChain = parent->d_func()->scopeChain; + scopeChain.prepend(scopeObj); +} + +void QmlContextPrivate::addDefaultObject(QObject *object, Priority priority) +{ + if(defaultObjects.count() >= (MAXIMUM_DEFAULT_OBJECTS - 1)) { + qWarning("QmlContext: Cannot have more than %d default objects on " + "one bind context.", MAXIMUM_DEFAULT_OBJECTS - 1); + return; + } + + if(priority == HighPriority) { + defaultObjects.insert(highPriorityCount++, object); + } else { + defaultObjects.append(object); + } + QObject::connect(object, SIGNAL(destroyed(QObject*)), + q_ptr, SLOT(objectDestroyed(QObject*))); +} + + +/*! + \class QmlContext + \brief The QmlContext class defines a context within a QML engine. + \mainclass + + Contexts allow data to be exposed to the QML components instantiated by the + QML engine. + + Each QmlContext contains a set of properties, distinct from + its QObject properties, that allow data to be + explicitly bound to a context by name. The context properties are defined or + updated by calling QmlContext::setContextProperty(). The following example shows + a Qt model being bound to a context and then accessed from a QML file. + + \code + QmlEngine engine; + QmlContext context(engine.rootContext()); + context.setContextProperty("myModel", modelData); + + QmlComponent component("<ListView model=\"{myModel}\" />"); + component.create(&context); + \endcode + + To simplify binding and maintaining larger data sets, QObject's can be + added to a QmlContext. These objects are known as the context's default + objects. In this case all the properties of the QObject are + made available by name in the context, as though they were all individually + added by calling QmlContext::setContextProperty(). Changes to the property's + values are detected through the property's notify signal. This method is + also slightly more faster than manually adding property values. + + The following example has the same effect as the one above, but it is + achieved using a default object. + + \code + class MyDataSet : ... { + ... + Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged); + ... + }; + + MyDataSet myDataSet; + QmlEngine engine; + QmlContext context(engine.rootContext()); + context.addDefaultObject(&myDataSet); + + QmlComponent component("<ListView model=\"{myModel}\" />"); + component.create(&context); + \endcode + + Each context may have up to 32 default objects, and objects added first take + precedence over those added later. All properties added explicitly by + QmlContext::setContextProperty() take precedence over default object properties. + + Contexts are hierarchal, with the \l {QmlEngine::rootContext()}{root context} + being created by the QmlEngine. A component instantiated in a given context + has access to that context's data, as well as the data defined by its + ancestor contexts. Data values (including those added implicitly by the + default objects) in a context override those in ancestor contexts. Data + that should be available to all components instantiated by the QmlEngine + should be added to the \l {QmlEngine::rootContext()}{root context}. + + In the following example, + + \code + QmlEngine engine; + QmlContext context1(engine.rootContext()); + QmlContext context2(&context1); + QmlContext context3(&context2); + + context1.setContextProperty("a", 12); + context2.setContextProperty("b", 13); + context3.setContextProperty("a", 14); + context3.setContextProperty("c", 14); + \endcode + + a QML component instantiated in context1 would have access to the "a" data, + a QML component instantiated in context2 would have access to the "a" and + "b" data, and a QML component instantiated in context3 would have access to + the "a", "b" and "c" data - although its "a" data would return 14, unlike + that in context1 or context2. +*/ + +/*! \internal */ +QmlContext::QmlContext(QmlEngine *e) +: QObject(*(new QmlContextPrivate)) +{ + Q_D(QmlContext); + d->engine = e; + d->init(); +} + +/*! + Create a new QmlContext with the given \a parentContext, and the + QObject \a parent. +*/ +QmlContext::QmlContext(QmlContext *parentContext, QObject *parent) +: QObject(*(new QmlContextPrivate), parent) +{ + Q_D(QmlContext); + d->parent = parentContext; + d->engine = parentContext->engine(); + d->init(); +} + +/*! + Destroys the QmlContext. + + Any expressions, or sub-contexts dependent on this context will be + invalidated, but not destroyed (unless they are parented to the QmlContext + object). + */ +QmlContext::~QmlContext() +{ + Q_D(QmlContext); + if(d->component) d->component->release(); +} + + +/*! + Return the context's QmlEngine, or 0 if the context has no QmlEngine or the + QmlEngine was destroyed. +*/ +QmlEngine *QmlContext::engine() const +{ + Q_D(const QmlContext); + return d->engine; +} + +/*! + Return the context's parent QmlContext, or 0 if this context has no + parent or if the parent has been destroyed. +*/ +QmlContext *QmlContext::parentContext() const +{ + Q_D(const QmlContext); + return d->parent; +} + +/*! + Add a default \a object to this context. The object will be added after + any existing default objects. +*/ +void QmlContext::addDefaultObject(QObject *defaultObject) +{ + Q_D(QmlContext); + d->addDefaultObject(defaultObject, QmlContextPrivate::NormalPriority); +} + +/*! + Set a the \a value of the \a name property on this context. +*/ +void QmlContext::setContextProperty(const QString &name, const QVariant &value) +{ + Q_D(QmlContext); + d->variantProperties.insert(name, value); +} + +/*! + Set a the \a value of the \a name property on this context. + + QmlContext does \b not take ownership of \a value. +*/ +void QmlContext::setContextProperty(const QString &name, QObject *value) +{ + Q_D(QmlContext); + d->properties.insert(name, value); + QObject::connect(value, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); +} + +/*! + Activate this bind context. + + \sa QmlEngine::activeContext() QmlContext::activeContext() +*/ +void QmlContext::activate() +{ + QmlEnginePrivate *ep = engine()->d_func(); + ep->activeContexts.push(this); + ep->setCurrentBindContext(this); + ep->contextActivated(this); +} + +/*! + Deactivate this bind context. The previously active bind context will + become active, or, if there was no previously active bind context, no + context will be active. + + \sa QmlEngine::activeContext() QmlContext::activeContext() +*/ +void QmlContext::deactivate() +{ + QmlEnginePrivate *ep = engine()->d_func(); + Q_ASSERT(ep->activeContexts.top() == this); + ep->activeContexts.pop(); + if(ep->activeContexts.isEmpty()) + ep->setCurrentBindContext(0); + else + ep->setCurrentBindContext(ep->activeContexts.top()); + ep->contextDeactivated(this); +} + +/*! + Returns the currently active context, or 0 if no context is active. + + This method is thread-safe. The active context is maintained individually + for each thread. This method is equivalent to + \code + QmlEngine::activeEngine()->activeContext() + \endcode +*/ +QmlContext *QmlContext::activeContext() +{ + QmlEngine *engine = QmlEngine::activeEngine(); + if(engine) + return engine->activeContext(); + else + return 0; +} + +/*! + Resolves the URL \a src relative to the URL of the + containing component. + + If \a src is absolute, it is simply returned. + + If there is no containing component, an empty URL is returned. + + \sa componentUrl +*/ +QUrl QmlContext::resolvedUrl(const QUrl &src) +{ + QmlContext *ctxt = this; + if(src.isRelative()) { + if(ctxt) { + while(ctxt) { + if(ctxt->d_func()->component) + break; + else + ctxt = ctxt->parentContext(); + } + + if(ctxt) + return ctxt->d_func()->component->url.resolved(src); + } + return QUrl(); + } else { + return src; + } +} + +/*! + Resolves the component URI \a src relative to the URL of the + containing component, and according to the + \link QmlEngine::nameSpacePaths() namespace paths\endlink of the + context's engine, returning the resolved URL. + + \sa componentUrl +*/ +QUrl QmlContext::resolvedUri(const QUrl &src) +{ + QmlContext *ctxt = this; + if(src.isRelative()) { + if(ctxt) { + while(ctxt) { + if(ctxt->d_func()->component) + break; + else + ctxt = ctxt->parentContext(); + } + + if(ctxt) + return ctxt->d_func()->engine->componentUrl(src, ctxt->d_func()->component->url); + } + return QUrl(); + } else { + return ctxt->d_func()->engine->componentUrl(src, QUrl()); + } +} + +void QmlContext::objectDestroyed(QObject *object) +{ + Q_D(QmlContext); + d->destroyed(object); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcontext.h b/src/declarative/qml/qmlcontext.h new file mode 100644 index 0000000..9e3b6d8 --- /dev/null +++ b/src/declarative/qml/qmlcontext.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLCONTEXT_H +#define QMLCONTEXT_H + +#include <QtCore/qurl.h> +#include <QtCore/qobject.h> +#include <QtScript/qscriptvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QString; +class QmlEngine; +class QmlRefCount; +class QmlContextPrivate; +class Q_DECLARATIVE_EXPORT QmlContext : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlContext) + +public: + QmlContext(QmlContext *parent, QObject *objParent=0); + virtual ~QmlContext(); + + QmlEngine *engine() const; + QmlContext *parentContext() const; + + void addDefaultObject(QObject *); + void setContextProperty(const QString &, QObject *); + void setContextProperty(const QString &, const QVariant &); + + void activate(); + void deactivate(); + + static QmlContext *activeContext(); + + QUrl resolvedUri(const QUrl &); + QUrl resolvedUrl(const QUrl &); + +private Q_SLOTS: + void objectDestroyed(QObject *); + +private: + friend class QmlEngine; + friend class QmlEnginePrivate; + friend class QmlExpression; + friend class QmlBasicScript; + friend class QmlContextScriptClass; + friend class QmlObjectScriptClass; + friend class QmlComponent; + friend class QmlScriptPrivate; + friend class QmlBoundSignalProxy; + QmlContext(QmlEngine *); +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLCONTEXT_H diff --git a/src/declarative/qml/qmlcontext_p.h b/src/declarative/qml/qmlcontext_p.h new file mode 100644 index 0000000..8c51a6b --- /dev/null +++ b/src/declarative/qml/qmlcontext_p.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 QtDeclarative 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 QMLCONTEXT_P_H +#define QMLCONTEXT_P_H + +#include <qmlcontext.h> +#include <private/qobject_p.h> +#include <qhash.h> +#include <qscriptvalue.h> + +QT_BEGIN_NAMESPACE +class QmlContext; +class QmlEngine; +class QmlCompiledComponent; + +class QmlContextPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlContext) +public: + QmlContextPrivate(); + + QmlContext *parent; + QmlEngine *engine; + QHash<QString, QObject *> properties; + QHash<QString, QVariant> variantProperties; + + QObjectList defaultObjects; + int highPriorityCount; + + QScriptValueList scopeChain; + + QmlCompiledComponent *component; + void init(); + + void dump(); + void dump(int depth); + + void destroyed(QObject *); + + enum Priority { + HighPriority, + NormalPriority + }; + void addDefaultObject(QObject *, Priority); +}; +QT_END_NAMESPACE + +#endif // QMLCONTEXT_P_H diff --git a/src/declarative/qml/qmlcustomparser.cpp b/src/declarative/qml/qmlcustomparser.cpp new file mode 100644 index 0000000..a342ca8 --- /dev/null +++ b/src/declarative/qml/qmlcustomparser.cpp @@ -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 QtDeclarative 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 "qmlcustomparser.h" + + +QT_BEGIN_NAMESPACE + +/*! + \class QmlCustomParser + \brief The QmlCustomParser class allows you to add new arbitrary types to QML. + + By subclassing QmlCustomParser, you can add an XML parser for building a + particular type. + + The subclass must implement compile() and create(), and define itself in + the meta type system with one of the macros: + + \code + QML_DEFINE_CUSTOM_PARSER(Name, parserClass) + \endcode + + \code + QML_DEFINE_CUSTOM_PARSER_NS("XmlNamespaceUri", Name, parserClass) + \endcode +*/ + +/*! + \fn QByteArray QmlCustomParser::compile(QXmlStreamReader& reader, bool *ok) + + Upon entry to this function, \a reader is positioned on a QXmlStreamReader::StartElement + with the name specified when the class was defined with the QML_DEFINE_CUSTOM_PARSER macro. + + The custom parser must consume tokens from \a reader until the EndElement matching the + initial start element is reached, or until error. + + On return, \c *ok indicates success. + + The returned QByteArray contains data meaningful only to the custom parser; the + type engine will pass this same data to create() when making an instance of the data. + + The QByteArray may be cached between executions of the system, so it must contain + correctly-serialized data (not, for example, pointers to stack objects). +*/ + +/*! + \fn QVariant QmlCustomParser::create(const QByteArray &data) + + This function returns a QVariant containing the value represented by \a data, which + is a block of data previously returned by a call to compile(). + + If the compile is for a type, the variant should be a pointer to the + correctly-named QObject subclass (i.e. the one defined by QML_DEFINE_TYPE for + the same-named type as this custom parser is defined for). +*/ + + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlcustomparser.h b/src/declarative/qml/qmlcustomparser.h new file mode 100644 index 0000000..9de1be4 --- /dev/null +++ b/src/declarative/qml/qmlcustomparser.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLCUSTOMPARSER_H +#define QMLCUSTOMPARSER_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qxmlstream.h> +#include <QtDeclarative/qfxglobal.h> +#include <QtDeclarative/qmlmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QmlCustomParser +{ +public: + virtual ~QmlCustomParser() {} + + virtual QByteArray compile(QXmlStreamReader&, bool *ok)=0; + virtual QVariant create(const QByteArray &)=0; + + struct Register { + Register(const char *name, QmlCustomParser *parser) { + qmlRegisterCustomParser(name, parser); + } + }; + template<typename T> + struct Define { + static Register instance; + }; +}; +#define QML_DEFINE_CUSTOM_PARSER(name, parserClass) \ + template<> QmlCustomParser::Register QmlCustomParser::Define<parserClass>::instance(# name, new parserClass); +#define QML_DEFINE_CUSTOM_PARSER_NS(namespacestring, name, parserClass) \ + template<> QmlCustomParser::Register QmlCustomParser::Define<parserClass>::instance(namespacestring "/" # name, new parserClass); + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/qml/qmldom.cpp b/src/declarative/qml/qmldom.cpp new file mode 100644 index 0000000..274b542 --- /dev/null +++ b/src/declarative/qml/qmldom.cpp @@ -0,0 +1,1372 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmldom.h" +#include "qmldom_p.h" +#include "private/qmlxmlparser_p.h" +#include "private/qmlcompiler_p.h" +#include "qmlcompiledcomponent_p.h" +#include <QtCore/qbytearray.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +QmlDomDocumentPrivate::QmlDomDocumentPrivate() +: root(0) +{ +} + +QmlDomDocumentPrivate::QmlDomDocumentPrivate(const QmlDomDocumentPrivate &other) +: QSharedData(other), root(0) +{ + root = other.root; + if(root) root->addref(); +} + +QmlDomDocumentPrivate::~QmlDomDocumentPrivate() +{ + if(root) root->release(); +} + +/*! + \class QmlDomDocument + \brief The QmlDomDocument class represents the root of a QML document + + A QML document is a self-contained snippet of QML, usually contained in a + single file. Each document has a version number, accessible through + QmlDomDocument::version(), and a root object, accessible through + QmlDomDocument::rootObject(). + + The QmlDomDocument class allows the programmer to load a QML document, by + calling QmlDomDocument::load(), manipulate it and save it to textual form + by calling QmlDomDocument::save(). By using the QML DOM API, editors can + non-destructively modify a QML document even if they only understand a + subset of the total QML functionality. + + The following example loads a QML file from disk, and prints out its root + object type and the properties assigned in the root object. + \code + QFile file(inputFileName); + file.open(QIODevice::ReadOnly); + QByteArray xmlData = file.readAll(); + + QDomDocument document; + document.load(xmlData); + + QDomObject rootObject = document.rootObject(); + qDebug() << rootObject.objectType(); + foreach(QmlDomProperty property, rootObject.properties()) + qDebug() << property.propertyName(); + \endcode +*/ + +/*! + Construct an empty QmlDomDocument. +*/ +QmlDomDocument::QmlDomDocument() +: d(new QmlDomDocumentPrivate) +{ +} + +/*! + Create a copy of \a other QmlDomDocument. +*/ +QmlDomDocument::QmlDomDocument(const QmlDomDocument &other) +: d(other.d) +{ +} + +/*! + Destroy the QmlDomDocument +*/ +QmlDomDocument::~QmlDomDocument() +{ +} + +/*! + Assign \a other to this QmlDomDocument. +*/ +QmlDomDocument &QmlDomDocument::operator=(const QmlDomDocument &other) +{ + d = other.d; + return *this; +} + +/*! + Return the version number of the Qml document. Currently only version + 1 exists. +*/ +int QmlDomDocument::version() const +{ + return 1; +} + +/*! + Loads a QmlDomDocument from \a data. \a data should be valid QML XML + data. On success, true is returned. If the \a data is malformed, false + is returned and QmlDomDocument::loadError() contains an error description. + + \sa QmlDomDocument::save() QmlDomDocument::loadError() +*/ +bool QmlDomDocument::load(QmlEngine *engine, const QByteArray &data) +{ + d->error = QString(); + + QmlXmlParser parser; + if(!parser.parse(data)) { + d->error = parser.errorDescription(); + return false; + } + + QmlCompiledComponent component; + QmlCompiler compiler; + // ### +// compiler.compile(engine, parser, &component); + + if(compiler.isError()) { + d->error = compiler.errorDescription(); + return false; + } + + if(parser.tree()) { + component.dump(0, parser.tree()); + d->root = parser.tree(); + d->root->addref(); + } + + return true; +} + +/*! + Returns the last load error. The load error will be reset after a + successful call to load(). + + \sa load() +*/ +QString QmlDomDocument::loadError() const +{ + return d->error; +} + +/*! + Return a saved copy of the QmlDomDocument. The returned data will be valid + QML XML data. + + \sa load() +*/ +QByteArray QmlDomDocument::save() const +{ + return QByteArray(); +} + +/*! + Returns the document's root object, or an invalid QmlDomObject if the + document has no root. + + In the sample QML below, the root object will be the QFxItem type. + \code + <Item> + <Text text="Hello World" /> + </Item> + \endcode +*/ +QmlDomObject QmlDomDocument::rootObject() const +{ + QmlDomObject rv; + rv.d->object = d->root; + if(rv.d->object) rv.d->object->addref(); + return rv; +} + +QmlDomPropertyPrivate::QmlDomPropertyPrivate() +: property(0) +{ +} + +QmlDomPropertyPrivate::QmlDomPropertyPrivate(const QmlDomPropertyPrivate &other) +: QSharedData(other), property(0) +{ + property = other.property; + if(property) property->addref(); +} + +QmlDomPropertyPrivate::~QmlDomPropertyPrivate() +{ + if(property) property->release(); +} + +/*! + \class QmlDomProperty + \brief The QmlDomProperty class represents one property assignment in the + QML DOM tree + + Properties in QML can be assigned QML \l {QmlDomValue}{values}. + + \sa QmlDomObject +*/ + +/*! + Construct an invalid QmlDomProperty. +*/ +QmlDomProperty::QmlDomProperty() +: d(new QmlDomPropertyPrivate) +{ +} + +/*! + Create a copy of \a other QmlDomProperty. +*/ +QmlDomProperty::QmlDomProperty(const QmlDomProperty &other) +: d(other.d) +{ +} + +/*! + Destroy the QmlDomProperty. +*/ +QmlDomProperty::~QmlDomProperty() +{ +} + +/*! + Assign \a other to this QmlDomProperty. +*/ +QmlDomProperty &QmlDomProperty::operator=(const QmlDomProperty &other) +{ + d = other.d; + return *this; +} + +/*! + Return the name of this property. + + \code + <Text x="10" y="10" font.bold="true" /> + \endcode + + As illustrated above, a property name can be a simple string, such as "x" or + "y", or a more complex "dot property", such as "font.bold". In both cases + the full name is returned ("x", "y" and "font.bold") by this method. + + For dot properties, a split version of the name can be accessed by calling + QmlDomProperty::propertyNameParts(). + + \sa QmlDomProperty::propertyNameParts() +*/ +QByteArray QmlDomProperty::propertyName() const +{ + return d->propertyName; +} + +/*! + Return the name of this property, split into multiple parts in the case + of dot properties. + + \code + <Text x="10" y="10" font.bold="true" /> + \endcode + + For each of the properties shown above, this method would return ("x"), + ("y") and ("font", "bold"). + + \sa QmlDomProperty::propertyName() +*/ +QList<QByteArray> QmlDomProperty::propertyNameParts() const +{ + if(d->propertyName.isEmpty()) return QList<QByteArray>(); + else return d->propertyName.split('.'); +} + +/*! + Return true if this property is used as a default property in the QML + document. + + \code + <Text text="hello" /> + <Text>hello</Text> + \endcode + + The above two examples return the same DOM tree, except that the second has + the default property flag set on the text property. Observe that whether + or not a property has isDefaultProperty set is determined by how the + property is used, and not only by whether the property is the types default + property. +*/ +bool QmlDomProperty::isDefaultProperty() const +{ + return d->property && d->property->isDefault; +} + +/*! + Returns the QmlDomValue that is assigned to this property, or an invalid + QmlDomValue if no value is assigned. +*/ +QmlDomValue QmlDomProperty::value() const +{ + QmlDomValue rv; + if(d->property) { + rv.d->property = d->property; + rv.d->value = d->property->values.at(0); + rv.d->property->addref(); + rv.d->value->addref(); + } + return rv; +} + +/*! + Sets the QmlDomValue that is assigned to this property to \a value. +*/ +void QmlDomProperty::setValue(const QmlDomValue &value) +{ + Q_UNUSED(value); + qWarning("QmlDomProperty::setValue(const QmlDomValue &): Not Implemented"); +} + +QmlDomObjectPrivate::QmlDomObjectPrivate() +: object(0), isVirtualComponent(false) +{ +} + +QmlDomObjectPrivate::QmlDomObjectPrivate(const QmlDomObjectPrivate &other) +: QSharedData(other), object(0), isVirtualComponent(false) +{ + object = other.object; + if(object) object->addref(); + isVirtualComponent = other.isVirtualComponent; +} + +QmlDomObjectPrivate::~QmlDomObjectPrivate() +{ + if(object) object->release(); +} + +QmlDomObjectPrivate::Properties +QmlDomObjectPrivate::properties() const +{ + Properties rv; + + for(QHash<QByteArray, QmlParser::Property *>::ConstIterator iter = + object->properties.begin(); + iter != object->properties.end(); + ++iter) { + + rv << properties(*iter); + + } + return rv; +} + +QmlDomObjectPrivate::Properties +QmlDomObjectPrivate::properties(QmlParser::Property *property) const +{ + Properties rv; + + if(property->value) { + + for(QHash<QByteArray, QmlParser::Property *>::ConstIterator iter = + property->value->properties.begin(); + iter != property->value->properties.end(); + ++iter) { + + rv << properties(*iter); + + } + + QByteArray name(property->name + "."); + for(Properties::Iterator iter = rv.begin(); iter != rv.end(); ++iter) + iter->second.prepend(name); + + } else { + + // We don't display "id" sets as a property in the dom + if(property->values.count() != 1 || + property->values.at(0)->type != QmlParser::Value::Id) + rv << qMakePair(property, property->name); + + } + + return rv; +} + +/*! + \class QmlDomObject + \brief The QmlDomObject class represents an object instantiation. + + Each object instantiated in a QML file has a corresponding QmlDomObject + node in the QML DOM. + + In addition to the type information that determines the object to + instantiate, QmlDomObject's also have a set of associated QmlDomProperty's. + Each QmlDomProperty represents a QML property assignment on the instantiated + object. For example, + + \code + <QGraphicsWidget opacity="0.5" size="100x100" /> + \endcode + + describes a single QmlDomObject - "QGraphicsWidget" - with two properties, + "opacity" and "size". Obviously QGraphicsWidget has many more properties than just + these two, but the QML DOM representation only contains those assigned + values (or bindings) in the QML file. + + The DOM tree can be modified to include new property assignments by calling + QmlDomObject::addProperty(). Existing property assignments can be modified + through the QmlDomProperty::setValue() method, or removed entirely by + calling QmlDomObject::removeProperty(). +*/ + +/*! + Construct an invalid QmlDomObject. +*/ +QmlDomObject::QmlDomObject() +: d(new QmlDomObjectPrivate) +{ +} + +/*! + Construct a new QmlDomObject with the specified \a objectType. +*/ +QmlDomObject::QmlDomObject(const QByteArray &objectType) +: d(new QmlDomObjectPrivate) +{ + Q_UNUSED(objectType); + qWarning("QmlDomObject::QmlDomObject(const QByteArray &): Not implemented"); +} + +/*! + Create a copy of \a other QmlDomObject. +*/ +QmlDomObject::QmlDomObject(const QmlDomObject &other) +: d(other.d) +{ +} + +/*! + Destroy the QmlDomObject. +*/ +QmlDomObject::~QmlDomObject() +{ +} + +/*! + Assign \a other to this QmlDomObject. +*/ +QmlDomObject &QmlDomObject::operator=(const QmlDomObject &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this is a valid QmlDomObject, false otherwise. +*/ +bool QmlDomObject::isValid() const +{ + return d->object != 0; +} + +/*! + Returns the type name of this object. + + For example, the type of this object would be "QGraphicsWidget". + \code + <QGraphicsWidget /> + \endcode +*/ +QByteArray QmlDomObject::objectType() const +{ + if(d->object) return d->object->typeName; + else return QByteArray(); +} + +/*! + Returns the QML id assigned to this object, or an empty QByteArray if no id + has been assigned. + + For example, the object id of this object would be "MyText". + \code + <Text id="MyText" /> + \endcode +*/ +QByteArray QmlDomObject::objectId() const +{ + if(d->object) return d->object->id; + else return QByteArray(); +} + +/*! + Set the object \a id. If any other object within the DOM tree has the same + id, the other object's id will be cleared. +*/ +void QmlDomObject::setObjectId(const QByteArray &id) +{ + Q_UNUSED(id); + qWarning("QmlDomObject::setObjectId(const QByteArray &): Not implemented"); +} + + +/*! + Returns the list of assigned properties on this object. + + In the following example, "text" and "x" properties would be returned. + \code + <Text text="Hello world!" x="100" /> + \endcode +*/ +QList<QmlDomProperty> QmlDomObject::properties() const +{ + QList<QmlDomProperty> rv; + + if(!d->object) + return rv; + + QmlDomObjectPrivate::Properties properties = d->properties(); + for(int ii = 0; ii < properties.count(); ++ii) { + + QmlDomProperty domProperty; + domProperty.d->property = properties.at(ii).first; + domProperty.d->property->addref(); + domProperty.d->propertyName = properties.at(ii).second; + rv << domProperty; + + } + + if(d->object->defaultProperty) { + QmlDomProperty domProperty; + domProperty.d->property = d->object->defaultProperty; + domProperty.d->property->addref(); + domProperty.d->propertyName = d->object->defaultProperty->name; + rv << domProperty; + } + + return rv; +} + +/*! + Returns the object's \a name property if a value has been assigned to + it, or an invalid QmlDomProperty otherwise. + + In the example below, \c {object.property("src")} would return a valid + QmlDomProperty, and \c {object.property("tile")} an invalid QmlDomProperty. + + \code + <Image src="sample.jpg" /> + \endcode +*/ +QmlDomProperty QmlDomObject::property(const QByteArray &name) const +{ + QList<QmlDomProperty> props = properties(); + for(int ii = 0; ii < props.count(); ++ii) + if(props.at(ii).propertyName() == name) + return props.at(ii); + return QmlDomProperty(); +} + +/*! + Remove the property \a name from this object, if it exists. Otherwise does + nothing. +*/ +void QmlDomObject::removeProperty(const QByteArray &name) +{ + Q_UNUSED(name); + qWarning("QmlDomObject::removeProperty(const QByteArray &): Not implemented"); +} + +/*! + Adds the property \a name with the specified \a value to this object. If + a property by \a name already exists, it will be removed. +*/ +void QmlDomObject::addProperty(const QByteArray &name, const QmlDomValue &value) +{ + Q_UNUSED(name); + Q_UNUSED(value); + qWarning("QmlDomObject::addProperty(const QByteArray &, const QmlDomValue &): Not implemented"); +} + +/*! + Returns true if this object is a custom type. Custom types are special + types that allow embeddeding non-QML data, such as SVG or HTML data, + directly into QML files. + + \note Currently this method will always return false, and is a placekeeper + for future functionality. + + \sa QmlDomObject::customTypeData() +*/ +bool QmlDomObject::isCustomType() const +{ + return false; +} + +/*! + Sets the custom type \a data. If this type is not a custom type, this + method does nothing. + + \sa QmlDomObject::isCustomType() QmlDomObject::customTypeData() +*/ +void QmlDomObject::setCustomTypeData(const QByteArray &data) +{ + Q_UNUSED(data); + qWarning("QmlDomObject::setCustomTypeData(const QByteArray &): Not implemented"); +} + +/*! + If this object represents a custom type, returns the data associated with + the custom type, otherwise returns an empty QByteArray(). + QmlDomObject::isCustomType() can be used to check if this object represents + a custom type. +*/ +QByteArray QmlDomObject::customTypeData() const +{ + return QByteArray(); +} + +/*! + Returns true if this object is a sub-component object. Sub-component + objects can be converted into QmlDomComponent instances by calling + QmlDomObject::toComponent(). + + \sa QmlDomObject::toComponent() +*/ +bool QmlDomObject::isComponent() const +{ + return d->isVirtualComponent || + (d->object && d->object->typeName == "Component"); +} + +/*! + Returns a QmlDomComponent for this object if it is a sub-component, or + an invalid QmlDomComponent if not. QmlDomObject::isComponent() can be used + to check if this object represents a sub-component. + + \sa QmlDomObject::isComponent() +*/ +QmlDomComponent QmlDomObject::toComponent() const +{ + QmlDomComponent rv; + if(isComponent()) + rv.d = d; + return rv; +} + +QmlDomBasicValuePrivate::QmlDomBasicValuePrivate() +: value(0) +{ +} + +QmlDomBasicValuePrivate::QmlDomBasicValuePrivate(const QmlDomBasicValuePrivate &other) +: QSharedData(other), value(0) +{ + value = other.value; + if(value) value->addref(); +} + +QmlDomBasicValuePrivate::~QmlDomBasicValuePrivate() +{ + if(value) value->release(); +} + +/*! + \class QmlDomValueLiteral + \brief The QmlDomValueLiteral class represents a literal value. + + A literal value is a simple value, written inline with the QML. In the + example below, the "x", "y" and "color" properties are being assigned + literal values. + + \code + <Rect x="10" y="10" color="red" /> + \endcode +*/ + +/*! + Construct an empty QmlDomValueLiteral. +*/ +QmlDomValueLiteral::QmlDomValueLiteral(): + d(new QmlDomBasicValuePrivate) +{ +} + +/*! + Create a copy of \a other QmlDomValueLiteral. +*/ +QmlDomValueLiteral::QmlDomValueLiteral(const QmlDomValueLiteral &other) +: d(other.d) +{ +} + +/*! + Destroy the QmlDomValueLiteral. +*/ +QmlDomValueLiteral::~QmlDomValueLiteral() +{ +} + +/*! + Assign \a other to this QmlDomValueLiteral. +*/ +QmlDomValueLiteral &QmlDomValueLiteral::operator=(const QmlDomValueLiteral &other) +{ + d = other.d; + return *this; +} + +/*! + Return the literal value. + + In the example below, the literal value will be the string "10". + \code + <Rect x="10" /> + \endcode +*/ +QString QmlDomValueLiteral::literal() const +{ + if(d->value) return d->value->primitive; + else return QString(); +} + +/*! + Sets the literal \a value. +*/ +void QmlDomValueLiteral::setLiteral(const QString &value) +{ + Q_UNUSED(value); + qWarning("QmlDomValueLiteral::setLiteral(const QString &): Not implemented"); +} + +/*! + \class QmlDomValueBinding + \brief The QmlDomValueBinding class represents a property binding. + + A property binding is an ECMAScript expression assigned to a property. In + the example below, the "x" property is being assigned a property binding. + + \code + <Rect x="{Other.x}" /> + \endcode +*/ + +/*! + Construct an empty QmlDomValueBinding. +*/ +QmlDomValueBinding::QmlDomValueBinding() +{ +} + +/*! + Create a copy of \a other QmlDomValueBinding. +*/ +QmlDomValueBinding::QmlDomValueBinding(const QmlDomValueBinding &other) +: d(other.d) +{ +} + +/*! + Destroy the QmlDomValueBinding. +*/ +QmlDomValueBinding::~QmlDomValueBinding() +{ +} + +/*! + Assign \a other to this QmlDomValueBinding. +*/ +QmlDomValueBinding &QmlDomValueBinding::operator=(const QmlDomValueBinding &other) +{ + d = other.d; + return *this; +} + +/*! + Return the binding expression. + + In the example below, the string "Other.x" will be returned. + \code + <Rect x="{Other.x}" /> + \endcode +*/ +QString QmlDomValueBinding::binding() const +{ + if(d->value) + return d->value->primitive.mid(1, d->value->primitive.length() - 2); + else + return QString(); +} + +/*! + Sets the binding \a expression. +*/ +void QmlDomValueBinding::setBinding(const QString &expression) +{ + Q_UNUSED(expression); + qWarning("QmlDomValueBinding::setBinding(const QString &): Not implemented"); +} + +/*! + \class QmlDomValueValueSource + \brief The QmlDomValueValueSource class represents a value source assignment value. + + In QML, value sources are special value generating types that may be + assigned to properties. Value sources inherit the QmlPropertyValueSource + class. In the example below, the "x" property is being assigned the + NumericAnimation value source. + + \code + <Rect> + <x> + <NumericAnimation from="0" to="100" repeat="true" running="true" /> + </x> + </Rect> + \endcode +*/ + +/*! + Construct an empty QmlDomValueValueSource. +*/ +QmlDomValueValueSource::QmlDomValueValueSource() +{ +} + +/*! + Create a copy of \a other QmlDomValueValueSource. +*/ +QmlDomValueValueSource::QmlDomValueValueSource(const QmlDomValueValueSource &other) +: d(other.d) +{ +} + +/*! + Destroy the QmlDomValueValueSource. +*/ +QmlDomValueValueSource::~QmlDomValueValueSource() +{ +} + +/*! + Assign \a other to this QmlDomValueValueSource. +*/ +QmlDomValueValueSource &QmlDomValueValueSource::operator=(const QmlDomValueValueSource &other) +{ + d = other.d; + return *this; +} + +/*! + Return the value source object. + + In the example below, an object representing the NumericAnimation will be + returned. + \code + <Rect> + <x> + <NumericAnimation from="0" to="100" repeat="true" running="true" /> + </x> + </Rect> + \endcode +*/ +QmlDomObject QmlDomValueValueSource::object() const +{ + QmlDomObject rv; + if(d->value) { + rv.d->object = d->value->object; + rv.d->object->addref(); + } + return QmlDomObject(); +} + +/*! + Sets the value source \a object. +*/ +void QmlDomValueValueSource::setObject(const QmlDomObject &object) +{ + Q_UNUSED(object); + qWarning("QmlDomValueValueSource::setObject(const QmlDomObject &): Not implemented"); +} + +QmlDomValuePrivate::QmlDomValuePrivate() +: property(0), value(0) +{ +} + +QmlDomValuePrivate::QmlDomValuePrivate(const QmlDomValuePrivate &other) +: QSharedData(other), property(0), value(0) +{ + property = other.property; + value = other.value; + if(property) property->addref(); + if(value) value->addref(); +} + +QmlDomValuePrivate::~QmlDomValuePrivate() +{ + if(property) property->release(); + if(value) value->release(); +} + +/*! + \class QmlDomValue + \brief The QmlDomValue class represents a generic Qml value. + + QmlDomValue's can be assigned to QML \l {QmlDomProperty}{properties}. In + QML, properties can be assigned various different values, including basic + literals, property bindings, property value sources, objects and lists of + values. The QmlDomValue class allows a programmer to determine the specific + value type being assigned and access more detailed information through a + corresponding value type class. + + For example, in the following example, + + \code + <Text text="Hello World!" y="{Other.y}" /> + \endcode + + The text property is being assigned a literal, and the y property a property + binding. To output the values assigned to the text and y properties in the + above example from C++, + + \code + QmlDomDocument document; + QmlDomObject root = document.rootObject(); + + QmlDomProperty text = root.property("text"); + if(text.value().isLiteral()) { + QmlDomValueLiteral literal = text.value().toLiteral(); + qDebug() << literal.literal(); + } + + QmlDomProperty y = root.property("y"); + if(y.value().isBinding()) { + QmlDomValueBinding binding = y.value().toBinding(); + qDebug() << binding.binding(); + } + \endcode +*/ + +/*! + Construct an invalid QmlDomValue. +*/ +QmlDomValue::QmlDomValue() +: d(new QmlDomValuePrivate) +{ +} + +/*! + Create a copy of \a other QmlDomValue. +*/ +QmlDomValue::QmlDomValue(const QmlDomValue &other) +: d(other.d) +{ +} + +/*! + Destroy the QmlDomValue +*/ +QmlDomValue::~QmlDomValue() +{ +} + +/*! + Assign \a other to this QmlDomValue. +*/ +QmlDomValue &QmlDomValue::operator=(const QmlDomValue &other) +{ + d = other.d; + return *this; +} + +/*! + \enum QmlDomValue::Type + + The type of the QmlDomValue node. + + \value Invalid The QmlDomValue is invalid. + \value Literal The QmlDomValue is a literal value assignment. Use QmlDomValue::toLiteral() to access the type instance. + \value PropertyBinding The QmlDomValue is a property binding. Use QmlDomValue::toBinding() to access the type instance. + \value ValueSource The QmlDomValue is a property value source. Use QmlDomValue::toValueSource() to access the type instance. + \value Object The QmlDomValue is an object assignment. Use QmlDomValue::toObject() to access the type instnace. + \value List The QmlDomValue is a list of other values. Use QmlDomValue::toList() to access the type instance. +*/ + +/*! + Returns the type of this QmlDomValue. +*/ +QmlDomValue::Type QmlDomValue::type() const +{ + if(d->property) + if(QmlMetaType::isList(d->property->type) || + QmlMetaType::isQmlList(d->property->type) || + (d->property && d->property->values.count() > 1)) + return List; + + QmlParser::Value *value = d->value; + if (!value && !d->property) + return Invalid; + + switch(value->type) { + case QmlParser::Value::Unknown: + return Invalid; + case QmlParser::Value::Literal: + return Literal; + case QmlParser::Value::PropertyBinding: + return PropertyBinding; + case QmlParser::Value::ValueSource: + return ValueSource; + case QmlParser::Value::Component: + case QmlParser::Value::CreatedObject: + return Object; + case QmlParser::Value::SignalObject: + return Invalid; + case QmlParser::Value::SignalExpression: + return Literal; + case QmlParser::Value::Id: + return Invalid; + } + return Invalid; +} + +/*! + Returns true if this is an invalid value, otherwise false. +*/ +bool QmlDomValue::isInvalid() const +{ + return type() == Invalid; +} + +/*! + Returns true if this is a literal value, otherwise false. +*/ +bool QmlDomValue::isLiteral() const +{ + return type() == Literal; +} + +/*! + Returns true if this is a property binding value, otherwise false. +*/ +bool QmlDomValue::isBinding() const +{ + return type() == PropertyBinding; +} + +/*! + Returns true if this is a value source value, otherwise false. +*/ +bool QmlDomValue::isValueSource() const +{ + return type() == ValueSource; +} + +/*! + Returns true if this is an object value, otherwise false. +*/ +bool QmlDomValue::isObject() const +{ + return type() == Object; +} + +/*! + Returns true if this is a list value, otherwise false. +*/ +bool QmlDomValue::isList() const +{ + return type() == List; +} + +/*! + Returns a QmlDomValueLiteral if this value is a literal type, otherwise + returns an invalid QmlDomValueLiteral. + + \sa QmlDomValue::type() +*/ +QmlDomValueLiteral QmlDomValue::toLiteral() const +{ + QmlDomValueLiteral rv; + if(type() == Literal) { + rv.d->value = d->value; + rv.d->value->addref(); + } + return rv; +} + +/*! + Returns a QmlDomValueBinding if this value is a property binding type, + otherwise returns an invalid QmlDomValueBinding. + + \sa QmlDomValue::type() +*/ +QmlDomValueBinding QmlDomValue::toBinding() const +{ + QmlDomValueBinding rv; + if(type() == PropertyBinding) { + rv.d->value = d->value; + rv.d->value->addref(); + } + return rv; +} + +/*! + Returns a QmlDomValueValueSource if this value is a property value source + type, otherwise returns an invalid QmlDomValueValueSource. + + \sa QmlDomValue::type() +*/ +QmlDomValueValueSource QmlDomValue::toValueSource() const +{ + QmlDomValueValueSource rv; + if(type() == ValueSource) { + rv.d->value = d->value; + rv.d->value->addref(); + } + return rv; +} + +/*! + Returns a QmlDomObject if this value is an object assignment type, otherwise + returns an invalid QmlDomObject. + + \sa QmlDomValue::type() +*/ +QmlDomObject QmlDomValue::toObject() const +{ + QmlDomObject rv; + if(type() == Object) { + rv.d->object = d->value->object; + rv.d->object->addref(); + } + return rv; +} + +/*! + Returns a QmlDomList if this value is a list type, otherwise returns an + invalid QmlDomList. + + \sa QmlDomValue::type() +*/ +QmlDomList QmlDomValue::toList() const +{ + QmlDomList rv; + if(type() == List) { + rv.d = d; + } + return rv; +} + +/*! + \class QmlDomList + \brief The QmlDomList class represents a list of values assigned to a QML property. + + Lists of values can be assigned to properties. For example, the following + example assigns multiple objects to Item's "children" property + \code + <Item> + <children> + <Text /> + <Rect /> + </children> + </Item> + \endcode + + Lists can also be implicitly created by assigning multiple + \l {QmlDomValueValueSource}{value sources} or constants to a property. + \code + <Item x="10"> + <x> + <NumericAnimation running="false" from="0" to="100" /> + </x> + </Item> + \endcode +*/ + +/*! + Construct an empty QmlDomList. +*/ +QmlDomList::QmlDomList() +{ +} + +/*! + Create a copy of \a other QmlDomList. +*/ +QmlDomList::QmlDomList(const QmlDomList &other) +: d(other.d) +{ +} + +/*! + Destroy the QmlDomList. +*/ +QmlDomList::~QmlDomList() +{ +} + +/*! + Assign \a other to this QmlDomList. +*/ +QmlDomList &QmlDomList::operator=(const QmlDomList &other) +{ + d = other.d; + return *this; +} + +/*! + Returns the list of QmlDomValue's. +*/ +QList<QmlDomValue> QmlDomList::values() const +{ + QList<QmlDomValue> rv; + if(!d->property) + return rv; + + for(int ii = 0; ii < d->property->values.count(); ++ii) { + QmlDomValue v; + v.d->value = d->property->values.at(ii); + v.d->value->addref(); + rv << v; + } + + return rv; +} + +/*! + Set the list of QmlDomValue's to \a values. +*/ +void QmlDomList::setValues(const QList<QmlDomValue> &values) +{ + Q_UNUSED(values); + qWarning("QmlDomList::setValues(const QList<QmlDomValue> &): Not implemented"); +} + + +/*! + \class QmlDomComponent + \brief The QmlDomComponent class represents sub-component within a QML document. + + Sub-components are QmlComponents defined within a QML document. The + following example shows the definition of a sub-component with the id + "ListDelegate". + + \code + <Item> + <Component id="ListDelegate"> + <Text text="{modelData.text}" /> + </Component> + </Item> + \endcode + + Like QmlDomDocument's, components contain a single root object. +*/ + +/*! + Construct an empty QmlDomComponent. +*/ +QmlDomComponent::QmlDomComponent() +{ +} + +/*! + Create a copy of \a other QmlDomComponent. +*/ +QmlDomComponent::QmlDomComponent(const QmlDomComponent &other) +: QmlDomObject(other) +{ +} + +/*! + Destroy the QmlDomComponent. +*/ +QmlDomComponent::~QmlDomComponent() +{ +} + +/*! + Assign \a other to this QmlDomComponent. +*/ +QmlDomComponent &QmlDomComponent::operator=(const QmlDomComponent &other) +{ + static_cast<QmlDomObject &>(*this) = other; + return *this; +} + +/*! + Returns the component's root object. + + In the example below, the root object is the "Text" object. + \code + <Item> + <Component id="ListDelegate"> + <Text text="{modelData.text}" /> + </Component> + </Item> + \endcode +*/ +QmlDomObject QmlDomComponent::componentRoot() const +{ + QmlDomObject rv; + if(d->isVirtualComponent) { + rv.d->object = d->object; + rv.d->object->addref(); + } else if(d->object) { + QmlParser::Object *obj = 0; + if(d->object->defaultProperty && + d->object->defaultProperty->values.count() == 1 && + d->object->defaultProperty->values.at(0)->object) + obj = d->object->defaultProperty->values.at(0)->object; + + if(obj) { + rv.d->object = obj; + rv.d->object->addref(); + } + } + + return rv; +} + +/*! + Set the component's \a root object. +*/ +void QmlDomComponent::setComponentRoot(const QmlDomObject &root) +{ + Q_UNUSED(root); + qWarning("QmlDomComponent::setComponentRoot(const QmlDomObject &): Not implemented"); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qmldom.h b/src/declarative/qml/qmldom.h new file mode 100644 index 0000000..47a89d9 --- /dev/null +++ b/src/declarative/qml/qmldom.h @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLDOM_H +#define QMLDOM_H + +#include <QtCore/qlist.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QString; +class QByteArray; +class QmlDomObject; +class QmlDomList; +class QmlDomValue; +class QmlEngine; +class QmlDomComponent; +class QIODevice; + +class QmlDomDocumentPrivate; +class Q_DECLARATIVE_EXPORT QmlDomDocument +{ +public: + QmlDomDocument(); + QmlDomDocument(const QmlDomDocument &); + ~QmlDomDocument(); + QmlDomDocument &operator=(const QmlDomDocument &); + + int version() const; + + QString loadError() const; + bool load(QmlEngine *, const QByteArray &); + QByteArray save() const; + + QmlDomObject rootObject() const; +private: + QSharedDataPointer<QmlDomDocumentPrivate> d; +}; + +class QmlDomPropertyPrivate; +class Q_DECLARATIVE_EXPORT QmlDomProperty +{ +public: + QmlDomProperty(); + QmlDomProperty(const QmlDomProperty &); + ~QmlDomProperty(); + QmlDomProperty &operator=(const QmlDomProperty &); + + QByteArray propertyName() const; + QList<QByteArray> propertyNameParts() const; + + bool isDefaultProperty() const; + + QmlDomValue value() const; + void setValue(const QmlDomValue &); + +private: + friend class QmlDomObject; + QSharedDataPointer<QmlDomPropertyPrivate> d; +}; + +class QmlDomObjectPrivate; +class Q_DECLARATIVE_EXPORT QmlDomObject +{ +public: + QmlDomObject(); + QmlDomObject(const QByteArray &); + QmlDomObject(const QmlDomObject &); + ~QmlDomObject(); + QmlDomObject &operator=(const QmlDomObject &); + + bool isValid() const; + + QByteArray objectType() const; + QByteArray objectId() const; + + void setObjectId(const QByteArray &); + + QList<QmlDomProperty> properties() const; + QmlDomProperty property(const QByteArray &) const; + + void removeProperty(const QByteArray &); + void addProperty(const QByteArray &, const QmlDomValue &); + + bool isCustomType() const; + QByteArray customTypeData() const; + void setCustomTypeData(const QByteArray &); + + bool isComponent() const; + QmlDomComponent toComponent() const; + +private: + friend class QmlDomDocument; + friend class QmlDomComponent; + friend class QmlDomValue; + friend class QmlDomValueValueSource; + QSharedDataPointer<QmlDomObjectPrivate> d; +}; + +class QmlDomValuePrivate; +class QmlDomBasicValuePrivate; +class Q_DECLARATIVE_EXPORT QmlDomValueLiteral +{ +public: + QmlDomValueLiteral(); + QmlDomValueLiteral(const QmlDomValueLiteral &); + ~QmlDomValueLiteral(); + QmlDomValueLiteral &operator=(const QmlDomValueLiteral &); + + QString literal() const; + void setLiteral(const QString &); + +private: + friend class QmlDomValue; + QSharedDataPointer<QmlDomBasicValuePrivate> d; +}; + +class Q_DECLARATIVE_EXPORT QmlDomValueBinding +{ +public: + QmlDomValueBinding(); + QmlDomValueBinding(const QmlDomValueBinding &); + ~QmlDomValueBinding(); + QmlDomValueBinding &operator=(const QmlDomValueBinding &); + + QString binding() const; + void setBinding(const QString &); + +private: + friend class QmlDomValue; + QSharedDataPointer<QmlDomBasicValuePrivate> d; +}; + +class Q_DECLARATIVE_EXPORT QmlDomValueValueSource +{ +public: + QmlDomValueValueSource(); + QmlDomValueValueSource(const QmlDomValueValueSource &); + ~QmlDomValueValueSource(); + QmlDomValueValueSource &operator=(const QmlDomValueValueSource &); + + QmlDomObject object() const; + void setObject(const QmlDomObject &); + +private: + friend class QmlDomValue; + QSharedDataPointer<QmlDomBasicValuePrivate> d; +}; + +class Q_DECLARATIVE_EXPORT QmlDomComponent : public QmlDomObject +{ +public: + QmlDomComponent(); + QmlDomComponent(const QmlDomComponent &); + ~QmlDomComponent(); + QmlDomComponent &operator=(const QmlDomComponent &); + + QmlDomObject componentRoot() const; + void setComponentRoot(const QmlDomObject &); +}; + +class Q_DECLARATIVE_EXPORT QmlDomValue +{ +public: + enum Type { + Invalid, + Literal, + PropertyBinding, + ValueSource, + Object, + List + }; + + QmlDomValue(); + QmlDomValue(const QmlDomValue &); + ~QmlDomValue(); + QmlDomValue &operator=(const QmlDomValue &); + + Type type() const; + + bool isInvalid() const; + bool isLiteral() const; + bool isBinding() const; + bool isValueSource() const; + bool isObject() const; + bool isList() const; + + QmlDomValueLiteral toLiteral() const; + QmlDomValueBinding toBinding() const; + QmlDomValueValueSource toValueSource() const; + QmlDomObject toObject() const; + QmlDomList toList() const; + +private: + friend class QmlDomProperty; + friend class QmlDomList; + QSharedDataPointer<QmlDomValuePrivate> d; +}; + +class Q_DECLARATIVE_EXPORT QmlDomList +{ +public: + QmlDomList(); + QmlDomList(const QmlDomList &); + ~QmlDomList(); + QmlDomList &operator=(const QmlDomList &); + + QList<QmlDomValue> values() const; + void setValues(const QList<QmlDomValue> &); + +private: + friend class QmlDomValue; + QSharedDataPointer<QmlDomValuePrivate> d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLDOM_H diff --git a/src/declarative/qml/qmldom_p.h b/src/declarative/qml/qmldom_p.h new file mode 100644 index 0000000..8ea56bf --- /dev/null +++ b/src/declarative/qml/qmldom_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLDOM_P_H +#define QMLDOM_P_H + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +#include "qmlparser_p.h" + +class QmlDomDocumentPrivate : public QSharedData +{ +public: + QmlDomDocumentPrivate(); + QmlDomDocumentPrivate(const QmlDomDocumentPrivate &); + ~QmlDomDocumentPrivate(); + + QString error; + QmlParser::Object *root; +}; + +class QmlDomObjectPrivate : public QSharedData +{ +public: + QmlDomObjectPrivate(); + QmlDomObjectPrivate(const QmlDomObjectPrivate &); + ~QmlDomObjectPrivate(); + + typedef QList<QPair<QmlParser::Property *, QByteArray> > Properties; + Properties properties() const; + Properties properties(QmlParser::Property *) const; + + QmlParser::Object *object; + bool isVirtualComponent; +}; + +class QmlDomPropertyPrivate : public QSharedData +{ +public: + QmlDomPropertyPrivate(); + QmlDomPropertyPrivate(const QmlDomPropertyPrivate &); + ~QmlDomPropertyPrivate(); + + QByteArray propertyName; + QmlParser::Property *property; +}; + +class QmlDomValuePrivate : public QSharedData +{ +public: + QmlDomValuePrivate(); + QmlDomValuePrivate(const QmlDomValuePrivate &); + ~QmlDomValuePrivate(); + + QmlParser::Property *property; + QmlParser::Value *value; +}; + +class QmlDomBasicValuePrivate : public QSharedData +{ +public: + QmlDomBasicValuePrivate(); + QmlDomBasicValuePrivate(const QmlDomBasicValuePrivate &); + ~QmlDomBasicValuePrivate(); + + QmlParser::Value *value; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLDOM_P_H + diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp new file mode 100644 index 0000000..63f45b4 --- /dev/null +++ b/src/declarative/qml/qmlengine.cpp @@ -0,0 +1,1295 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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$ +** +****************************************************************************/ + +// XXX ;) +#define private public +#include <QMetaProperty> +#undef private + +#include <private/qmlengine_p.h> +#include <private/qmlcontext_p.h> + +#ifdef QT_SCRIPTTOOLS_LIB +#include <QScriptEngineDebugger> +#endif + +#include <QScriptClass> +#include <QNetworkReply> +#include <QNetworkRequest> +#include <QNetworkAccessManager> +#include <QList> +#include <QPair> +#include <QDebug> +#include <QMetaObject> +#include "qml.h" +#include <qfxperf.h> +#include <QStack> +#include "private/qmlbasicscript_p.h" +#include "private/qmlcompiledcomponent_p.h" +#include "qmlengine.h" +#include "qmlcontext.h" +#include "qmlexpression.h" +#include <QtCore/qthreadstorage.h> +#include <QtCore/qthread.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdir.h> +#include <qmlcomponent.h> +#include "private/qmlmetaproperty_p.h" + + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(bindValueDebug, QML_BINDVALUE_DEBUG); +#ifdef QT_SCRIPTTOOLS_LIB +DEFINE_BOOL_CONFIG_OPTION(debuggerEnabled, QML_DEBUGGER); +#endif + +Q_DECLARE_METATYPE(QmlMetaProperty); + +QML_DEFINE_TYPE(QObject,Object); + +static QScriptValue qmlMetaProperty_emit(QScriptContext *ctx, QScriptEngine *engine) +{ + QmlMetaProperty mp = qscriptvalue_cast<QmlMetaProperty>(ctx->thisObject()); + if(mp.type() & QmlMetaProperty::Signal) + mp.emitSignal(); + return engine->nullValue(); +} + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; } +}; + + +struct QmlEngineStack { + QmlEngineStack(); + + QStack<QmlEngine *> mainThreadEngines; + QThread *mainThread; + + QThreadStorage<QStack<QmlEngine *> *> storage; + + QStack<QmlEngine *> *engines(); +}; + +Q_GLOBAL_STATIC(QmlEngineStack, engineStack); + +QmlEngineStack::QmlEngineStack() +: mainThread(0) +{ +} + +QStack<QmlEngine *> *QmlEngineStack::engines() +{ + if(mainThread== 0) { + if(!QCoreApplication::instance()) + return 0; + mainThread = QCoreApplication::instance()->thread(); + } + + // Note: This is very slightly faster than just using the thread storage + // for everything. + QStack<QmlEngine *> *engines = 0; + if(QThread::currentThread() == mainThread) { + engines = &mainThreadEngines; + } else { + engines = storage.localData(); + if(!engines) { + engines = new QStack<QmlEngine *>; + storage.setLocalData(engines); + } + } + return engines; +} + + +QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) +: rootContext(0), currentBindContext(0), currentExpression(0), q(e), + rootComponent(0), networkAccessManager(0), typeManager(e) +{ + QScriptValue proto = scriptEngine.newObject(); + proto.setProperty(QLatin1String("emit"), + scriptEngine.newFunction(qmlMetaProperty_emit)); + scriptEngine.setDefaultPrototype(qMetaTypeId<QmlMetaProperty>(), proto); + + QScriptValue qtObject = scriptEngine.newQMetaObject(StaticQtMetaObject::get()); + scriptEngine.globalObject().setProperty(QLatin1String("Qt"), qtObject); +} + +QmlEnginePrivate::~QmlEnginePrivate() +{ + delete rootContext; + rootContext = 0; + delete contextClass; + contextClass = 0; + delete objectClass; + objectClass = 0; + delete networkAccessManager; + networkAccessManager = 0; +} + +void QmlEnginePrivate::init() +{ + contextClass = new QmlContextScriptClass(q); + objectClass = new QmlObjectScriptClass(q); + rootContext = new QmlContext(q); +#ifdef QT_SCRIPTTOOLS_LIB + if(debuggerEnabled()){ + debugger = new QScriptEngineDebugger(q); + debugger->attachTo(&scriptEngine); + } +#endif +} + +QmlContext *QmlEnginePrivate::setCurrentBindContext(QmlContext *c) +{ + QmlContext *old = currentBindContext; + currentBindContext = c; + return old; +} + +//////////////////////////////////////////////////////////////////// +typedef QHash<QPair<const QMetaObject *, QString>, bool> FunctionCache; +Q_GLOBAL_STATIC(FunctionCache, functionCache); + +QScriptClass::QueryFlags +QmlEnginePrivate::queryObject(const QString &propName, + uint *id, QObject *obj) +{ + QScriptClass::QueryFlags rv = 0; + + QmlMetaProperty prop(obj, propName); + if(prop.type() == QmlMetaProperty::Invalid) { + QPair<const QMetaObject *, QString> key = + qMakePair(obj->metaObject(), propName); + bool isFunction = false; + if (functionCache()->contains(key)) { + isFunction = functionCache()->value(key); + } else { + QScriptValue sobj = scriptEngine.newQObject(obj); + QScriptValue func = sobj.property(propName); + isFunction = func.isFunction(); + functionCache()->insert(key, isFunction); + } + + if(isFunction) { + *id = QmlScriptClass::FunctionId; + rv |= QScriptClass::HandlesReadAccess; + } + } else { + *id = QmlScriptClass::PropertyId; + *id |= prop.save(); + + rv |= QScriptClass::HandlesReadAccess; + if(prop.isWritable()) + rv |= QScriptClass::HandlesWriteAccess; + } + + return rv; +} + +QScriptValue QmlEnginePrivate::propertyObject(const QScriptString &propName, + QObject *obj, uint id) +{ + if(id == QmlScriptClass::FunctionId) { + QScriptValue sobj = scriptEngine.newQObject(obj); + QScriptValue func = sobj.property(propName); + return func; + } else { + QmlMetaProperty prop; + prop.restore(id, obj); + if(!prop.isValid()) + return QScriptValue(); + + if(prop.type() & QmlMetaProperty::Signal) { + return scriptEngine.newVariant(qVariantFromValue(prop)); + } else { + QVariant var = prop.read(); + capturedProperties << prop; + QObject *varobj = QmlMetaType::toQObject(var); + if(!varobj) + varobj = qvariant_cast<QObject *>(var); + if(varobj) { + return scriptEngine.newObject(objectClass, scriptEngine.newVariant(QVariant::fromValue(varobj))); + } else { + if (var.type() == QVariant::Bool) + return QScriptValue(&scriptEngine, var.toBool()); + return scriptEngine.newVariant(var); + } + } + } + + return QScriptValue(); +} + +void QmlEnginePrivate::contextActivated(QmlContext *) +{ + Q_Q(QmlEngine); + QmlEngineStack *stack = engineStack(); + if(!stack) + return; + QStack<QmlEngine *> *engines = stack->engines(); + if(engines) + engines->push(q); +} + +void QmlEnginePrivate::contextDeactivated(QmlContext *) +{ + QmlEngineStack *stack = engineStack(); + if(!stack) + return; + QStack<QmlEngine *> *engines = stack->engines(); + if(engines) { + Q_ASSERT(engines->top() == q_func()); + engines->pop(); + } +} + + +//////////////////////////////////////////////////////////////////// + +bool QmlEnginePrivate::fetchCache(QmlBasicScriptNodeCache &cache, const QString &propName, QObject *obj) +{ + QmlMetaProperty prop(obj, propName); + + if(!prop.isValid()) + return false; + + capturedProperties << prop; + + if(prop.type() & QmlMetaProperty::Attached) { + + cache.object = obj; + cache.type = QmlBasicScriptNodeCache::Attached; + cache.attached = prop.d->attachedObject(); + return true; + + } else if(prop.type() & QmlMetaProperty::Property) { + + cache.object = obj; + cache.type = QmlBasicScriptNodeCache::Core; + cache.core = prop.property().idx + prop.property().mobj->propertyOffset(); + cache.coreType = prop.propertyType(); + return true; + + } else if(prop.type() & QmlMetaProperty::SignalProperty) { + + cache.object = obj; + cache.type = QmlBasicScriptNodeCache::SignalProperty; + cache.core = prop.coreIndex(); + return true; + + } else if(prop.type() & QmlMetaProperty::Signal) { + + cache.object = obj; + cache.type = QmlBasicScriptNodeCache::Signal; + cache.core = prop.coreIndex(); + return true; + + } + + return false; +} + +bool QmlEnginePrivate::loadCache(QmlBasicScriptNodeCache &cache, const QString &propName, QmlContextPrivate *context) +{ + while(context) { + if(context->variantProperties.contains(propName)) { + cache.object = 0; + cache.type = QmlBasicScriptNodeCache::Variant; + cache.context = context; + return true; + } + + if(context->properties.contains(propName)) { + cache.object = context->properties[propName]; + cache.type = QmlBasicScriptNodeCache::Explicit; + return true; + } + + foreach(QObject *obj, context->defaultObjects) { + if(fetchCache(cache, propName, obj)) + return true; + } + + if(context->parent) + context = context->parent->d_func(); + else + context = 0; + } + return false; +} + + +/*! + \class QmlEngine + \brief The QmlEngine class provides an environment for instantiating QML components. + \mainclass + + Each QML component is instantiated in a QmlContext. QmlContext's are + essential for passing data to QML components. In QML, contexts are arranged + hierarchically and this hierarchy is managed by the QmlEngine. + + Prior to creating any QML components, an application must have created a + QmlEngine to gain access to a QML context. The following example shows how + to create a simple Text item. + + \code + QmlEngine engine; + QmlComponent component("<Text text=\"Hello world!\"/>"); + QFxItem *item = qobject_cast<QFxItem *>(component.create(&engine)); + + //add item to view, etc + ... + \endcode + + In this case, the Text item will be created in the engine's + \l {QmlEngine::rootContext()}{root context}. + + \sa QmlComponent QmlContext +*/ + +/*! + Create a new QmlEngine with the given \a parent. +*/ +QmlEngine::QmlEngine(QObject *parent) +: QObject(*new QmlEnginePrivate(this), parent) +{ + Q_D(QmlEngine); + d->init(); + + qRegisterMetaType<QVariant>("QVariant"); +} + +/*! + Destroys the QmlEngine. + + Any QmlContext's created on this engine will be invalidated, but not + destroyed (unless they are parented to the QmlEngine object). +*/ +QmlEngine::~QmlEngine() +{ +} + +void QmlEngine::clearComponentCache() +{ + Q_D(QmlEngine); + d->typeManager.clearCache(); +} + +/*! + Returns the engine's root context. + + The root context is automatically created by the QmlEngine. Data that + should be available to all QML component instances instantiated by the + engine should be put in the root context. + + Additional data that should only be available to a subset of component + instances should be added to sub-contexts parented to the root context. +*/ +QmlContext *QmlEngine::rootContext() +{ + Q_D(QmlEngine); + return d->rootContext; +} + +/*! + Returns this engine's active context, or 0 if no context is active on this + engine. + + Contexts are activated and deactivated by calling QmlContext::activate() and + QmlContext::deactivate() respectively. + + Context activation holds no special semantic, other than it allows types + instantiated by QML to access "their" context without having it passed as + a parameter in their constructor, as shown below. + \code + class MyClass : ... { + ... + MyClass() { + qWarning() << "I was instantiated in this context:" + << QmlContext::activeContext(); + } + }; + \endcode +*/ +QmlContext *QmlEngine::activeContext() +{ + Q_D(QmlEngine); + if(d->currentBindContext) + return d->currentBindContext; + else + return 0; +} + +/*! + Sets the mappings from namespace URIs to URL to \a map. + + \sa nameSpacePaths +*/ +void QmlEngine::setNameSpacePaths(const QMap<QString,QString>& map) +{ + Q_D(QmlEngine); + d->nameSpacePaths = map; +} + +/*! + Adds mappings (given by \a map) from namespace URIs to URL. + + \sa nameSpacePaths +*/ +void QmlEngine::addNameSpacePaths(const QMap<QString,QString>& map) +{ + Q_D(QmlEngine); + d->nameSpacePaths.unite(map); +} + +/*! + Adds a mapping from namespace URI \a ns to URL \a path. + + \sa nameSpacePaths +*/ +void QmlEngine::addNameSpacePath(const QString& ns, const QString& path) +{ + Q_D(QmlEngine); + d->nameSpacePaths.insertMulti(ns,path); +} + +/*! + Returns the mapping from namespace URIs to URLs. + + Namespaces in QML allow types to be specified by a URI, + using standard XML namespaces: + + \code + <Item xmlns:foo="xyz://abc/def"> + <foo:Bar/> + </Item> + \endcode + + Actual QML types can be defined in URLs, in which case a mapping + may be made from URIs (such as "xyz://abc/def/Bar.qml" above, to + URLs (such as "file:///opt/abcdef/Bar.qml"): + + \code + engine->addNameSpacePath("xyz://abc/def","file:///opt/abcdef"); + \endcode + + If only a prefix of the URI is mapped, the path of the URI is + mapped similarly to the URL: + + \code + engine->addNameSpacePath("xyz://abc","file:///opt/jkl"); + \endcode + + In the above case, "xyz://abc/def/Bar.qml" would then map to + "file:///opt/jkl/def/Bar.qml". + + \sa componentUrl +*/ +QMap<QString,QString> QmlEngine::nameSpacePaths() const +{ + Q_D(const QmlEngine); + return d->nameSpacePaths; +} + +/*! + Returns the URL for the component source \a src, as mapped + by the nameSpacePaths(), resolved relative to \a baseUrl. + + \sa nameSpacePaths +*/ +QUrl QmlEngine::componentUrl(const QUrl& src, const QUrl& baseUrl) const +{ + Q_D(const QmlEngine); + + // Find the most-specific namespace matching src. + // For files, multiple paths can be given, the first found is used. + QUrl r; + QMap<QString, QString>::const_iterator i = d->nameSpacePaths.constBegin(); + QString rns=QLatin1String(":"); // ns of r, if file found, initial an imposible namespace + QString srcstring = src.toString(); + while (i != d->nameSpacePaths.constEnd()) { + QString ns = i.key(); + QString path = i.value(); + if (ns != rns) { + if (srcstring.startsWith(ns) && (ns.length()==0 || srcstring[ns.length()]==QLatin1Char('/'))) { + QString file = ns.length()==0 ? srcstring : srcstring.mid(ns.length()+1); + QUrl cr = baseUrl.resolved(QUrl(path + QLatin1String("/") + file)); + QString lf = cr.toLocalFile(); + if (lf.isEmpty() || QFile::exists(lf)) { + r = cr; + rns = ns; + } + } + } + ++i; + } + if (r.isEmpty()) + r = baseUrl.resolved(src); + return r; +} + +/*! + Sets the common QNetworkAccessManager, \a network, used by all QML elements instantiated + by this engine. + + Any previously set manager is deleted and \a network is owned by the QmlEngine. This + method should only be called before any QmlComponents are instantiated. +*/ +void QmlEngine::setNetworkAccessManager(QNetworkAccessManager *network) +{ + Q_D(QmlEngine); + delete d->networkAccessManager; + d->networkAccessManager = network; +} + +/*! + Returns the common QNetworkAccessManager used by all QML elements instantiated by + this engine. + + The default implements no caching, cookiejar, etc., just a default + QNetworkAccessManager. +*/ +QNetworkAccessManager *QmlEngine::networkAccessManager() const +{ + Q_D(const QmlEngine); + if(!d->networkAccessManager) + d->networkAccessManager = new QNetworkAccessManager; + return d->networkAccessManager; +} + +/*! \internal */ +QScriptEngine *QmlEngine::scriptEngine() +{ + Q_D(QmlEngine); + return &d->scriptEngine; +} + +/*! + Returns the currently active QmlEngine. + + The active engine is the engine associated with the last activated + QmlContext. This method is thread-safe - the "active" engine is maintained + independently for each thread. +*/ +QmlEngine *QmlEngine::activeEngine() +{ + QmlEngineStack *stack = engineStack(); + if(!stack) return 0; + + QStack<QmlEngine *> *engines = stack->engines(); + if(!engines) { + qWarning("QmlEngine::activeEngine() cannot be called before the construction of QCoreApplication"); + return 0; + } + + if(engines->isEmpty()) + return 0; + else + return engines->top(); +} + + + +QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b) +: q(b), ctxt(0), sseData(0), proxy(0), me(0), trackChange(false) +{ +} + +QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, void *expr, QmlRefCount *rc) +: q(b), ctxt(0), sse((const char *)expr, rc), sseData(0), proxy(0), me(0), trackChange(true) +{ +} + +QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, const QString &expr, bool ssecompile) +: q(b), ctxt(0), expression(expr), sseData(0), proxy(0), me(0), trackChange(true) +{ + if(ssecompile) { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BindCompile> pt; +#endif + sse.compile(expr.toLatin1()); + } +} + +QmlExpressionPrivate::~QmlExpressionPrivate() +{ + sse.deleteScriptState(sseData); + sseData = 0; + delete proxy; +} + +/*! + Create an invalid QmlExpression. + + As the expression will not have an associated QmlContext, this will be a + null expression object and its value will always be an invalid QVariant. + */ +QmlExpression::QmlExpression() +: d(new QmlExpressionPrivate(this)) +{ +} + +/*! \internal */ +QmlExpression::QmlExpression(QmlContext *ctxt, void *expr, + QmlRefCount *rc, QObject *me) +: d(new QmlExpressionPrivate(this, expr, rc)) +{ + d->ctxt = ctxt; + d->me = me; +} + +/*! \internal */ +QmlExpression::QmlExpression(QmlContext *ctxt, const QString &expr, + QObject *me, bool ssecompile) +: d(new QmlExpressionPrivate(this, expr, ssecompile)) +{ + d->ctxt = ctxt; + d->me = me; +} + +/*! + Create a QmlExpression object. + + The \a expression ECMAScript will be executed in the \a ctxt QmlContext. + If specified, the \a scope object's properties will also be in scope during + the expression's execution. +*/ +QmlExpression::QmlExpression(QmlContext *ctxt, const QString &expression, + QObject *scope) +: d(new QmlExpressionPrivate(this, expression, true)) +{ + d->ctxt = ctxt; + d->me = scope; +} + +/*! + Destroy the QmlExpression instance. +*/ +QmlExpression::~QmlExpression() +{ + delete d; d = 0; +} + +/*! + Returns the QmlEngine this expression is associated with, or 0 if there + is no association or the QmlEngine has been destroyed. +*/ +QmlEngine *QmlExpression::engine() const +{ + return d->ctxt->engine(); +} + +/*! + Returns teh QmlContext this expression is associated with, or 0 if there + is no association or the QmlContext has been destroyed. +*/ +QmlContext *QmlExpression::context() const +{ + return d->ctxt; +} + +/*! + Returns the expression string. +*/ +QString QmlExpression::expression() const +{ + if(d->sse.isValid()) + return QLatin1String(d->sse.expression()); + else + return d->expression; +} + +/*! + Clear the expression. +*/ +void QmlExpression::clearExpression() +{ + setExpression(QString()); +} + +/*! + Set the expression to \a expression. +*/ +void QmlExpression::setExpression(const QString &expression) +{ + if(d->sseData) { + d->sse.deleteScriptState(d->sseData); + d->sseData = 0; + } + + delete d->proxy; d->proxy = 0; + + d->expression = expression; + + if(d->expression.isEmpty()) + d->sse.clear(); + else + d->sse.compile(expression.toLatin1()); +} + +/*! + Called by QmlExpression each time the expression value changes from the + last time it was evaluated. The expression must have been evaluated at + least once (by calling QmlExpression::value()) before this callback will + be made. + + The default implementation does nothing. +*/ +void QmlExpression::valueChanged() +{ +} + +Q_DECLARE_METATYPE(QList<QObject *>); + +void BindExpressionProxy::changed() +{ + e->valueChanged(); +} + +/*! + Returns the value of the expression, or an invalid QVariant if the + expression is invalid or has an error. +*/ +QVariant QmlExpression::value() +{ + if(bindValueDebug()) + qWarning() << "QmlEngine: Evaluating:" << expression(); + QVariant rv; + if(!d->ctxt || (!d->sse.isValid() && d->expression.isEmpty())) + return rv; + +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BindValue> perf; +#endif + + QmlBasicScript::CacheState cacheState = QmlBasicScript::Reset; + + QmlEnginePrivate *ep = engine()->d_func(); + QmlExpression *lastCurrentExpression = ep->currentExpression; + ep->currentExpression = this; + if(d->sse.isValid()) { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BindValueSSE> perfsse; +#endif + + context()->d_func()->defaultObjects.insert(context()->d_func()->highPriorityCount, d->me); + + if(!d->sseData) + d->sseData = d->sse.newScriptState(); + rv = d->sse.run(context(), d->sseData, &cacheState); + + context()->d_func()->defaultObjects.removeAt(context()->d_func()->highPriorityCount); + } else { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::BindValueQt> perfqt; +#endif + context()->d_func()->defaultObjects.insert(context()->d_func()->highPriorityCount, d->me); + + QScriptEngine *scriptEngine = engine()->scriptEngine(); + QScriptValueList oldScopeChain = scriptEngine->currentContext()->scopeChain(); + for (int i = 0; i < oldScopeChain.size(); ++i) { + scriptEngine->currentContext()->popScope(); + } + for (int i = context()->d_func()->scopeChain.size() - 1; i > -1; --i) { + scriptEngine->currentContext()->pushScope(context()->d_func()->scopeChain.at(i)); + } + QScriptValue svalue = scriptEngine->evaluate(expression()); + context()->d_func()->defaultObjects.removeAt(context()->d_func()->highPriorityCount); + if(svalue.isArray()) { + int length = svalue.property(QLatin1String("length")).toInt32(); + if(length && svalue.property(0).isObject()) { + QList<QObject *> list; + for(int ii = 0; ii < length; ++ii) { + QScriptValue arrayItem = svalue.property(ii); + QObject *d = qvariant_cast<QObject *>(arrayItem.data().toVariant()); + if(d) { + list << d; + } else { + list << 0; + } + } + rv = QVariant::fromValue(list); + } + } /*else if (svalue.isVariant()) { + rv = svalue.toVariant(); + }*/ else if (svalue.isObject()) { + QScriptValue objValue = svalue.data(); + if (objValue.isValid()) + rv = objValue.toVariant(); + } + if(rv.isNull()) { + rv = svalue.toVariant(); + } + + for (int i = 0; i < context()->d_func()->scopeChain.size(); ++i) { + scriptEngine->currentContext()->popScope(); + } + for (int i = oldScopeChain.size() - 1; i > -1; --i) { + scriptEngine->currentContext()->pushScope(oldScopeChain.at(i)); + } + } + ep->currentExpression = lastCurrentExpression; + + if(cacheState != QmlBasicScript::NoChange) { + if(cacheState != QmlBasicScript::Incremental && d->proxy) { + delete d->proxy; + d->proxy = 0; + } + + if(trackChange() && ep->capturedProperties.count()) { + if(!d->proxy) + d->proxy = new BindExpressionProxy(this); + + static int changedIndex = -1; + if(changedIndex == -1) + changedIndex = BindExpressionProxy::staticMetaObject.indexOfSlot("changed()"); + + if(bindValueDebug()) + qWarning() << " Depends on:"; + + for(int ii = 0; ii < ep->capturedProperties.count(); ++ii) { + const QmlMetaProperty &prop = + ep->capturedProperties.at(ii); + + if(prop.hasChangedNotifier()) { + prop.connectNotifier(d->proxy, changedIndex); + if(bindValueDebug()) + qWarning() << " property" + << prop.name() + << prop.object() + << prop.object()->metaObject()->superClass()->className(); + } else if(bindValueDebug()) { + qWarning() << " non-subscribable property" + << prop.name() + << prop.object() + << prop.object()->metaObject()->superClass()->className(); + } + } + } + } + ep->capturedProperties.clear(); + + if(bindValueDebug()) + qWarning() << " Result:" << rv + << "(SSE: " << d->sse.isValid() << ")"; + return rv; +} + +/*! + Returns true if the expression results in a constant value. + QmlExpression::value() must have been invoked at least once before the + return from this method is valid. + */ +bool QmlExpression::isConstant() const +{ + return d->proxy == 0; +} + +/*! + Returns true if the changes are tracked in the expression's value. +*/ +bool QmlExpression::trackChange() const +{ + return d->trackChange; +} + +/*! + Set whether changes are tracked in the expression's value to \a trackChange. + + If true, the QmlExpression will monitor properties involved in the + expression's evaluation, and call QmlExpression::valueChanged() if they have + changed. This allows an application to ensure that any value associated + with the result of the expression remains up to date. + + If false, the QmlExpression will not montitor properties involved in the + expression's evaluation, and QmlExpression::valueChanged() will never be + called. This is more efficient if an application wants a "one off" + evaluation of the expression. + + By default, trackChange is true. +*/ +void QmlExpression::setTrackChange(bool trackChange) +{ + d->trackChange = trackChange; +} + +/*! + Returns the expression's scope object, if provided, otherwise 0. + + In addition to data provided by the expression's QmlContext, the scope + object's properties are also in scope during the expression's evaluation. +*/ +QObject *QmlExpression::scopeObject() const +{ + return d->me; +} + +/*! + \class QmlExpression + \brief The QmlExpression class evaluates ECMAScript in a QML context. +*/ + +/*! + \class QmlExpressionObject + \brief The QmlExpressionObject class extends QmlExpression with signals and slots. + + To remain as lightweight as possible, QmlExpression does not inherit QObject + and consequently cannot use signals or slots. For the cases where this is + more convenient in an application, QmlExpressionObject can be used instead. + + QmlExpressionObject behaves identically to QmlExpression, except that the + QmlExpressionObject::value() method is a slot, and the + QmlExpressionObject::valueChanged() callback is a signal. +*/ +/*! + Create a QmlExpression with the specified \a parent. + + As the expression will not have an associated QmlContext, this will be a + null expression object and its value will always be an invalid QVariant. +*/ +QmlExpressionObject::QmlExpressionObject(QObject *parent) +: QObject(parent) +{ +} + +/*! + Create a QmlExpressionObject with the specified \a parent. + + The \a expression ECMAScript will be executed in the \a ctxt QmlContext. + If specified, the \a scope object's properties will also be in scope during + the expression's execution. +*/ +QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, const QString &expression, QObject *scope, QObject *parent) +: QObject(parent), QmlExpression(ctxt, expression, scope, true) +{ +} + +/*! \internal */ +QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, const QString &expr, QObject *scope, bool sse) +: QmlExpression(ctxt, expr, scope, sse) +{ +} + +/*! \internal */ +QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, void *d, QmlRefCount *rc, QObject *me) +: QmlExpression(ctxt, d, rc, me) +{ +} + +/*! + Returns the value of the expression, or an invalid QVariant if the + expression is invalid or has an error. +*/ +QVariant QmlExpressionObject::value() +{ + return QmlExpression::value(); +} + +/*! + \fn void QmlExpressionObject::valueChanged() + + Emitted each time the expression value changes from the last time it was + evaluated. The expression must have been evaluated at least once (by + calling QmlExpressionObject::value()) before this signal will be emitted. +*/ + +QmlScriptClass::QmlScriptClass(QmlEngine *bindengine) +: QScriptClass(bindengine->scriptEngine()), engine(bindengine) +{ +} + +///////////////////////////////////////////////////////////// +/* + The QmlContextScriptClass handles property access for a QmlContext + via QtScript. + */ +QmlContextScriptClass::QmlContextScriptClass(QmlEngine *bindEngine) + : QmlScriptClass(bindEngine) +{ +} + +QmlContextScriptClass::~QmlContextScriptClass() +{ +} + +QScriptClass::QueryFlags +QmlContextScriptClass::queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id) +{ + Q_UNUSED(flags); +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::ContextQuery> perf; +#endif + QmlContext *bindContext = + static_cast<QmlContext*>(object.data().toQObject()); + QueryFlags rv = 0; + + QString propName = name.toString(); + +#ifdef PROPERTY_DEBUG + qWarning() << "Query Context:" << propName << bindContext; +#endif + + *id = InvalidId; + if (bindContext->d_func()->variantProperties.contains(propName)) { + rv |= HandlesReadAccess; + *id = VariantPropertyId; + } else if (bindContext->d_func()->properties.contains(propName)) { + rv |= HandlesReadAccess; + *id = ObjectListPropertyId; + } + + for(int ii = 0; !rv && ii < bindContext->d_func()->defaultObjects.count(); ++ii) { + rv = engine->d_func()->queryObject(propName, id, + bindContext->d_func()->defaultObjects.at(ii)); + if(rv) + *id |= (ii << 24); + } + + return rv; +} + +QScriptValue QmlContextScriptClass::property(const QScriptValue &object, + const QScriptString &name, + uint id) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::ContextProperty> perf; +#endif + QmlContext *bindContext = + static_cast<QmlContext*>(object.data().toQObject()); + +#ifdef PROPERTY_DEBUG + QString propName = name.toString(); + qWarning() << "Context Property:" << propName << bindContext; +#endif + + uint basicId = id & QmlScriptClass::ClassIdMask; + + QScriptEngine *scriptEngine = engine->scriptEngine(); + + switch (basicId) { + case VariantPropertyId: + { + QString propName = name.toString(); + QScriptValue rv = scriptEngine->newVariant(bindContext->d_func()->variantProperties[propName]); +#ifdef PROPERTY_DEBUG + qWarning() << "Context Property: Resolved property" << propName + << "to context variant property list" << bindContext <<". Value:" << rv.toVariant(); +#endif + return rv; + } + case ObjectListPropertyId: + { + QString propName = name.toString(); + QObject *o = bindContext->d_func()->properties[propName]; + QScriptValue rv = scriptEngine->newObject(engine->d_func()->objectClass, scriptEngine->newVariant(QVariant::fromValue(o))); +#ifdef PROPERTY_DEBUG + qWarning() << "Context Property: Resolved property" << propName + << "to context object property list" << bindContext <<". Value:" << rv.toVariant(); +#endif + return rv; + } + default: + { + int objId = (id & ClassIdSelectorMask) >> 24; + QObject *obj = bindContext->d_func()->defaultObjects.at(objId); + QScriptValue rv = engine->d_func()->propertyObject(name, obj, + id & ~QmlScriptClass::ClassIdSelectorMask); + if(rv.isValid()) { +#ifdef PROPERTY_DEBUG + qWarning() << "~Property: Resolved property" << propName + << "to context default object" << bindContext << obj <<". Value:" << rv.toVariant(); +#endif + return rv; + } + break; + } + } + + return QScriptValue(); +} + +void QmlContextScriptClass::setProperty(QScriptValue &object, + const QScriptString &name, + uint id, + const QScriptValue &value) +{ + Q_UNUSED(name); + +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::ObjectSetProperty> perf; +#endif + QmlContext *bindContext = + static_cast<QmlContext*>(object.data().toQObject()); + +#ifdef PROPERTY_DEBUG + QString propName = name.toString(); + qWarning() << "Set QmlObject Property" << name.toString() << value.toVariant(); +#endif + + int objIdx = (id & QmlScriptClass::ClassIdSelectorMask) >> 24; + QObject *obj = bindContext->d_func()->defaultObjects.at(objIdx); + + QScriptEngine *scriptEngine = engine->scriptEngine(); + QScriptValue oldact = scriptEngine->currentContext()->activationObject(); + scriptEngine->currentContext()->setActivationObject(scriptEngine->globalObject()); + + QmlMetaProperty prop; + prop.restore(id, obj); + + QVariant v; + QObject *data = value.data().toQObject(); + if (data) { + v = QVariant::fromValue(data); + } else { + v = value.toVariant(); + } + prop.write(v); + + scriptEngine->currentContext()->setActivationObject(oldact); +} + +///////////////////////////////////////////////////////////// +/* + The QmlObjectScriptClass handles property access for QObjects + via QtScript. + */ +QmlObjectScriptClass::QmlObjectScriptClass(QmlEngine *bindEngine) + : QmlScriptClass(bindEngine) +{ + engine = bindEngine; +} + +QmlObjectScriptClass::~QmlObjectScriptClass() +{ +} + +QScriptClass::QueryFlags QmlObjectScriptClass::queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id) +{ + Q_UNUSED(flags); +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::ObjectQuery> perf; +#endif + QObject *obj = object.data().toQObject(); + QueryFlags rv = 0; + QString propName = name.toString(); + +#ifdef PROPERTY_DEBUG + qWarning() << "Query QmlObject:" << propName << obj; +#endif + + if (obj) + rv = engine->d_func()->queryObject(propName, id, obj); + + return rv; +} + +QScriptValue QmlObjectScriptClass::property(const QScriptValue &object, + const QScriptString &name, + uint id) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::ObjectProperty> perf; +#endif + QObject *obj = object.data().toQObject(); + +#ifdef PROPERTY_DEBUG + QString propName = name.toString(); + qWarning() << "QmlObject Property:" << propName << obj; +#endif + + QScriptValue rv = engine->d_func()->propertyObject(name, obj, id); + if(rv.isValid()) { +#ifdef PROPERTY_DEBUG + qWarning() << "~Property: Resolved property" << propName + << "to object" << obj <<". Value:" << rv.toVariant(); +#endif + return rv; + } + + return QScriptValue(); +} + +void QmlObjectScriptClass::setProperty(QScriptValue &object, + const QScriptString &name, + uint id, + const QScriptValue &value) +{ + Q_UNUSED(name); + +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::ObjectSetProperty> perf; +#endif + QObject *obj = object.data().toQObject(); + +#ifdef PROPERTY_DEBUG + QString propName = name.toString(); + qWarning() << "Set QmlObject Property" << name.toString() << value.toVariant(); +#endif + + QScriptEngine *scriptEngine = engine->scriptEngine(); + QScriptValue oldact = scriptEngine->currentContext()->activationObject(); + scriptEngine->currentContext()->setActivationObject(scriptEngine->globalObject()); + + QmlMetaProperty prop; + prop.restore(id, obj); + + QVariant v; + QObject *data = value.data().toQObject(); + if (data) { + v = QVariant::fromValue(data); + } else { + v = value.toVariant(); + } + prop.write(v); + + scriptEngine->currentContext()->setActivationObject(oldact); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlengine.h b/src/declarative/qml/qmlengine.h new file mode 100644 index 0000000..086595a --- /dev/null +++ b/src/declarative/qml/qmlengine.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLENGINE_H +#define QMLENGINE_H + +#include <QtCore/qobject.h> +#include <QtCore/qmap.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlComponent; +class QmlEnginePrivate; +class QmlExpression; +class QmlContext; +class QUrl; +class QScriptEngine; +class QNetworkAccessManager; +class Q_DECLARATIVE_EXPORT QmlEngine : public QObject +{ + Q_OBJECT +public: + QmlEngine(QObject *p = 0); + virtual ~QmlEngine(); + + static QmlEngine *activeEngine(); + + QmlContext *rootContext(); + QmlContext *activeContext(); + + void clearComponentCache(); + + void setNameSpacePaths(const QMap<QString,QString>& map); + void addNameSpacePaths(const QMap<QString,QString>& map); + void addNameSpacePath(const QString&,const QString&); + QMap<QString,QString> nameSpacePaths() const; + QUrl componentUrl(const QUrl& src, const QUrl& baseUrl) const; + + void setNetworkAccessManager(QNetworkAccessManager *); + QNetworkAccessManager *networkAccessManager() const; + +private: + // LK: move to the private class + QScriptEngine *scriptEngine(); + friend class QFxItem; // XXX + friend class QmlScriptPrivate; + friend class QmlCompositeTypeManager; + friend class QmlCompiler; + friend class QmlScriptClass; + friend class QmlContext; + friend class QmlContextPrivate; + friend class QmlExpression; + friend class QmlBasicScript; + friend class QmlVME; + friend class QmlComponent; + friend class QmlContextScriptClass; //### + friend class QmlObjectScriptClass; //### + Q_DECLARE_PRIVATE(QmlEngine) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLENGINE_H diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h new file mode 100644 index 0000000..b72c680 --- /dev/null +++ b/src/declarative/qml/qmlengine_p.h @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLENGINE_P_H +#define QMLENGINE_P_H + +#include <QScriptClass> +#include <QScriptValue> +#include <QScriptString> +#include <QtCore/qstring.h> +#include <QtCore/qlist.h> +#include <QtCore/qpair.h> +#include <QtCore/qstack.h> +#include <private/qobject_p.h> +#include <private/qmlclassfactory_p.h> +#include <private/qmlcompositetypemanager_p.h> +#include <qml.h> +#include <qmlbasicscript.h> +#include <qmlcontext.h> +#include <qmlengine.h> +#include <qmlexpression.h> +#include <QtScript/qscriptengine.h> + +QT_BEGIN_NAMESPACE +class QmlContext; +class QmlEngine; +class QmlContextPrivate; +class QmlExpression; +class QmlBasicScriptNodeCache; +class QmlContextScriptClass; +class QmlObjectScriptClass; +class QScriptEngineDebugger; +class QNetworkReply; +class QNetworkAccessManager; + +class QmlEnginePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlEngine) +public: + QmlEnginePrivate(QmlEngine *); + ~QmlEnginePrivate(); + + void init(); + + void contextActivated(QmlContext *); + void contextDeactivated(QmlContext *); + + bool fetchCache(QmlBasicScriptNodeCache &cache, const QString &propName, QObject *); + bool loadCache(QmlBasicScriptNodeCache &cache, const QString &propName, QmlContextPrivate *context); + + QScriptClass::QueryFlags queryObject(const QString &name, uint *id, QObject *); + QScriptValue propertyObject(const QScriptString &propName, QObject *, uint id = 0); + + QList<QmlMetaProperty> capturedProperties; + + QmlContext *rootContext; + QmlContext *currentBindContext; + QmlExpression *currentExpression; + QmlEngine *q; +#ifdef QT_SCRIPTTOOLS_LIB + QScriptEngineDebugger *debugger; +#endif + + QmlContextScriptClass *contextClass; + QmlObjectScriptClass *objectClass; + + QmlContext *setCurrentBindContext(QmlContext *); + QStack<QmlContext *> activeContexts; + + QScriptEngine scriptEngine; + + QList<QmlBindableValue *> currentBindValues; + QList<QmlParserStatus *> currentParserStatus; + QmlComponent *rootComponent; + mutable QNetworkAccessManager *networkAccessManager; + + QmlCompositeTypeManager typeManager; + QMap<QString,QString> nameSpacePaths; +}; + + +class BindExpressionProxy : public QObject +{ +Q_OBJECT +public: + BindExpressionProxy(QmlExpression *be) + :e(be) + { + } + +private: + QmlExpression *e; + +private Q_SLOTS: + void changed(); +}; + +class QmlScriptClass : public QScriptClass +{ +public: + enum ClassId + { + InvalidId = -1, + + ObjectListPropertyId = 0xC0000000, + FunctionId = 0x80000000, + VariantPropertyId = 0x40000000, + PropertyId = 0x00000000, + + ClassIdMask = 0xC0000000, + + ClassIdSelectorMask = 0x3F000000, + }; + + QmlScriptClass(QmlEngine *); + +protected: + QmlEngine *engine; +}; + +class QmlContextScriptClass : public QmlScriptClass +{ +public: + QmlContextScriptClass(QmlEngine *); + ~QmlContextScriptClass(); + + virtual QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id); + virtual QScriptValue property(const QScriptValue &object, + const QScriptString &name, + uint id); + virtual void setProperty(QScriptValue &object, + const QScriptString &name, + uint id, + const QScriptValue &value); +}; + +class QmlObjectScriptClass : public QmlScriptClass +{ +public: + QmlObjectScriptClass(QmlEngine *); + ~QmlObjectScriptClass(); + + virtual QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id); + virtual QScriptValue property(const QScriptValue &object, + const QScriptString &name, + uint id); + virtual void setProperty(QScriptValue &object, + const QScriptString &name, + uint id, + const QScriptValue &value); +}; + +class QmlExpressionPrivate +{ +public: + QmlExpressionPrivate(QmlExpression *); + QmlExpressionPrivate(QmlExpression *, const QString &expr, bool); + QmlExpressionPrivate(QmlExpression *, void *expr, QmlRefCount *rc); + ~QmlExpressionPrivate(); + + QmlExpression *q; + QmlContext *ctxt; + QString expression; + QmlBasicScript sse; + void *sseData; + BindExpressionProxy *proxy; + QObject *me; + bool trackChange; +}; +QT_END_NAMESPACE + +#endif // QMLENGINE_P_H + diff --git a/src/declarative/qml/qmlexpression.h b/src/declarative/qml/qmlexpression.h new file mode 100644 index 0000000..4f9502b --- /dev/null +++ b/src/declarative/qml/qmlexpression.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLEXPRESSION_H +#define QMLEXPRESSION_H + +#include <QtCore/qobject.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QString; +class QmlRefCount; +class QmlEngine; +class QmlContext; +class QmlExpressionPrivate; +class QmlBasicScript; +class Q_DECLARATIVE_EXPORT QmlExpression +{ +public: + QmlExpression(); + QmlExpression(QmlContext *, const QString &, QObject *); + QmlExpression(QmlContext *, const QString &, QObject *, bool); + QmlExpression(QmlContext *, void *, QmlRefCount *rc, QObject *me); + virtual ~QmlExpression(); + + QmlEngine *engine() const; + QmlContext *context() const; + + QString expression() const; + void clearExpression(); + virtual void setExpression(const QString &); + QVariant value(); + bool isConstant() const; + + bool trackChange() const; + void setTrackChange(bool); + + QObject *scopeObject() const; + +protected: + virtual void valueChanged(); + +private: + friend class BindExpressionProxy; + QmlExpressionPrivate *d; +}; + +// LK: can't we merge with QmlExpression???? +class Q_DECLARATIVE_EXPORT QmlExpressionObject : public QObject, + public QmlExpression +{ + Q_OBJECT +public: + QmlExpressionObject(QObject *parent = 0); + QmlExpressionObject(QmlContext *, const QString &, QObject *scope, QObject *parent = 0); + QmlExpressionObject(QmlContext *, const QString &, QObject *scope, bool); + QmlExpressionObject(QmlContext *, void *, QmlRefCount *, QObject *); + +public Q_SLOTS: + QVariant value(); + +Q_SIGNALS: + void valueChanged(); +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLEXPRESSION_H + diff --git a/src/declarative/qml/qmlinfo.cpp b/src/declarative/qml/qmlinfo.cpp new file mode 100644 index 0000000..dc7f44c --- /dev/null +++ b/src/declarative/qml/qmlinfo.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlinfo.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QmlInfo + \brief The QmlInfo class prints warnings messages that include the file and line number for QML types. + + When QML types display warning messages, it improves tracibility if they include the + QML file and line number on which the particular instance was instantiated. + + QmlInfo statements work just like regular Qt qDebug() statements. To include the file + and line number, an object must be passed. If the file and line number is not available + for that instance (either it was not instantiated by the QML engine or location + information is disabled), "unknown location" will be used instead. + + For example, + + \code + qmlInfo(this) << "component property is a write-once property"; + \endcode + + prints + + \code + QML ComponentInstance (unknown location): component property is a write-once property + \endcode +*/ + +/*! + Construct a QmlInfo, using \a object for file and line number information. +*/ +QmlInfo::QmlInfo(QObject *object) +: QDebug(QtWarningMsg) +{ + *this << "QML"; + if(object) + *this << object->metaObject()->className(); + *this << "(unknown location):"; +} + +/*! + \internal +*/ +QmlInfo::~QmlInfo() +{ +} + +/*! + \fn QmlInfo qmlInfo(QObject *me) + \internal + + XXX - how do we document these? +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlinfo.h b/src/declarative/qml/qmlinfo.h new file mode 100644 index 0000000..da8144c --- /dev/null +++ b/src/declarative/qml/qmlinfo.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 QtDeclarative 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 QMLINFO_H +#define QMLINFO_H + +#include <QtCore/qdebug.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QmlInfo : public QDebug +{ +public: + QmlInfo(QObject *); + ~QmlInfo(); +}; + +Q_DECLARATIVE_EXPORT inline QmlInfo qmlInfo(QObject *me) +{ + return QmlInfo(me); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLINFO_H diff --git a/src/declarative/qml/qmlinstruction.cpp b/src/declarative/qml/qmlinstruction.cpp new file mode 100644 index 0000000..9938022 --- /dev/null +++ b/src/declarative/qml/qmlinstruction.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 QtDeclarative 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 "private/qmlinstruction_p.h" +#include "private/qmlcompiledcomponent_p.h" +#include <QDebug> + +QT_BEGIN_NAMESPACE +void QmlCompiledComponent::dump(QmlInstruction *instr, int idx) +{ + QByteArray lineNumber = QByteArray::number(instr->line); + if(instr->line == (unsigned short)-1) + lineNumber = "NA"; + const char *line = lineNumber.constData(); + + switch(instr->type) { + case QmlInstruction::Init: + qWarning() << idx << "\t" << line << "\t" << "INIT\t\t\t" << instr->init.dataSize; + break; + case QmlInstruction::CreateObject: + qWarning() << idx << "\t" << line << "\t" << "CREATE\t\t\t" << instr->create.type << "\t\t\t" << types.at(instr->create.type).className; + break; + case QmlInstruction::CreateCustomObject: + qWarning() << idx << "\t" << line << "\t" << "CREATE_CUSTOM\t\t" << instr->createCustom.type << "\t" << instr->createCustom.data << "\t\t" << types.at(instr->create.type).className; + break; + case QmlInstruction::SetId: + qWarning() << idx << "\t" << line << "\t" << "SETID\t\t\t" << instr->setId.value << "\t" << instr->setId.save << "\t\t" << primitives.at(instr->setId.value); + break; + case QmlInstruction::SetDefault: + qWarning() << idx << "\t" << line << "\t" << "SET_DEFAULT"; + break; + case QmlInstruction::CreateComponent: + qWarning() << idx << "\t" << line << "\t" << "CREATE_COMPONENT\t" << instr->createComponent.count; + break; + case QmlInstruction::StoreMetaObject: + qWarning() << idx << "\t" << line << "\t" << "STORE_META\t\t" << instr->storeMeta.data; + break; + case QmlInstruction::StoreReal: + qWarning() << idx << "\t" << line << "\t" << "STORE_REAL\t\t" << instr->storeReal.propertyIndex << "\t" << instr->storeReal.value; + break; + case QmlInstruction::StoreInteger: + qWarning() << idx << "\t" << line << "\t" << "STORE_INTEGER\t\t" << instr->storeInteger.propertyIndex << "\t" << instr->storeInteger.value; + break; + case QmlInstruction::StoreBool: + qWarning() << idx << "\t" << line << "\t" << "STORE_BOOL\t\t" << instr->storeBool.propertyIndex << "\t" << instr->storeBool.value; + break; + case QmlInstruction::StoreString: + qWarning() << idx << "\t" << line << "\t" << "STORE_STRING\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); + break; + case QmlInstruction::StoreColor: + qWarning() << idx << "\t" << line << "\t" << "STORE_COLOR\t\t" << instr->storeColor.propertyIndex << "\t" << QString::number(instr->storeColor.value, 16); + break; + case QmlInstruction::StoreDate: + qWarning() << idx << "\t" << line << "\t" << "STORE_DATE\t\t" << instr->storeDate.propertyIndex << "\t" << instr->storeDate.value; + break; + case QmlInstruction::StoreTime: + qWarning() << idx << "\t" << line << "\t" << "STORE_TIME\t\t" << instr->storeTime.propertyIndex << "\t" << instr->storeTime.valueIndex; + break; + case QmlInstruction::StoreDateTime: + qWarning() << idx << "\t" << line << "\t" << "STORE_DATETIME\t\t" << instr->storeDateTime.propertyIndex << "\t" << instr->storeDateTime.valueIndex; + break; + case QmlInstruction::StorePoint: + qWarning() << idx << "\t" << line << "\t" << "STORE_POINT\t\t" << instr->storeRealPair.propertyIndex << "\t" << instr->storeRealPair.valueIndex; + break; + case QmlInstruction::StorePointF: + qWarning() << idx << "\t" << line << "\t" << "STORE_POINTF\t\t" << instr->storeRealPair.propertyIndex << "\t" << instr->storeRealPair.valueIndex; + break; + case QmlInstruction::StoreSize: + qWarning() << idx << "\t" << line << "\t" << "STORE_SIZE\t\t" << instr->storeRealPair.propertyIndex << "\t" << instr->storeRealPair.valueIndex; + break; + case QmlInstruction::StoreSizeF: + qWarning() << idx << "\t" << line << "\t" << "STORE_SIZEF\t\t" << instr->storeRealPair.propertyIndex << "\t" << instr->storeRealPair.valueIndex; + break; + case QmlInstruction::StoreRect: + qWarning() << idx << "\t" << line << "\t" << "STORE_RECT\t\t" << instr->storeRect.propertyIndex << "\t" << instr->storeRect.valueIndex; + break; + case QmlInstruction::StoreRectF: + qWarning() << idx << "\t" << line << "\t" << "STORE_RECTF\t\t" << instr->storeRect.propertyIndex << "\t" << instr->storeRect.valueIndex; + break; + case QmlInstruction::StoreVariant: + qWarning() << idx << "\t" << line << "\t" << "STORE_VARIANT\t\t" << instr->storeString.propertyIndex << "\t" << instr->storeString.value << "\t\t" << primitives.at(instr->storeString.value); + break; + case QmlInstruction::StoreObject: + qWarning() << idx << "\t" << line << "\t" << "STORE_OBJECT\t\t" << instr->storeObject.propertyIndex << "\t" << instr->storeObject.cast; + break; + case QmlInstruction::AssignCustomType: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_CUSTOMTYPE\t\t" << instr->assignCustomType.propertyIndex << "\t" << instr->assignCustomType.valueIndex; + break; + case QmlInstruction::StoreSignal: + qWarning() << idx << "\t" << line << "\t" << "STORE_SIGNAL\t\t" << instr->storeSignal.signalIndex << "\t" << instr->storeSignal.value << "\t\t" << primitives.at(instr->storeSignal.value); + break; + case QmlInstruction::AssignConstant: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_CONSTANT\t" << instr->assignConstant.property << "\t" << instr->assignConstant.constant << "\t\t" << datas.at(instr->assignConstant.property) << primitives.at(instr->assignConstant.constant); + break; + case QmlInstruction::AssignSignal: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_SIGNAL\t\t" << instr->assignSignal.signal << "\t" << instr->assignSignal.value << "\t\t" << datas.at(instr->assignSignal.signal) << primitives.at(instr->assignSignal.value); + break; + case QmlInstruction::AssignSignalObject: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_SIGNAL_OBJECT\t" << instr->assignSignalObject.signal << "\t\t\t" << datas.at(instr->assignSignalObject.signal); + break; + case QmlInstruction::AssignBinding: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_BINDING\t\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t\t" << instr->assignBinding.context << datas.at(instr->assignBinding.property) << primitives.at(instr->assignBinding.value); + break; + case QmlInstruction::AssignCompiledBinding: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_COMPILED_BINDING\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t\t" << instr->assignBinding.context << datas.at(instr->assignBinding.property); + break; + case QmlInstruction::AssignValueSource: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_VALUE_SOURCE\t" << instr->assignValueSource.property << "\t\t\t" << datas.at(instr->assignValueSource.property); + break; + case QmlInstruction::StoreBinding: + qWarning() << idx << "\t" << line << "\t" << "STORE_BINDING\t\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t\t" << instr->assignBinding.context << primitives.at(instr->assignBinding.value); + break; + case QmlInstruction::StoreCompiledBinding: + qWarning() << idx << "\t" << line << "\t" << "STORE_COMPILED_BINDING\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t\t" << instr->assignBinding.context; + break; + case QmlInstruction::StoreValueSource: + qWarning() << idx << "\t" << line << "\t" << "STORE_VALUE_SOURCE\t" << instr->assignValueSource.property; + break; + case QmlInstruction::TryBeginObject: + qWarning() << idx << "\t" << line << "\t" << "TRY_BEGIN"; + break; + case QmlInstruction::BeginObject: + qWarning() << idx << "\t" << line << "\t" << "BEGIN\t\t\t" << instr->begin.castValue; + break; + case QmlInstruction::TryCompleteObject: + qWarning() << idx << "\t" << line << "\t" << "TRY_COMPLETE"; + break; + case QmlInstruction::CompleteObject: + qWarning() << idx << "\t" << line << "\t" << "COMPLETE\t\t" << instr->complete.castValue; + break; + case QmlInstruction::AssignObject: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_OBJECT\t\t" << instr->assignObject.property << "\t" << instr->assignObject.castValue << "\t\t" << ((instr->assignObject.property == -1)?QByteArray("default"):datas.at(instr->assignObject.property)); + break; + case QmlInstruction::AssignObjectList: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_OBJECT_LIST\t" << instr->assignObject.property << "\t" << instr->assignObject.castValue << "\t\t" << ((instr->assignObject.property == -1)?QByteArray("default"):datas.at(instr->assignObject.property)); + break; + case QmlInstruction::FetchAttached: + qWarning() << idx << "\t" << line << "\t" << "FETCH_ATTACHED\t\t" << instr->fetchAttached.idx << "\t\t\t" << primitives.at(instr->fetchAttached.idx); + break; + case QmlInstruction::FetchQmlList: + qWarning() << idx << "\t" << line << "\t" << "FETCH_QMLLIST\t\t" << instr->fetchQmlList.property << "\t" << instr->fetchQmlList.type; + break; + case QmlInstruction::FetchQList: + qWarning() << idx << "\t" << line << "\t" << "FETCH_QLIST\t\t" << instr->fetch.property; + break; + case QmlInstruction::FetchObject: + qWarning() << idx << "\t" << line << "\t" << "FETCH\t\t\t" << instr->fetch.property; + break; + case QmlInstruction::ResolveFetchObject: + qWarning() << idx << "\t" << line << "\t" << "RESOLVE_FETCH\t\t" << instr->fetch.property << "\t\t\t" << datas.at(instr->fetch.property); + break; + case QmlInstruction::PopFetchedObject: + qWarning() << idx << "\t" << line << "\t" << "POP"; + break; + case QmlInstruction::PopQList: + qWarning() << idx << "\t" << line << "\t" << "POP_QLIST"; + break; + case QmlInstruction::NoOp: + qWarning() << idx << "\t" << line << "\t" << "NOOP"; + break; + case QmlInstruction::PushProperty: + qWarning() << idx << "\t" << line << "\t" << "PUSH_PROPERTY" << "\t\t" << instr->pushProperty.property; + break; + case QmlInstruction::AssignStackObject: + qWarning() << idx << "\t" << line << "\t" << "ASSIGN_STACK_OBJ" << "\t" << instr->assignStackObject.property << "\t" << instr->assignStackObject.object; + break; + case QmlInstruction::StoreStackObject: + qWarning() << idx << "\t" << line << "\t" << "STORE_STACK_OBJ" << "\t" << instr->assignStackObject.property << "\t" << instr->assignStackObject.object; + break; + default: + qWarning() << idx << "\t" << line << "\t" << "XXX UNKOWN INSTRUCTION"; + break; + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h new file mode 100644 index 0000000..40a0b84 --- /dev/null +++ b/src/declarative/qml/qmlinstruction_p.h @@ -0,0 +1,299 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLINSTRUCTION_P_H +#define QMLINSTRUCTION_P_H + +#include <qfxglobal.h> + + +QT_BEGIN_NAMESPACE +class QmlCompiledComponent; +class Q_DECLARATIVE_EXPORT QmlInstruction +{ +public: + enum Type { + // + // Object Creation + // + // CreateObject - Create a new object instance and push it on the + // object stack + // SetId - Set the id of the object on the top of the object stack + // SetDefault - Sets the instance on the top of the object stack to + // be the context's default object. + // StoreMetaObject - Assign the dynamic metaobject to object on the + // top of the stack. + Init, /* init */ + CreateObject, /* create */ + CreateCustomObject, /* createCustom */ + SetId, /* setId */ + SetDefault, + CreateComponent, /* createComponent */ + StoreMetaObject, /* storeMeta */ + + // + // Precomputed single assignment + // + // StoreReal - Store a qreal in a core property + // StoreInteger - Store a int or uint in a core property + // StoreBool - Store a bool in a core property + // StoreString - Store a QString in a core property + // StoreColor - Store a QColor in a core property + // StoreDate - Store a QDate in a core property + // StoreTime - Store a QTime in a core property + // StoreDateTime - Store a QDateTime in a core property + // StoreVariant - Store a QVariant in a core property + // StoreObject - Pop the object on the top of the object stack and + // store it in a core property + StoreReal, /* storeReal */ + StoreInstructionsStart = StoreReal, + StoreInteger, /* storeInteger */ + StoreBool, /* storeBool */ + StoreString, /* storeString */ + StoreColor, /* storeColor */ + StoreDate, /* storeDate */ + StoreTime, /* storeTime */ + StoreDateTime, /* storeDateTime */ + StorePoint, /* storeRealPair */ + StorePointF, /* storeRealPair */ + StoreSize, /* storeRealPair */ + StoreSizeF, /* storeRealPair */ + StoreRect, /* storeRect */ + StoreRectF, /* storeRect */ + StoreVariant, /* storeString */ + StoreObject, /* storeObject */ + StoreInstructionsEnd = StoreObject, + + StoreSignal, /* storeSignal */ + + StoreObjectQmlList, + + // XXX need to handle storing objects in variants + + // + // Unresolved single assignment + // + // AssignConstant - Store a value in a property. Will resolve into + // a Store* instruction. + // AssignSignal - Set a signal handler on the property. Will resolve + // into a Store*Signal instruction. + AssignConstant, /* assignConstant */ + AssignSignal, /* assignSignal */ + AssignSignalObject, /* assignSignalObject */ + AssignCustomType, /* assignCustomType */ + + AssignBinding, /* assignBinding */ + AssignCompiledBinding, /* assignBinding */ + AssignValueSource, /* assignValueSource */ + StoreBinding, /* assignBinding */ + StoreCompiledBinding, /* assignBinding */ + StoreValueSource, /* assignValueSource */ + + TryBeginObject, + BeginObject, /* begin */ + TryCompleteObject, + CompleteObject, /* complete */ + + AssignObject, /* assignObject */ + AssignObjectList, /* assignObject */ + + FetchAttached, /* fetchAttached */ + FetchQmlList, /* fetchQmlList */ + FetchQList, /* fetch */ + FetchObject, /* fetch */ + ResolveFetchObject, /* fetch */ + + // + // Stack manipulation + // + // PopFetchedObject - Remove an object from the object stack + // PopQList - Remove a list from the list stack + PopFetchedObject, + PopQList, + + // + // Expression optimizations + // + // PushProperty - Save the property for later use + // AssignStackObject - Assign the stack object + // StoreStackObject - Assign the stack object (no checks) + PushProperty, /* pushProperty */ + AssignStackObject, /* assignStackObject */ + StoreStackObject, /* assignStackObject */ + + + // + // Miscellaneous + // + // NoOp - Do nothing + NoOp + }; + Type type; + unsigned short line; + union { + struct { + int dataSize; + } init; + struct { + int type; + } create; + struct { + int data; + } storeMeta; + struct { + int type; + int data; + } createCustom; + struct { + int value; + int save; + } setId; + struct { + int property; + int constant; + } assignConstant; + struct { + int property; + int castValue; + } assignObject; + struct { + int property; + } assignValueSource; + struct { + int property; + int value; + short context; + short category; + } assignBinding; + struct { + int property; + bool isObject; + } fetch; + struct { + int property; + int type; + } fetchQmlList; + struct { + int castValue; + } complete; + struct { + int castValue; + } begin; + struct { + int propertyIndex; + float value; + } storeReal; + struct { + int propertyIndex; + int value; + } storeInteger; + struct { + int propertyIndex; + bool value; + } storeBool; + struct { + int propertyIndex; + int value; + } storeString; + struct { + int propertyIndex; + unsigned int value; + } storeColor; + struct { + int propertyIndex; + int value; + } storeDate; + struct { + int propertyIndex; + int valueIndex; + } storeTime; + struct { + int propertyIndex; + int valueIndex; + } storeDateTime; + struct { + int propertyIndex; + int valueIndex; + } storeRealPair; + struct { + int propertyIndex; + int valueIndex; + } storeRect; + struct { + int propertyIndex; + int cast; + } storeObject; + struct { + int propertyIndex; + int valueIndex; + } assignCustomType; + struct { + int signalIndex; + int value; + } storeSignal; + struct { + int signal; + int value; + } assignSignal; + struct { + int signal; + } assignSignalObject; + struct { + int count; + } createComponent; + struct { + int idx; + } fetchAttached; + struct { + int property; + } pushProperty; + struct { + int property; + int object; + } assignStackObject; + }; + + void dump(QmlCompiledComponent *); +}; + +#endif // QMLINSTRUCTION_P_H + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmllist.h b/src/declarative/qml/qmllist.h new file mode 100644 index 0000000..3a1e665 --- /dev/null +++ b/src/declarative/qml/qmllist.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLLIST_H +#define QMLLIST_H + +#include <QtDeclarative/qmlprivate.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +template<typename T> +class QmlList : private QmlPrivate::ListInterface +{ +public: + virtual void append(T) = 0; + virtual void insert(int, T) = 0; + virtual T at(int) const = 0; + virtual void clear() = 0; + QmlList<T> &operator<<(T t) { append(t); return *this; } + +protected: + virtual int type() const { return qMetaTypeId<T>(); } + virtual void append(void *d) { const T &v = *(T *)d; append(v); } + virtual void insert(int i, void *d) { const T &v = *(T *)d; insert(i, v); } + virtual void at(int i, void *p) const { const T &v = at(i); *((T*)p) = v; } +}; + +template<typename T> +class QmlConcreteList : public QList<T>, public QmlList<T> +{ +public: + virtual void append(T v) { QList<T>::append(v); } + virtual void insert(int i, T v) { QList<T>::insert(i, v); } + virtual void clear() { QList<T>::clear(); } + virtual T at(int i) const { return QList<T>::at(i); } + virtual void removeAt(int i) { QList<T>::removeAt(i); } + virtual int count() const { return QList<T>::count(); } +}; + +#define QML_DECLARE_LIST_PROXY(ClassName, ListType, ListName) \ +class Qml_ProxyList_ ##ListName : public QmlList<ListType> \ +{ \ + public: \ + virtual void removeAt(int idx) \ + { \ + ClassName *p = (ClassName *)((char *)this + ((char *)(ClassName *)(0x10000000) - (char *)&((ClassName *)(0x10000000))->ListName)); \ + p->ListName ## _removeAt(idx); \ + } \ + virtual int count() const \ + { \ + ClassName *p = (ClassName *)((char *)this + ((char *)(ClassName *)(0x10000000) - (char *)&((ClassName *)(0x10000000))->ListName)); \ + return p->ListName ## _count(); \ + } \ + virtual void append(ListType v) \ + { \ + ClassName *p = (ClassName *)((char *)this + ((char *)(ClassName *)(0x10000000) - (char *)&((ClassName *)(0x10000000))->ListName)); \ + p->ListName ## _append(v); \ + } \ + virtual void insert(int idx, ListType v) \ + { \ + ClassName *p = (ClassName *)((char *)this + ((char *)(ClassName *)(0x10000000) - (char *)&((ClassName *)(0x10000000))->ListName)); \ + p->ListName ## _insert(idx, v); \ + } \ + virtual ListType at(int idx) const \ + { \ + ClassName *p = (ClassName *)((char *)this + ((char *)(ClassName *)(0x10000000) - (char *)&((ClassName *)(0x10000000))->ListName)); \ + return p->ListName ## _at(idx); \ + } \ + virtual void clear() \ + { \ + ClassName *p = (ClassName *)((char *)this + ((char *)(ClassName *)(0x10000000) - (char *)&((ClassName *)(0x10000000))->ListName)); \ + p->ListName ## _clear(); \ + } \ +}; \ +friend class Qml_ProxyList_ ##ListName ; \ +Qml_ProxyList_##ListName ListName; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLLIST_H diff --git a/src/declarative/qml/qmlmetaproperty.cpp b/src/declarative/qml/qmlmetaproperty.cpp new file mode 100644 index 0000000..79db6ce --- /dev/null +++ b/src/declarative/qml/qmlmetaproperty.cpp @@ -0,0 +1,847 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlmetaproperty.h" +#include "qmlmetaproperty_p.h" +#include <qml.h> +#include <qfxperf.h> +#include <QStringList> +#include <qmlbindablevalue.h> +#include <qmlcontext.h> +#include "qmlboundsignal_p.h" +#include <math.h> +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE + +class QMetaPropertyEx : public QMetaProperty +{ +public: + QMetaPropertyEx() + : propertyType(-1) {} + + QMetaPropertyEx(const QMetaProperty &p) + : QMetaProperty(p), propertyType(p.userType()) {} + + QMetaPropertyEx(const QMetaPropertyEx &o) + : QMetaProperty(o), + propertyType(o.propertyType) {} + + QMetaPropertyEx &operator=(const QMetaPropertyEx &o) + { + static_cast<QMetaProperty *>(this)->operator=(o); + propertyType = o.propertyType; + return *this; + } + + private: + friend class QmlMetaProperty; + int propertyType; +}; + + +/*! + \class QmlMetaProperty + \brief The QmlMetaProperty class abstracts accessing QML properties. + */ + +/*! + Create an invalid QmlMetaProperty. +*/ +QmlMetaProperty::QmlMetaProperty() +: d(new QmlMetaPropertyPrivate) +{ +} + +QmlMetaProperty::~QmlMetaProperty() +{ + delete d; d = 0; +} + +// ### not thread safe +static QHash<const QMetaObject *, QMetaPropertyEx> qmlCacheDefProp; + +/*! + Creates a QmlMetaProperty for the default property of \a obj. If there is no + default property, an invalid QmlMetaProperty will be created. + */ +QmlMetaProperty::QmlMetaProperty(QObject *obj, QmlContext *ctxt) +: d(new QmlMetaPropertyPrivate) +{ + d->context = ctxt; + if(!obj) + return; + + d->object = obj; + QHash<const QMetaObject *, QMetaPropertyEx>::ConstIterator iter = + qmlCacheDefProp.find(obj->metaObject()); + if(iter != qmlCacheDefProp.end()) { + d->prop = *iter; + d->propType = iter->propertyType; + d->coreIdx = iter->propertyType; + } else { + QMetaPropertyEx p(QmlMetaType::defaultProperty(obj)); + d->prop = p; + d->propType = p.propertyType; + d->coreIdx = d->prop.propertyIndex(); + if(!QObjectPrivate::get(obj)->metaObject) + qmlCacheDefProp.insert(obj->metaObject(), d->prop); + } + if(d->prop.name() != 0) { + d->type = Property | Default; + d->name = QLatin1String(d->prop.name()); + } +} + +/*! + \internal + + Creates a QmlMetaProperty for the property at index \a idx of \a obj. + + The QmlMetaProperty is assigned category \a cat. + */ +QmlMetaProperty::QmlMetaProperty(QObject *obj, int idx, PropertyCategory cat, QmlContext *ctxt) +: d(new QmlMetaPropertyPrivate) +{ + d->context = ctxt; + d->object = obj; + d->type = Property; + d->category = cat; + QMetaPropertyEx p(obj->metaObject()->property(idx)); + d->prop = p; + d->propType = p.propertyType; + d->coreIdx = idx; + if(d->prop.name() != 0) + d->name = QLatin1String(d->prop.name()); +} + +// ### Not thread safe!!!! +static QHash<const QMetaObject *, QHash<QString, QMetaPropertyEx> > qmlCacheProps; +/*! + Creates a QmlMetaProperty for the property \a name of \a obj. + */ +QmlMetaProperty::QmlMetaProperty(QObject *obj, const QString &name, QmlContext *ctxt) +: d(new QmlMetaPropertyPrivate) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::MetaProperty> perf; +#endif + + d->context = ctxt; + d->name = name; + d->object = obj; + if(name.isEmpty() || !obj) + return; + + if(name.at(0).isUpper()) { + // Attached property + d->attachedFunc = QmlMetaType::attachedPropertiesFuncId(name.toLatin1()); + if(d->attachedFunc != -1) + d->type = Property | Attached; + return; + } else if(name.count() >= 3 && name.startsWith(QLatin1String("on")) && name.at(2).isUpper()) { + // Signal + QString signalName = name.mid(2); + signalName[0] = signalName.at(0).toLower(); + + d->findSignalInt(obj, signalName); + if(d->signal.signature() != 0) { + d->type = SignalProperty; + return; + } + } + + // Property + QHash<QString, QMetaPropertyEx> &props = qmlCacheProps[obj->metaObject()]; + QHash<QString, QMetaPropertyEx>::ConstIterator iter = props.find(name); + if(iter != props.end()) { + d->prop = *iter; + d->propType = iter->propertyType; + d->coreIdx = iter->propertyIndex(); + } else { + QMetaPropertyEx p = QmlMetaType::property(obj, name.toLatin1().constData()); + d->prop = p; + d->propType = p.propertyType; + d->coreIdx = p.propertyIndex(); + if (!QObjectPrivate::get(obj)->metaObject) + props.insert(name, p); + } + if(d->prop.name() != 0) + d->type = Property; + + if(d->type == Invalid) { + int sig = findSignal(obj, name.toLatin1()); + if(sig != -1) { + d->signal = obj->metaObject()->method(sig); + d->type = Signal; + d->coreIdx = sig; + } + } +} + +/*! + Create a copy of \a other. +*/ +QmlMetaProperty::QmlMetaProperty(const QmlMetaProperty &other) +: d(new QmlMetaPropertyPrivate(*other.d)) +{ +} + +/*! + Returns the property category. +*/ +QmlMetaProperty::PropertyCategory QmlMetaProperty::propertyCategory() const +{ + if(d->category == Unknown) { + int type = propertyType(); + if(!isValid()) + d->category = InvalidProperty; + else if(type == qMetaTypeId<QmlBindableValue *>()) + d->category = Bindable; + else if(QmlMetaType::isList(type)) + d->category = List; + else if(QmlMetaType::isQmlList(type)) + d->category = QmlList; + else if(QmlMetaType::isObject(type)) + d->category = Object; + else + d->category = Normal; + } + return d->category; +} + +/*! + Returns the property category of \a prop. +*/ +QmlMetaProperty::PropertyCategory +QmlMetaProperty::propertyCategory(const QMetaProperty &prop) +{ + if(prop.name()) { + int type = 0; + if(prop.type() == QVariant::LastType) + type = qMetaTypeId<QVariant>(); + else if(prop.type() == QVariant::UserType) + type = prop.userType(); + else + type = prop.type(); + + if(type == qMetaTypeId<QmlBindableValue *>()) + return Bindable; + else if(QmlMetaType::isList(type)) + return List; + else if(QmlMetaType::isQmlList(type)) + return QmlList; + else if(QmlMetaType::isObject(type)) + return Object; + else + return Normal; + } else { + return InvalidProperty; + } +} + +/*! + Returns the type name of the property, or 0 if the property has no type + name. +*/ +const char *QmlMetaProperty::propertyTypeName() const +{ + if(d->prop.name()) { + return d->prop.typeName(); + } else { + return 0; + } +} + +/*! + Returns true if \a other and this QmlMetaProperty represent the same + property. +*/ +bool QmlMetaProperty::operator==(const QmlMetaProperty &other) const +{ + return d->prop.name() == other.d->prop.name() && + d->signal.signature() == other.d->signal.signature() && + d->type == other.d->type && + d->object == other.d->object; +} + +/*! + Returns the QVariant type of the property, or QVariant::Invalid if the + property has no QVariant type. +*/ +int QmlMetaProperty::propertyType() const +{ + int rv = QVariant::Invalid; + + if(d->prop.name()) { + if(d->propType == (int)QVariant::LastType) + rv = qMetaTypeId<QVariant>(); + else + rv = d->propType; + } else if(d->attachedFunc) { + rv = qMetaTypeId<QObject *>(); + } + + return rv; +} + +/*! + Returns the type of the property. +*/ +QmlMetaProperty::Type QmlMetaProperty::type() const +{ + return (Type)d->type; +} + +/*! + Returns true if this QmlMetaProperty represents a regular Qt property. +*/ +bool QmlMetaProperty::isProperty() const +{ + return type() & Property; +} + +/*! + Returns true if this QmlMetaProperty represents a default property. +*/ +bool QmlMetaProperty::isDefault() const +{ + return type() & Default; +} + +/*! + Returns the QmlMetaProperty's QObject. +*/ +QObject *QmlMetaProperty::object() const +{ + return d->object; +} + +/*! + Assign \a other to this QmlMetaProperty. +*/ +QmlMetaProperty &QmlMetaProperty::operator=(const QmlMetaProperty &other) +{ + d->name = other.d->name; + d->prop = other.d->prop; + d->propType = other.d->propType; + d->type = other.d->type; + d->signal = other.d->signal; + d->coreIdx = other.d->coreIdx; + d->attachedFunc = other.d->attachedFunc; + d->object = other.d->object; + d->category = other.d->category; + return *this; +} + +/*! + Returns true if the property is writable, otherwise false. +*/ +bool QmlMetaProperty::isWritable() const +{ + if(propertyCategory() == List || propertyCategory() == QmlList) + return true; + else if(d->prop.name() != 0) + return d->prop.isWritable(); + else if(type() & SignalProperty) + return true; + else + return false; +} + +/*! + Returns true if the property is designable, otherwise false. +*/ +bool QmlMetaProperty::isDesignable() const +{ + if(d->prop.name() != 0) + return d->prop.isDesignable(); + else + return false; +} + +/*! + Returns true if the QmlMetaProperty refers to a valid property, otherwise + false. +*/ +bool QmlMetaProperty::isValid() const +{ + return type() != Invalid; +} + +/*! + Returns all of \a obj's Qt properties. +*/ +QStringList QmlMetaProperty::properties(QObject *obj) +{ + if(!obj) + return QStringList(); + + QStringList rv; + const QMetaObject *mo = obj->metaObject(); + for(int ii = 0; ii < mo->propertyCount(); ++ii) { + QMetaProperty prop = mo->property(ii); + rv << QLatin1String(prop.name()); + } + + return rv; +} + +/*! + Return the name of this property. +*/ +QString QmlMetaProperty::name() const +{ + return d->name; +} + +const QMetaProperty &QmlMetaProperty::property() const +{ + return d->prop; +} + +/*! + Returns the binding associated with this property, or 0 if no binding + exists. +*/ +QmlBindableValue *QmlMetaProperty::binding() +{ + if(!isProperty() || type() & Attached) + return 0; + + const QObjectList &children = object()->children(); + for(QObjectList::ConstIterator iter = children.begin(); + iter != children.end(); ++iter) { + QObject *child = *iter; + if(child->metaObject() == &QmlBindableValue::staticMetaObject) { + QmlBindableValue *v = static_cast<QmlBindableValue *>(child); + if(v->property() == *this) + return v; + } + } + return 0; +} + +/*! \internal */ +int QmlMetaProperty::findSignal(const QObject *obj, const char *name) +{ + const QMetaObject *mo = obj->metaObject(); + int methods = mo->methodCount(); + for(int ii = 0; ii < methods; ++ii) { + QMetaMethod method = mo->method(ii); + if(method.methodType() != QMetaMethod::Signal) + continue; + + QByteArray methodName = method.signature(); + int idx = methodName.indexOf('('); + methodName = methodName.left(idx); + + if(methodName == name) + return ii; + } + return -1; +} + +void QmlMetaPropertyPrivate::findSignalInt(QObject *obj, const QString &name) +{ + const QMetaObject *mo = obj->metaObject(); + + int methods = mo->methodCount(); + for(int ii = 0; ii < methods; ++ii) { + QMetaMethod method = mo->method(ii); + QString methodName = QLatin1String(method.signature()); + int idx = methodName.indexOf(QLatin1Char('(')); + methodName = methodName.left(idx); + + if(methodName == name) { + signal = method; + coreIdx = ii; + return; + } + } +} + +QObject *QmlMetaPropertyPrivate::attachedObject() const +{ + if(attachedFunc == -1) + return 0; + else + return QmlMetaType::attachedPropertiesFuncById(attachedFunc)(object); +} + +/*! + Returns the property value. +*/ +QVariant QmlMetaProperty::read() const +{ + if(type() & SignalProperty) { + + const QObjectList &children = object()->children(); + + for(int ii = 0; ii < children.count(); ++ii) { + QmlBoundSignal *sig = qobject_cast<QmlBoundSignal *>(children.at(ii)); + if(sig && sig->index() == d->coreIdx) + return sig->expression(); + } + } else if(type() & Property) { + if(type() & Attached) + return QVariant::fromValue(d->attachedObject()); + else + return d->prop.read(object()); + } + return QVariant(); +} + +Q_DECLARE_METATYPE(QList<QObject *>); +/*! + Set the property value to \a value. +*/ +void QmlMetaProperty::write(const QVariant &value) const +{ + if(type() & SignalProperty) { + + QString expr = value.toString(); + const QObjectList &children = object()->children(); + + for(int ii = 0; ii < children.count(); ++ii) { + QmlBoundSignal *sig = qobject_cast<QmlBoundSignal *>(children.at(ii)); + if(sig && sig->index() == d->coreIdx) { + if(expr.isEmpty()) { + sig->disconnect(); + sig->deleteLater(); + } else { + sig->setExpression(expr); + } + return; + } + } + + if(!expr.isEmpty()) { + // XXX scope + (void *)new QmlBoundSignal(QmlContext::activeContext(), expr, object(), d->coreIdx, object()); + } + + } else if(d->prop.name()) { + + if(d->prop.isEnumType()) { + QVariant v = value; + if (value.type() == QVariant::Double) { //enum values come through the script engine as doubles + double integral; + double fractional = modf(value.toDouble(), &integral); + if (qFuzzyCompare(fractional, (double)0.0)) + v.convert(QVariant::Int); + } + d->prop.write(object(), v); + } else { + if(!value.isValid()) + return; + + int t = propertyType(); + int vt = value.type(); + + if(vt == t || + value.userType() == t) { + + void *a[1]; + a[0] = (void *)value.constData(); + QMetaObject::metacall(object(), QMetaObject::WriteProperty, d->coreIdx, a); + + } else if(qMetaTypeId<QVariant>() == t) { + + d->prop.write(object(), value); + + } else if(propertyCategory() == Object) { + + QObject *o = QmlMetaType::toQObject(value); + if(o) + d->prop.write(object(), QmlMetaType::fromObject(o, propertyType())); + + } else if (propertyCategory() == List) { + + int listType = QmlMetaType::listType(t); + if(value.userType() == qMetaTypeId<QList<QObject *> >()) { + const QList<QObject *> &list = + qvariant_cast<QList<QObject *> >(value); + QVariant listVar = d->prop.read(object()); + QmlMetaType::clear(listVar); + for(int ii = 0; ii < list.count(); ++ii) { + QVariant v = QmlMetaType::fromObject(list.at(ii), listType); + QmlMetaType::append(listVar, v); + } + + } else if(vt == listType || + value.userType() == listType) { + QVariant listVar = d->prop.read(object()); + if (!QmlMetaType::append(listVar, value)) { + qWarning() << "QmlMetaProperty: Unable to assign object to list"; + } + } + } else if (propertyCategory() == QmlList) { + // XXX - optimize! + QVariant list = d->prop.read(object()); + QmlPrivate::ListInterface *li = + *(QmlPrivate::ListInterface **)list.constData(); + + int type = li->type(); + + if (QObject *obj = QmlMetaType::toQObject(value)) { + const QMetaObject *mo = + QmlMetaType::rawMetaObjectForType(type); + + const QMetaObject *objMo = obj->metaObject(); + bool found = false; + while(!found && objMo) { + if(objMo == mo) + found = true; + else + objMo = objMo->superClass(); + } + + if(!found) { + qWarning() << "Unable to assign object to list"; + return; + } + + // NOTE: This assumes a cast to QObject does not alter + // the object pointer + void *d = (void *)&obj; + li->append(d); + } + } else if(propertyCategory() == Normal) { + + switch(t) { + case QVariant::Double: + { + qreal r; + bool found = true; + if(vt == QVariant::Int) { + r = value.toInt(); + } else if(vt == QVariant::UInt) { + r = value.toUInt(); + } else { + found = false; + } + + if(found) { + void *a[1]; + a[0] = &r; + QMetaObject::metacall(object(), + QMetaObject::WriteProperty, + d->coreIdx, a); + return; + } + } + break; + + case QVariant::Int: + { + int i; + bool found = true; + if(vt == QVariant::Double) { + i = (int)value.toDouble(); + } else if(vt == QVariant::UInt) { + i = (int)value.toUInt(); + } else { + found = false; + } + + if(found) { + void *a[1]; + a[0] = &i; + QMetaObject::metacall(object(), + QMetaObject::WriteProperty, + d->coreIdx, a); + return; + } + } + break; + + case QVariant::String: + { + QString s; + bool found = true; + if(vt == QVariant::ByteArray) { + s = QLatin1String(value.toByteArray()); + } else { + found = false; + } + + if(found) { + void *a[1]; + a[0] = &s; + QMetaObject::metacall(object(), + QMetaObject::WriteProperty, + d->coreIdx, a); + return; + } + } + break; + + + default: + break; + } + d->prop.write(object(), value); + } + + } + } +} + +/*! + Returns true if the property has a change notifier signal, otherwise false. +*/ +bool QmlMetaProperty::hasChangedNotifier() const +{ + if(type() & Property && !(type() & Attached)) { + return d->prop.hasNotifySignal(); + } + return false; +} + +/*! + Connect the property's change notifier signal to the \a dest \a method. +*/ +bool QmlMetaProperty::connectNotifier(QObject *dest, int method) const +{ + if(!(type() & Property) || type() & Attached) + return false; + + if(d->prop.hasNotifySignal()) { + return QMetaObject::connect(d->object, d->prop.notifySignalIndex(), dest, method, Qt::DirectConnection); + } else { + return false; + } +} + +/*! + Connect the property's change notifier signal to the \a dest \a slot. +*/ +bool QmlMetaProperty::connectNotifier(QObject *dest, const char *slot) const +{ + if(!(type() & Property) || type() & Attached) + return false; + + if(d->prop.hasNotifySignal()) { + QByteArray signal(QByteArray("2") + d->prop.notifySignal().signature()); + return QObject::connect(d->object, signal.constData(), dest, slot); + } else { + return false; + } +} + +/*! \internal */ +void QmlMetaProperty::emitSignal() +{ + if(type() & Signal) { + if(d->signal.parameterTypes().isEmpty()) + d->object->metaObject()->activate(d->object, d->coreIdx, 0); + else + qWarning() << "QmlMetaProperty: Cannot emit signal with parameters"; + } +} + +/*! + Return the Qt metaobject index of the property. +*/ +int QmlMetaProperty::coreIndex() const +{ + return d->coreIdx; +} + +/*! + Returns the property information serialized into a single integer. + QmlMetaProperty uses the bottom 24 bits only. +*/ +quint32 QmlMetaProperty::save() const +{ + quint32 rv = 0; + if(type() & Attached) { + rv = d->attachedFunc; + } else if(type() != Invalid) { + rv = d->coreIdx; + } + + Q_ASSERT(rv <= 0xFFFF); + Q_ASSERT(type() <= 0xFF); + rv |= (type() << 16); + + return rv; +} + +/*! + Restore a QmlMetaProperty from a previously saved id. \a obj must be the + same object as used in the previous call to QmlMetaProperty::save(). Only + the bottom 24-bits are used, the high bits can be set to any value. +*/ +void QmlMetaProperty::restore(quint32 id, QObject *obj) +{ + *this = QmlMetaProperty(); + d->object = obj; + + id &= 0xFFFFFF; + d->type = id >> 16; + id &= 0xFFFF; + + if(d->type & Attached) { + d->attachedFunc = id; + } else if(d->type & Property) { + QMetaPropertyEx p(obj->metaObject()->property(id)); + d->prop = p; + d->propType = p.propertyType; + d->coreIdx = id; + } else if(d->type & SignalProperty || d->type & Signal) { + d->signal = obj->metaObject()->method(id); + d->coreIdx = id; + } +} + +/*! + Return the QMetaMethod for this property if it is a SignalProperty, + otherwise returns an invalid QMetaMethod. +*/ +QMetaMethod QmlMetaProperty::method() const +{ + return d->signal; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlmetaproperty.h b/src/declarative/qml/qmlmetaproperty.h new file mode 100644 index 0000000..a2939f9 --- /dev/null +++ b/src/declarative/qml/qmlmetaproperty.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 QtDeclarative 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 QMLMETAPROPERTY_H +#define QMLMETAPROPERTY_H + +#include <QtDeclarative/qfxglobal.h> +#include <QMetaProperty> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QObject; +class QmlBindableValue; +class QStringList; +class QVariant; +struct QMetaObject; +class QmlContext; + +class QmlMetaPropertyPrivate; +class Q_DECLARATIVE_EXPORT QmlMetaProperty +{ +public: + enum PropertyCategory { + Unknown, + InvalidProperty, + Bindable, + List, + QmlList, //XXX + Object, + Normal + }; + QmlMetaProperty(); + QmlMetaProperty(QObject *, QmlContext * = 0); + QmlMetaProperty(QObject *, const QString &, QmlContext * = 0); + QmlMetaProperty(const QmlMetaProperty &); + QmlMetaProperty &operator=(const QmlMetaProperty &); + QmlMetaProperty(QObject *, int, PropertyCategory = Unknown, QmlContext * = 0); + ~QmlMetaProperty(); + + static QStringList properties(QObject *); + QString name() const; + + QVariant read() const; + void write(const QVariant &) const; + void emitSignal(); + + bool hasChangedNotifier() const; + bool connectNotifier(QObject *dest, const char *slot) const; + bool connectNotifier(QObject *dest, int method) const; + + quint32 save() const; + void restore(quint32, QObject *); + + QMetaMethod method() const; + + enum Type { Invalid = 0x00, + Property = 0x01, + SignalProperty = 0x02, + Signal = 0x04, + Default = 0x08, + Attached = 0x10 }; + + Type type() const; + bool isProperty() const; + bool isDefault() const; + bool isWritable() const; + bool isDesignable() const; + bool isValid() const; + QObject *object() const; + + PropertyCategory propertyCategory() const; + static PropertyCategory propertyCategory(const QMetaProperty &); + + int propertyType() const; + const char *propertyTypeName() const; + + bool operator==(const QmlMetaProperty &) const; + + const QMetaProperty &property() const; + + QmlBindableValue *binding(); + static int findSignal(const QObject *, const char *); + + int coreIndex() const; +private: + friend class QmlEnginePrivate; + QmlMetaPropertyPrivate *d; +}; +typedef QList<QmlMetaProperty> QmlMetaProperties; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLMETAPROPERTY_H diff --git a/src/declarative/qml/qmlmetaproperty_p.h b/src/declarative/qml/qmlmetaproperty_p.h new file mode 100644 index 0000000..5c13f5e --- /dev/null +++ b/src/declarative/qml/qmlmetaproperty_p.h @@ -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 QtDeclarative 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 QMLMETAPROPERTY_P_H +#define QMLMETAPROPERTY_P_H + +class QmlContext; +class QmlMetaPropertyPrivate +{ +public: + QmlMetaPropertyPrivate() + : context(0), coreIdx(-1), type(QmlMetaProperty::Invalid), attachedFunc(-1), + object(0), propType(-1), category(QmlMetaProperty::Unknown) {} + QmlMetaPropertyPrivate(const QmlMetaPropertyPrivate &other) + : name(other.name), signal(other.signal), context(other.context), + coreIdx(other.coreIdx), type(other.type), attachedFunc(other.attachedFunc), + object(other.object), prop(other.prop), propType(other.propType), + category(other.category) {} + + QString name; + QMetaMethod signal; + QmlContext *context; + int coreIdx; + uint type; + int attachedFunc; + QObject *object; + QMetaProperty prop; + int propType; + + mutable QmlMetaProperty::PropertyCategory category; + + QObject *attachedObject() const; + void findSignalInt(QObject *, const QString &); +}; + +#endif // QMLMETAPROPERTY_P_H + diff --git a/src/declarative/qml/qmlmetatype.cpp b/src/declarative/qml/qmlmetatype.cpp new file mode 100644 index 0000000..584ccde --- /dev/null +++ b/src/declarative/qml/qmlmetatype.cpp @@ -0,0 +1,1164 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlmetatype.h" +#include <QtCore/qdebug.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qbitarray.h> +#include <QtCore/qreadwritelock.h> +#include <private/qmlproxymetaobject_p.h> + +#include <qmetatype.h> +#include <qobjectdefs.h> +#include <qdatetime.h> +#include <qbytearray.h> +#include <qreadwritelock.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvector.h> +#include <qlocale.h> +#include <QtCore/qcryptographichash.h> +#include <qmlcustomparser.h> + +QT_BEGIN_NAMESPACE +#ifdef QT_BOOTSTRAPPED +# ifndef QT_NO_GEOM_VARIANT +# define QT_NO_GEOM_VARIANT +# endif +#else +# include <qbitarray.h> +# include <qurl.h> +# include <qvariant.h> +#endif + +#ifndef QT_NO_GEOM_VARIANT +# include <qsize.h> +# include <qpoint.h> +# include <qrect.h> +# include <qline.h> +#endif +#define NS(x) QT_PREPEND_NAMESPACE(x) + +struct QmlMetaTypeData +{ + QList<QmlType *> types; + typedef QHash<int, QmlType *> Ids; + Ids idToType; + typedef QHash<QByteArray, QmlType *> Names; + Names nameToType; + typedef QHash<const QMetaObject *, QmlType *> MetaObjects; + MetaObjects metaObjectToType; + typedef QHash<QByteArray, QmlCustomParser *> CustomParsers; + CustomParsers customParsers; + typedef QHash<int, QmlMetaType::StringConverter> StringConverters; + StringConverters stringConverters; + + QBitArray objects; + QBitArray interfaces; + QBitArray qmllists; + QBitArray lists; +}; +Q_GLOBAL_STATIC(QmlMetaTypeData, metaTypeData); +Q_GLOBAL_STATIC(QReadWriteLock, metaTypeDataLock); + +class QmlTypePrivate +{ +public: + QmlTypePrivate(); + + void init() const; + + bool m_isInterface : 1; + const char *m_iid; + QByteArray m_name; + int m_typeId; int m_listId; int m_qmlListId; + QmlPrivate::Func m_opFunc; + const QMetaObject *m_baseMetaObject; + QmlAttachedPropertiesFunc m_attachedPropertiesFunc; + int m_parserStatusCast; + QmlPrivate::CreateFunc m_extFunc; + const QMetaObject *m_extMetaObject; + int m_index; + mutable volatile bool m_isSetup:1; + mutable QList<QmlProxyMetaObject::ProxyData> m_metaObjects; + mutable QByteArray m_hash; +}; + +QmlTypePrivate::QmlTypePrivate() +: m_isInterface(false), m_iid(0), m_typeId(0), m_listId(0), m_qmlListId(0), + m_opFunc(0), m_baseMetaObject(0), m_attachedPropertiesFunc(0), + m_parserStatusCast(-1), m_extFunc(0), m_extMetaObject(0), m_index(-1), m_isSetup(false) +{ +} + + +QmlType::QmlType(int type, int listType, int qmlListType, + QmlPrivate::Func opFunc, const char *iid, int index) +: d(new QmlTypePrivate) +{ + d->m_isInterface = true; + d->m_iid = iid; + d->m_typeId = type; + d->m_listId = listType; + d->m_qmlListId = qmlListType; + d->m_opFunc = opFunc; + d->m_index = index; + d->m_isSetup = true; +} + +QmlType::QmlType(int type, int listType, int qmlListType, + QmlPrivate::Func opFunc, const char *qmlName, + const QMetaObject *metaObject, + QmlAttachedPropertiesFunc attachedPropertiesFunc, + int parserStatusCast, QmlPrivate::CreateFunc extFunc, + const QMetaObject *extMetaObject, int index) +: d(new QmlTypePrivate) +{ + d->m_name = qmlName; + d->m_typeId = type; + d->m_listId = listType; + d->m_qmlListId = qmlListType; + d->m_opFunc = opFunc; + d->m_baseMetaObject = metaObject; + d->m_attachedPropertiesFunc = attachedPropertiesFunc; + d->m_parserStatusCast = parserStatusCast; + d->m_extFunc = extFunc; + d->m_index = index; + + if(extMetaObject) + d->m_extMetaObject = extMetaObject; +} + +QmlType::~QmlType() +{ + delete d; +} + +void QmlTypePrivate::init() const +{ + if(m_isSetup) return; + + QWriteLocker lock(metaTypeDataLock()); + if(m_isSetup) + return; + + // Setup extended meta object + // XXX - very inefficient + const QMetaObject *mo = m_baseMetaObject; + if(m_extFunc) { + QMetaObject *mmo = new QMetaObject; + *mmo = *m_extMetaObject; + mmo->d.superdata = mo; + QmlProxyMetaObject::ProxyData data = { mmo, m_extFunc, 0 }; + m_metaObjects << data; + } + + mo = mo->d.superdata; + while(mo) { + QmlType *t = metaTypeData()->metaObjectToType.value(mo); + if(t) { + if(t->d->m_extFunc) { + QMetaObject *mmo = new QMetaObject; + *mmo = *t->d->m_extMetaObject; + mmo->d.superdata = m_baseMetaObject; + if(!m_metaObjects.isEmpty()) + m_metaObjects.last().metaObject->d.superdata = mmo; + QmlProxyMetaObject::ProxyData data = { mmo, t->d->m_extFunc, 0 }; + m_metaObjects << data; + } + } + mo = mo->d.superdata; + } + + for(int ii = 0; ii < m_metaObjects.count(); ++ii) + m_metaObjects[ii].propertyOffset = + m_metaObjects.at(ii).metaObject->propertyOffset(); + + // Calculate hash + QByteArray hashData; + + const QMetaObject *myMetaObject = m_metaObjects.isEmpty()?m_baseMetaObject:m_metaObjects.first().metaObject; + + for(int ii = 0; ii < myMetaObject->propertyCount(); ++ii) { + QMetaProperty prop = myMetaObject->property(ii); + hashData.append(prop.type()); + hashData.append("|"); + hashData.append(prop.name()); + hashData.append("|"); + } + + for(int ii = 0; ii < myMetaObject->methodCount(); ++ii) { + QMetaMethod method = myMetaObject->method(ii); + hashData.append(method.signature()); + hashData.append("|"); + } + + m_hash = QCryptographicHash::hash(hashData, QCryptographicHash::Md5); + + m_isSetup = true; + lock.unlock(); +} + +QByteArray QmlType::typeName() const +{ + if(d->m_baseMetaObject) + return d->m_baseMetaObject->className(); + else + return QByteArray(); +} + +QByteArray QmlType::qmlTypeName() const +{ + return d->m_name; +} + +QByteArray QmlType::hash() const +{ + d->init(); + + return d->m_hash; +} + +QObject *QmlType::create() const +{ + d->init(); + + QVariant v; + QObject *rv = 0; + d->m_opFunc(QmlPrivate::Create, 0, v, v, (void **)&rv); + + if (rv && !d->m_metaObjects.isEmpty()) + (void *)new QmlProxyMetaObject(rv, &d->m_metaObjects); + + return rv; +} + +bool QmlType::isInterface() const +{ + return d->m_isInterface; +} + +int QmlType::typeId() const +{ + return d->m_typeId; +} + +int QmlType::qListTypeId() const +{ + return d->m_listId; +} + +int QmlType::qmlListTypeId() const +{ + return d->m_qmlListId; +} + +void QmlType::listClear(const QVariant &list) +{ + Q_ASSERT(list.userType() == qListTypeId()); + QVariant arg; + d->m_opFunc(QmlPrivate::Clear, 0, list, arg, 0); +} + +void QmlType::listAppend(const QVariant &list, const QVariant &item) +{ + Q_ASSERT(list.userType() == qListTypeId()); + d->m_opFunc(QmlPrivate::Append, 0, list, item, 0); +} + +QVariant QmlType::listAt(const QVariant &list, int idx) +{ + Q_ASSERT(list.userType() == qListTypeId()); + QVariant rv; + void *ptr = (void *)&rv; + d->m_opFunc(QmlPrivate::Value, idx, list, QVariant(), &ptr); + return rv; +} + +int QmlType::listCount(const QVariant &list) +{ + Q_ASSERT(list.userType() == qListTypeId()); + return d->m_opFunc(QmlPrivate::Length, 0, list, QVariant(), 0); +} + +const QMetaObject *QmlType::metaObject() const +{ + d->init(); + + if(d->m_metaObjects.isEmpty()) + return d->m_baseMetaObject; + else + return d->m_metaObjects.first().metaObject; + +} + +const QMetaObject *QmlType::baseMetaObject() const +{ + return d->m_baseMetaObject; +} + +QmlAttachedPropertiesFunc QmlType::attachedPropertiesFunction() const +{ + return d->m_attachedPropertiesFunc; +} + +int QmlType::parserStatusCast() const +{ + return d->m_parserStatusCast; +} + +QVariant QmlType::fromObject(QObject *obj) const +{ + QVariant rv; + QVariant *v_ptr = &rv; + QVariant vobj = QVariant::fromValue(obj); + d->m_opFunc(QmlPrivate::FromObject, 0, QVariant(), vobj, (void **)&v_ptr); + return rv; +} + +const char *QmlType::interfaceIId() const +{ + return d->m_iid; +} + +int QmlType::index() const +{ + return d->m_index; +} + +int QmlMetaType::registerInterface(const QmlPrivate::MetaTypeIds &id, + QmlPrivate::Func listFunction, + const char *iid) +{ + QWriteLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + int index = data->types.count(); + + QmlType *type = new QmlType(id.typeId, id.listId, id.qmlListId, + listFunction, iid, index); + + data->types.append(type); + data->idToType.insert(type->typeId(), type); + data->idToType.insert(type->qListTypeId(), type); + data->idToType.insert(type->qmlListTypeId(), type); + data->nameToType.insert(type->qmlTypeName(), type); + + if(data->interfaces.size() < id.typeId) + data->interfaces.resize(id.typeId + 16); + if(data->qmllists.size() < id.qmlListId) + data->qmllists.resize(id.qmlListId + 16); + if(data->lists.size() < id.listId) + data->lists.resize(id.listId + 16); + data->interfaces.setBit(id.typeId, true); + data->qmllists.setBit(id.qmlListId, true); + data->lists.setBit(id.listId, true); + + return index; +} + +int QmlMetaType::registerType(const QmlPrivate::MetaTypeIds &id, QmlPrivate::Func func, const char *cname, const QMetaObject *mo, QmlAttachedPropertiesFunc attach, int pStatus, int object, QmlPrivate::CreateFunc extFunc, const QMetaObject *extmo) +{ + Q_UNUSED(object); + QWriteLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + QString name = QLatin1String(cname); + for(int ii = 0; ii < name.count(); ++ii) { + if(!name.at(ii).isLetterOrNumber()) { + qWarning("QmlMetaType: Invalid QML name %s", cname); + return -1; + } + } + + int index = data->types.count(); + + QmlType *type = new QmlType(id.typeId, id.listId, id.qmlListId, + func, cname, mo, attach, pStatus, extFunc, + extmo, index); + + data->types.append(type); + data->idToType.insert(type->typeId(), type); + data->idToType.insert(type->qListTypeId(), type); + data->idToType.insert(type->qmlListTypeId(), type); + + if(!type->qmlTypeName().isEmpty()) + data->nameToType.insert(type->qmlTypeName(), type); + + data->metaObjectToType.insert(type->baseMetaObject(), type); + + if(data->objects.size() <= id.typeId) + data->objects.resize(id.typeId + 16); + if(data->qmllists.size() <= id.qmlListId) + data->qmllists.resize(id.qmlListId + 16); + if(data->lists.size() <= id.listId) + data->lists.resize(id.listId + 16); + data->objects.setBit(id.typeId, true); + data->qmllists.setBit(id.qmlListId, true); + data->lists.setBit(id.listId, true); + + return index; +} + +void QmlMetaType::registerCustomParser(const char *qmlName, + QmlCustomParser *parser) +{ + QWriteLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + Q_ASSERT(parser); + if(data->customParsers.contains(qmlName)) { + delete parser; + return; + } + + data->customParsers.insert(qmlName, parser); +} + +QmlCustomParser *QmlMetaType::customParser(const QByteArray &name) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + return data->customParsers.value(name); +} + + +int QmlMetaType::qmlParserStatusCast(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->idToType.value(userType); + if(type && type->typeId() == userType) + return type->parserStatusCast(); + else + return -1; +} + +QObject *QmlMetaType::toQObject(const QVariant &v) +{ + if(!isObject(v.userType())) + return 0; + + // NOTE: This assumes a cast to QObject does not alter the + // object pointer + QObject *rv = *(QObject **)v.constData(); + return rv; +} + +/* + Returns the item type for a list of type \a id. + */ +int QmlMetaType::listType(int id) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->idToType.value(id); + if(type && type->qListTypeId() == id) + return type->typeId(); + else + return 0; +} + +/* + Returns the item type for a qml list of type \a id. + */ +int QmlMetaType::qmlListType(int id) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->idToType.value(id); + if(type && type->qmlListTypeId() == id) + return type->typeId(); + else + return 0; +} + +bool QmlMetaType::clear(const QVariant &list) +{ + int userType = list.userType(); + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->idToType.value(userType); + lock.unlock(); + if(type && type->qListTypeId() == userType) { + type->listClear(list); + return true; + } else { + return false; + } +} + +bool QmlMetaType::append(const QVariant &list, const QVariant &item) +{ + int userType = list.userType(); + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->idToType.value(userType); + lock.unlock(); + if(type && type->qListTypeId() == userType && + item.userType() == type->typeId()) { + type->listAppend(list, item); + return true; + } else { + return false; + } +} + +QObject *QmlMetaType::create(const QByteArray &name) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + lock.unlock(); + + QmlType *type = data->nameToType.value(name); + if(type) + return type->create(); + else + return 0; +} + +QVariant QmlMetaType::fromObject(QObject *obj, int typeId) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + QmlType *type = data->idToType.value(typeId); + if(type && type->typeId() == typeId) + return type->fromObject(obj); + else + return QVariant(); +} + +const QMetaObject *QmlMetaType::rawMetaObjectForType(int id) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + QmlType *type = data->idToType.value(id); + if(type && type->typeId() == id) + return type->baseMetaObject(); + else + return 0; +} + +const QMetaObject *QmlMetaType::rawMetaObjectForType(const QByteArray &name) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + QmlType *type = data->nameToType.value(name); + if(type) + return type->baseMetaObject(); + else + return 0; +} + +const QMetaObject *QmlMetaType::metaObjectForType(int id) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->idToType.value(id); + lock.unlock(); + + if(type && type->typeId() == id) + return type->metaObject(); + else + return 0; +} + +const QMetaObject *QmlMetaType::metaObjectForType(const QByteArray &name) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->nameToType.value(name); + lock.unlock(); + + if(type) + return type->metaObject(); + else + return 0; +} + +int QmlMetaType::type(const QByteArray &name) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + QmlType *type = data->nameToType.value(name); + if(type) + return type->typeId(); + else + return 0; +} + +int QmlMetaType::attachedPropertiesFuncId(const QByteArray &name) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + QmlType *type = data->nameToType.value(name); + if(type && type->attachedPropertiesFunction()) + return type->index(); + else + return -1; +} + +QmlAttachedPropertiesFunc QmlMetaType::attachedPropertiesFuncById(int id) +{ + if(id < 0) + return 0; + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + return data->types.at(id)->attachedPropertiesFunction(); +} + +QmlAttachedPropertiesFunc +QmlMetaType::attachedPropertiesFunc(const QByteArray &name) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->nameToType.value(name); + if(type) + return type->attachedPropertiesFunction(); + else + return 0; +} + +QMetaProperty QmlMetaType::defaultProperty(const QMetaObject *metaObject) +{ + int idx = metaObject->indexOfClassInfo("DefaultProperty"); + while(idx == -1 && metaObject) { + metaObject = metaObject->superClass(); + if(metaObject) + idx = metaObject->indexOfClassInfo("DefaultProperty"); + } + if(-1 == idx) + return QMetaProperty(); + + QMetaClassInfo info = metaObject->classInfo(idx); + if(!info.value()) + return QMetaProperty(); + + idx = metaObject->indexOfProperty(info.value()); + if(-1 == idx) + return QMetaProperty(); + + return metaObject->property(idx); +} + +QMetaProperty QmlMetaType::defaultProperty(QObject *obj) +{ + if(!obj) + return QMetaProperty(); + + const QMetaObject *metaObject = obj->metaObject(); + return defaultProperty(metaObject); +} + +QMetaMethod QmlMetaType::defaultMethod(const QMetaObject *metaObject) +{ + int idx = metaObject->indexOfClassInfo("DefaultMethod"); + while(idx == -1 && metaObject) { + metaObject = metaObject->superClass(); + if(metaObject) + idx = metaObject->indexOfClassInfo("DefaultMethod"); + } + if(-1 == idx) + return QMetaMethod(); + + QMetaClassInfo info = metaObject->classInfo(idx); + if(!info.value()) + return QMetaMethod(); + + idx = metaObject->indexOfMethod(info.value()); + if(-1 == idx) + return QMetaMethod(); + + return metaObject->method(idx); +} + +QMetaMethod QmlMetaType::defaultMethod(QObject *obj) +{ + if(!obj) + return QMetaMethod(); + + const QMetaObject *metaObject = obj->metaObject(); + return defaultMethod(metaObject); +} + +/*! + */ +QMetaProperty QmlMetaType::property(QObject *obj, const QByteArray &bname) +{ + return property(obj, bname.constData()); +} + +/*! + */ +QMetaProperty QmlMetaType::property(QObject *obj, const char *name) +{ + if(!obj) + return QMetaProperty(); + + const QMetaObject *metaObject = obj->metaObject(); + + int idx = metaObject->indexOfProperty(name); + if(-1 == idx) + return QMetaProperty(); + + return metaObject->property(idx); +} + +bool QmlMetaType::isObject(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->objects.size() && data->objects.testBit(userType); +} + +bool QmlMetaType::isInterface(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->interfaces.size() && data->interfaces.testBit(userType); +} + +const char *QmlMetaType::interfaceIId(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->idToType.value(userType); + lock.unlock(); + if(type && type->isInterface() && type->typeId() == userType) + return type->interfaceIId(); + else + return 0; +} + +bool QmlMetaType::isObject(const QMetaObject *mo) +{ + while(mo) { + if(mo == &QObject::staticMetaObject) + return true; + mo = mo->superClass(); + } + return false; +} + +bool QmlMetaType::isQmlList(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->qmllists.size() && data->qmllists.testBit(userType); +} + +bool QmlMetaType::isList(int userType) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + return userType >= 0 && userType < data->lists.size() && data->lists.testBit(userType); +} + +bool QmlMetaType::isList(const QVariant &v) +{ + return (v.type() == QVariant::UserType && isList(v.userType())); +} + +int QmlMetaType::listCount(const QVariant &v) +{ + int userType = v.userType(); + + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->idToType.value(userType); + lock.unlock(); + + if(type && type->qListTypeId() == userType) + return type->listCount(v); + else + return 0; +} + +QVariant QmlMetaType::listAt(const QVariant &v, int idx) +{ + int userType = v.userType(); + + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + QmlType *type = data->idToType.value(userType); + lock.unlock(); + + if(type && type->qListTypeId() == userType) + return type->listAt(v, idx); + else + return 0; +} + +/*! + A custom string convertor allows you to specify a function pointer that + returns a variant of \a type. For example, if you have written your own icon + class that you want to support as an object property assignable in QML: + + \code + int type = qRegisterMetaType<SuperIcon>("SuperIcon"); + QML::addCustomStringConvertor(type, &SuperIcon::pixmapFromString); + \endcode + + The function pointer must be of the form: + \code + QVariant (*StringConverter)(const QString &); + \endcode + */ +void QmlMetaType::registerCustomStringConverter(int type, StringConverter converter) +{ + QWriteLocker lock(metaTypeDataLock()); + + QmlMetaTypeData *data = metaTypeData(); + if(data->stringConverters.contains(type)) + return; + data->stringConverters.insert(type, converter); +} + +/*! + Return the custom string converter for \a type, previously installed through + registerCustomStringConverter() + */ +QmlMetaType::StringConverter QmlMetaType::customStringConverter(int type) +{ + QReadLocker lock(metaTypeDataLock()); + + QmlMetaTypeData *data = metaTypeData(); + return data->stringConverters.value(type); +} + +QmlType *QmlMetaType::qmlType(const QByteArray &name) +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + return data->nameToType.value(name); +} + +QList<QByteArray> QmlMetaType::qmlTypeNames() +{ + QReadLocker lock(metaTypeDataLock()); + QmlMetaTypeData *data = metaTypeData(); + + return data->nameToType.keys(); +} + +/*! + Copies \a copy into \a data, assuming they both are of type \a type. If + \a copy is zero, a default type is copied. Returns true if the copy was + successful and false if not. + + \note This should move into QMetaType once complete + +*/ +bool QmlMetaType::copy(int type, void *data, const void *copy) +{ + if (copy) { + switch(type) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + *static_cast<void **>(data) = *static_cast<void* const *>(copy); + return true; + case QMetaType::Long: + *static_cast<long *>(data) = *static_cast<const long*>(copy); + return true; + case QMetaType::Int: + *static_cast<int *>(data) = *static_cast<const int*>(copy); + return true; + case QMetaType::Short: + *static_cast<short *>(data) = *static_cast<const short*>(copy); + return true; + case QMetaType::Char: + *static_cast<char *>(data) = *static_cast<const char*>(copy); + return true; + case QMetaType::ULong: + *static_cast<ulong *>(data) = *static_cast<const ulong*>(copy); + return true; + case QMetaType::UInt: + *static_cast<uint *>(data) = *static_cast<const uint*>(copy); + return true; + case QMetaType::LongLong: + *static_cast<qlonglong *>(data) = *static_cast<const qlonglong*>(copy); + return true; + case QMetaType::ULongLong: + *static_cast<qulonglong *>(data) = *static_cast<const qulonglong*>(copy); + return true; + case QMetaType::UShort: + *static_cast<ushort *>(data) = *static_cast<const ushort*>(copy); + return true; + case QMetaType::UChar: + *static_cast<uchar *>(data) = *static_cast<const uchar*>(copy); + return true; + case QMetaType::Bool: + *static_cast<bool *>(data) = *static_cast<const bool*>(copy); + return true; + case QMetaType::Float: + *static_cast<float *>(data) = *static_cast<const float*>(copy); + return true; + case QMetaType::Double: + *static_cast<double *>(data) = *static_cast<const double*>(copy); + return true; + case QMetaType::QChar: + *static_cast<NS(QChar) *>(data) = *static_cast<const NS(QChar)*>(copy); + return true; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QVariantMap: + *static_cast<NS(QVariantMap) *>(data) = *static_cast<const NS(QVariantMap)*>(copy); + return true; + case QMetaType::QVariantHash: + *static_cast<NS(QVariantHash) *>(data) = *static_cast<const NS(QVariantHash)*>(copy); + return true; + case QMetaType::QVariantList: + *static_cast<NS(QVariantList) *>(data) = *static_cast<const NS(QVariantList)*>(copy); + return true; +#endif + case QMetaType::QByteArray: + *static_cast<NS(QByteArray) *>(data) = *static_cast<const NS(QByteArray)*>(copy); + return true; + case QMetaType::QString: + *static_cast<NS(QString) *>(data) = *static_cast<const NS(QString)*>(copy); + return true; + case QMetaType::QStringList: + *static_cast<NS(QStringList) *>(data) = *static_cast<const NS(QStringList)*>(copy); + return true; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QBitArray: + *static_cast<NS(QBitArray) *>(data) = *static_cast<const NS(QBitArray)*>(copy); + return true; +#endif + case QMetaType::QDate: + *static_cast<NS(QDate) *>(data) = *static_cast<const NS(QDate)*>(copy); + return true; + case QMetaType::QTime: + *static_cast<NS(QTime) *>(data) = *static_cast<const NS(QTime)*>(copy); + return true; + case QMetaType::QDateTime: + *static_cast<NS(QDateTime) *>(data) = *static_cast<const NS(QDateTime)*>(copy); + return true; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QUrl: + *static_cast<NS(QUrl) *>(data) = *static_cast<const NS(QUrl)*>(copy); + return true; +#endif + case QMetaType::QLocale: + *static_cast<NS(QLocale) *>(data) = *static_cast<const NS(QLocale)*>(copy); + return true; +#ifndef QT_NO_GEOM_VARIANT + case QMetaType::QRect: + *static_cast<NS(QRect) *>(data) = *static_cast<const NS(QRect)*>(copy); + return true; + case QMetaType::QRectF: + *static_cast<NS(QRectF) *>(data) = *static_cast<const NS(QRectF)*>(copy); + return true; + case QMetaType::QSize: + *static_cast<NS(QSize) *>(data) = *static_cast<const NS(QSize)*>(copy); + return true; + case QMetaType::QSizeF: + *static_cast<NS(QSizeF) *>(data) = *static_cast<const NS(QSizeF)*>(copy); + return true; + case QMetaType::QLine: + *static_cast<NS(QLine) *>(data) = *static_cast<const NS(QLine)*>(copy); + return true; + case QMetaType::QLineF: + *static_cast<NS(QLineF) *>(data) = *static_cast<const NS(QLineF)*>(copy); + return true; + case QMetaType::QPoint: + *static_cast<NS(QPoint) *>(data) = *static_cast<const NS(QPoint)*>(copy); + return true; + case QMetaType::QPointF: + *static_cast<NS(QPointF) *>(data) = *static_cast<const NS(QPointF)*>(copy); + return true; +#endif +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + *static_cast<NS(QRegExp) *>(data) = *static_cast<const NS(QRegExp)*>(copy); + return true; +#endif + case QMetaType::Void: + return true; + default: + ; + } + } else { + switch(type) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + *static_cast<void **>(data) = 0; + return true; + case QMetaType::Long: + *static_cast<long *>(data) = long(0); + return true; + case QMetaType::Int: + *static_cast<int *>(data) = int(0); + return true; + case QMetaType::Short: + *static_cast<short *>(data) = short(0); + return true; + case QMetaType::Char: + *static_cast<char *>(data) = char(0); + return true; + case QMetaType::ULong: + *static_cast<ulong *>(data) = ulong(0); + return true; + case QMetaType::UInt: + *static_cast<uint *>(data) = uint(0); + return true; + case QMetaType::LongLong: + *static_cast<qlonglong *>(data) = qlonglong(0); + return true; + case QMetaType::ULongLong: + *static_cast<qulonglong *>(data) = qulonglong(0); + return true; + case QMetaType::UShort: + *static_cast<ushort *>(data) = ushort(0); + return true; + case QMetaType::UChar: + *static_cast<uchar *>(data) = uchar(0); + return true; + case QMetaType::Bool: + *static_cast<bool *>(data) = bool(false); + return true; + case QMetaType::Float: + *static_cast<float *>(data) = float(0); + return true; + case QMetaType::Double: + *static_cast<double *>(data) = double(); + return true; + case QMetaType::QChar: + *static_cast<NS(QChar) *>(data) = NS(QChar)(); + return true; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QVariantMap: + *static_cast<NS(QVariantMap) *>(data) = NS(QVariantMap)(); + return true; + case QMetaType::QVariantHash: + *static_cast<NS(QVariantHash) *>(data) = NS(QVariantHash)(); + return true; + case QMetaType::QVariantList: + *static_cast<NS(QVariantList) *>(data) = NS(QVariantList)(); + return true; +#endif + case QMetaType::QByteArray: + *static_cast<NS(QByteArray) *>(data) = NS(QByteArray)(); + return true; + case QMetaType::QString: + *static_cast<NS(QString) *>(data) = NS(QString)(); + return true; + case QMetaType::QStringList: + *static_cast<NS(QStringList) *>(data) = NS(QStringList)(); + return true; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QBitArray: + *static_cast<NS(QBitArray) *>(data) = NS(QBitArray)(); + return true; +#endif + case QMetaType::QDate: + *static_cast<NS(QDate) *>(data) = NS(QDate)(); + return true; + case QMetaType::QTime: + *static_cast<NS(QTime) *>(data) = NS(QTime)(); + return true; + case QMetaType::QDateTime: + *static_cast<NS(QDateTime) *>(data) = NS(QDateTime)(); + return true; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QUrl: + *static_cast<NS(QUrl) *>(data) = NS(QUrl)(); + return true; +#endif + case QMetaType::QLocale: + *static_cast<NS(QLocale) *>(data) = NS(QLocale)(); + return true; +#ifndef QT_NO_GEOM_VARIANT + case QMetaType::QRect: + *static_cast<NS(QRect) *>(data) = NS(QRect)(); + return true; + case QMetaType::QRectF: + *static_cast<NS(QRectF) *>(data) = NS(QRectF)(); + return true; + case QMetaType::QSize: + *static_cast<NS(QSize) *>(data) = NS(QSize)(); + return true; + case QMetaType::QSizeF: + *static_cast<NS(QSizeF) *>(data) = NS(QSizeF)(); + return true; + case QMetaType::QLine: + *static_cast<NS(QLine) *>(data) = NS(QLine)(); + return true; + case QMetaType::QLineF: + *static_cast<NS(QLineF) *>(data) = NS(QLineF)(); + return true; + case QMetaType::QPoint: + *static_cast<NS(QPoint) *>(data) = NS(QPoint)(); + return true; + case QMetaType::QPointF: + *static_cast<NS(QPointF) *>(data) = NS(QPointF)(); + return true; +#endif +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + *static_cast<NS(QRegExp) *>(data) = NS(QRegExp)(); + return true; +#endif + case QMetaType::Void: + return true; + default: + ; + } + } + + return false; +} + +void qmlRegisterCustomParser(const char *qmlName, QmlCustomParser *parser) +{ + QmlMetaType::registerCustomParser(qmlName, parser); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlmetatype.h b/src/declarative/qml/qmlmetatype.h new file mode 100644 index 0000000..3daa14b --- /dev/null +++ b/src/declarative/qml/qmlmetatype.h @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLMETATYPE_H +#define QMLMETATYPE_H + +#include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> +#include <QtCore/qbitarray.h> +#include <QtDeclarative/qmlprivate.h> +#include <QtDeclarative/qmlparserstatus.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlType; +class QmlCustomParser; +class Q_DECLARATIVE_EXPORT QmlMetaType +{ +public: + static int registerType(const QmlPrivate::MetaTypeIds &, QmlPrivate::Func, const char *, const QMetaObject *, QmlAttachedPropertiesFunc, int pStatus, int object, QmlPrivate::CreateFunc extFunc, const QMetaObject *extmo); + static int registerInterface(const QmlPrivate::MetaTypeIds &, QmlPrivate::Func, const char *); + static void registerCustomParser(const char *, QmlCustomParser *); + + static QmlCustomParser *customParser(const QByteArray &); + + static bool copy(int type, void *data, const void *copy = 0); + + static QmlType *qmlType(const QByteArray &); + static QList<QByteArray> qmlTypeNames(); + + static QMetaProperty defaultProperty(const QMetaObject *); + static QMetaProperty defaultProperty(QObject *); + static QMetaMethod defaultMethod(const QMetaObject *); + static QMetaMethod defaultMethod(QObject *); + static QMetaProperty property(QObject *, const QByteArray &); + static QMetaProperty property(QObject *, const char *); + static QObject *toQObject(const QVariant &); + static int qmlParserStatusCast(int); + static int listType(int); + static int type(const QByteArray &); + static int type(const QString &); + static bool clear(const QVariant &); + static bool append(const QVariant &, const QVariant &); + static QVariant fromObject(QObject *, int type); + static QObject *create(const QByteArray &); + static const QMetaObject *rawMetaObjectForType(const QByteArray &); + static const QMetaObject *rawMetaObjectForType(int); + static const QMetaObject *metaObjectForType(const QByteArray &); + static const QMetaObject *metaObjectForType(int); + static int attachedPropertiesFuncId(const QByteArray &); + static QmlAttachedPropertiesFunc attachedPropertiesFuncById(int); + static QmlAttachedPropertiesFunc attachedPropertiesFunc(const QByteArray &); + + static bool isInterface(int); + static const char *interfaceIId(int); + static bool isObject(int); + static bool isObject(const QMetaObject *); + static bool isList(int); + static bool isList(const QVariant &); + static bool isQmlList(int); + static int qmlListType(int); + static int listCount(const QVariant &); + static QVariant listAt(const QVariant &, int); + + typedef QVariant (*StringConverter)(const QString &); + static void registerCustomStringConverter(int, StringConverter); + static StringConverter customStringConverter(int); +}; + +class QmlTypePrivate; +class QmlType +{ +public: + QByteArray typeName() const; + QByteArray qmlTypeName() const; + + QByteArray hash() const; + + QObject *create() const; + + bool isInterface() const; + int typeId() const; + int qListTypeId() const; + int qmlListTypeId() const; + + void listClear(const QVariant &); + void listAppend(const QVariant &, const QVariant &); + QVariant listAt(const QVariant &, int); + int listCount(const QVariant &); + + const QMetaObject *metaObject() const; + const QMetaObject *baseMetaObject() const; + + QmlAttachedPropertiesFunc attachedPropertiesFunction() const; + + int parserStatusCast() const; + QVariant fromObject(QObject *) const; + const char *interfaceIId() const; + + int index() const; +private: + friend class QmlMetaType; + friend class QmlTypePrivate; + QmlType(int, int, int, QmlPrivate::Func, const char *, int); + QmlType(int, int, int, QmlPrivate::Func, const char *, const QMetaObject *, QmlAttachedPropertiesFunc, int, QmlPrivate::CreateFunc, const QMetaObject *, int); + ~QmlType(); + + QmlTypePrivate *d; +}; + +template<typename T> +int qmlRegisterType(const char *typeName) +{ + QByteArray name(typeName); + QmlPrivate::MetaTypeIds ids = { + qRegisterMetaType<T *>(QByteArray(name + "*").constData()), + qRegisterMetaType<T *>(QByteArray("QList<" + name + "*>*").constData()), + qRegisterMetaType<T *>(QByteArray("QmlList<" + name + "*>*").constData()) + }; + + return QmlMetaType::registerType(ids, QmlPrivate::list_nocreate_op<T>, 0, + &T::staticMetaObject, + QmlPrivate::attachedPropertiesFunc<T>(), + QmlPrivate::StaticCastSelector<T,QmlParserStatus>::cast(), + QmlPrivate::StaticCastSelector<T,QObject>::cast(), + 0, 0); +} + +template<typename T> +int qmlRegisterType(const char *qmlName, const char *typeName) +{ + QByteArray name(typeName); + QmlPrivate::MetaTypeIds ids = { + qRegisterMetaType<T *>(QByteArray(name + "*").constData()), + qRegisterMetaType<T *>(QByteArray("QList<" + name + "*>*").constData()), + qRegisterMetaType<T *>(QByteArray("QmlList<" + name + "*>*").constData()) + }; + + return QmlMetaType::registerType(ids, QmlPrivate::list_op<T>, qmlName, + &T::staticMetaObject, + QmlPrivate::attachedPropertiesFunc<T>(), + QmlPrivate::StaticCastSelector<T,QmlParserStatus>::cast(), + QmlPrivate::StaticCastSelector<T,QObject>::cast(), + 0, 0); +} + +template<typename T, typename E> +int qmlRegisterExtendedType(const char *typeName) +{ + QByteArray name(typeName); + QmlPrivate::MetaTypeIds ids = { + qRegisterMetaType<T *>(QByteArray(name + "*").constData()), + qRegisterMetaType<T *>(QByteArray("QList<" + name + "*>*").constData()), + qRegisterMetaType<T *>(QByteArray("QmlList<" + name + "*>*").constData()) + }; + + QmlAttachedPropertiesFunc attached = + QmlPrivate::attachedPropertiesFunc<E>(); + if(!attached) + attached = QmlPrivate::attachedPropertiesFunc<T>(); + + return QmlMetaType::registerType(ids, QmlPrivate::list_nocreate_op<T>, 0, + &T::staticMetaObject, attached, + QmlPrivate::StaticCastSelector<T,QmlParserStatus>::cast(), + QmlPrivate::StaticCastSelector<T,QObject>::cast(), + &QmlPrivate::CreateParent<E>::create, &E::staticMetaObject); +} + +template<typename T, typename E> +int qmlRegisterExtendedType(const char *qmlName, const char *typeName) +{ + QByteArray name(typeName); + QmlPrivate::MetaTypeIds ids = { + qRegisterMetaType<T *>(QByteArray(name + "*").constData()), + qRegisterMetaType<T *>(QByteArray("QList<" + name + "*>*").constData()), + qRegisterMetaType<T *>(QByteArray("QmlList<" + name + "*>*").constData()) + }; + + QmlAttachedPropertiesFunc attached = + QmlPrivate::attachedPropertiesFunc<E>(); + if(!attached) + attached = QmlPrivate::attachedPropertiesFunc<T>(); + + return QmlMetaType::registerType(ids, QmlPrivate::list_op<T>, + qmlName, + &T::staticMetaObject, + attached, + QmlPrivate::StaticCastSelector<T,QmlParserStatus>::cast(), + QmlPrivate::StaticCastSelector<T,QObject>::cast(), + &QmlPrivate::CreateParent<E>::create, + &E::staticMetaObject); +} + +template<typename T> +int qmlRegisterInterface(const char *typeName) +{ + QByteArray name(typeName); + QmlPrivate::MetaTypeIds ids = { + qRegisterMetaType<T *>(QByteArray(name + "*").constData()), + qRegisterMetaType<T *>(QByteArray("QList<" + name + "*>*").constData()), + qRegisterMetaType<T *>(QByteArray("QmlList<" + name + "*>*").constData()) + }; + + return QmlMetaType::registerInterface(ids, + QmlPrivate::list_interface_op<T>, + qobject_interface_iid<T *>()); +} + +void qmlRegisterCustomParser(const char *qmlName, QmlCustomParser *); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLMETATYPE_H + diff --git a/src/declarative/qml/qmlparser.cpp b/src/declarative/qml/qmlparser.cpp new file mode 100644 index 0000000..54db32e --- /dev/null +++ b/src/declarative/qml/qmlparser.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlparser_p.h" +#include <QStack> +#include <qmlpropertyvaluesource.h> +#include <QColor> +#include <QPointF> +#include <QSizeF> +#include <QRectF> +#include <private/qmlvme_p.h> +#include <qmlbindablevalue.h> +#include <qfxperf.h> +#include <qml.h> +#include "private/qmlcomponent_p.h" +#include <qmlcomponent.h> +#include <qmlbasicscript.h> +#include "private/qmetaobjectbuilder_p.h" +#include <private/qmlvmemetaobject_p.h> +#include "private/qmlxmlparser_p.h" +#include <private/qmlcompiler_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QmlParser; + +QmlParser::Object::Object() +: type(-1), metatype(0), extObject(0), defaultProperty(0), line(-1), + dynamicPropertiesProperty(0), dynamicSignalsProperty(0) +{ +} + +QmlParser::Object::~Object() +{ + if(defaultProperty) defaultProperty->release(); + foreach(Property *prop, properties) + prop->release(); + if(dynamicPropertiesProperty) dynamicPropertiesProperty->release(); + if(dynamicSignalsProperty) dynamicSignalsProperty->release(); +} + +const QMetaObject *Object::metaObject() const +{ + if(extObject && metatype) + return extObject; + else + return metatype; +} + +QmlParser::Property *Object::getDefaultProperty() +{ + if(!defaultProperty) + defaultProperty = new Property; + return defaultProperty; +} + +Property *QmlParser::Object::getProperty(const QByteArray &name, bool create) +{ + if(!properties.contains(name)) { + if(create) + properties.insert(name, new Property(name)); + else + return 0; + } + return properties[name]; +} + +QmlParser::Object::DynamicProperty::DynamicProperty() +: isDefaultProperty(false), type(Variant), defaultValue(0) +{ +} + +QmlParser::Object::DynamicProperty::DynamicProperty(const DynamicProperty &o) +: isDefaultProperty(o.isDefaultProperty), + type(o.type), + name(o.name), + onValueChanged(o.onValueChanged), + defaultValue(o.defaultValue) +{ +} + +QmlParser::Object::DynamicSignal::DynamicSignal() +{ +} + +QmlParser::Object::DynamicSignal::DynamicSignal(const DynamicSignal &o) +: name(o.name) +{ +} + +QmlParser::Property::Property() +: type(0), index(-1), value(0), isDefault(true), line(-1) +{ +} + +QmlParser::Property::Property(const QByteArray &n) +: type(0), index(-1), value(0), name(n), isDefault(false), line(-1) +{ +} + +QmlParser::Property::~Property() +{ + foreach(Value *value, values) + value->release(); + if(value) value->release(); +} + +Object *QmlParser::Property::getValue() +{ + if(!value) value = new Object; + return value; +} + +void QmlParser::Property::addValue(Value *v) +{ + values << v; +} + +QmlParser::Value::Value() +: type(Unknown), object(0), line(-1) +{ +} + +QmlParser::Value::~Value() +{ + if(object) object->release(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlparser_p.h b/src/declarative/qml/qmlparser_p.h new file mode 100644 index 0000000..9d3f3f6 --- /dev/null +++ b/src/declarative/qml/qmlparser_p.h @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLPARSER_P_H +#define QMLPARSER_P_H + +#include <QByteArray> +#include <QList> +#include <qml.h> +#include "qmlcomponent_p.h" +#include <private/qmlrefcount_p.h> +#include "qmlcompiledcomponent_p.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlXmlParser; + +/* + XXX + + These types are created (and owned) by the QmlXmlParser and consumed by the + QmlCompiler. During the compilation phase the compiler will update some of + the fields for both its own use and for the use of the upcoming QmlDom API. + + The types are part of the generic sounding "QmlParser" namespace for legacy + reasons (there used to be more in this namespace) and will be cleaned up and + migrated into a more appropriate location shortly. +*/ +namespace QmlParser +{ + class Property; + class Object : public QmlRefCount + { + public: + Object(); + virtual ~Object(); + + // Type of the object. The integer is an index into the + // QmlCompiledData::types array, or -1 if the object is a fetched + // object. + int type; + // The name of this type + QByteArray typeName; + // The id assigned to the object (if any). + QByteArray id; + // Custom parsed data + QByteArray custom; + // Returns the metaobject for this type, or 0 if not available. + // Internally selectd between the metatype and extObject variables + const QMetaObject *metaObject() const; + + // The compile time metaobject for this type + const QMetaObject *metatype; + // The synthesized metaobject, if QML added signals or properties to + // this type. Otherwise null + QMetaObject *extObject; + + Property *getDefaultProperty(); + Property *getProperty(const QByteArray &name, bool create=true); + + Property *defaultProperty; + QHash<QByteArray, Property *> properties; + + qint64 line; + + struct DynamicProperty { + DynamicProperty(); + DynamicProperty(const DynamicProperty &); + + enum Type { Variant, Int, Bool, Real, String, Color, Date }; + + bool isDefaultProperty; + Type type; + QByteArray name; + QString onValueChanged; + QmlParser::Property *defaultValue; + }; + struct DynamicSignal { + DynamicSignal(); + DynamicSignal(const DynamicSignal &); + + QByteArray name; + }; + + // The "properties" property + Property *dynamicPropertiesProperty; + // The "signals" property + Property *dynamicSignalsProperty; + // The list of dynamic properties described in the "properties" property + QList<DynamicProperty> dynamicProperties; + // The list of dynamic signals described in the "signals" property + QList<DynamicSignal> dynamicSignals; + }; + + class Value : public QmlRefCount + { + public: + Value(); + virtual ~Value(); + + enum Type { + // The type of this value assignment is not yet known + Unknown, + // This is used as a literal property assignment + Literal, + // This is used as a property binding assignment + PropertyBinding, + // This is used as a QmlPropertyValueSource assignment + ValueSource, + // This is used as a property QObject assignment + CreatedObject, + // This is used as a signal object assignment + SignalObject, + // This is used as a signal expression assignment + SignalExpression, + // This is used as an implicit component creation + Component, + // This is used as an id assignment only + Id + }; + Type type; + + // Primitive value + QString primitive; + // Object value + Object *object; + + qint64 line; + }; + + class Property : public QmlRefCount + { + public: + Property(); + Property(const QByteArray &n); + virtual ~Property(); + + Object *getValue(); + void addValue(Value *v); + + // The QVariant::Type of the property, or 0 (QVariant::Invalid) if + // unknown. + int type; + // The metaobject index of this property, or -1 if unknown. + int index; + + // The list of values assigned to this property. Content in values + // and value are mutually exclusive + QList<Value *> values; + // The accessed property. This is used to represent dot properties. + // Content in value and values are mutually exclusive. + Object *value; + // The property name + QByteArray name; + // True if this property was accessed as the default property. + bool isDefault; + + qint64 line; + }; +} + +#endif // QMLPARSER_P_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/qml/qmlparserstatus.cpp b/src/declarative/qml/qmlparserstatus.cpp new file mode 100644 index 0000000..3bb421d --- /dev/null +++ b/src/declarative/qml/qmlparserstatus.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 QtDeclarative 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 "qmlparserstatus.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QmlParserStatus + \brief provides updates on the parser state. +*/ + +/*! + Invoked after class creation, but before any properties have been set. +*/ +void QmlParserStatus::classBegin() +{ +} + +/*! + Invoked after all properties have been set to their static values. At this + point bindings are still to be evaluated. +*/ +void QmlParserStatus::classComplete() +{ +} + +/*! + Invoked after the root component that caused this instantiation has + completed construction. At this point all static values and binding values + have been assigned to the class. +*/ +void QmlParserStatus::componentComplete() +{ +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlparserstatus.h b/src/declarative/qml/qmlparserstatus.h new file mode 100644 index 0000000..1ec50ba --- /dev/null +++ b/src/declarative/qml/qmlparserstatus.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 QtDeclarative 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 QMLPARSERSTATUS_H +#define QMLPARSERSTATUS_H + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QmlParserStatus +{ +public: + virtual ~QmlParserStatus() {} + + virtual void classBegin(); + virtual void classComplete(); + virtual void componentComplete(); +}; +Q_DECLARE_INTERFACE(QmlParserStatus, "com.trolltech.qml.QmlParserStatus"); + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLPARSERSTATUS_H diff --git a/src/declarative/qml/qmlprivate.cpp b/src/declarative/qml/qmlprivate.cpp new file mode 100644 index 0000000..21b19e8 --- /dev/null +++ b/src/declarative/qml/qmlprivate.cpp @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlprivate.h" + +QT_BEGIN_NAMESPACE + +QmlPrivate::InstanceType::InstanceType(int) {} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlprivate.h b/src/declarative/qml/qmlprivate.h new file mode 100644 index 0000000..183f42b --- /dev/null +++ b/src/declarative/qml/qmlprivate.h @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLPRIVATE_H +#define QMLPRIVATE_H + +#include <QtCore/qglobal.h> +#ifndef Q_OS_WIN32 +#include <stdint.h> +#endif +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +typedef QObject *(*QmlAttachedPropertiesFunc)(QObject *); + +namespace QmlPrivate +{ + class ListInterface + { + public: + virtual ~ListInterface() {} + virtual int type() const = 0; + virtual void append(void *) = 0; + virtual void insert(int, void *) = 0; + virtual void removeAt(int) = 0; + virtual void at(int, void *) const = 0; + virtual int count() const = 0; + virtual void clear() = 0; + }; + + enum ListOp { Append, Set, Insert, Prepend, Length, FromObject, + Object, Create, Value, Clear }; + + template<typename T> + int list_op(ListOp op, int val, + const QVariant &vlist, + const QVariant &value, + void **out); + + template<typename T> + int list_nocreate_op(ListOp op, int val, + const QVariant &vlist, + const QVariant &value, + void **out); + + template<typename T> + int list_interface_op(ListOp op, int val, + const QVariant &vlist, + const QVariant &value, + void **out); + + template<class From, class To, int N> + struct StaticCastSelectorClass + { + static inline int cast() { return -1; } + }; + + template<class From, class To> + struct StaticCastSelectorClass<From, To, sizeof(int)> + { +#ifndef Q_OS_SYMBIAN + static inline int cast() { return (int)((intptr_t)static_cast<To *>((From *)0x10000000)) - 0x10000000; } +#else + static inline int cast() { return (int)(static_cast<To *>((From *)0x10000000)) - 0x10000000; } +#endif + }; + + template<class From, class To> + struct StaticCastSelector + { + typedef int yes_type; + typedef char no_type; + + static yes_type check(To *); + static no_type check(...); + + static inline int cast() + { + return StaticCastSelectorClass<From, To, sizeof(check((From *)0))>::cast(); + } + }; + + template<typename T, int N> + struct AttachedPropertySelector + { + static inline QmlAttachedPropertiesFunc func() + { + return 0; + } + }; + template<typename T> + struct AttachedPropertySelector<T, 1> + { + static inline QmlAttachedPropertiesFunc func() + { + return &T::qmlAttachedProperties; + } + }; + + template < typename T > + class has_attachedProperties { + typedef int yes_type; + typedef char no_type; + + template<typename S, QObject *(S::*)(QObject *)> + struct dummy {}; + + template<typename S, QObject *(S::*)(QObject *) const> + struct dummy_const {}; + + template<typename S, QObject *(*) (QObject *)> + struct dummy_static {}; + + template<typename S> + static no_type check(dummy<S, &S::qmlAttachedProperties> *); + + template<typename S> + static no_type check(dummy_const<S, &S::qmlAttachedProperties> *); + + template<typename S> + static yes_type check(dummy_static<S, &S::qmlAttachedProperties> *); + + template<typename S> + static no_type check(...); + + public: + static bool const value = sizeof(check<T>(0)) == sizeof(yes_type); + }; + + template<typename T> + inline QmlAttachedPropertiesFunc attachedPropertiesFunc() + { + return AttachedPropertySelector<T, has_attachedProperties<T>::value>::func(); + } + + struct MetaTypeIds { + int typeId; + int listId; + int qmlListId; + }; + typedef int (*Func)(QmlPrivate::ListOp, int, const QVariant &, const QVariant &, void **); + typedef QObject *(*CreateFunc)(QObject *); + + template<typename T> + struct CreateParent { + static QObject *create(QObject *other) { + return new T(other); + } + }; + + template<typename T> + struct CreateNoParent { + static QObject *create() { + return new T; + } + }; + + struct Q_DECLARATIVE_EXPORT InstanceType { + InstanceType(int); + }; + + template<typename T> + struct Define { + static InstanceType instance; + }; + + template<typename T> + struct ExtCreate { + static QObject *create(QObject *other) { + return new T(other); + } + }; +}; + +template<typename T> +int QmlPrivate::list_op(QmlPrivate::ListOp op, int val, + const QVariant &vlist, + const QVariant &value, + void **out) +{ + if(op == QmlPrivate::Create) { + QObject *obj = static_cast<QObject *>(new T); + *((QObject **)out) = obj; + return 0; + } + QList<T *> *list = vlist.value<QList<T *> *>(); + switch(op) { + case QmlPrivate::Append: + list->append(value.value<T *>()); + break; + case QmlPrivate::Set: + (*list)[val] = value.value<T *>(); + break; + case QmlPrivate::Insert: + list->insert(val, value.value<T *>()); + break; + case QmlPrivate::Prepend: + list->prepend(value.value<T *>()); + break; + case QmlPrivate::Length: + return list->count(); + break; + case QmlPrivate::Clear: + list->clear(); + return 0; + break; + case QmlPrivate::Create: + break; + case QmlPrivate::Object: + *out = static_cast<QObject *>(value.value<T *>()); + break; + case QmlPrivate::FromObject: + { + QObject *fromObj = value.value<QObject *>(); + T *me = qobject_cast<T *>(fromObj); + if(me) { + *((QVariant *)*out) = QVariant::fromValue(me); + } + } + break; + case QmlPrivate::Value: + *((QVariant *)*out) = QVariant::fromValue(list->at(val)); + break; + } + return 0; +} + +template<typename T> +int QmlPrivate::list_nocreate_op(QmlPrivate::ListOp op, int val, + const QVariant &vlist, + const QVariant &value, + void **out) +{ + QList<T *> *list = vlist.value<QList<T *> *>(); + switch(op) { + case QmlPrivate::Append: + list->append(value.value<T *>()); + break; + case QmlPrivate::Set: + (*list)[val] = value.value<T *>(); + break; + case QmlPrivate::Insert: + list->insert(val, value.value<T *>()); + break; + case QmlPrivate::Prepend: + list->prepend(value.value<T *>()); + break; + case QmlPrivate::Length: + return list->count(); + break; + case QmlPrivate::Clear: + list->clear(); + return 0; + break; + case QmlPrivate::Create: + break; + case QmlPrivate::Object: + *out = static_cast<QObject *>(value.value<T *>()); + break; + case QmlPrivate::FromObject: + { + QObject *fromObj = value.value<QObject *>(); + T *me = qobject_cast<T *>(fromObj); + if(me) { + *((QVariant *)*out) = QVariant::fromValue(me); + } + } + break; + case QmlPrivate::Value: + *((QVariant *)*out) = QVariant::fromValue(list->at(val)); + break; + } + return 0; +} + +template<typename T> +int QmlPrivate::list_interface_op(QmlPrivate::ListOp op, int val, + const QVariant &vlist, + const QVariant &value, + void **out) +{ + QList<T *> *list = vlist.value<QList<T *> *>(); + switch(op) { + case QmlPrivate::Append: + list->append(value.value<T *>()); + break; + case QmlPrivate::Set: + (*list)[val] = value.value<T *>(); + break; + case QmlPrivate::Insert: + list->insert(val, value.value<T *>()); + break; + case QmlPrivate::Prepend: + list->prepend(value.value<T *>()); + break; + case QmlPrivate::Length: + return list->count(); + break; + case QmlPrivate::Clear: + list->clear(); + return 0; + break; + case QmlPrivate::Create: + break; + case QmlPrivate::Object: + break; + case QmlPrivate::FromObject: + break; + case QmlPrivate::Value: + *((QVariant *)*out) = QVariant::fromValue(list->at(val)); + break; + } + return 0; +} + + +#endif // QMLPRIVATE_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/qml/qmlpropertyvaluesource.cpp b/src/declarative/qml/qmlpropertyvaluesource.cpp new file mode 100644 index 0000000..78b0495 --- /dev/null +++ b/src/declarative/qml/qmlpropertyvaluesource.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 QtDeclarative 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 "qmlpropertyvaluesource.h" +#include "qml.h" + + +QT_BEGIN_NAMESPACE +/*! + \class QmlPropertyValueSource + \brief The QmlPropertyValueSource class is inherited by property value sources such as animations and bindings. + */ +QML_DEFINE_NOCREATE_TYPE(QmlPropertyValueSource); + +QmlPropertyValueSource::QmlPropertyValueSource(QObject *parent) + : QObject(parent) + +{ +} + +QmlPropertyValueSource::QmlPropertyValueSource(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Set the target \a property for the value source. This method will be called + by the QML engine when assigning a value source. + + The default implementation does nothing. +*/ +void QmlPropertyValueSource::setTarget(const QmlMetaProperty &property) +{ + Q_UNUSED(property); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlpropertyvaluesource.h b/src/declarative/qml/qmlpropertyvaluesource.h new file mode 100644 index 0000000..6ef2e38 --- /dev/null +++ b/src/declarative/qml/qmlpropertyvaluesource.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 QtDeclarative 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 QMLPROPERTYVALUESOURCE_H +#define QMLPROPERTYVALUESOURCE_H + +#include <qfxglobal.h> +#include <qml.h> +#include <QObject> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QObjectPrivate; +class QmlMetaProperty; +class Q_DECLARATIVE_EXPORT QmlPropertyValueSource : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QObject) + +public: + QmlPropertyValueSource(QObject *parent); + virtual void setTarget(const QmlMetaProperty &); + +protected: + QmlPropertyValueSource(QObjectPrivate &dd, QObject *parent); + +private: + Q_DISABLE_COPY(QmlPropertyValueSource) +}; +QML_DECLARE_TYPE(QmlPropertyValueSource); + +#endif // QMLPROPERTYVALUESOURCE_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/qml/qmlproxymetaobject.cpp b/src/declarative/qml/qmlproxymetaobject.cpp new file mode 100644 index 0000000..3b9f6ca --- /dev/null +++ b/src/declarative/qml/qmlproxymetaobject.cpp @@ -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 QtDeclarative 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 "qmlproxymetaobject_p.h" + + +QT_BEGIN_NAMESPACE +QmlProxyMetaObject::QmlProxyMetaObject(QObject *obj, QList<ProxyData> *mList) +: metaObjects(mList), proxies(0), parent(0), object(obj) +{ +#ifdef QMLPROXYMETAOBJECT_DEBUG + qWarning() << "QmlProxyMetaObject" << obj->metaObject()->className(); +#endif + + *static_cast<QMetaObject *>(this) = *metaObjects->last().metaObject; + + QObjectPrivate *op = QObjectPrivate::get(obj); + if(op->metaObject) + parent = static_cast<QAbstractDynamicMetaObject*>(op->metaObject); + + op->metaObject = this; + +#ifdef QMLPROXYMETAOBJECT_DEBUG + const QMetaObject *mo = obj->metaObject(); + while(mo) { + qWarning() << " " << mo->className(); + mo = mo->superClass(); + } +#endif +} + +QmlProxyMetaObject::~QmlProxyMetaObject() +{ + if(parent) + delete parent; + parent = 0; + + if(proxies) + delete [] proxies; + proxies = 0; +} + +int QmlProxyMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +{ + if((c == QMetaObject::ReadProperty || + c == QMetaObject::WriteProperty) && + id >= metaObjects->last().propertyOffset) { + + for(int ii = 0; ii < metaObjects->count(); ++ii) { + const ProxyData &data = metaObjects->at(ii); + if(id >= data.propertyOffset) { + if(!proxies) { + proxies = new QObject*[metaObjects->count()]; + ::memset(proxies, 0, + sizeof(QObject *) * metaObjects->count()); + } + + if(!proxies[ii]) + proxies[ii] = data.createFunc(object); + + int proxyOffset = proxies[ii]->metaObject()->propertyOffset(); + int proxyId = id - data.propertyOffset + proxyOffset; + + return proxies[ii]->qt_metacall(c, proxyId, a); + } + } + } + + if(parent) + return parent->metaCall(c, id, a); + else + return object->qt_metacall(c, id, a); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlproxymetaobject_p.h b/src/declarative/qml/qmlproxymetaobject_p.h new file mode 100644 index 0000000..594e7a3 --- /dev/null +++ b/src/declarative/qml/qmlproxymetaobject_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 QtDeclarative 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 QMLPROXYMETAOBJECT_P_H +#define QMLPROXYMETAOBJECT_P_H + +#include <QMetaObject> +#include <private/qmetaobjectbuilder_p.h> +#include <private/qobject_p.h> +#include <QObject> +#include <qml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlProxyMetaObject : public QAbstractDynamicMetaObject +{ +public: + struct ProxyData { + typedef QObject *(*CreateFunc)(QObject *); + QMetaObject *metaObject; + CreateFunc createFunc; + int propertyOffset; + }; + + QmlProxyMetaObject(QObject *, QList<ProxyData> *); + virtual ~QmlProxyMetaObject(); + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + +private: + QList<ProxyData> *metaObjects; + QObject **proxies; + + QAbstractDynamicMetaObject *parent; + QObject *object; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLPROXYMETAOBJECT_P_H + diff --git a/src/declarative/qml/qmlrefcount.cpp b/src/declarative/qml/qmlrefcount.cpp new file mode 100644 index 0000000..4e47ee1 --- /dev/null +++ b/src/declarative/qml/qmlrefcount.cpp @@ -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 QtDeclarative 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 "qmlrefcount_p.h" + +QmlRefCount::QmlRefCount() +: refCount(1) +{ +} + +QmlRefCount::~QmlRefCount() +{ +} + +void QmlRefCount::addref() +{ + Q_ASSERT(refCount > 0); + ++refCount; +} + +void QmlRefCount::release() +{ + Q_ASSERT(refCount > 0); + --refCount; + if(refCount == 0) + delete this; +} + diff --git a/src/declarative/qml/qmlrefcount_p.h b/src/declarative/qml/qmlrefcount_p.h new file mode 100644 index 0000000..90b50a8 --- /dev/null +++ b/src/declarative/qml/qmlrefcount_p.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 QtDeclarative 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 QMLREFCOUNT_P_H +#define QMLREFCOUNT_P_H + +#include <qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlRefCount +{ +public: + QmlRefCount(); + virtual ~QmlRefCount(); + void addref(); + void release(); + +private: + int refCount; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLREFCOUNT_P_H diff --git a/src/declarative/qml/qmlstringconverters.cpp b/src/declarative/qml/qmlstringconverters.cpp new file mode 100644 index 0000000..82d6eee --- /dev/null +++ b/src/declarative/qml/qmlstringconverters.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <QtGui/qcolor.h> +#include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtCore/qsize.h> +#include <QtCore/qvariant.h> +#include "qmlstringconverters_p.h" + +QT_BEGIN_NAMESPACE + +static uchar fromHex(const uchar c, const uchar c2) +{ + uchar rv = 0; + if(c >= '0' && c <= '9') + rv += (c - '0') * 16; + else if(c >= 'A' && c <= 'F') + rv += (c - 'A' + 10) * 16; + else if(c >= 'a' && c <= 'f') + rv += (c - 'a' + 10) * 16; + + if(c2 >= '0' && c2 <= '9') + rv += (c2 - '0'); + else if(c2 >= 'A' && c2 <= 'F') + rv += (c2 - 'A' + 10); + else if(c2 >= 'a' && c2 <= 'f') + rv += (c2 - 'a' + 10); + + return rv; +} + +static uchar fromHex(const QString &s, int idx) +{ + uchar c = s.at(idx).toAscii(); + uchar c2 = s.at(idx + 1).toAscii(); + return fromHex(c, c2); +} + +QVariant QmlStringConverters::variantFromString(const QString &s) +{ + if(s.isEmpty()) + return QVariant(s); + if(s.startsWith(QLatin1Char('\'')) && s.endsWith(QLatin1Char('\''))) { + QString data = s.mid(1, s.length() - 2); + return QVariant(data); + } + bool ok = false; + QRectF r = rectFFromString(s, &ok); + if(ok) return QVariant(r); + QColor c = colorFromString(s, &ok); + if(ok) return QVariant(c); + QPointF p = pointFFromString(s, &ok); + if(ok) return QVariant(p); + QSizeF sz = sizeFFromString(s, &ok); + if(ok) return QVariant(sz); + bool b = boolFromString(s, &ok); + if(ok) return QVariant(b); + + return QVariant(s); +} + +QColor QmlStringConverters::colorFromString(const QString &s, bool *ok) +{ + if(s.startsWith(QLatin1Char('#')) && s.length() == 9) { + uchar a = fromHex(s, 1); + uchar r = fromHex(s, 3); + uchar g = fromHex(s, 5); + uchar b = fromHex(s, 7); + if(ok) *ok = true; + return QColor(r, g, b, a); + } else { + QColor rv; + if(s.startsWith(QLatin1Char('#')) || QColor::colorNames().contains(s.toLower())) + rv = QColor(s); + if(ok) *ok = rv.isValid(); + return rv; + } +} + +//expects input of "x,y" +QPointF QmlStringConverters::pointFFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char(',')) != 1) { + if (ok) + *ok = false; + return QPointF(); + } + + bool xGood, yGood; + int index = s.indexOf(QLatin1Char(',')); + qreal xCoord = s.left(index).toDouble(&xGood); + qreal yCoord = s.mid(index+1).toDouble(&yGood); + if (!xGood || !yGood) { + if (ok) + *ok = false; + return QPointF(); + } + + if (ok) + *ok = true; + return QPointF(xCoord, yCoord); +} + +//expects input of "widthxheight" +QSizeF QmlStringConverters::sizeFFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char('x')) != 1) { + if (ok) + *ok = false; + return QSizeF(); + } + + bool wGood, hGood; + int index = s.indexOf(QLatin1Char('x')); + qreal width = s.left(index).toDouble(&wGood); + qreal height = s.mid(index+1).toDouble(&hGood); + if (!wGood || !hGood) { + if (ok) + *ok = false; + return QSizeF(); + } + + if (ok) + *ok = true; + return QSizeF(width, height); +} + +//expects input of "x,y,widthxheight" //### use space instead of second comma? +QRectF QmlStringConverters::rectFFromString(const QString &s, bool *ok) +{ + if (s.count(QLatin1Char(',')) != 2 || s.count(QLatin1Char('x')) != 1) { + if (ok) + *ok = false; + return QRectF(); + } + + bool xGood, yGood, wGood, hGood; + int index = s.indexOf(QLatin1Char(',')); + qreal x = s.left(index).toDouble(&xGood); + int index2 = s.indexOf(QLatin1Char(','), index+1); + qreal y = s.mid(index+1, index2-index-1).toDouble(&yGood); + index = s.indexOf(QLatin1Char('x'), index2+1); + qreal width = s.mid(index2+1, index-index2-1).toDouble(&wGood); + qreal height = s.mid(index+1).toDouble(&hGood); + if (!xGood || !yGood || !wGood || !hGood) { + if (ok) + *ok = false; + return QRectF(); + } + + if (ok) + *ok = true; + return QRectF(x, y, width, height); +} + +bool QmlStringConverters::boolFromString(const QString &str, bool *ok) +{ + if (str.isEmpty() || str == QLatin1String("false") || str == QLatin1String("0")) { + if (ok) + *ok = true; + return false; + } else if (str == QLatin1String("true") || str == QLatin1String("1")) { + if (ok) + *ok = true; + return true; + } + + if (ok) + *ok = false; + return true; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlstringconverters_p.h b/src/declarative/qml/qmlstringconverters_p.h new file mode 100644 index 0000000..ed1f959 --- /dev/null +++ b/src/declarative/qml/qmlstringconverters_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLSTRINGCONVERTERS_P_H +#define QMLSTRINGCONVERTERS_P_H + +#include <QtCore/qglobal.h> +class QColor; +class QPointF; +class QSizeF; +class QRectF; +class QString; +class QByteArray; + +QT_BEGIN_NAMESPACE + +// XXX - Bauhaus currently uses these methods which is why they're exported +namespace QmlStringConverters +{ + QVariant Q_DECLARATIVE_EXPORT variantFromString(const QString &); + QColor Q_DECLARATIVE_EXPORT colorFromString(const QString &, bool *ok = 0); + QPointF Q_DECLARATIVE_EXPORT pointFFromString(const QString &, bool *ok = 0); + QSizeF Q_DECLARATIVE_EXPORT sizeFFromString(const QString &, bool *ok = 0); + QRectF Q_DECLARATIVE_EXPORT rectFFromString(const QString &, bool *ok = 0); + bool Q_DECLARATIVE_EXPORT boolFromString(const QString &, bool *ok = 0); +}; + +QT_END_NAMESPACE + +#endif // QMLSTRINGCONVERTERS_P_H diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp new file mode 100644 index 0000000..966ef8a --- /dev/null +++ b/src/declarative/qml/qmlvme.cpp @@ -0,0 +1,1345 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlvme_p.h" +#include <qfxperf.h> +#include <private/qmlboundsignal_p.h> +#include <private/qmlstringconverters_p.h> +#include "private/qmetaobjectbuilder_p.h" +#include <qml.h> +#include <qmlcustomparser.h> +#include <qperformancelog.h> +#include <QStack> +#include <private/qmlcompiledcomponent_p.h> +#include <QColor> +#include <QPointF> +#include <QSizeF> +#include <QRectF> +#include <qmlengine.h> +#include <qmlcontext.h> +#include <qmlcomponent.h> +#include <qmlbindablevalue.h> +#include <private/qmlengine_p.h> +#include <private/qmlcomponent_p.h> +#include "private/qmlvmemetaobject_p.h" +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE +Q_DECLARE_PERFORMANCE_LOG(QFxCompiler) { + Q_DECLARE_PERFORMANCE_METRIC(InstrCreateObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrCreateCustomObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrSetId); + Q_DECLARE_PERFORMANCE_METRIC(InstrSetDefault); + Q_DECLARE_PERFORMANCE_METRIC(InstrCreateComponent); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreMetaObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreReal); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreInteger); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreBool); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreString); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreColor); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreDate); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreDateTime); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreTime); + Q_DECLARE_PERFORMANCE_METRIC(InstrStorePoint); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreSize); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreVariant); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreSignal); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreObjectQmlList); + Q_DECLARE_PERFORMANCE_METRIC(InstrAssignConstant); + Q_DECLARE_PERFORMANCE_METRIC(InstrAssignSignal); + Q_DECLARE_PERFORMANCE_METRIC(InstrAssignSignalObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrAssignBinding); + Q_DECLARE_PERFORMANCE_METRIC(InstrAssignCompiledBinding); + Q_DECLARE_PERFORMANCE_METRIC(InstrAssignValueSource); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreBinding); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreCompiledBinding); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreValueSource); + Q_DECLARE_PERFORMANCE_METRIC(InstrTryBeginObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrBeginObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrTryCompleteObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrCompleteObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrAssignObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrAssignObjectList); + Q_DECLARE_PERFORMANCE_METRIC(InstrFetchAttached); + Q_DECLARE_PERFORMANCE_METRIC(InstrFetchQmlList); + Q_DECLARE_PERFORMANCE_METRIC(InstrFetchQList); + Q_DECLARE_PERFORMANCE_METRIC(InstrFetchObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrResolveFetchObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrPopFetchedObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrPopQList); + Q_DECLARE_PERFORMANCE_METRIC(InstrPushProperty); + Q_DECLARE_PERFORMANCE_METRIC(InstrAssignStackObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrStoreStackObject); + Q_DECLARE_PERFORMANCE_METRIC(InstrNoOp); + Q_DECLARE_PERFORMANCE_METRIC(Dummy); +} + +Q_DEFINE_PERFORMANCE_LOG(QFxCompiler, "QFxCompiler") { + Q_DEFINE_PERFORMANCE_METRIC(InstrCreateObject, "CreateObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrCreateCustomObject, "CreateCustomObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrSetId, "SetId"); + Q_DEFINE_PERFORMANCE_METRIC(InstrSetDefault, "SetDefault"); + Q_DEFINE_PERFORMANCE_METRIC(InstrCreateComponent, "CreateComponent"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreMetaObject, "StoreMetaObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreReal, "StoreReal"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreInteger, "StoreInteger"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreBool, "StoreBool"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreString, "StoreString"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreColor, "StoreColor"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreDate, "StoreDate"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreDateTime, "StoreDateTime"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreTime, "StoreTime"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStorePoint, "StorePoint(F)"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreSize, "StoreSize(F)"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreVariant, "StoreVariant"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreObject, "StoreObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreSignal, "StoreSignal"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreObjectQmlList, "StoreObjectQmlList"); + Q_DEFINE_PERFORMANCE_METRIC(InstrAssignConstant, "AssignConstant"); + Q_DEFINE_PERFORMANCE_METRIC(InstrAssignSignal, "AssignSignal"); + Q_DEFINE_PERFORMANCE_METRIC(InstrAssignSignalObject, "AssignSignalObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrAssignBinding, "AssignBinding"); + Q_DEFINE_PERFORMANCE_METRIC(InstrAssignCompiledBinding, "AssignCompiledBinding"); + Q_DEFINE_PERFORMANCE_METRIC(InstrAssignValueSource, "AssignValueSource"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreBinding, "StoreBinding"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreCompiledBinding, "StoreCompiledBinding"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreValueSource, "StoreValueSource"); + Q_DEFINE_PERFORMANCE_METRIC(InstrTryBeginObject, "TryBeginObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrBeginObject, "BeginObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrTryCompleteObject, "TryCompleteObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrCompleteObject, "CompleteObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrAssignObject, "AssignObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrAssignObjectList, "AssignObjectList"); + Q_DEFINE_PERFORMANCE_METRIC(InstrFetchAttached, "FetchAttached"); + Q_DEFINE_PERFORMANCE_METRIC(InstrFetchQmlList, "FetchQmlList"); + Q_DEFINE_PERFORMANCE_METRIC(InstrFetchQList, "FetchQList"); + Q_DEFINE_PERFORMANCE_METRIC(InstrFetchObject, "FetchObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrResolveFetchObject, "ResolveFetchObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrPopFetchedObject, "PopFetchedObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrPopQList, "PopQList"); + Q_DEFINE_PERFORMANCE_METRIC(InstrPushProperty, "PushProperty"); + Q_DEFINE_PERFORMANCE_METRIC(InstrAssignStackObject, "AssignStackObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrStoreStackObject, "StoreStackObject"); + Q_DEFINE_PERFORMANCE_METRIC(InstrNoOp, "NoOp"); + Q_DEFINE_PERFORMANCE_METRIC(Dummy, "Dummy"); +} + +static inline int qIndexOfProperty(QObject *o, const char *name) +{ + int idx = o->metaObject()->indexOfProperty(name); + return idx; +} + +QmlVME::QmlVME() +: exceptionLine(-1) +{ +} + +#define VME_EXCEPTION(desc) \ + { \ + exceptionLine = instr.line; \ + QDebug d(&exceptionDescription); \ + d << desc; \ + break; \ + } + +struct ListInstance +{ + ListInstance() {} + ListInstance(const QVariant &l, int t) + : list(l), type(t), qmlListInterface(0) {} + ListInstance(QmlPrivate::ListInterface *q, int t) + : type(t), qmlListInterface(q) {} + + QVariant list; + int type; + QmlPrivate::ListInterface *qmlListInterface; +}; + +QObject *QmlVME::run(QmlContext *ctxt, QmlCompiledComponent *comp, int start, int count) +{ + // XXX - All instances of QmlContext::activeContext() here should be + // replaced with the use of ctxt. However, this cannot be done until + // behaviours stop modifying the active context and expecting the + // instantiation to notice. Instead, QmlParserStatus::beginClass() should + // be able to return a QmlContext that is used for expressions and + // sub-instances on that type. + Q_ASSERT(comp); + Q_ASSERT(ctxt); + const QList<QmlCompiledComponent::TypeReference> &types = comp->types; + const QList<QString> &primitives = comp->primitives; + const QList<QByteArray> &datas = comp->datas; + const QList<QMetaObject *> &mos = comp->mos; + const QList<QmlCompiledData::CustomTypeData> &customTypeData = comp->customTypeData; + +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::CompileRun> cr; +#endif + + QList<QmlParserStatus *> parserStatuses; + QList<QmlBindableValue *> bindableValues; + + QStack<QObject *> stack; + QStack<ListInstance> qliststack; + + QStack<QmlMetaProperty> pushedProperties; + QObject **savedObjects = 0; + + if(start == -1) start = 0; + if(count == -1) count = comp->bytecode.count(); + + for(int ii = start; !isError() && ii < (start + count); ++ii) { + QmlInstruction &instr = comp->bytecode[ii]; + + if(instr.type >= QmlInstruction::StoreInstructionsStart && + instr.type <= QmlInstruction::StoreInstructionsEnd) { + + runStoreInstruction(stack, instr, comp); + + } else { + + switch(instr.type) { + case QmlInstruction::Init: + { + if(instr.init.dataSize) { + savedObjects = new QObject*[instr.init.dataSize]; + ::memset(savedObjects, 0, + sizeof(QObject *)*instr.init.dataSize); + } + } + break; + + case QmlInstruction::CreateObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrCreateObject> cc; +#endif + QObject *o = types.at(instr.create.type).createInstance(); + if(!o) + VME_EXCEPTION("Unable to create object of type" << types.at(instr.create.type).className); + + if(!stack.isEmpty()) { + QObject *parent = stack.top(); + o->setParent(parent); + } + stack.push(o); + } + break; + + case QmlInstruction::CreateCustomObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrCreateCustomObject> cc; +#endif + QVariant v = + types.at(instr.createCustom.type).parser->create(datas.at(instr.createCustom.data)); + // XXX + QObject *o = QmlMetaType::toQObject(v); + if(!o) + VME_EXCEPTION("Unable to create" << types.at(instr.create.type).className); + + if(!stack.isEmpty()) { + QObject *parent = stack.top(); + o->setParent(parent); + } + stack.push(o); + } + break; + + case QmlInstruction::SetId: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrSetId> cc; +#endif + QObject *target = stack.top(); + QmlContext *ctxt = + QmlContext::activeContext(); + ctxt->setContextProperty(primitives.at(instr.setId.value), target); + + if(instr.setId.save != -1) + savedObjects[instr.setId.save] = target; + } + break; + + + case QmlInstruction::SetDefault: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrSetDefault> cc; +#endif + QObject *target = stack.top(); + QmlContext::activeContext()->addDefaultObject(target); + } + break; + + case QmlInstruction::CreateComponent: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrCreateComponent> cc; +#endif + QObject *qcomp = new QmlComponent(ctxt->engine(), comp, ii + 1, instr.createComponent.count, stack.isEmpty() ? 0 : stack.top()); + stack.push(qcomp); + ii += instr.createComponent.count; + } + break; + + case QmlInstruction::StoreMetaObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreMetaObject> cc; +#endif + QObject *target = stack.top(); + new QmlVMEMetaObject(target, mos.at(instr.storeMeta.data), comp); + } + break; + + case QmlInstruction::AssignCustomType: + { + QObject *target = stack.top(); + void *a[1]; + QmlCompiledComponent::CustomTypeData data = customTypeData.at(instr.assignCustomType.valueIndex); + const QString &primitive = primitives.at(data.index); + QmlMetaType::StringConverter converter = + QmlMetaType::customStringConverter(data.type); + QVariant v = (*converter)(primitive); + + QMetaProperty prop = + target->metaObject()->property(instr.assignCustomType.propertyIndex); + if (v.isNull() || ((int)prop.type() != data.type && prop.userType() != data.type)) + VME_EXCEPTION("Cannot assign value" << primitive << "to property" << prop.name()); + + a[0] = (void *)v.data(); + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.assignCustomType.propertyIndex, a); + } + break; + + case QmlInstruction::AssignSignal: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrAssignSignal> cc; +#endif + // Fixup instruction + QObject *target = stack.top(); + int sigIdx = instr.assignSignal.signal; + const QByteArray &pr = datas.at(sigIdx); + + QmlMetaProperty prop(target, QLatin1String(pr)); + if(prop.type() & QmlMetaProperty::SignalProperty) { + int coreIdx = prop.coreIndex(); + int primRef = instr.assignSignal.value; + instr.type = QmlInstruction::StoreSignal; + instr.storeSignal.signalIndex = coreIdx; + instr.storeSignal.value = primRef; + --ii; + } else if(prop.type() & QmlMetaProperty::Property) { + int prop = sigIdx; + int primRef = instr.assignSignal.value; + instr.type = QmlInstruction::AssignConstant; + instr.assignConstant.property = prop; + instr.assignConstant.constant = primRef; + --ii; + } else { + VME_EXCEPTION("Cannot assign a signal to property" << pr); + } + } + break; + + case QmlInstruction::AssignSignalObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrAssignSignalObject> cc; +#endif + // XXX optimize + + QObject *assign = stack.pop(); + QObject *target = stack.top(); + int sigIdx = instr.assignSignalObject.signal; + const QByteArray &pr = datas.at(sigIdx); + + QmlMetaProperty prop(target, QLatin1String(pr)); + if(prop.type() & QmlMetaProperty::SignalProperty) { + + QMetaMethod method = QmlMetaType::defaultMethod(assign); + if(method.signature() == 0) + VME_EXCEPTION("Cannot assign object type" << assign->metaObject()->className() << "with no default method"); + + if(!QMetaObject::checkConnectArgs(prop.method().signature(), method.signature())) + VME_EXCEPTION("Cannot connect mismatched signal/slot" << method.signature() << prop.method().signature()); + + QMetaObject::connect(target, prop.coreIndex(), assign, method.methodIndex()); + + } else if(prop.type() & QmlMetaProperty::Property) { + instr.type = QmlInstruction::AssignObject; + instr.assignObject.castValue = 0; + instr.assignObject.property = sigIdx; + --ii; + } else { + VME_EXCEPTION("Cannot assign an object to signal property" << pr); + } + + + } + break; + + case QmlInstruction::StoreSignal: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreSignal> cc; +#endif + QObject *target = stack.top(); + // XXX scope + QMetaMethod signal = + target->metaObject()->method(instr.storeSignal.signalIndex); + + if(signal.parameterTypes().isEmpty()) { + (void *)new QmlBoundSignal(QmlContext::activeContext(), primitives.at(instr.storeSignal.value), target, instr.storeSignal.signalIndex, target); + } else { + (void *)new QmlBoundSignalProxy(new QmlContext(QmlContext::activeContext(), target), primitives.at(instr.storeSignal.value), target, instr.storeSignal.signalIndex, target); + } + } + break; + + case QmlInstruction::AssignConstant: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrAssignConstant> cc; +#endif + // Fixup instruction + QObject *target = stack.top(); + int propIdx = instr.assignConstant.property; + int idx = instr.assignConstant.constant; + QByteArray pr; + if(propIdx == -1) { + pr = QmlMetaType::defaultProperty(target).name(); + if(pr.isEmpty()) + VME_EXCEPTION("Cannot resolve defalt property on type" << target->metaObject()->className()); + } else { + pr = datas.at(propIdx); + } + + int coreIdx = qIndexOfProperty(target, pr); + + if(coreIdx != -1) { + QMetaProperty prop = + target->metaObject()->property(coreIdx); + bool replace = !prop.isDynamic(); + + QmlInstruction *writeInstr = 0; + QmlInstruction dummy; + if(replace) { + writeInstr = &instr; + } else { + writeInstr = &dummy; + dummy = instr; + } + + QmlCompiler::StoreInstructionResult r = QmlCompiler::generateStoreInstruction(*comp, *writeInstr, prop, + coreIdx, idx, &primitives.at(idx)); + if(r != QmlCompiler::Ok) { + if(prop.isEnumType()){ + VME_EXCEPTION(primitives.at(idx) << "is not a valid enumeration value"); + } else if (r == QmlCompiler::UnknownType) { + VME_EXCEPTION("Property" << prop.name() << "is of an unknown type"); + } else if (r == QmlCompiler::InvalidData) { + VME_EXCEPTION("Cannot assign value" << primitives.at(idx) << "to property" << prop.name()); + } else if (r == QmlCompiler::ReadOnly) { + VME_EXCEPTION("Cannot assign value" << primitives.at(idx) << "to read-only property" << prop.name()); + } else { + VME_EXCEPTION("Invalid property assignment for property" << prop.name()); + } + } else { + runStoreInstruction(stack, *writeInstr, comp); + } + + } else { + VME_EXCEPTION("Unknown property" << pr); + } + } + break; + + case QmlInstruction::TryBeginObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrTryBeginObject> cc; +#endif + QObject *target = stack.top(); + QmlParserStatus *status = + qobject_cast<QmlParserStatus *>(target); + + if(status) { + instr.type = QmlInstruction::BeginObject; + instr.begin.castValue = int(reinterpret_cast<char *>(status) - reinterpret_cast<char *>(target)); + --ii; + } else { + instr.type = QmlInstruction::NoOp; + } + } + break; + + case QmlInstruction::BeginObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrBeginObject> cc; +#endif + QObject *target = stack.top(); + QmlParserStatus *status = reinterpret_cast<QmlParserStatus *>(reinterpret_cast<char *>(target) + instr.begin.castValue); + status->classBegin(); + parserStatuses << status; + } + break; + + case QmlInstruction::TryCompleteObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrTryCompleteObject> cc; +#endif + QObject *target = stack.top(); + QmlParserStatus *status = + qobject_cast<QmlParserStatus *>(target); + + if(status) { + instr.type = QmlInstruction::CompleteObject; + instr.complete.castValue = int(reinterpret_cast<char *>(status) - reinterpret_cast<char *>(target)); + --ii; + } else { + instr.type = QmlInstruction::NoOp; + } + } + break; + + case QmlInstruction::CompleteObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrCompleteObject> cc; +#endif + QObject *target = stack.top(); + QmlParserStatus *status = reinterpret_cast<QmlParserStatus *>(reinterpret_cast<char *>(target) + instr.complete.castValue); + status->classComplete(); + } + break; + + case QmlInstruction::AssignCompiledBinding: + case QmlInstruction::AssignBinding: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrAssignBinding> cc; +#endif + QObject *target = stack.top(); + const QByteArray &pr = datas.at(instr.fetch.property); + int idx = qIndexOfProperty(target, pr); + + // XXX - need to check if the type is QmlBindableValue* + if(idx == -1) { + VME_EXCEPTION("Unknown property" << pr); + } else { + if(QmlInstruction::AssignCompiledBinding == instr.type) + instr.type = QmlInstruction::StoreCompiledBinding; + else + instr.type = QmlInstruction::StoreBinding; + instr.assignBinding.property = idx; + instr.assignBinding.category = QmlMetaProperty::Unknown; + } + ii--; + } + break; + + case QmlInstruction::AssignValueSource: + { + QObject *target = stack.at(stack.count() - 2); + int propIdx = instr.assignValueSource.property; + QByteArray pr; + if(propIdx == -1) { + pr = QmlMetaType::defaultProperty(target).name(); + if(pr.isEmpty()) + VME_EXCEPTION("Unable to resolve default property"); + } else { + pr = datas.at(propIdx); + } + + int coreIdx = qIndexOfProperty(target, pr); + if(coreIdx != -1) { + instr.type = QmlInstruction::StoreValueSource; + instr.assignValueSource.property = coreIdx; + ii--; + } else { + VME_EXCEPTION("Unknown property" << pr); + } + } + break; + + case QmlInstruction::PushProperty: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrPushProperty> cc; +#endif + QObject *target = stack.top(); + QmlMetaProperty mp(target, instr.pushProperty.property, + QmlMetaProperty::Object); + pushedProperties.push(mp); + } + break; + + case QmlInstruction::StoreCompiledBinding: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreCompiledBinding> cc; +#endif + QObject *target = stack.top(); + QObject *context = + stack.at(stack.count() - 1 - instr.assignBinding.context); + + QmlMetaProperty mp(target, instr.assignBinding.property, + (QmlMetaProperty::PropertyCategory)instr.assignBinding.category); + if (!mp.isWritable()) + VME_EXCEPTION("Cannot assign a binding to read-only property" << mp.name()); + + QmlBindableValue *bind = new QmlBindableValue((void *)datas.at(instr.assignBinding.value).constData(), comp, context, 0); + QFx_setParent_noEvent(bind, target); + + bind->setTarget(mp); + bindableValues << bind; + } + break; + + case QmlInstruction::StoreBinding: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreBinding> cc; +#endif + QObject *target = stack.top(); + QObject *context = + stack.at(stack.count() - 1 - instr.assignBinding.context); + + QmlMetaProperty mp(target, instr.assignBinding.property, + (QmlMetaProperty::PropertyCategory)instr.assignBinding.category); + if (!mp.isWritable()) + VME_EXCEPTION("Cannot assign a binding to read-only property" << mp.name()); + + QmlBindableValue *bind = new QmlBindableValue(primitives.at(instr.assignBinding.value), context, false); + QFx_setParent_noEvent(bind, target); + + bind->setTarget(mp); + bindableValues << bind; + } + break; + + case QmlInstruction::StoreValueSource: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreValueSource> cc; +#endif + QObject *assign = stack.pop(); + QmlPropertyValueSource *vs = + static_cast<QmlPropertyValueSource *>(assign); + QObject *target = stack.top(); + vs->setParent(target); + vs->setTarget(QmlMetaProperty(target, instr.assignValueSource.property)); + } + break; + + case QmlInstruction::AssignObjectList: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrAssignObjectList> cc; +#endif + QObject *assign = stack.pop(); + const ListInstance &list = qliststack.top(); + if(list.qmlListInterface) { + int type = list.type; + + void *d = 0; + void *ptr = 0; + bool found = false; + + if(QmlMetaType::isInterface(type)) { + const char *iid = QmlMetaType::interfaceIId(type); + if(iid) + ptr = assign->qt_metacast(iid); + if(ptr) { + d = &ptr; + found = true; + } + } else { + const QMetaObject *mo = + QmlMetaType::rawMetaObjectForType(type); + + const QMetaObject *assignMo = assign->metaObject(); + while(!found && assignMo) { + if(assignMo == mo) + found = true; + else + assignMo = assignMo->superClass(); + } + + // NOTE: This assumes a cast to QObject does not alter + // the object pointer + d = (void *)&assign; + } + + + if(!found) + VME_EXCEPTION("Cannot assign object to list"); + + list.qmlListInterface->append(d); + + } else { + int type = list.type; + + if(QmlMetaType::isInterface(type)) { + void *ptr = 0; + const char *iid = QmlMetaType::interfaceIId(type); + if(iid) + ptr = assign->qt_metacast(iid); + QVariant v(list.type, &ptr); + QmlMetaType::append(list.list, v); + } else { + QVariant v = QmlMetaType::fromObject(assign, list.type); + QmlMetaType::append(list.list, v); + } + } + } + break; + + case QmlInstruction::AssignObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrAssignObject> cc; +#endif + QObject *assign = stack.pop(); + QObject *target = stack.top(); + + QByteArray property; + if(instr.assignObject.property == -1) { + // XXX - optimize! + property = + QmlMetaType::defaultProperty(target).name(); + } else { + property = datas.at(instr.assignObject.property); + } + + int coreIdx = qIndexOfProperty(target, property); + + if(coreIdx != -1) { + QMetaProperty prop = + target->metaObject()->property(coreIdx); + int t = prop.userType(); + // XXX - optimize! + if(QmlMetaType::isList(t)) { + QVariant list = prop.read(target); + int listtype = QmlMetaType::listType(t); + QVariant v = QmlMetaType::fromObject(assign, listtype); + QmlMetaType::append(list, v); + } else if(QmlMetaType::isQmlList(t)) { + + // XXX - optimize! + QVariant list = prop.read(target); + QmlPrivate::ListInterface *li = + *(QmlPrivate::ListInterface **)list.constData(); + + int type = li->type(); + + const QMetaObject *mo = + QmlMetaType::rawMetaObjectForType(type); + + const QMetaObject *assignMo = assign->metaObject(); + bool found = false; + while(!found && assignMo) { + if(assignMo == mo) + found = true; + else + assignMo = assignMo->superClass(); + } + + if(!found) + VME_EXCEPTION("Cannot assign object to list"); + + // NOTE: This assumes a cast to QObject does not alter + // the object pointer + void *d = (void *)&assign; + li->append(d); + + } else if(QmlMetaType::isInterface(t)) { + const char *iid = QmlMetaType::interfaceIId(t); + bool ok = false; + if(iid) { + void *ptr = assign->qt_metacast(iid); + if(ptr) { + void *a[1]; + a[0] = &ptr; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + coreIdx, a); + ok = true; + } + } + + if(!ok) + VME_EXCEPTION("Cannot assign object to interface property" << property); + + } else if(prop.userType() == -1 /* means qvariant */) { + prop.write(target, qVariantFromValue(assign)); + } else { + const QMetaObject *propmo = + QmlMetaType::rawMetaObjectForType(t); + + bool isPropertyValue = false; + bool isAssignable = false; + const QMetaObject *c = assign->metaObject(); + while(c) { + isPropertyValue = isPropertyValue || (c == &QmlPropertyValueSource::staticMetaObject); + isAssignable = isAssignable || (c == propmo); + c = c->superClass(); + } + + if(isAssignable) { + // XXX - optimize! + QVariant v = QmlMetaType::fromObject(assign, t); + prop.write(target, v); + } else if(isPropertyValue) { + QmlPropertyValueSource *vs = + static_cast<QmlPropertyValueSource *>(assign); + vs->setParent(target); + vs->setTarget(QmlMetaProperty(target, coreIdx)); + } else { + VME_EXCEPTION("Cannot assign to" << property); + } + } + + + } else { + VME_EXCEPTION("Cannot assign to non-existant property" << property); + } + + } + break; + + case QmlInstruction::FetchAttached: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrFetchAttached> cc; +#endif + QObject *target = stack.top(); + + QmlAttachedPropertiesFunc attachFunc = + QmlMetaType::attachedPropertiesFunc(datas.at(instr.fetchAttached.idx)); + if(!attachFunc) + VME_EXCEPTION("No such attached object" << primitives.at(instr.fetchAttached.idx)); + + QObject *qmlObject = attachFunc(target); + if(!qmlObject) + VME_EXCEPTION("Internal error - unable to create attached object" << primitives.at(instr.fetchAttached.idx)); + + stack.push(qmlObject); + } + break; + + case QmlInstruction::FetchQmlList: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrFetchQmlList> cc; +#endif + QObject *target = stack.top(); + + void *a[1]; + // We know that QmlList<*> can be converted to + // QmlPrivate::ListInterface + QmlPrivate::ListInterface *list = 0; + a[0] = &list; + QMetaObject::metacall(target, QMetaObject::ReadProperty, + instr.fetchQmlList.property, a); + if(!list) + VME_EXCEPTION("Cannot assign to null list"); + + qliststack.push(ListInstance(list, instr.fetchQmlList.type)); + } + break; + + case QmlInstruction::FetchQList: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrFetchQList> cc; +#endif + QObject *target = stack.top(); + QMetaProperty prop = + target->metaObject()->property(instr.fetch.property); + QVariant v = prop.read(target); + qliststack.push(ListInstance(v, QmlMetaType::listType(prop.userType()))); + } + break; + + case QmlInstruction::ResolveFetchObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrResolveFetchObject> cc; +#endif + QObject *target = stack.top(); + const QByteArray &pr = datas.at(instr.fetch.property); + int idx = qIndexOfProperty(target, pr); + if(idx == -1) + VME_EXCEPTION("Cannot resolve property" << pr); + QMetaProperty prop = target->metaObject()->property(idx); + instr.type = QmlInstruction::FetchObject; + instr.fetch.property = idx; + if(QmlMetaType::isObject(prop.userType())) { + instr.fetch.isObject = true; + } else if(prop.userType() == -1) { + instr.fetch.isObject = false; + } else { + VME_EXCEPTION("Cannot set properties on" << prop.name() << "as it is of unknown type"); + } + ii--; + } + break; + + case QmlInstruction::FetchObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrFetchObject> cc; +#endif + QObject *target = stack.top(); + + QObject *obj = 0; + if(instr.fetch.isObject) { + // NOTE: This assumes a cast to QObject does not alter the + // object pointer + void *a[1]; + a[0] = &obj; + QMetaObject::metacall(target, QMetaObject::ReadProperty, + instr.fetch.property, a); + } else { + void *a[1]; + QVariant var; + a[0] = &var; + QMetaObject::metacall(target, QMetaObject::ReadProperty, + instr.fetch.property, a); + obj = QmlMetaType::toQObject(var); + + } + + if(!obj) + VME_EXCEPTION("Cannot set properties on" << target->metaObject()->property(instr.fetch.property).name() << "as it is null"); + + stack.push(obj); + } + break; + + case QmlInstruction::PopQList: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrPopQList> cc; +#endif + qliststack.pop(); + } + break; + + case QmlInstruction::PopFetchedObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrPopFetchedObject> cc; +#endif + stack.pop(); + } + break; + + case QmlInstruction::AssignStackObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrAssignStackObject> cc; +#endif + + QObject *obj = savedObjects[instr.assignStackObject.object]; + const QmlMetaProperty &prop = + pushedProperties.at(instr.assignStackObject.property); + + + const QMetaObject *mo = + QmlMetaType::rawMetaObjectForType(prop.propertyType()); + const QMetaObject *assignMo = obj->metaObject(); + + bool found = false; + while(!found && assignMo) { + if(assignMo == mo) + found = true; + else + assignMo = assignMo->superClass(); + } + + if(!found) + VME_EXCEPTION("Unable to assign object"); + + instr.type = QmlInstruction::StoreStackObject; + --ii; + } + break; + + case QmlInstruction::StoreStackObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreStackObject> cc; +#endif + + const QmlMetaProperty &prop = + pushedProperties.at(instr.assignStackObject.property); + QObject *obj = savedObjects[instr.assignStackObject.object]; + + // NOTE: This assumes a cast to QObject does not alter the + // object pointer + void *a[1]; + a[0] = (void *)&obj; + QMetaObject::metacall(prop.object(), QMetaObject::WriteProperty, + prop.coreIndex(), a); + } + break; + + case QmlInstruction::NoOp: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrNoOp> cc; +#endif + } + break; + + default: + qFatal("QmlCompiledComponent: Internal error - unknown instruction %d", instr.type); + break; + } + } + } + + if(isError()) { + if(!stack.isEmpty()) { + delete stack.at(0); + } + return 0; + } + + QmlEnginePrivate *ep = ctxt->engine()->d_func(); + ep->currentBindValues << bindableValues; + ep->currentParserStatus << parserStatuses; + + comp->dumpPost(); + + if(savedObjects) + delete [] savedObjects; + + if(stack.isEmpty()) + return 0; + else + return stack.top(); + return 0; +} + +bool QmlVME::isError() const +{ + return exceptionLine != -1; +} + +qint64 QmlVME::errorLine() const +{ + return exceptionLine; +} + +QString QmlVME::errorDescription() const +{ + return exceptionDescription; +} + +void QmlVME::runStoreInstruction(QStack<QObject *> &stack, + QmlInstruction &instr, + QmlCompiledData *comp) +{ + const QList<QString> &primitives = comp->primitives; + const QList<int> &intData = comp->intData; + const QList<float> &floatData = comp->floatData; + + switch(instr.type) { + case QmlInstruction::StoreVariant: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreVariant> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + // XXX - can be more efficient + QVariant v = QmlStringConverters::variantFromString(primitives.at(instr.storeString.value)); + a[0] = (void *)&v; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeString.propertyIndex, a); + } + break; + + case QmlInstruction::StoreString: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreString> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + a[0] = (void *)&primitives.at(instr.storeString.value); + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeString.propertyIndex, a); + } + break; + + case QmlInstruction::StoreReal: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreReal> cc; +#endif + QObject *target = stack.top(); + qreal r = instr.storeReal.value; + void *a[1]; + a[0] = &r; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeReal.propertyIndex, a); + } + break; + + case QmlInstruction::StoreBool: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreBool> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + a[0] = (void *)&instr.storeBool.value; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeBool.propertyIndex, a); + } + break; + + case QmlInstruction::StoreInteger: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreInteger> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + a[0] = (void *)&instr.storeInteger.value; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeReal.propertyIndex, a); + } + break; + + case QmlInstruction::StoreColor: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreColor> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QColor c = QColor::fromRgba(instr.storeColor.value); + a[0] = (void *)&c; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeColor.propertyIndex, a); + } + break; + + case QmlInstruction::StoreDate: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreDate> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QDate d = QDate::fromJulianDay(instr.storeDate.value); + a[0] = (void *)&d; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeDate.propertyIndex, a); + } + break; + + case QmlInstruction::StoreTime: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + //QFxCompilerTimer<QFxCompiler::InstrStoreTime> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QTime t; + t.setHMS(intData.at(instr.storeTime.valueIndex), + intData.at(instr.storeTime.valueIndex+1), + intData.at(instr.storeTime.valueIndex+2), + intData.at(instr.storeTime.valueIndex+3)); + a[0] = (void *)&t; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeTime.propertyIndex, a); + } + break; + + case QmlInstruction::StoreDateTime: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + //QFxCompilerTimer<QFxCompiler::InstrStoreDateTime> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QTime t; + t.setHMS(intData.at(instr.storeDateTime.valueIndex+1), + intData.at(instr.storeDateTime.valueIndex+2), + intData.at(instr.storeDateTime.valueIndex+3), + intData.at(instr.storeDateTime.valueIndex+4)); + QDateTime dt(QDate::fromJulianDay(intData.at(instr.storeDateTime.valueIndex)), t); + a[0] = (void *)&dt; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeDateTime.propertyIndex, a); + } + break; + + case QmlInstruction::StorePoint: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStorePoint> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QPoint p = QPointF(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)).toPoint(); + a[0] = (void *)&p; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QmlInstruction::StorePointF: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStorePoint> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QPointF p(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)); + a[0] = (void *)&p; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QmlInstruction::StoreSize: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreSize> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QSize p = QSizeF(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)).toSize(); + a[0] = (void *)&p; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QmlInstruction::StoreSizeF: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreSize> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QSizeF s(floatData.at(instr.storeRealPair.valueIndex), + floatData.at(instr.storeRealPair.valueIndex+1)); + a[0] = (void *)&s; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRealPair.propertyIndex, a); + } + break; + + case QmlInstruction::StoreRect: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + //QFxCompilerTimer<QFxCompiler::InstrStoreRect> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QRect r = QRectF(floatData.at(instr.storeRect.valueIndex), + floatData.at(instr.storeRect.valueIndex+1), + floatData.at(instr.storeRect.valueIndex+2), + floatData.at(instr.storeRect.valueIndex+3)).toRect(); + a[0] = (void *)&r; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRect.propertyIndex, a); + } + break; + + case QmlInstruction::StoreRectF: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + //QFxCompilerTimer<QFxCompiler::InstrStoreRect> cc; +#endif + QObject *target = stack.top(); + void *a[1]; + QRectF r(floatData.at(instr.storeRect.valueIndex), + floatData.at(instr.storeRect.valueIndex+1), + floatData.at(instr.storeRect.valueIndex+2), + floatData.at(instr.storeRect.valueIndex+3)); + a[0] = (void *)&r; + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeRect.propertyIndex, a); + } + break; + + case QmlInstruction::StoreObject: + { +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxCompilerTimer<QFxCompiler::InstrStoreObject> cc; +#endif + QObject *assignObj = stack.pop(); + QObject *target = stack.top(); + + void *a[1]; + void *obj = (void *)(((char *)assignObj) + instr.storeObject.cast); + a[0] = (void *)&obj; + + QMetaObject::metacall(target, QMetaObject::WriteProperty, + instr.storeObject.propertyIndex, a); + } + break; + default: + qFatal("QmlCompiledComponent: Internal error - unknown instruction %d", instr.type); + break; + } + +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlvme_p.h b/src/declarative/qml/qmlvme_p.h new file mode 100644 index 0000000..2a3be06 --- /dev/null +++ b/src/declarative/qml/qmlvme_p.h @@ -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 QtDeclarative 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 QMLVME_P_H +#define QMLVME_P_H + +#include <QString> +#include <QStack> +class QObject; + +QT_BEGIN_NAMESPACE +class QmlInstruction; +class QmlCompiledComponent; +class QmlCompiledData; +class QmlContext; + +class QmlVME +{ +public: + QmlVME(); + + QObject *run(QmlContext *, QmlCompiledComponent *, int start = -1, int end = -1); + + bool isError() const; + qint64 errorLine() const; + QString errorDescription() const; + +private: + void runStoreInstruction(QStack<QObject *> &stack, + QmlInstruction &, QmlCompiledData *); + + qint64 exceptionLine; + QString exceptionDescription; +}; + +QT_END_NAMESPACE +#endif // QMLVME_P_H diff --git a/src/declarative/qml/qmlvmemetaobject.cpp b/src/declarative/qml/qmlvmemetaobject.cpp new file mode 100644 index 0000000..2b1060b --- /dev/null +++ b/src/declarative/qml/qmlvmemetaobject.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlvmemetaobject_p.h" +#include <qml.h> +#include <private/qmlrefcount_p.h> +#include <QColor> +#include <QDate> + + +QT_BEGIN_NAMESPACE +QmlVMEMetaObject::QmlVMEMetaObject(QObject *obj, + const QMetaObject *other, + QmlRefCount *rc) +: object(obj), ref(rc) +{ + if(ref) + ref->addref(); + + *static_cast<QMetaObject *>(this) = *other; + this->d.superdata = obj->metaObject(); + QObjectPrivate::get(obj)->metaObject = this; + + baseProp = propertyOffset(); + baseSig = methodOffset(); + data = new QVariant[propertyCount() - baseProp]; + vTypes.resize(propertyCount() - baseProp); + + for(int ii = baseProp; ii < propertyCount(); ++ii) { + QMetaProperty prop = property(ii); + if((int)prop.type() != -1) { + data[ii - baseProp] = QVariant((QVariant::Type)prop.userType()); + } else { + vTypes.setBit(ii - baseProp, true); + } + } +} + +QmlVMEMetaObject::~QmlVMEMetaObject() +{ + if(ref) + ref->release(); + delete [] data; +} + +int QmlVMEMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +{ + if(id >= baseProp) { + int propId = id - baseProp; + bool needActivate = false; + + if(vTypes.testBit(propId)) { + if(c == QMetaObject::ReadProperty) { + *reinterpret_cast<QVariant *>(a[0]) = data[propId]; + } else if(c == QMetaObject::WriteProperty) { + needActivate = + (data[propId] != *reinterpret_cast<QVariant *>(a[0])); + data[propId] = *reinterpret_cast<QVariant *>(a[0]); + } + } else { + if(c == QMetaObject::ReadProperty) { + switch(data[propId].type()) { + case QVariant::Int: + *reinterpret_cast<int *>(a[0]) = data[propId].toInt(); + break; + case QVariant::Bool: + *reinterpret_cast<bool *>(a[0]) = data[propId].toBool(); + break; + case QVariant::Double: + *reinterpret_cast<double *>(a[0]) = data[propId].toDouble(); + break; + case QVariant::String: + *reinterpret_cast<QString *>(a[0]) = data[propId].toString(); + break; + case QVariant::Color: + *reinterpret_cast<QColor *>(a[0]) = data[propId].value<QColor>(); + break; + case QVariant::Date: + *reinterpret_cast<QDate *>(a[0]) = data[propId].toDate(); + break; + default: + qFatal("Unknown type"); + break; + } + } else if(c == QMetaObject::WriteProperty) { + + QVariant value = QVariant((QVariant::Type)data[propId].type(), a[0]); + needActivate = (data[propId] != value); + data[propId] = value; + } + } + + if(c == QMetaObject::WriteProperty && needActivate) { + activate(object, baseSig + propId, 0); + } + + return id; + } else { + return object->qt_metacall(c, id, a); + } +} +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlvmemetaobject_p.h b/src/declarative/qml/qmlvmemetaobject_p.h new file mode 100644 index 0000000..3fb1c46 --- /dev/null +++ b/src/declarative/qml/qmlvmemetaobject_p.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 QtDeclarative 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 QMLVMEMETAOBJECT_P_H +#define QMLVMEMETAOBJECT_P_H + +#include <qml.h> +#include <QMetaObject> +#include <QBitArray> +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE +class QmlRefCount; +class QmlVMEMetaObject : public QAbstractDynamicMetaObject +{ +public: + QmlVMEMetaObject(QObject *, const QMetaObject *, QmlRefCount * = 0); + ~QmlVMEMetaObject(); + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + +private: + QObject *object; + QmlRefCount *ref; + int baseProp; + int baseSig; + QVariant *data; + QBitArray vTypes; +}; + +QT_END_NAMESPACE +#endif // QMLVMEMETAOBJECT_P_H diff --git a/src/declarative/qml/qmlxmlparser.cpp b/src/declarative/qml/qmlxmlparser.cpp new file mode 100644 index 0000000..f001bda --- /dev/null +++ b/src/declarative/qml/qmlxmlparser.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlxmlparser_p.h" +#include "qmlcustomparser.h" +#include <qfxperf.h> +#include <QXmlStreamReader> +#include <QStack> +#include "qmlparser_p.h" +#include <private/qmlparser_p.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE +using namespace QmlParser; + +struct QmlXmlParserState { + QmlXmlParserState() : object(0), property(0) {} + QmlXmlParserState(Object *o) : object(o), property(0) {} + QmlXmlParserState(Object *o, Property *p) : object(o), property(p) {} + + Object *object; + Property *property; +}; + +struct QmlXmlParserStateStack : public QStack<QmlXmlParserState> +{ + void pushObject(Object *obj) + { + push(QmlXmlParserState(obj)); + } + + void pushProperty(const QString &name, int lineNumber) + { + const QmlXmlParserState &state = top(); + if(state.property) { + QmlXmlParserState s(state.property->getValue(), + state.property->getValue()->getProperty(name.toLatin1())); + s.property->line = lineNumber; + push(s); + } else { + QmlXmlParserState s(state.object, + state.object->getProperty(name.toLatin1())); + s.property->line = lineNumber; + push(s); + } + } +}; + +QmlXmlParser::~QmlXmlParser() +{ + if(root) + root->release(); +} + +QmlXmlParser::QmlXmlParser() +: root(0) +{ +} + +static QString flatXml(QXmlStreamReader& reader) +{ + QString result; + int depth=0; + QStringRef ns = reader.namespaceUri(); + while (depth>=0) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + result += QLatin1Char('<'); + result += reader.name(); + if (reader.namespaceUri() != ns || depth==0) { + result += QLatin1String(" xmlns=\""); + result += reader.namespaceUri(); + result += QLatin1Char('"'); + } + foreach(QXmlStreamAttribute attr, reader.attributes()) { + result += QLatin1Char(' '); + result += attr.name(); + result += QLatin1String("=\""); + result += attr.value(); // XXX escape + result += QLatin1Char('"'); + } + result += QLatin1Char('>'); + ++depth; + break; + case QXmlStreamReader::EndElement: + result += QLatin1String("</"); + result += reader.name(); + result += QLatin1Char('>'); + --depth; + break; + case QXmlStreamReader::Characters: + result += reader.text(); + break; + default: + reader.raiseError(QLatin1String("Only StartElement, EndElement, and Characters permitted")); + break; + } + if (depth>=0) + reader.readNext(); + } + return result; +} + +bool QmlXmlParser::parse(const QByteArray &data, const QUrl &url) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::XmlParsing> pt; +#endif + + QString fileDisplayName; + if (url.isEmpty()) { + fileDisplayName = QLatin1String("<unspecified file>"); + } else if (url.scheme() == QLatin1String("file")) { + fileDisplayName = url.toLocalFile(); + } else { + fileDisplayName = url.toString(); + } + if (data.isEmpty()) { + _error = QLatin1String("No Qml was specified for parsing @") + fileDisplayName; + return false; + } + + QmlXmlParserStateStack states; + + QXmlStreamReader reader; + reader.addData(data); + + while(!reader.atEnd()) { + switch(reader.readNext()) { + case QXmlStreamReader::Invalid: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + break; + + case QXmlStreamReader::StartElement: + { + QString name = reader.name().toString(); + QString nameSpace = reader.namespaceUri().toString(); + int line = reader.lineNumber(); + bool isType = name.at(0).isUpper() && !name.contains(QLatin1Char('.')); + QString qualifiedname; + if (!nameSpace.isEmpty()) { + qualifiedname = nameSpace; + qualifiedname += QLatin1Char('/'); + } + qualifiedname += name; + QByteArray qualifiednameL1 = qualifiedname.toLatin1(); + QXmlStreamAttributes attrs = reader.attributes(); + + if (isType) { + // Class + int typeId = _typeNames.indexOf(qualifiedname); + if(typeId == -1) { + typeId = _typeNames.count(); + _typeNames.append(qualifiedname); + } + + Object *obj = new Object; + obj->type = typeId; + obj->typeName = qualifiednameL1; + obj->line = line; + + QmlCustomParser *customparser = QmlMetaType::customParser(qualifiednameL1); + if (customparser) { + bool ok; + obj->custom = customparser->compile(reader, &ok); + if (reader.tokenType() != QXmlStreamReader::EndElement) { + reader.raiseError(QLatin1String("Parser for ") + qualifiedname + QLatin1String(" did not end on end element")); + ok = false; + } + if (!ok) { + delete obj; + break; + } + } + + + if(!root) { + root = obj; + states.pushObject(obj); + } else { + const QmlXmlParserState &state = states.top(); + Value *v = new Value; + v->object = obj; + v->line = line; + if(state.property) + state.property->addValue(v); + else + state.object->getDefaultProperty()->addValue(v); + states.pushObject(obj); + } + } else { + // Property + if (!root) { + reader.raiseError(QLatin1String("Can't have a property with no object")); + break; + } + QStringList str = name.split(QLatin1Char('.')); + for(int ii = 0; ii < str.count(); ++ii) { + QString s = str.at(ii); + states.pushProperty(s, line); + } + if (!nameSpace.isEmpty()) { + // Pass non-QML as flat text property value + const QmlXmlParserState &state = states.top(); + Value *v = new Value; + v->primitive = flatXml(reader); + v->line = line; + state.property->addValue(v); + } + } + + // (even custom parsed content gets properties set) + foreach(QXmlStreamAttribute attr, attrs) { + QStringList str = attr.name().toString().split(QLatin1Char('.')); + + for(int ii = 0; ii < str.count(); ++ii) { + QString s = str.at(ii); + states.pushProperty(s, line); + } + + const QmlXmlParserState &state = states.top(); + Value *v = new Value; + v->primitive = attr.value().toString(); + v->line = reader.lineNumber(); + state.property->addValue(v); + + for(int ii = str.count() - 1; ii >= 0; --ii) + states.pop(); + } + } + + // Custom parsers and namespaced properties move + // the reader to the end element, so we handle that + // BEFORE continuing. + // + if (reader.tokenType()!=QXmlStreamReader::EndElement) + break; + // ELSE fallthrough to EndElement... + case QXmlStreamReader::EndElement: + { + QString name = reader.name().toString(); + Q_ASSERT(!name.isEmpty()); + if(name.at(0).isUpper() && !name.contains(QLatin1Char('.'))) { + // Class + states.pop(); + } else { + // Property + QStringList str = name.split(QLatin1Char('.')); + for(int ii = 0; ii < str.count(); ++ii) + states.pop(); + } + } + break; + case QXmlStreamReader::Characters: + if(!reader.isWhitespace()) { + const QmlXmlParserState &state = states.top(); + Value *v = new Value; + v->primitive = reader.text().toString(); + v->line = reader.lineNumber(); + if(state.property) + state.property->addValue(v); + else + state.object->getDefaultProperty()->addValue(v); + } + break; + + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + break; + case QXmlStreamReader::ProcessingInstruction: + if(reader.processingInstructionTarget() == QLatin1String("qtfx")) { + QString str = reader.processingInstructionData().toString(); + QString token, data; + int idx = str.indexOf(QLatin1Char(':')); + if(-1 != idx) { + token = str.left(idx); + data = str.mid(idx + 1); + } else { + token = str; + } + token = token.trimmed(); + data = data.trimmed(); + + // <?qtfx namespacepath: namespace=path> + + if(token == QLatin1String("namespacepath")) { + int eq=data.indexOf(QLatin1Char('=')); + if (eq>=0) { + _nameSpacePaths.insertMulti(data.left(eq),data.mid(eq+1)); + } + } else { + str = str.trimmed(); + qWarning().nospace() << "Unknown processing instruction " << str.toLatin1().constData() << " @" << fileDisplayName.toLatin1().constData() << ":" << reader.lineNumber(); + } + } + break; + } + } + + if(reader.hasError()) { + if (root) { + root->release(); + root = 0; + } + _error = reader.errorString() + QLatin1String(" @") + fileDisplayName + + QLatin1String(":") + QString::number(reader.lineNumber()); + } + + return root != 0; +} + +QMap<QString,QString> QmlXmlParser::nameSpacePaths() const +{ + return _nameSpacePaths; +} + +QStringList QmlXmlParser::types() const +{ + return _typeNames; +} + +QmlParser::Object *QmlXmlParser::tree() const +{ + return root; +} + +QString QmlXmlParser::errorDescription() const +{ + return _error; +} + +void QmlXmlParser::clear() +{ + if(root) { + root->release(); + root = 0; + } + _nameSpacePaths.clear(); + _typeNames.clear(); + _error.clear(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlxmlparser_p.h b/src/declarative/qml/qmlxmlparser_p.h new file mode 100644 index 0000000..3680172 --- /dev/null +++ b/src/declarative/qml/qmlxmlparser_p.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 QtDeclarative 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 QMLXMLPARSER_P_H +#define QMLXMLPARSER_P_H + +#include <QList> +#include <QUrl> +#include <qml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +namespace QmlParser { + class Object; +} + +class QByteArray; +class QmlXmlParser +{ +public: + QmlXmlParser(); + ~QmlXmlParser(); + + bool parse(const QByteArray &data, const QUrl &url=QUrl()); + QString errorDescription() const; + + QMap<QString,QString> nameSpacePaths() const; + QStringList types() const; + + QmlParser::Object *tree() const; + + void clear(); + +private: + QMap<QString,QString> _nameSpacePaths; + QmlParser::Object *root; + QStringList _typeNames; + QString _error; +}; + +#endif // QMLXMLPARSER_P_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/qml/script/generator/generator.pro b/src/declarative/qml/script/generator/generator.pro new file mode 100644 index 0000000..1b2a4c7 --- /dev/null +++ b/src/declarative/qml/script/generator/generator.pro @@ -0,0 +1,11 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Mon Apr 2 20:15:52 2007 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +SOURCES += main.cpp diff --git a/src/declarative/qml/script/generator/main.cpp b/src/declarative/qml/script/generator/main.cpp new file mode 100644 index 0000000..676671f --- /dev/null +++ b/src/declarative/qml/script/generator/main.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QList> +#include <QByteArray> + + +QT_BEGIN_NAMESPACE +struct Keyword { + const char *lexem; + const char *token; +}; + +struct State +{ + State(const char* token) : token(token) + { + ::memset(next, 0, sizeof(next)); + } + State(const State &other) : token(other.token) + { + ::memcpy(next, other.next, sizeof(next)); + } + State &operator=(const State &other) + { + token = other.token; + ::memcpy(next, other.next, sizeof(next)); + return *this; + } + + QByteArray token; + int next[128]; +}; + +Keyword keywords[] = +{ + {"<", "LANGLE" }, + {">", "RANGLE" }, + {"+", "PLUS" }, + {"-", "MINUS" }, + {"*", "STAR" }, + {"==", "EQUALS" }, + {"&&", "AND" }, + {".", "DOT"}, + {"true", "TOKEN_TRUE"}, + {"false", "TOKEN_FALSE"}, + {" ", "WHITESPACE"}, + {"\t", "WHITESPACE"}, + {0, 0} +}; + +bool is_character(char s) +{ + return (s >= 'a' && s <= 'z') || + (s >= 'A' && s <= 'Z') || + (s >= '0' && s <= '9') || + s == '_'; +} + +void newState(QList<State> &states, const char *token, const char *lexem) +{ + int state = 0; + bool character = is_character(*lexem); + + while(*lexem) { + int next = states[state].next[(int)*lexem]; + + if(!next) { + next = states.size(); + states += State(character?"CHARACTER":"INCOMPLETE"); + states[state].next[(int)*lexem] = next; + } + + state = next; + ++lexem; + character = character && is_character(*lexem); + } + + states[state].token = token; +} + +void newState(QList<State> &states, const char *token, char lexem) +{ + int next = states[0].next[(int)lexem]; + if(!next) { + next = states.size(); + states += State(token); + states[0].next[(int)lexem] = next; + } else { + states[next].token = token; + } +} + +int main() +{ + QList<State> states; + states += State("NOTOKEN"); + + // identifiers + for (int cc = 'a'; cc <= 'z'; ++cc) + newState(states, "CHARACTER", cc); + for (int cc = 'A'; cc <= 'Z'; ++cc) + newState(states, "CHARACTER", cc); + newState(states, "CHARACTER", '_'); + + // add digits + for(int cc = '0'; cc <= '9'; ++cc) + newState(states, "DIGIT", cc); + + // keywords + for(int ii = 0; keywords[ii].lexem; ++ii) + newState(states, keywords[ii].token, keywords[ii].lexem); + + ::printf("static const struct\n{\n" + " Token token;\n" + " char next[128];\n" + "} keywords[] = {\n"); + + for(int ii = 0; ii < states.size(); ++ii) { + printf("%s { %s, { ", ii?",\n":"", states[ii].token.data()); + for(int jj = 0; jj < 128; jj++) + printf("%s%d", jj?",":"", states[ii].next[jj]); + printf(" } }"); + } + + printf("\n};\n"); +} +QT_END_NAMESPACE diff --git a/src/declarative/qml/script/instructions.h b/src/declarative/qml/script/instructions.h new file mode 100644 index 0000000..a21cbce --- /dev/null +++ b/src/declarative/qml/script/instructions.h @@ -0,0 +1,32 @@ +#ifndef _INSTRUCTIONS_H_ +#define _INSTRUCTIONS_H_ + +struct ScriptInstruction { + enum { + Load, // fetch + Fetch, // fetch + + Add, // NA + Subtract, // NA + Multiply, // NA + Equals, // NA + And, // NA + + Int, // integer + Bool, // boolean + } type; + + union { + struct { + int idx; + } fetch; + struct { + int value; + } integer; + struct { + bool value; + } boolean; + }; +}; + +#endif // _INSTRUCTIONS_H_ diff --git a/src/declarative/qml/script/keywords.cpp b/src/declarative/qml/script/keywords.cpp new file mode 100644 index 0000000..4cde65b --- /dev/null +++ b/src/declarative/qml/script/keywords.cpp @@ -0,0 +1,89 @@ +static const struct +{ + Token token; + char next[128]; +} keywords[] = { + { NOTOKEN, { 0,0,0,0,0,0,0,0,0,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,0,0,0,0,0,71,0,0,0,68,66,0,67,73,0,54,55,56,57,58,59,60,61,62,63,0,0,64,69,65,0,0,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,0,0,0,0,53,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,77,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 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,74,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DIGIT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { LANGLE, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { RANGLE, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { PLUS, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { MINUS, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { STAR, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { INCOMPLETE, { 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,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,70,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,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 } }, + { EQUALS, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { INCOMPLETE, { 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,0,0,0,72,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { AND, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { DOT, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,75,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,76,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 } }, + { TOKEN_TRUE, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,0,0,0,0,0,0,0,0,0,0,79,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { CHARACTER, { 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,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,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,80,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 } }, + { TOKEN_FALSE, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { WHITESPACE, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { WHITESPACE, { 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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } +}; diff --git a/src/declarative/qml/script/lexer.cpp b/src/declarative/qml/script/lexer.cpp new file mode 100644 index 0000000..d3ef935 --- /dev/null +++ b/src/declarative/qml/script/lexer.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#include <QByteArray> +#include "lexer.h" +#include "keywords.cpp" +#include <QDebug> + + +QT_BEGIN_NAMESPACE +QList<LexerToken> tokenize(const char *text) +{ + QList<LexerToken> rv; + + int lineNo = 0; + int charNo = 0; + int state = 0; + int tokenStart = 0; + bool other = false; + + const char *textprog = text; + + bool done = false; + while (!done) { + char textchar = *textprog; + done = !textchar; + + if (other) { + if (keywords[state].next[(int)textchar]) { + + // Do other token + LexerToken token; + token.token = OTHER; + token.start = tokenStart; + token.end = textprog - text - 1; + token.line = lineNo + 1; + token.offset = charNo - (token.end - token.start); + tokenStart = token.end + 1; + rv.append(token); + other = false; + + } else { + goto continue_loop; + } + } + + if (keywords[state].next[(int)textchar]) { + + state = keywords[state].next[(int)textchar]; + + } else if (0 == state || + keywords[state].token == INCOMPLETE) { + + other = true; + if (keywords[state].token == INCOMPLETE) { + state = 0; + continue; + } + + } else { + + // Token completed + Token tokenType = keywords[state].token; + bool tokenCollapsed = false; + if (tokenType == CHARACTER || + tokenType == DIGIT || + tokenType == WHITESPACE) { + + Token lastTokenType = + rv.isEmpty()?NOTOKEN:rv.last().token; + if (tokenType == lastTokenType) { + + rv.last().end = textprog - text - 1; + tokenStart = rv.last().end + 1; + + tokenCollapsed = true; + } + } + + if (!tokenCollapsed) { + LexerToken token; + token.token = keywords[state].token; + token.start = tokenStart; + token.end = textprog - text - 1; + token.line = lineNo + 1; + token.offset = charNo - (token.end - token.start); + tokenStart = token.end + 1; + rv.append(token); + } + + state = keywords[0].next[(int)textchar]; + if (0 == state) + other = true; + } + +continue_loop: + // Reset error reporting variables + if (textchar == '\n') { + ++lineNo; + charNo = 0; + } else { + charNo++; + } + + // Increment ptrs + ++textprog; + } + + if (other && ((textprog - text - 1) != tokenStart)) { + // Do other token + LexerToken token; + token.token = OTHER; + token.start = tokenStart; + token.end = textprog - text - 1; + token.line = lineNo + 1; + token.offset = charNo - (token.end - token.start); + tokenStart = token.end + 1; + rv.append(token); + other = false; + } + return rv; +} + +void dumpTokens(const char *text, const QList<LexerToken> &tokens) +{ + for (int ii = 0; ii < tokens.count(); ++ii) { + QByteArray ba(text + tokens.at(ii).start, tokens.at(ii).end - tokens.at(ii).start + 1); + qWarning() << tokens.at(ii).line << ":" << tokens.at(ii).offset << tokenToString(tokens.at(ii).token) << "(" << tokens.at(ii).start << "-" << tokens.at(ii).end << ")" << ba; + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/script/lexer.h b/src/declarative/qml/script/lexer.h new file mode 100644 index 0000000..7781ee8 --- /dev/null +++ b/src/declarative/qml/script/lexer.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef LEXER_H +#define LEXER_H + +#include <QList> +#include "tokens.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +struct LexerToken +{ + LexerToken() : token(NOTOKEN), start(-1), end(-1), line(-1), offset(-1) {} + LexerToken(const LexerToken &other) : token(other.token), + start(other.start), + end(other.end), + line(other.line), + offset(other.offset) {} + LexerToken &operator=(const LexerToken &other) { + token = other.token; + start = other.start; + end = other.end; + line = other.line; + offset = other.offset; + return *this; + } + + Token token; + int start; + int end; + int line; + int offset; +}; + +QList<LexerToken> tokenize(const char *text); +void dumpTokens(const char *text, const QList<LexerToken> &tokens); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/qml/script/qmlbasicscript.cpp b/src/declarative/qml/script/qmlbasicscript.cpp new file mode 100644 index 0000000..4f40016 --- /dev/null +++ b/src/declarative/qml/script/qmlbasicscript.cpp @@ -0,0 +1,923 @@ +/**************************************************************************** +** +** 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. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qmlbasicscript.h" +#include "qmlbasicscript_p.h" +#include "lexer.h" +#include <QColor> +#include <QDebug> +#include <private/qmlengine_p.h> +#include <private/qmlcontext_p.h> +#include <QStack> +#include <qfxperf.h> +#include <private/qmlrefcount_p.h> + + +QT_BEGIN_NAMESPACE +DEFINE_BOOL_CONFIG_OPTION(scriptWarnings, QML_SCRIPT_WARNINGS); + +class QmlBasicScriptPrivate +{ +public: + enum Flags { OwnData = 0x00000001 }; + + int size; + int stateSize; + int instructionCount; + int exprLen; + + ScriptInstruction *instructions() const { return (ScriptInstruction *)((char *)this + sizeof(QmlBasicScriptPrivate)); } + + const char *expr() const + { + return (const char *)(instructions() + instructionCount); + } + + const char *data() const + { + return (const char *)(instructions() + instructionCount) + exprLen + 1; + } + + static unsigned int alignRound(int s) + { + if(s % 4) + s += 4 - (s % 4); + return s; + } +}; + +QDebug operator<<(QDebug lhs, const QmlBasicScriptNodeCache &rhs) +{ + switch(rhs.type) { + case QmlBasicScriptNodeCache::Invalid: + lhs << "Invalid"; + break; + case QmlBasicScriptNodeCache::Core: + lhs << "Core" << rhs.object << rhs.core; + break; + case QmlBasicScriptNodeCache::Attached: + lhs << "Attached" << rhs.object << rhs.attached; + break; + case QmlBasicScriptNodeCache::Signal: + lhs << "Signal" << rhs.object << rhs.core; + break; + case QmlBasicScriptNodeCache::SignalProperty: + lhs << "SignalProperty" << rhs.object << rhs.core; + break; + case QmlBasicScriptNodeCache::Explicit: + lhs << "Explicit" << rhs.object; + break; + case QmlBasicScriptNodeCache::Variant: + lhs << "Variant" << rhs.context; + break; + case QmlBasicScriptNodeCache::ScriptValue: + lhs << "ScriptValue" << rhs.context; + break; + } + + return lhs; +} + +void QmlBasicScriptNodeCache::clear() +{ + object = 0; + metaObject = 0; + type = Invalid; +} + +static QVariant toObjectOrVariant(const QVariant &v) +{ + switch(v.type()) { + case QVariant::String: + case QVariant::UInt: + case QVariant::Int: + case 135: + case QVariant::Double: + case QVariant::Color: + case QVariant::Bool: + default: + return v; + case QVariant::UserType: + { + QObject *o = QmlMetaType::toQObject(v); + if (o) + return qVariantFromValue(o); + else + return v; + } + break; + } +} + +static QVariant fetch_value(QObject *o, int idx, int type) +{ + switch(type) { + case QVariant::String: + { + QString val; + void *args[] = { &val, 0 }; + QMetaObject::metacall(o, QMetaObject::ReadProperty, idx, args); + return QVariant(val); + } + break; + case QVariant::UInt: + { + uint val; + void *args[] = { &val, 0 }; + QMetaObject::metacall(o, QMetaObject::ReadProperty, idx, args); + return QVariant(val); + } + break; + case QVariant::Int: + { + int val; + void *args[] = { &val, 0 }; + QMetaObject::metacall(o, QMetaObject::ReadProperty, idx, args); + return QVariant(val); + } + break; + case 135: + case QVariant::Double: + { + qreal val; + void *args[] = { &val, 0 }; + QMetaObject::metacall(o, QMetaObject::ReadProperty, idx, args); + return QVariant(val); + } + break; + case QVariant::Color: + { + QColor val; + void *args[] = { &val, 0 }; + QMetaObject::metacall(o, QMetaObject::ReadProperty, idx, args); + return QVariant(val); + } + break; + case QVariant::Bool: + { + bool val; + void *args[] = { &val, 0 }; + QMetaObject::metacall(o, QMetaObject::ReadProperty, idx, args); + return QVariant(val); + } + break; + default: + { + if(QmlMetaType::isObject(type)) { + // NOTE: This assumes a cast to QObject does not alter the + // object pointer + QObject *val = 0; + void *args[] = { &val, 0 }; + QMetaObject::metacall(o, QMetaObject::ReadProperty, idx, args); + return QVariant::fromValue(val); + } else { + QVariant var = o->metaObject()->property(idx).read(o); + if(QmlMetaType::isObject(var.userType())) { + QObject *obj = 0; + obj = *(QObject **)var.data(); + var = QVariant::fromValue(obj); + } + return var; + } + } + break; + }; +} + +QVariant QmlBasicScriptNodeCache::value(const char *name) const +{ + //QFxPerfTimer<QFxPerf::BasicScriptValue> pt; + switch(type) { + case Invalid: + break; + case Core: + return fetch_value(object, core, coreType); + break; + case Attached: + return qVariantFromValue(static_cast<QObject *>(attached)); + break; + case Signal: + // XXX + Q_ASSERT(!"Not implemented"); + break; + case SignalProperty: + break; + case Explicit: + return qVariantFromValue(object); + break; + case Variant: + return toObjectOrVariant(context->variantProperties[QLatin1String(name)]); + break; + case ScriptValue: + return qVariantFromValue(context->properties[QLatin1String(name)]); + break; + }; + return QVariant(); +} + +struct QmlBasicScriptCompiler +{ + QmlBasicScriptCompiler() + : script(0), stateSize(0), src(0), idx(0) {} + QmlBasicScript *script; + QList<LexerToken> tokens; + int stateSize; + const char *src; + int idx; + + bool compile(); + bool compileExpr(); + + bool parseFetch(); + bool parseName(); + bool parseConstant(); + void skipWhitespace(); + + QByteArray data; + QList<ScriptInstruction> bytecode; + + QByteArray string(int, int); + Token token() const; + bool atEnd() const; + void adv(); + int index() const; +}; + +/*! + \class QmlBasicScript + \brief The QmlBasicScript class provides a fast implementation of a limited subset of JavaScript bindings. + + QmlBasicScript instances are used to accelerate binding. Instead of using + the slower, fully fledged JavaScript engine, many simple bindings can be + evaluated using the QmlBasicScript engine. + + To see if the QmlBasicScript engine can handle a binding, call compile() + and check the return value, or isValid() afterwards. + + To evaluate the binding, the QmlBasicScript instance needs some memory in + which to cache state. This may be allocated by calling newScriptState() + and destroyed by calling deleteScriptState(). The state data is then passed + to the run() method when evaluating the binding. + + To further accelerate binding, QmlBasicScript can return a precompiled + version of itself that can be saved for future use. Call compileData() to + get an opaque pointer to the compiled state, and compileDataSize() for the + size of this data in bytes. This data can be saved and passed to future + instances of the QmlBasicScript constructor. The initial copy of compile + data is owned by the QmlBindScript instance on which compile() was called. +*/ + +/*! + Create a new QmlBasicScript instance. +*/ +QmlBasicScript::QmlBasicScript() +: flags(0), d(0), rc(0) +{ +} + +/*! + Create a new QmlBasicScript instance from saved \a data. + + \a data \b must be data previously acquired from calling compileData() on a + previously created QmlBasicScript instance. Any other data will almost + certainly cause the QmlBasicScript engine to crash. + + \a data must continue to be valid throughout the QmlBasicScript instance + life. It does not assume ownership of the memory. + + If \a owner is set, it is referenced on creation and dereferenced on + destruction of this instance. +*/ +QmlBasicScript::QmlBasicScript(const char *data, QmlRefCount *owner) +: flags(0), d((QmlBasicScriptPrivate *)data), rc(owner) +{ + if(rc) rc->addref(); +} + +/*! + Return the text of the script expression. + */ +QByteArray QmlBasicScript::expression() const +{ + if(!d) + return QByteArray(); + else + return QByteArray(d->expr()); +} + +/*! + Destroy the script instance. +*/ +QmlBasicScript::~QmlBasicScript() +{ + if(flags & QmlBasicScriptPrivate::OwnData) + free(d); + if(rc) rc->release(); + d = 0; + rc = 0; +} + +/*! + Clear this script. The object will then be in its initial state, as though + it were freshly constructed with default constructor. +*/ +void QmlBasicScript::clear() +{ + if(flags & QmlBasicScriptPrivate::OwnData) + free(d); + if(rc) rc->release(); + d = 0; + rc = 0; + flags = 0; +} + +/*! + Return the script state memory for this script instance. This memory should + only be destroyed by calling deleteScriptState(). + */ +void *QmlBasicScript::newScriptState() +{ + if(!d) { + return 0; + } else { + void *rv = ::malloc(d->stateSize * sizeof(QmlBasicScriptNodeCache)); + ::memset(rv, 0, d->stateSize * sizeof(QmlBasicScriptNodeCache)); + return rv; + } +} + +/*! + Delete the \a data previously allocated by newScriptState(). + */ +void QmlBasicScript::deleteScriptState(void *data) +{ + if(!data) return; + Q_ASSERT(d); + clearCache(data); + free(data); +} + +/*! + Dump the script instructions to stderr for debugging. + */ +void QmlBasicScript::dump() +{ + if(!d) + return; + + qWarning() << d->instructionCount << "instructions:"; + const char *data = d->data(); + for(int ii = 0; ii < d->instructionCount; ++ii) { + const ScriptInstruction &instr = d->instructions()[ii]; + + switch(instr.type) { + case ScriptInstruction::Load: + qWarning().nospace() << "LOAD\t\t" << instr.fetch.idx << "\t\t" + << QByteArray(data + instr.fetch.idx); + break; + case ScriptInstruction::Fetch: + qWarning().nospace() << "FETCH\t\t" << instr.fetch.idx << "\t\t" + << QByteArray(data + instr.fetch.idx); + break; + case ScriptInstruction::Add: + qWarning().nospace() << "ADD"; + break; + case ScriptInstruction::Subtract: + qWarning().nospace() << "SUBTRACT"; + break; + case ScriptInstruction::Multiply: + qWarning().nospace() << "MULTIPLY"; + break; + case ScriptInstruction::Equals: + qWarning().nospace() << "EQUALS"; + break; + case ScriptInstruction::Int: + qWarning().nospace() << "INT\t\t" << instr.integer.value; + break; + case ScriptInstruction::Bool: + qWarning().nospace() << "BOOL\t\t" << instr.boolean.value; + break; + default: + qWarning().nospace() << "UNKNOWN"; + break; + } + } +} + +/*! + Return true if this is a valid script binding, otherwise returns false. + */ +bool QmlBasicScript::isValid() const +{ + return d != 0; +} + +/*! + Compile \a src and return true if the compilation is successful, otherwise + returns false. + */ +bool QmlBasicScript::compile(const QByteArray &src) +{ + bool rv = compile(src.constData()); + return rv; +} + +/*! + \overload + + Compile \a src and return true if the compilation is successful, otherwise + returns false. + */ +bool QmlBasicScript::compile(const char *src) +{ + if(!src) return false; + + QmlBasicScriptCompiler bsc; + bsc.script = this; + bsc.tokens = tokenize(src); + bsc.src = src; + // dumpTokens(src, bsc.tokens); + + if(d) { + if(flags & QmlBasicScriptPrivate::OwnData) + free(d); + d = 0; + flags = 0; + } + + if(bsc.compile()) { + int len = ::strlen(src); + flags = QmlBasicScriptPrivate::OwnData; + int size = sizeof(QmlBasicScriptPrivate) + + bsc.bytecode.count() * sizeof(ScriptInstruction) + + QmlBasicScriptPrivate::alignRound(bsc.data.count() + len + 1); + d = (QmlBasicScriptPrivate *) malloc(size); + d->size = size; + d->stateSize = bsc.stateSize; + d->instructionCount = bsc.bytecode.count(); + d->exprLen = len; + ::memcpy((char *)d->expr(), src, len + 1); + for(int ii = 0; ii < d->instructionCount; ++ii) + d->instructions()[ii] = bsc.bytecode.at(ii); + ::memcpy((char *)d->data(), bsc.data.constData(), bsc.data.count()); + } + + return d != 0; +} + +void QmlBasicScriptCompiler::skipWhitespace() +{ + while(idx < tokens.count() && tokens.at(idx).token == WHITESPACE) + ++idx; +} + +bool QmlBasicScriptCompiler::compile() +{ + if(!compileExpr()) + return false; + + skipWhitespace(); + + if(atEnd()) + return true; + + int t = token(); + if(t != AND) + return false; + + adv(); + skipWhitespace(); + if(!compileExpr()) + return false; + + ScriptInstruction instr; + instr.type = ScriptInstruction::And; + bytecode.append(instr); + + skipWhitespace(); + + return atEnd(); +} + +bool QmlBasicScriptCompiler::compileExpr() +{ + /* + EXPRESSION := <NAME><OPERATOR>[<CONSTANT>|<NAME>] + */ + + if(!parseName()) + return false; + + skipWhitespace(); + + if(atEnd()) + return true; + + int t = token(); + switch(t) { + case PLUS: + case MINUS: + /* + case LANGLE: + case RANGLE: + */ + case STAR: + case EQUALS: + break; + default: + return true; + } + adv(); + + skipWhitespace(); + + if(!parseConstant() && + !parseName()) + return false; + + ScriptInstruction instr; + switch(t) { + case PLUS: + instr.type = ScriptInstruction::Add; + break; + case MINUS: + instr.type = ScriptInstruction::Subtract; + break; + case STAR: + instr.type = ScriptInstruction::Multiply; + break; + case EQUALS: + instr.type = ScriptInstruction::Equals; + break; + default: + break; + } + bytecode.append(instr); + + skipWhitespace(); + + return true; +} + +bool QmlBasicScriptCompiler::parseName() +{ + skipWhitespace(); + + bool named = false; + bool seenchar = false; + bool seendot = false; + int namestart = -1; + bool pushed = false; + while(!atEnd()) { + int t = token(); + if(t == CHARACTER) { + named = true; + seendot = false; + seenchar = true; + namestart = index(); + adv(); + } else if(t == DIGIT) { + if(!seenchar) break; + adv(); + } else if(t == DOT) { + seendot = true; + if(namestart == -1) + break; + + seenchar = false; + QByteArray name = string(namestart, index() - 1); + int nref = data.count(); + data.append(name); + data.append('\0'); + ScriptInstruction instr; + if(pushed) + instr.type = ScriptInstruction::Fetch; + else + instr.type = ScriptInstruction::Load; + pushed = true; + instr.fetch.idx = nref; + bytecode.append(instr); + ++stateSize; + namestart = -1; + adv(); + } else { + break; + } + } + + if(namestart != -1) { + QByteArray name = string(namestart, index() - 1); + int nref = data.count(); + data.append(name); + data.append('\0'); + ScriptInstruction instr; + if(pushed) + instr.type = ScriptInstruction::Fetch; + else + instr.type = ScriptInstruction::Load; + pushed = true; + instr.fetch.idx = nref; + bytecode.append(instr); + ++stateSize; + } + + if(seendot) + return false; + else + return named; +} + +bool QmlBasicScriptCompiler::parseConstant() +{ + switch(token()) { + case DIGIT: + { + ScriptInstruction instr; + instr.type = ScriptInstruction::Int; + instr.integer.value = string(index(), index()).toUInt(); + bytecode.append(instr); + adv(); + } + break; + case TOKEN_TRUE: + case TOKEN_FALSE: + { + ScriptInstruction instr; + instr.type = ScriptInstruction::Bool; + instr.boolean.value = (token() == TOKEN_TRUE); + bytecode.append(instr); + adv(); + } + break; + + default: + return false; + } + + return true; +} + +bool QmlBasicScriptCompiler::atEnd() const +{ + return idx >= tokens.count(); +} + +Token QmlBasicScriptCompiler::token() const +{ + return tokens.at(idx).token; +} + +void QmlBasicScriptCompiler::adv() +{ + ++idx; +} + +int QmlBasicScriptCompiler::index() const +{ + return idx; +} + +QByteArray QmlBasicScriptCompiler::string(int from, int to) +{ + QByteArray rv; + for(int ii = from; ii <= to; ++ii) { + const LexerToken &token = tokens.at(ii); + rv.append(QByteArray(src + token.start, token.end - token.start + 1)); + } + return rv; +} + +/*! + \internal +*/ +void QmlBasicScript::clearCache(void *voidCache) +{ + QmlBasicScriptNodeCache *dataCache = + reinterpret_cast<QmlBasicScriptNodeCache *>(voidCache); + + for(int ii = 0; ii < d->stateSize; ++ii) { + if(!dataCache[ii].isCore() && !dataCache[ii].isExplicit() && + dataCache[ii].object) { + QMetaObject::removeGuard(&dataCache[ii].object); + dataCache[ii].object = 0; + } + dataCache[ii].clear(); + } +} + +void QmlBasicScript::guard(QmlBasicScriptNodeCache &n) +{ + if(n.object) { + if(n.isExplicit()) { + } else if(n.isCore()) { + n.metaObject = + n.object->metaObject(); + } else { + QMetaObject::addGuard(&n.object); + } + } +} + +bool QmlBasicScript::valid(QmlBasicScriptNodeCache &n, QObject *obj) +{ + return n.object == obj && + (!n.isCore() || obj->metaObject() == n.metaObject); +} + + +/*! + \enum QmlBasicScript::CacheState + \value NoChange The query has not change. Any previous monitoring is still + valid. + \value Incremental The query has been incrementally changed. Any previous + monitoring is still valid, but needs to have the fresh properties added to + it. + \value Reset The entire query has been reset from the beginning. Any previous + monitoring is now invalid. +*/ + +/*! + Run the script in \a context and return the result. \a voidCache should + contain state memory previously acquired from newScript. + */ +QVariant QmlBasicScript::run(QmlContext *context, void *voidCache, CacheState *cached) +{ + if(!isValid()) + return QVariant(); + + QmlBasicScriptNodeCache *dataCache = + reinterpret_cast<QmlBasicScriptNodeCache *>(voidCache); + int dataCacheItem; + QStack<QVariant> stack; + + bool resetting = false; + bool hasReset = false; + + const char *data = d->data(); + + if(dataCache[0].type == QmlBasicScriptNodeCache::Invalid) { + resetting = true; + hasReset = true; + } + + CacheState state = NoChange; + + dataCacheItem = 0; + for(int idx = 0; idx < d->instructionCount; ++idx) { + const ScriptInstruction &instr = d->instructions()[idx]; + + switch(instr.type) { + case ScriptInstruction::Load: // either an object or a property + case ScriptInstruction::Fetch: // can only be a property + { + const char *id = data + instr.fetch.idx; + QmlBasicScriptNodeCache &n = dataCache[dataCacheItem]; + + if(instr.type == ScriptInstruction::Load) { + + if(n.type == QmlBasicScriptNodeCache::Invalid) { + context->engine()->d_func()->loadCache(n, QLatin1String(id), static_cast<QmlContextPrivate*>(context->d_ptr)); + state = Incremental; + } + + } else { // instr.type == ScriptInstruction::Fetch + + QVariant o = stack.pop(); + QObject *obj = qvariant_cast<QObject *>(o); + if(!obj) { + if(n.type == QmlBasicScriptNodeCache::Invalid) { + if(scriptWarnings()) + qWarning() << "QmlBasicScript: Unable to convert" << o; + *cached = state; + return QVariant(); + } else { + clearCache(dataCache); + *cached = Reset; + CacheState dummy; + return run(context, voidCache, &dummy); + } + } else if(n.type == QmlBasicScriptNodeCache::Invalid) { + context->engine()->d_func()->fetchCache(n, QLatin1String(id), obj); + guard(n); + state = Incremental; + } else if(!valid(n, obj)) { + clearCache(dataCache); + *cached = Reset; + CacheState dummy; + return run(context, voidCache, &dummy); + } + + } + + QVariant var = n.value(id); + stack.push(var); + ++dataCacheItem; + } + break; + case ScriptInstruction::Int: + stack.push(QVariant(instr.integer.value)); + break; + case ScriptInstruction::Bool: + stack.push(QVariant(instr.boolean.value)); + break; + case ScriptInstruction::Add: + { + QVariant rhs = stack.pop(); + QVariant lhs = stack.pop(); + + stack.push(rhs.toDouble() + lhs.toDouble()); + } + break; + case ScriptInstruction::Subtract: + { + QVariant rhs = stack.pop(); + QVariant lhs = stack.pop(); + + stack.push(lhs.toDouble() - rhs.toDouble()); + } + break; + case ScriptInstruction::Multiply: + { + QVariant rhs = stack.pop(); + QVariant lhs = stack.pop(); + + stack.push(rhs.toDouble() * lhs.toDouble()); + } + break; + case ScriptInstruction::Equals: + { + QVariant rhs = stack.pop(); + QVariant lhs = stack.pop(); + + stack.push(rhs == lhs); + } + break; + case ScriptInstruction::And: + { + QVariant rhs = stack.pop(); + QVariant lhs = stack.pop(); + + stack.push(rhs.toBool() && lhs.toBool()); + } + break; + default: + break; + } + } + + *cached = state; + + if(stack.isEmpty()) + return QVariant(); + else + return stack.top(); +} + +/*! + Return a pointer to the script's compile data, or null if there is no data. + */ +const char *QmlBasicScript::compileData() const +{ + return (const char *)d; +} + +/*! + Return the size of the script's compile data, or zero if there is no data. + The size will always be a multiple of 4. + */ +unsigned int QmlBasicScript::compileDataSize() const +{ + if(d) + return d->size; + else + return 0; +} + +bool QmlBasicScript::isSingleLoad() const +{ + if(!d) + return false; + + return d->instructionCount == 1 && + d->instructions()[0].type == ScriptInstruction::Load; +} + +QByteArray QmlBasicScript::singleLoadTarget() const +{ + if(!isSingleLoad()) + return QByteArray(); + + // We know there is one instruction and it is a load + return QByteArray(d->data() + d->instructions()[0].fetch.idx); +} + + +QT_END_NAMESPACE diff --git a/src/declarative/qml/script/qmlbasicscript.h b/src/declarative/qml/script/qmlbasicscript.h new file mode 100644 index 0000000..d465f04 --- /dev/null +++ b/src/declarative/qml/script/qmlbasicscript.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 $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLBASICSCRIPT_H +#define QMLBASICSCRIPT_H + +#include "instructions.h" +#include <QList> +#include <QByteArray> +#include "lexer.h" +#include <QVariant> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlRefCount; +class QmlContext; +class QmlBasicScriptPrivate; +class QmlBasicScriptNodeCache; +class QmlBasicScript +{ +public: + QmlBasicScript(); + QmlBasicScript(const char *, QmlRefCount * = 0); + ~QmlBasicScript(); + + // Always 4-byte aligned + const char *compileData() const; + unsigned int compileDataSize() const; + + QByteArray expression() const; + + bool compile(const QByteArray &); + bool compile(const char *); + bool isValid() const; + + void clear(); + + void dump(); + void *newScriptState(); + void deleteScriptState(void *); + + enum CacheState { NoChange, Incremental, Reset }; + QVariant run(QmlContext *, void *, CacheState *); + + // Optimization opportunities + bool isSingleLoad() const; + QByteArray singleLoadTarget() const; + +private: + int flags; + QmlBasicScriptPrivate *d; + QmlRefCount *rc; + + void clearCache(void *); + void guard(QmlBasicScriptNodeCache &); + bool valid(QmlBasicScriptNodeCache &, QObject *); +}; + +#endif // QMLBASICSCRIPT_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/qml/script/qmlbasicscript_p.h b/src/declarative/qml/script/qmlbasicscript_p.h new file mode 100644 index 0000000..bcb7d00 --- /dev/null +++ b/src/declarative/qml/script/qmlbasicscript_p.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** 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. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLBASICSCRIPT_P_H +#define QMLBASICSCRIPT_P_H + +QT_BEGIN_NAMESPACE + +class QObject; +class QmlContextPrivate; +class QDebug; +class QByteArray; + +class QmlBasicScriptNodeCache +{ +public: + QObject *object; + const QMetaObject *metaObject; + enum { Invalid, + Core, + Attached, + Signal, + SignalProperty, + Explicit, + Variant, + ScriptValue } type; + union { + int core; + QObject *attached; + QmlContextPrivate *context; + }; + int coreType; + + bool isCore() const { return type == Core; } + bool isExplicit() const { return type == Explicit; } + void clear(); + QVariant value(const char *) const; +}; + +QDebug operator<<(QDebug, const QmlBasicScriptNodeCache &); + +#endif // QMLBASICSCRIPT_P_H + +QT_END_NAMESPACE diff --git a/src/declarative/qml/script/script.pri b/src/declarative/qml/script/script.pri new file mode 100644 index 0000000..6c43efe --- /dev/null +++ b/src/declarative/qml/script/script.pri @@ -0,0 +1,11 @@ +SOURCES += \ + qml/script/tokens.cpp \ + qml/script/lexer.cpp \ + qml/script/qmlbasicscript.cpp + +HEADERS += \ + qml/script/tokens.h \ + qml/script/lexer.h \ + qml/script/instructions.h \ + qml/script/qmlbasicscript.h \ + qml/script/qmlbasicscript_p.h diff --git a/src/declarative/qml/script/tokens.cpp b/src/declarative/qml/script/tokens.cpp new file mode 100644 index 0000000..51b46f0 --- /dev/null +++ b/src/declarative/qml/script/tokens.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "tokens.h" + + +/*! + \relates <tokens.h> + Returns a string representation of token \a tok. +*/ +const char *tokenToString(Token tok) +{ + switch(tok) { +#define CASE(X) case X: return #X; + CASE(NOTOKEN) + CASE(INCOMPLETE) + CASE(WHITESPACE) + CASE(LANGLE) + CASE(RANGLE) + CASE(PLUS) + CASE(MINUS) + CASE(STAR) + CASE(EQUALS) + CASE(DOT) + CASE(CHARACTER) + CASE(DIGIT) + CASE(OTHER) + CASE(AND) + case TOKEN_TRUE: + return "TRUE"; + case TOKEN_FALSE: + return "FALSE"; +#undef CASE + } + return 0; +} + diff --git a/src/declarative/qml/script/tokens.h b/src/declarative/qml/script/tokens.h new file mode 100644 index 0000000..753e40c --- /dev/null +++ b/src/declarative/qml/script/tokens.h @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** This file is part of the $PACKAGE_NAME$. +** +** Copyright (C) $THISYEAR$ $COMPANY_NAME$. +** +** $QT_EXTENDED_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef TOKENS_H +#define TOKENS_H + +enum Token { + // Lexer tokens + NOTOKEN, + INCOMPLETE, + WHITESPACE, + LANGLE, + RANGLE, + PLUS, + MINUS, + STAR, + EQUALS, + AND, + DOT, + CHARACTER, + DIGIT, + TOKEN_TRUE, + TOKEN_FALSE, + OTHER +}; + +const char *tokenToString(Token); + +#endif diff --git a/src/declarative/test/qfxtestengine.cpp b/src/declarative/test/qfxtestengine.cpp new file mode 100644 index 0000000..96ce451 --- /dev/null +++ b/src/declarative/test/qfxtestengine.cpp @@ -0,0 +1,457 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <QFile> +#include <QmlComponent> +#include <gfxtimeline.h> +#include "qfxtestengine.h" +#include "qfxtestobjects.h" +#include <QCryptographicHash> +#include <QApplication> +#include <QKeyEvent> +#include <QSimpleCanvas> +#include <QMouseEvent> +#include <qmlengine.h> +#include <private/qabstractanimation_p.h> +#include <QAbstractAnimation> + +QT_BEGIN_NAMESPACE + +#define MAX_MISMATCHED_FRAMES 5 +#define MAX_MISMATCHED_PIXELS 5 + +class QFxTestEnginePrivate : public QAbstractAnimation +{ +public: + QFxTestEnginePrivate(QFxTestEngine *p) + : q(p), canvas(0), testMode(QFxTestEngine::NoTest), fullFrame(true), + status(Working), exitOnFail(true), mismatchedFrames(0), + lastFrameMismatch(false) {} + + QFxTestEngine *q; + + QmlEngine engine; + QSimpleCanvas *canvas; + QFxTestEngine::TestMode testMode; + QString testDirectory; + + TestLog testData; + TestLog playbackTestData; + bool fullFrame; + QList<QImage> fullFrames; + + virtual void updateCurrentTime(int); + + void recordFrameEvent(const QImage &img); + void recordFullFrameEvent(const QImage &img); + void recordKeyEvent(QKeyEvent *e); + void recordMouseEvent(QMouseEvent *e); + void testPass(); + void save(const QString &filename, bool = true); + + enum MessageType { Success, Fail }; + void message(MessageType t, const char *); + + enum Status { Working, Failed, Passed }; + Status status; + bool exitOnFail; + + QList<TestObject *> toPost; + QSet<QEvent *> postedEvents; + + // OpenGL seems to give inconsistent rendering results. We allow a small + // tolerance to compensate - a maximum number of mismatched frames and only + // one mismatch in a row + int mismatchedFrames; + bool lastFrameMismatch; + + bool compare(const QImage &img1, const QImage &img2); + + virtual int duration() const { return -1; } +}; + +bool QFxTestEnginePrivate::compare(const QImage &img1, const QImage &img2) +{ + if(img1.size() != img2.size()) + return false; + + int errorCount = 0; + for(int yy = 0; yy < img1.height(); ++yy) { + for(int xx = 0; xx < img1.width(); ++xx) { + if(img1.pixel(xx, yy) != img2.pixel(xx, yy)) { + errorCount++; + if(errorCount > MAX_MISMATCHED_PIXELS) + return false; + } + } + } + + return true; +} + +QFxTestEngine::QFxTestEngine(TestMode mode, const QString &dir, + QSimpleCanvas *canvas, QObject *parent) +: QObject(parent), d(new QFxTestEnginePrivate(this)) +{ + Q_ASSERT(canvas); + + d->canvas = canvas; + d->start(); + + d->testDirectory = dir; + d->testMode = mode; + if(d->testMode == RecordTest) { + qWarning("QFxTestEngine: Record ON"); + } else if(d->testMode == PlaybackTest) { + + QString fileName(d->testDirectory + QLatin1String("/manifest.xml")); + QFile f(fileName); + if(!f.open(QIODevice::ReadOnly)) { + qWarning() << "QFxTestEngine: Unable to open file" << fileName; + return; + } + + QByteArray data = f.readAll(); + QmlComponent c(&d->engine, data, QUrl(d->testDirectory + QLatin1String("/manifest.xml"))); + QObject *o = c.create(); + TestLog *log = qobject_cast<TestLog *>(o); + if(log) { + log->setParent(this); + d->playbackTestData.actions() = log->actions(); + qWarning("QFxTestEngine: Playback ON"); + } else { + delete o; + qWarning() << "QFxTestEngine: File" << fileName << "is corrupt."; + return; + } + } + + if(d->testMode != NoTest) { + + QUnifiedTimer::instance()->setConsistentTiming(true); + QObject::connect(canvas, SIGNAL(framePainted()), + this, SLOT(framePainted())); + + canvas->installEventFilter(this); + for(int ii = 0; ii < d->playbackTestData.actions().count(); ++ii) { + TestObject *o = d->playbackTestData.actions().at(ii); + if(TestMouse *m = qobject_cast<TestMouse *>(o)) + d->toPost << m; + else if(TestKey *k = qobject_cast<TestKey *>(o)) + d->toPost << k; + } + } +} + +QFxTestEngine::~QFxTestEngine() +{ + delete d; d = 0; +} + +void QFxTestEngine::framePainted() +{ + QImage img = d->canvas->asImage(); + + if(d->fullFrame) { + d->fullFrame = false; + d->recordFullFrameEvent(img); + } else { + d->recordFrameEvent(img); + } +} + +void QFxTestEnginePrivate::recordFullFrameEvent(const QImage &img) +{ + TestFullFrame *ff = new TestFullFrame(q); + ff->setTime(QUnifiedTimer::instance()->elapsedTime()); + ff->setFrameId(fullFrames.count()); + + fullFrames << img; + testData.actions() << ff; + + if(testMode == QFxTestEngine::PlaybackTest) { + TestFullFrame *pf = qobject_cast<TestFullFrame *>(playbackTestData.next()); + QString filename = testDirectory + QLatin1String("/image") + QString::number(pf->frameId()) + QLatin1String(".png"); + QImage recImg(filename); + if(!pf || !compare(recImg, img) || pf->time() != QUnifiedTimer::instance()->elapsedTime()) { + message(Fail, "FFrame mismatch"); + } else { + message(Success, "FFrame OK"); + } + + testPass(); + } +} + +static QByteArray toHex(uchar c) +{ + QByteArray rv; + uint h = c / 16; + uint l = c % 16; + if(h >= 10) + rv.append(h - 10 + 'A'); + else + rv.append(h + '0'); + if(l >= 10) + rv.append(l - 10 + 'A'); + else + rv.append(l + '0'); + return rv; +} + +void QFxTestEnginePrivate::recordFrameEvent(const QImage &img) +{ + QCryptographicHash hash(QCryptographicHash::Md5); + + hash.addData((const char *)img.bits(), img.bytesPerLine() * img.height()); + + QByteArray result = hash.result(); + QByteArray hexResult; + for(int ii = 0; ii < result.count(); ++ii) + hexResult.append(toHex(result.at(ii))); + + TestFrame *f = new TestFrame(q); + f->setTime(QUnifiedTimer::instance()->elapsedTime()); + + f->setHash(QLatin1String(hexResult)); + testData.actions() << f; + if(testMode == QFxTestEngine::PlaybackTest) { + TestObject *o = playbackTestData.next(); + TestFrame *f = qobject_cast<TestFrame *>(o); + if(!f || f->time() != QUnifiedTimer::instance()->elapsedTime() || + f->hash() != QLatin1String(hexResult)) { + mismatchedFrames++; + if(mismatchedFrames > MAX_MISMATCHED_FRAMES || + lastFrameMismatch) + message(Fail, "Frame mismatch"); + else + message(Success, "Frame mismatch - within tolerance"); + lastFrameMismatch = true; + } else { + message(Success, "Frame OK"); + lastFrameMismatch = false; + } + + testPass(); + } +} + +void QFxTestEnginePrivate::updateCurrentTime(int) +{ + if(status != Working) + return; + + while(!toPost.isEmpty()) { + int t = QUnifiedTimer::instance()->elapsedTime(); + TestObject *o = toPost.first(); + if(testMode == QFxTestEngine::RecordTest) + o->setTime(t); + else if(o->time() != t) + return; + toPost.takeFirst(); + + if(TestMouse *m = qobject_cast<TestMouse *>(o)) { + QMouseEvent e((QEvent::Type)m->type(), m->pos(), m->globalPos(), (Qt::MouseButton)m->button(), (Qt::MouseButtons)m->buttons(), (Qt::KeyboardModifiers)0); + postedEvents.insert(&e); + QApplication::sendEvent(canvas, &e); + } else if(TestKey *k = qobject_cast<TestKey *>(o)) { + QKeyEvent e((QEvent::Type)k->type(), k->key(), (Qt::KeyboardModifiers)k->modifiers(), k->text()); + postedEvents.insert(&e); + QApplication::sendEvent(canvas, &e); + } + } +} + +bool QFxTestEngine::eventFilter(QObject *, QEvent *event) +{ + if(d->status != QFxTestEnginePrivate::Working) + return false; + + if(event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonDblClick || + event->type() == QEvent::MouseButtonRelease || + event->type() == QEvent::MouseMove) { + if(d->testMode == RecordTest && d->postedEvents.contains(event)) { + d->postedEvents.remove(event); + } else { + d->recordMouseEvent(static_cast<QMouseEvent *>(event)); + return d->testMode == RecordTest; + } + } else if(event->type() == QEvent::KeyPress || + event->type() == QEvent::KeyRelease) { + QKeyEvent *key = static_cast<QKeyEvent *>(event); + if(key->key() < Qt::Key_F1 || key->key() > Qt::Key_F9) { + + if(d->testMode == RecordTest && d->postedEvents.contains(event)) { + d->postedEvents.remove(event); + } else { + d->recordKeyEvent(key); + return d->testMode == RecordTest; + } + + } + } + + return false; +} + +void QFxTestEnginePrivate::recordMouseEvent(QMouseEvent *e) +{ + TestMouse *m = new TestMouse(q); + m->setTime(QUnifiedTimer::instance()->elapsedTime()); + m->setType(e->type()); + m->setButton(e->button()); + m->setButtons(e->buttons()); + m->setGlobalPos(e->globalPos()); + m->setPos(e->pos()); + testData.actions() << m; + + if(testMode == QFxTestEngine::PlaybackTest) { + TestMouse *m = qobject_cast<TestMouse *>(playbackTestData.next()); + if(!m || m->time() != QUnifiedTimer::instance()->elapsedTime() || + m->type() != e->type() || + m->button() != e->button() || + m->buttons() != e->buttons() || + m->globalPos() != e->globalPos() || + m->pos() != e->pos()) + message(Fail, "Mouse mismatch"); + else + message(Success, "Mouse OK"); + + testPass(); + } else { + toPost << m; + } + +} + +void QFxTestEnginePrivate::recordKeyEvent(QKeyEvent *e) +{ + TestKey *k = new TestKey(q); + k->setTime(QUnifiedTimer::instance()->elapsedTime()); + k->setType(e->type()); + k->setModifiers(e->QInputEvent::modifiers()); + k->setText(e->text()); + k->setKey(e->key()); + testData.actions() << k; + if(testMode == QFxTestEngine::PlaybackTest) { + TestKey *f = qobject_cast<TestKey *>(playbackTestData.next()); + if(!f || f->time() != QUnifiedTimer::instance()->elapsedTime() || + f->type() != e->type() || + f->modifiers() != e->QInputEvent::modifiers() || + f->text() != e->text() || + f->key() != e->key()) + message(Fail, "Key mismatch"); + else + message(Success, "Key OK"); + + testPass(); + } else { + toPost << k; + } +} + +void QFxTestEngine::captureFullFrame() +{ + d->fullFrame = true; +} + +void QFxTestEnginePrivate::message(MessageType t, const char *message) +{ + if(exitOnFail) + qWarning("%s", message); + if(t == Fail) { + if(exitOnFail) { + save(QLatin1String("manifest-fail.xml"), false); + qFatal("Failed"); + } else { + status = Failed; + } + } +} + +void QFxTestEnginePrivate::save(const QString &filename, bool images) +{ + qWarning() << "QFxTestEngine: Writing test data"; + + QFile manifest(testDirectory + QLatin1String("/") + filename); + manifest.open(QIODevice::WriteOnly); + testData.save(&manifest); + manifest.close(); + + if(images) { + for(int ii = 0; ii < fullFrames.count(); ++ii) + fullFrames.at(ii).save(testDirectory + QLatin1String("/image") + QString::number(ii) + QLatin1String(".png")); + } +} + +void QFxTestEngine::save() +{ + if(d->testMode != RecordTest) + return; + + d->save(QLatin1String("manifest.xml")); +} + +void QFxTestEnginePrivate::testPass() +{ + if(playbackTestData.atEnd()) { + qWarning("Test PASSED"); + if(exitOnFail) { + save(QLatin1String("manifest-play.xml")); + exit(0); + } else { + status = Passed; + } + } +} + +bool QFxTestEngine::runTest() +{ + d->exitOnFail = false; + while(d->status == QFxTestEnginePrivate::Working) + QApplication::processEvents(); + d->exitOnFail = true; + qWarning() << d->status; + return d->status == QFxTestEnginePrivate::Passed; +} + +QT_END_NAMESPACE diff --git a/src/declarative/test/qfxtestengine.h b/src/declarative/test/qfxtestengine.h new file mode 100644 index 0000000..6698645 --- /dev/null +++ b/src/declarative/test/qfxtestengine.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 QtDeclarative 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 _QFXTESTENGINE_H_ +#define _QFXTESTENGINE_H_ + +#include <QObject> +class QSimpleCanvas; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QFxTestEnginePrivate; +class Q_DECLARATIVE_EXPORT QFxTestEngine : public QObject +{ +Q_OBJECT +public: + enum TestMode { NoTest, RecordTest, PlaybackTest }; + + QFxTestEngine(TestMode, const QString &, + QSimpleCanvas *canvas, QObject * = 0); + virtual ~QFxTestEngine(); + + void captureFullFrame(); + void save(); + + bool runTest(); + +protected: + virtual bool eventFilter(QObject *, QEvent *); + +private Q_SLOTS: + void framePainted(); + +private: + QFxTestEnginePrivate *d; +}; + +#endif // _QFXTESTENGINE_H_ + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/test/qfxtestobjects.cpp b/src/declarative/test/qfxtestobjects.cpp new file mode 100644 index 0000000..e93b9ed --- /dev/null +++ b/src/declarative/test/qfxtestobjects.cpp @@ -0,0 +1,348 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qfxtestobjects.h" +#include <qml.h> +#include <QIODevice> + + +QT_BEGIN_NAMESPACE +QML_DECLARE_TYPE(TestObject); +QML_DEFINE_TYPE(TestObject,TestObject); +QML_DECLARE_TYPE(TestFrame); +QML_DEFINE_TYPE(TestFrame,TestFrame); +QML_DECLARE_TYPE(TestFullFrame); +QML_DEFINE_TYPE(TestFullFrame,TestFullFrame); +QML_DECLARE_TYPE(TestMouse); +QML_DEFINE_TYPE(TestMouse,TestMouse); +QML_DECLARE_TYPE(TestKey); +QML_DEFINE_TYPE(TestKey,TestKey); +QML_DECLARE_TYPE(TestLog); +QML_DEFINE_TYPE(TestLog,TestLog); + +TestObject::TestObject(QObject *parent) +: QObject(parent), _time(-1) +{ +} + +int TestObject::time() const +{ + return _time; +} + +void TestObject::setTime(int t) +{ + if(t == _time) + return; + _time = t; + emit dataChanged(); +} + +void TestObject::save(QXmlStreamWriter *device) +{ + device->writeStartElement(QLatin1String("TestObject")); + device->writeAttribute(QLatin1String("time"), QString::number(time())); + device->writeEndElement(); +} + + +TestFrame::TestFrame(QObject *parent) +: TestObject(parent) +{ +} + +QString TestFrame::hash() const +{ + return _hash; +} + +void TestFrame::setHash(const QString &h) +{ + if(_hash == h) + return; + _hash = h; + emit frameChanged(); +} + +void TestFrame::save(QXmlStreamWriter *device) +{ + device->writeStartElement(QLatin1String("TestFrame")); + device->writeAttribute(QLatin1String("time"), QLatin1String(QByteArray::number(time()))); + device->writeAttribute(QLatin1String("hash"), hash()); + device->writeEndElement(); +} + +TestFullFrame::TestFullFrame(QObject *parent) +: TestObject(parent), _frameId(-1) +{ +} + +int TestFullFrame::frameId() const +{ + return _frameId; +} + +void TestFullFrame::setFrameId(int id) +{ + if(id == _frameId) + return; + _frameId = id; + emit frameChanged(); +} + +void TestFullFrame::save(QXmlStreamWriter *device) +{ + device->writeStartElement(QLatin1String("TestFullFrame")); + device->writeAttribute(QLatin1String("time"), QLatin1String(QByteArray::number(time()))); + device->writeAttribute(QLatin1String("frameId"), QLatin1String(QByteArray::number(frameId()))); + device->writeEndElement(); +} + +TestMouse::TestMouse(QObject *parent) +: TestObject(parent), _type(-1), _button(-1), _buttons(-1) +{ +} + +int TestMouse::type() const +{ + return _type; +} + +void TestMouse::setType(int t) +{ + if(_type == t) + return; + _type = t; + emit mouseChanged(); +} + +int TestMouse::button() const +{ + return _button; +} + +void TestMouse::setButton(int b) +{ + if(b == _button) + return; + _button = b; + emit mouseChanged(); +} + +int TestMouse::buttons() const +{ + return _buttons; +} + +void TestMouse::setButtons(int buttons) +{ + if(_buttons == buttons) + return; + _buttons = buttons; + emit mouseChanged(); +} + +QPoint TestMouse::globalPos() const +{ + return _globalPos; +} + +void TestMouse::setGlobalPos(const QPoint &g) +{ + if(_globalPos == g) + return; + _globalPos = g; + emit mouseChanged(); +} + +QPoint TestMouse::pos() const +{ + return _pos; +} + +void TestMouse::setPos(const QPoint &p) +{ + if(p == _pos) + return; + _pos = p; + emit mouseChanged(); +} + +void TestMouse::save(QXmlStreamWriter *device) +{ + device->writeStartElement(QLatin1String("TestMouse")); + device->writeAttribute(QLatin1String("time"), QString::number(time())); + device->writeAttribute(QLatin1String("type"), QString::number(type())); + device->writeAttribute(QLatin1String("button"), QString::number(button())); + device->writeAttribute(QLatin1String("buttons"), QString::number(buttons())); + device->writeAttribute(QLatin1String("globalPos"), QString::number(globalPos().x()) + QLatin1String(",") + QString::number(globalPos().y())); + device->writeAttribute(QLatin1String("pos"), QString::number(pos().x()) + QLatin1String(",") + QString::number(pos().y())); + device->writeEndElement(); +} + +TestKey::TestKey(QObject *parent) +: TestObject(parent), _type(-1), _modifiers(-1), _key(-1) +{ +} + +int TestKey::type() const +{ + return _type; +} + +void TestKey::setType(int t) +{ + if(t == _type) + return; + _type = t; + emit keyChanged(); +} + +int TestKey::modifiers() const +{ + return _modifiers; +} + +void TestKey::setModifiers(int m) +{ + if(m == _modifiers) + return; + _modifiers = m; + emit keyChanged(); +} + +QString TestKey::text() const +{ + return _text; +} + +void TestKey::setText(const QString &t) +{ + if(_text == t) + return; + _text = t; + emit keyChanged(); +} + +int TestKey::key() const +{ + return _key; +} + +void TestKey::setKey(int k) +{ + if(_key == k) + return; + _key = k; + emit keyChanged(); +} + +void TestKey::save(QXmlStreamWriter *device) +{ + device->writeStartElement(QLatin1String("TestKey")); + device->writeAttribute(QLatin1String("time"), QString::number(time())); + device->writeAttribute(QLatin1String("type"), QString::number(type())); + device->writeAttribute(QLatin1String("modifiers"), QString::number(modifiers())); + device->writeAttribute(QLatin1String("key"), QString::number(key())); + if(key() != Qt::Key_Escape) + device->writeAttribute(QLatin1String("text"), text()); + device->writeEndElement(); +} + +TestLog::TestLog(QObject *parent) +: QObject(parent), _current(0) +{ +} + +QList<TestObject *> *TestLog::qmlActions() +{ + return &_actions; +} + +QList<TestObject *> &TestLog::actions() +{ + return _actions; +} + +bool lessThan(TestObject *lhs, TestObject *rhs) +{ + return lhs->time() < rhs->time(); +} + +void TestLog::save(QIODevice *device) +{ + // Order correctly + qStableSort(_actions.begin(), _actions.end(), lessThan); + + QXmlStreamWriter writer(device); + writer.setAutoFormatting(true); + writer.writeStartDocument(QLatin1String("1.0")); + writer.writeStartElement(QLatin1String("TestLog")); + for(int ii = 0; ii < _actions.count(); ++ii) + _actions.at(ii)->save(&writer); + writer.writeEndElement(); + writer.writeEndDocument(); +} + +TestObject *TestLog::next() +{ + if(atEnd()) + return 0; + TestObject *rv = _actions.at(_current); + _current++; + return rv; +} + +bool TestLog::atEnd() const +{ + if(_current >= _actions.count()) + return true; + else + return false; +} + +int TestLog::current() const +{ + return _current; +} + +QT_END_NAMESPACE diff --git a/src/declarative/test/qfxtestobjects.h b/src/declarative/test/qfxtestobjects.h new file mode 100644 index 0000000..4273d4e --- /dev/null +++ b/src/declarative/test/qfxtestobjects.h @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 _QFXTESTOBJECTS_H_ +#define _QFXTESTOBJECTS_H_ + +#include <QObject> +#include <QPoint> +#include <QList> +#include <QXmlStreamWriter> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QIODevice; +class TestObject : public QObject +{ +Q_OBJECT +public: + TestObject(QObject * = 0); + + Q_PROPERTY(int time READ time WRITE setTime NOTIFY dataChanged); + int time() const; + void setTime(int); + + virtual void save(QXmlStreamWriter *); +Q_SIGNALS: + void dataChanged(); + +private: + int _time; +}; + +class TestFrame : public TestObject +{ +Q_OBJECT +public: + TestFrame(QObject * = 0); + + Q_PROPERTY(QString hash READ hash WRITE setHash NOTIFY frameChanged); + QString hash() const; + void setHash(const QString &); + + virtual void save(QXmlStreamWriter *); +Q_SIGNALS: + void frameChanged(); + +private: + QString _hash; +}; + +class TestFullFrame : public TestObject +{ +Q_OBJECT +public: + TestFullFrame(QObject * = 0); + + Q_PROPERTY(int frameId READ frameId WRITE setFrameId NOTIFY frameChanged); + int frameId() const; + void setFrameId(int); + + virtual void save(QXmlStreamWriter *); +Q_SIGNALS: + void frameChanged(); + +private: + int _frameId; +}; + +class TestMouse : public TestObject +{ +Q_OBJECT +public: + TestMouse(QObject * = 0); + + Q_PROPERTY(int type READ type WRITE setType NOTIFY mouseChanged); + int type() const; + void setType(int); + + Q_PROPERTY(int button READ button WRITE setButton NOTIFY mouseChanged); + int button() const; + void setButton(int); + + Q_PROPERTY(int buttons READ buttons WRITE setButtons NOTIFY mouseChanged); + int buttons() const; + void setButtons(int); + + Q_PROPERTY(QPoint globalPos READ globalPos WRITE setGlobalPos NOTIFY mouseChanged); + QPoint globalPos() const; + void setGlobalPos(const QPoint &); + + Q_PROPERTY(QPoint pos READ pos WRITE setPos NOTIFY mouseChanged); + QPoint pos() const; + void setPos(const QPoint &); + + virtual void save(QXmlStreamWriter *); + +Q_SIGNALS: + void mouseChanged(); + +private: + int _type; + int _button; + int _buttons; + QPoint _globalPos; + QPoint _pos; +}; + +class TestKey : public TestObject +{ +Q_OBJECT +public: + TestKey(QObject * = 0); + + Q_PROPERTY(int type READ type WRITE setType NOTIFY keyChanged); + int type() const; + void setType(int); + + Q_PROPERTY(int modifiers READ modifiers WRITE setModifiers NOTIFY keyChanged); + int modifiers() const; + void setModifiers(int); + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY keyChanged); + QString text() const; + void setText(const QString &); + + Q_PROPERTY(int key READ key WRITE setKey NOTIFY keyChanged); + int key() const; + void setKey(int); + + virtual void save(QXmlStreamWriter *); + +Q_SIGNALS: + void keyChanged(); + +private: + int _type; + int _modifiers; + int _key; + QString _text; +}; + +class TestLog : public QObject +{ +Q_OBJECT +public: + TestLog(QObject * = 0); + + Q_CLASSINFO("DefaultProperty", "actions"); + Q_PROPERTY(QList<TestObject *> *actions READ qmlActions); + QList<TestObject *> *qmlActions(); + + QList<TestObject *> &actions(); + + int current() const; + void save(QIODevice *); + + TestObject *next(); + bool atEnd() const; + +private: + int _current; + QList<TestObject *> _actions; +}; + +#endif // _QFXTESTOBJECTS_H_ + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/test/qfxtestview.cpp b/src/declarative/test/qfxtestview.cpp new file mode 100644 index 0000000..67b6c15 --- /dev/null +++ b/src/declarative/test/qfxtestview.cpp @@ -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 QtDeclarative 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 "qfxtestview.h" +#include <QFile> +#include <QmlComponent> +#include <QFileInfo> +#include <QFxItem> +#include <QmlContext> +#include <QFxTestEngine> + + +QT_BEGIN_NAMESPACE +QFxTestView::QFxTestView(const QString &filename, const QString &testdir) +: testEngine(0) +{ + QObject::connect(this, SIGNAL(sceneResized(QSize)), + this, SLOT(setSceneSize(QSize))); + + testEngine = new QFxTestEngine(QFxTestEngine::PlaybackTest, testdir, this, this); + + QFile file(filename); + file.open(QFile::ReadOnly); + QString xml = QString::fromUtf8(file.readAll()); + setXml(xml, filename); + + execute(); +} + +void QFxTestView::setSceneSize(QSize s) +{ + setFixedSize(s); +} + +bool QFxTestView::runTest() +{ + show(); + return testEngine->runTest(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/test/qfxtestview.h b/src/declarative/test/qfxtestview.h new file mode 100644 index 0000000..33275b9 --- /dev/null +++ b/src/declarative/test/qfxtestview.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 _QFXTESTVIEW_H_ +#define _QFXTESTVIEW_H_ + +#include <QFxView> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxTestEngine; +class Q_DECLARATIVE_EXPORT QFxTestView : public QFxView +{ +Q_OBJECT +public: + QFxTestView(const QString &filename, const QString &testdir); + + bool runTest(); + +private Q_SLOTS: + void setSceneSize(QSize); + +private: + QFxTestEngine *testEngine; +}; + +#endif // _QFXTESTVIEW_H_ + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/test/test.pri b/src/declarative/test/test.pri new file mode 100644 index 0000000..eacd00f --- /dev/null +++ b/src/declarative/test/test.pri @@ -0,0 +1,9 @@ +SOURCES += \ + test/qfxtestengine.cpp \ + test/qfxtestobjects.cpp \ + test/qfxtestview.cpp + +HEADERS += \ + test/qfxtestengine.h \ + test/qfxtestobjects.h \ + test/qfxtestview.h diff --git a/src/declarative/timeline/gfxeasing.cpp b/src/declarative/timeline/gfxeasing.cpp new file mode 100644 index 0000000..c89ba88 --- /dev/null +++ b/src/declarative/timeline/gfxeasing.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 QtDeclarative 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 "gfxeasing.h" +#include <math.h> +#include <QHash> +#include <QPainter> +#include <QVariant> +#include <QDebug> +#include <QStringList> +#include <QMouseEvent> +#include "gfxtimeline.h" + + +QT_BEGIN_NAMESPACE +typedef QHash<QString, qreal> GfxEasingProperties; +class GfxEasingFunction +{ +public: + virtual ~GfxEasingFunction() {} + virtual float value(float t, float b, float c, float d) = 0; + virtual GfxEasingFunction *copy() const = 0; +}; + +#include "../3rdparty/easing.cpp" + +struct ElasticEase : public GfxEasingFunction +{ + enum Type { In, Out }; + ElasticEase(Type t) : _t(t), _p(0.0f), _a(0.0f) {} + + Type _t; + qreal _p; + qreal _a; + + GfxEasingFunction *copy() const + { + ElasticEase *rv = new ElasticEase(_t); + rv->_p = _p; + rv->_a = _a; + return rv; + } + + float value(float t, float b, float c, float d) + { + if (t==0) return b; + float t_adj = (float)t / (float)d; + if (t_adj==1) return b+c; + + qreal p = _p?_p:(d * 0.3f); + + qreal a; + qreal s; + + if(!_a || _a < ::fabs(c)) { + a = c; + s = p / 4.0f; + } else { + a = _a; + s = p / (2 * M_PI) * ::asin(c / a); + } + + if(_t == In) + t_adj -= 1.0f; + + return (a*::pow(2,-10*t_adj) * ::sin( (t_adj*d-s)*(2*M_PI)/p ) + c + b); + } +}; + +struct BounceEase : public GfxEasingFunction +{ + enum Type { In, Out }; + BounceEase(Type t) : _t(t), _a(-1.0) {} + + Type _t; + qreal _a; + + GfxEasingFunction *copy() const + { + BounceEase *rv = new BounceEase(_t); + rv->_t = _t; + rv->_a = _a; + return rv; + } + + float value(float t, float b, float c, float d) + { + if(In == _t) + return c - bounce(d - t, 0, c, d) + b; + else + return bounce(t, b, c, d); + } + + float bounce(float t, float b, float c, float d) + { + float t_adj = (float)t / (float)d; + float amp = (_a == -1.0)?c:_a; + if ((t_adj) < (1/2.75)) { + if(c == 0. && _a != -1.0) { + t_adj -= (0.5f/2.75f); + return -amp * (1. - (30.25*t_adj*t_adj)) + b; + } else { + return c*(7.5625*t_adj*t_adj) + b; + } + } else if (t_adj < (2/2.75)) { + t_adj -= (1.5f/2.75f); + return -amp * (1. - (7.5625*t_adj*t_adj + .75)) + (b + c); + } else if (t_adj < (2.5/2.75)) { + t_adj -= (2.25f/2.75f); + return -amp * (1. - (7.5625*t_adj*t_adj + .9375)) + (b + c); + } else { + t_adj -= (2.65f/2.75f); + return -amp * (1. - (7.5625*t_adj*t_adj + .984375)) + (b + c); + } + } +}; + +static GfxEasingFunction *easeInElasticC(const GfxEasingProperties &p) +{ + ElasticEase *rv = new ElasticEase(ElasticEase::In); + rv->_p = p[QLatin1String("period")]; + rv->_a = p[QLatin1String("amplitude")]; + return rv; +} + + +static GfxEasingFunction *easeOutElasticC(const GfxEasingProperties &p) +{ + ElasticEase *rv = new ElasticEase(ElasticEase::Out); + rv->_p = p[QLatin1String("period")]; + rv->_a = p[QLatin1String("amplitude")]; + return rv; +} + +static GfxEasingFunction *easeOutBounceC(const GfxEasingProperties &p) +{ + BounceEase *rv = new BounceEase(BounceEase::Out); + rv->_a = p[QLatin1String("amplitude")]; + return rv; +} + +static GfxEasingFunction *easeInBounceC(const GfxEasingProperties &p) +{ + BounceEase *rv = new BounceEase(BounceEase::Out); + rv->_a = p[QLatin1String("amplitude")]; + return rv; +} + + +struct SimpleConfig : public GfxEasingFunction +{ + GfxEasing::Function func; + + float value(float t, float b, float c, float d) + { + return func(t, b, c, d); + } + + GfxEasingFunction *copy() const + { + SimpleConfig *rv = new SimpleConfig; + rv->func = func; + return rv; + } +}; + +GfxEasing::Function curveToFunc(GfxEasing::Curve curve) +{ + switch(curve) + { + case GfxEasing::None: + return &easeNone; + case GfxEasing::InQuad: + return &easeInQuad; + case GfxEasing::OutQuad: + return &easeOutQuad; + case GfxEasing::InOutQuad: + return &easeInOutQuad; + case GfxEasing::OutInQuad: + return &easeOutInQuad; + case GfxEasing::InCubic: + return &easeInCubic; + case GfxEasing::OutCubic: + return &easeOutCubic; + case GfxEasing::InOutCubic: + return &easeInOutCubic; + case GfxEasing::OutInCubic: + return &easeOutInCubic; + case GfxEasing::InQuart: + return &easeInQuart; + case GfxEasing::OutQuart: + return &easeOutQuart; + case GfxEasing::InOutQuart: + return &easeInOutQuart; + case GfxEasing::OutInQuart: + return &easeOutInQuart; + case GfxEasing::InQuint: + return &easeInQuint; + case GfxEasing::OutQuint: + return &easeOutQuint; + case GfxEasing::InOutQuint: + return &easeInOutQuint; + case GfxEasing::OutInQuint: + return &easeOutInQuint; + case GfxEasing::InSine: + return &easeInSine; + case GfxEasing::OutSine: + return &easeOutSine; + case GfxEasing::InOutSine: + return &easeInOutSine; + case GfxEasing::OutInSine: + return &easeOutInSine; + case GfxEasing::InExpo: + return &easeInExpo; + case GfxEasing::OutExpo: + return &easeOutExpo; + case GfxEasing::InOutExpo: + return &easeInOutExpo; + case GfxEasing::OutInExpo: + return &easeOutInExpo; + case GfxEasing::InCirc: + return &easeInCirc; + case GfxEasing::OutCirc: + return &easeOutCirc; + case GfxEasing::InOutCirc: + return &easeInOutCirc; + case GfxEasing::OutInCirc: + return &easeOutInCirc; + case GfxEasing::InElastic: + return &easeInElastic; + case GfxEasing::OutElastic: + return &easeOutElastic; + case GfxEasing::InOutElastic: + return &easeInOutElastic; + case GfxEasing::OutInElastic: + return &easeOutInElastic; + case GfxEasing::InBack: + return &easeInBack; + case GfxEasing::OutBack: + return &easeOutBack; + case GfxEasing::InOutBack: + return &easeInOutBack; + case GfxEasing::OutInBack: + return &easeOutInBack; + case GfxEasing::InBounce: + return &easeInBounce; + case GfxEasing::OutBounce: + return &easeOutBounce; + case GfxEasing::InOutBounce: + return &easeInOutBounce; + case GfxEasing::OutInBounce: + return &easeOutInBounce; + default: + return 0; + }; +} + +struct NameFunctionMap : public QHash<QString, GfxEasing::Function> +{ + NameFunctionMap() + { + insert(QLatin1String("easeNone"), easeNone); + insert(QLatin1String("easeInQuad"), easeInQuad); + insert(QLatin1String("easeOutQuad"), easeOutQuad); + insert(QLatin1String("easeInOutQuad"), easeInOutQuad); + insert(QLatin1String("easeOutInQuad"), easeOutInQuad); + insert(QLatin1String("easeInCubic"), easeInCubic); + insert(QLatin1String("easeOutCubic"), easeOutCubic); + insert(QLatin1String("easeInOutCubic"), easeInOutCubic); + insert(QLatin1String("easeOutInCubic"), easeOutInCubic); + insert(QLatin1String("easeInQuart"), easeInQuart); + insert(QLatin1String("easeOutQuart"), easeOutQuart); + insert(QLatin1String("easeInOutQuart"), easeInOutQuart); + insert(QLatin1String("easeOutInQuart"), easeOutInQuart); + insert(QLatin1String("easeInQuint"), easeInQuint); + insert(QLatin1String("easeOutQuint"), easeOutQuint); + insert(QLatin1String("easeInOutQuint"), easeInOutQuint); + insert(QLatin1String("easeOutInQuint"), easeOutInQuint); + insert(QLatin1String("easeInSine"), easeInSine); + insert(QLatin1String("easeOutSine"), easeOutSine); + insert(QLatin1String("easeInOutSine"), easeInOutSine); + insert(QLatin1String("easeOutInSine"), easeOutInSine); + insert(QLatin1String("easeInExpo"), easeInExpo); + insert(QLatin1String("easeOutExpo"), easeOutExpo); + insert(QLatin1String("easeInOutExpo"), easeInOutExpo); + insert(QLatin1String("easeOutInExpo"), easeOutInExpo); + insert(QLatin1String("easeInCirc"), easeInCirc); + insert(QLatin1String("easeOutCirc"), easeOutCirc); + insert(QLatin1String("easeInOutCirc"), easeInOutCirc); + insert(QLatin1String("easeOutInCirc"), easeOutInCirc); + insert(QLatin1String("easeInElastic"), easeInElastic); + insert(QLatin1String("easeOutElastic"), easeOutElastic); + insert(QLatin1String("easeInOutElastic"), easeInOutElastic); + insert(QLatin1String("easeOutInElastic"), easeOutInElastic); + insert(QLatin1String("easeInBack"), easeInBack); + insert(QLatin1String("easeOutBack"), easeOutBack); + insert(QLatin1String("easeInOutBack"), easeInOutBack); + insert(QLatin1String("easeOutInBack"), easeOutInBack); + insert(QLatin1String("easeInBounce"), easeInBounce); + insert(QLatin1String("easeOutBounce"), easeOutBounce); + insert(QLatin1String("easeInOutBounce"), easeInOutBounce); + insert(QLatin1String("easeOutInBounce"), easeOutInBounce); + } +}; +Q_GLOBAL_STATIC(NameFunctionMap, nameFunctionMap); + +typedef GfxEasingFunction *(*ConfigurableFunction)(const GfxEasingProperties &); +struct ConfigFunctionMap : public QHash<QString, ConfigurableFunction> +{ + ConfigFunctionMap() + { + insert(QLatin1String("easeInElastic"), easeInElasticC); + insert(QLatin1String("easeOutElastic"), easeOutElasticC); + insert(QLatin1String("easeInBounce"), easeInBounceC); + insert(QLatin1String("easeOutBounce"), easeOutBounceC); + } +}; +Q_GLOBAL_STATIC(ConfigFunctionMap, configFunctionMap); + +/*! + \class GfxEasing + \ingroup animation + \brief The GfxEasing class provides easing curves for controlling animation. + + Easing curves describe a function that controls how a value changes over + time. Easing curves allow transitions from one value to another to appear + more natural than a simple linear motion would allow. The GfxEasing class + is usually used in conjunction with the GfxTimeLine class, but can be used + on its own. + + To calculate the value at a given time, the easing curve function requires + the starting value, the final value and the total time to change from the + starting to the final value. When using the GfxEasing class with + GfxTimeLine, these values are supplied by the GfxTimeLine. When using + the GfxEasing class on its own, the programmer must specify them using + the GfxEasing::setFrom(), GfxEasing::setTo() and GfxEasing::setLength() + methods, or by passing them explicitly to the GfxEasing::valueAt() method. + + For example, + \code + GfxEasing easing(GfxEasing::InOutQuad); + easing.setFrom(0); + easing.setTo(1000); + easing.setLength(1000); + + for(int milliseconds = 0; milliseconds < 1000; ++milliseconds) + qWarning() << "Value at" << milliseconds << "milliseconds is + << easing.valueAt(milliseconds); + \endcode + will print the value at each millisecond for an InOutQuad transition from + 0 to 1000 over 1 second. + + When using a GfxTimeLine, the values are communicated implicitly. + \code + GfxTimeLine timeline; + GfxValue value(0); + + timeline.move(value, 1000, GfxEasing(GfxEasing::InOutQuad), 1000); + \endcode + In this case, any values set using the previous setter methods would be + ignored. + */ + +/*! + \qmlproperty string NumericAnimation::easing + \brief the easing curve used for the transition. + + Available values are: + + \list + \i \e easeNone - Easing equation function for a simple linear tweening, with no easing. + \i \e easeInQuad - Easing equation function for a quadratic (t^2) easing in: accelerating from zero velocity. + \i \e easeOutQuad - Easing equation function for a quadratic (t^2) easing out: decelerating to zero velocity. + \i \e easeInOutQuad - Easing equation function for a quadratic (t^2) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInQuad - Easing equation function for a quadratic (t^2) easing out/in: deceleration until halfway, then acceleration. + \i \e easeInCubic - Easing equation function for a cubic (t^3) easing in: accelerating from zero velocity. + \i \e easeOutCubic - Easing equation function for a cubic (t^3) easing out: decelerating from zero velocity. + \i \e easeInOutCubic - Easing equation function for a cubic (t^3) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInCubic - Easing equation function for a cubic (t^3) easing out/in: deceleration until halfway, then acceleration. + \i \e easeInQuart - Easing equation function for a quartic (t^4) easing in: accelerating from zero velocity. + \i \e easeOutQuart - Easing equation function for a quartic (t^4) easing out: decelerating from zero velocity. + \i \e easeInOutQuart - Easing equation function for a quartic (t^4) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInQuart - Easing equation function for a quartic (t^4) easing out/in: deceleration until halfway, then acceleration. + \i \e easeInQuint - Easing equation function for a quintic (t^5) easing in: accelerating from zero velocity. + \i \e easeOutQuint - Easing equation function for a quintic (t^5) easing out: decelerating from zero velocity. + \i \e easeInOutQuint - Easing equation function for a quintic (t^5) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInQuint - Easing equation function for a quintic (t^5) easing out/in: deceleration until halfway, then acceleration. + \i \e easeInSine - Easing equation function for a sinusoidal (sin(t)) easing in: accelerating from zero velocity. + \i \e easeOutSine - Easing equation function for a sinusoidal (sin(t)) easing out: decelerating from zero velocity. + \i \e easeInOutSine - Easing equation function for a sinusoidal (sin(t)) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInSine - Easing equation function for a sinusoidal (sin(t)) easing out/in: deceleration until halfway, then acceleration. + \i \e easeInExpo - Easing equation function for an exponential (2^t) easing in: accelerating from zero velocity. + \i \e easeOutExpo - Easing equation function for an exponential (2^t) easing out: decelerating from zero velocity. + \i \e easeInOutExpo - Easing equation function for an exponential (2^t) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInExpo - Easing equation function for an exponential (2^t) easing out/in: deceleration until halfway, then acceleration. + \i \e easeInCirc - Easing equation function for a circular (sqrt(1-t^2)) easing in: accelerating from zero velocity. + \i \e easeOutCirc - Easing equation function for a circular (sqrt(1-t^2)) easing out: decelerating from zero velocity. + \i \e easeInOutCirc - Easing equation function for a circular (sqrt(1-t^2)) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInCirc - Easing equation function for a circular (sqrt(1-t^2)) easing out/in: deceleration until halfway, then acceleration. + \i \e easeInElastic - Easing equation function for an elastic (exponentially decaying sine wave) easing in: accelerating from zero velocity. The peak amplitude can be set with the \i amplitude parameter, and the period of decay by the \i period parameter. + \i \e easeOutElastic - Easing equation function for an elastic (exponentially decaying sine wave) easing out: decelerating from zero velocity. The peak amplitude can be set with the \i amplitude parameter, and the period of decay by the \i period parameter. + \i \e easeInOutElastic - Easing equation function for an elastic (exponentially decaying sine wave) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInElastic - Easing equation function for an elastic (exponentially decaying sine wave) easing out/in: deceleration until halfway, then acceleration. + \i \e easeInBack - Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. + \i \e easeOutBack - Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out: decelerating from zero velocity. + \i \e easeInOutBack - Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInBack - Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. + \i \e easeOutBounce - Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out: decelerating from zero velocity. + \i \e easeInBounce - Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in: accelerating from zero velocity. + \i \e easeInOutBounce - Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in/out: acceleration until halfway, then deceleration. + \i \e easeOutInBounce - Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out/in: deceleration until halfway, then acceleration. + \endlist +*/ + +/*! + \enum GfxEasing::Curve + + The type of easing curve. +*/ +/*! + \var GfxEasing::Curve GfxEasing::None + Easing equation function for a simple linear tweening, with no easing. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InQuad + Easing equation function for a quadratic (t^2) easing in: accelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutQuad + Easing equation function for a quadratic (t^2) easing out: decelerating to zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutQuad + Easing equation function for a quadratic (t^2) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInQuad + Easing equation function for a quadratic (t^2) easing out/in: deceleration until halfway, then acceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InCubic + Easing equation function for a cubic (t^3) easing in: accelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutCubic + Easing equation function for a cubic (t^3) easing out: decelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutCubic + Easing equation function for a cubic (t^3) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInCubic + Easing equation function for a cubic (t^3) easing out/in: deceleration until halfway, then acceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InQuart + Easing equation function for a quartic (t^4) easing in: accelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutQuart + Easing equation function for a quartic (t^4) easing out: decelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutQuart + Easing equation function for a quartic (t^4) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInQuart + Easing equation function for a quartic (t^4) easing out/in: deceleration until halfway, then acceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InQuint + Easing equation function for a quintic (t^5) easing in: accelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutQuint + Easing equation function for a quintic (t^5) easing out: decelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutQuint + Easing equation function for a quintic (t^5) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInQuint + Easing equation function for a quintic (t^5) easing out/in: deceleration until halfway, then acceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InSine + Easing equation function for a sinusoidal (sin(t)) easing in: accelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutSine + Easing equation function for a sinusoidal (sin(t)) easing out: decelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutSine + Easing equation function for a sinusoidal (sin(t)) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInSine + Easing equation function for a sinusoidal (sin(t)) easing out/in: deceleration until halfway, then acceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InExpo + Easing equation function for an exponential (2^t) easing in: accelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutExpo + Easing equation function for an exponential (2^t) easing out: decelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutExpo + Easing equation function for an exponential (2^t) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInExpo + Easing equation function for an exponential (2^t) easing out/in: deceleration until halfway, then acceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InCirc + Easing equation function for a circular (sqrt(1-t^2)) easing in: accelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutCirc + Easing equation function for a circular (sqrt(1-t^2)) easing out: decelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutCirc + Easing equation function for a circular (sqrt(1-t^2)) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInCirc + Easing equation function for a circular (sqrt(1-t^2)) easing out/in: deceleration until halfway, then acceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InElastic + Easing equation function for an elastic (exponentially decaying sine wave) easing in: accelerating from zero velocity. The peak amplitude can be set with the \i amplitude parameter, and the period of decay by the \i period parameter. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutElastic + Easing equation function for an elastic (exponentially decaying sine wave) easing out: decelerating from zero velocity. The peak amplitude can be set with the \i amplitude parameter, and the period of decay by the \i period parameter. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutElastic + Easing equation function for an elastic (exponentially decaying sine wave) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInElastic + Easing equation function for an elastic (exponentially decaying sine wave) easing out/in: deceleration until halfway, then acceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InBack + Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutBack + Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out: decelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutBack + Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInBack + Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutBounce + Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out: decelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InBounce + Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in: accelerating from zero velocity. +*/ +/*! + \var GfxEasing::Curve GfxEasing::InOutBounce + Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in/out: acceleration until halfway, then deceleration. +*/ +/*! + \var GfxEasing::Curve GfxEasing::OutInBounce + Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out/in: deceleration until halfway, then acceleration. +*/ + +/*! + Construct a linear easing object. This is equivalent to \c {GfxEasing(GfxEasing::None)}. + */ +GfxEasing::GfxEasing() +: _config(0), _func(&easeNone), _b(0.), _c(1.), _d(1.) +{ +} + +/*! + Construct an easing object with the given \a curve. + */ +GfxEasing::GfxEasing(Curve curve) +: _config(0), _func(0), _b(0.), _c(1.), _d(1.) +{ + _func = curveToFunc(curve); + if(!_func) { + qWarning("GfxEasing: Invalid curve type %d", curve); + _func = &easeNone; + } +} + +/*! + Construct an easing object with the given \a curve. If \a curve does not + describe a legal curve, a linear easing object is constructed. + + Curve names have the form + \c {ease<CurveName>[(<arg>: <arg value>[, <arg2>: <arg value>])]}. The + \i CurveName is equivalent to the GfxEasing::Curve enum name. Some more + advanced curves can take arguments to further refine their behaviour. Where + applicable, these parameters are described in the corresponding + GfxEasing::Curve value documentation. + + For example, + \code + GfxEasing easing("easeInOutQuad"); + GfxEasing easing2("easeInElastic(period: 5, amplitude: 100)"); + \endcode + */ +GfxEasing::GfxEasing(const QString &curve) +: _config(0), _func(&easeNone), _b(0.), _c(1.), _d(1.) +{ + if(curve.contains(QLatin1Char('('))) { + QString easeName = curve.trimmed(); + GfxEasingProperties prop; + if(!easeName.endsWith(QLatin1Char(')'))) { + qWarning("GfxEasing: Unmatched perenthesis in easing function '%s'", + curve.toLatin1().constData()); + return; + } + + int idx = easeName.indexOf(QLatin1Char('(')); + QString prop_str = + easeName.mid(idx + 1, easeName.length() - 1 - idx - 1); + easeName = easeName.left(idx); + + QStringList props = prop_str.split(QLatin1Char(',')); + foreach(QString str, props) { + int sep = str.indexOf(QLatin1Char(':')); + + if(sep == -1) { + qWarning("GfxEasing: Improperly specified property in easing function '%s'", + curve.toLatin1().constData()); + return; + } + + QString propName = str.left(sep).trimmed(); + bool isOk; + qreal propValue = str.mid(sep + 1).trimmed().toDouble(&isOk); + + if(propName.isEmpty() || !isOk) { + qWarning("GfxEasing: Improperly specified property in easing function '%s'", + curve.toLatin1().constData()); + return; + } + + prop.insert(propName, propValue); + } + + QHash<QString, ConfigurableFunction>::Iterator iter = + configFunctionMap()->find(easeName); + + if(iter != configFunctionMap()->end()) + _config = (*iter)(prop); + } else { + if(nameFunctionMap()->contains(curve)) + _func = *(nameFunctionMap()->find(curve)); + } + + if(!_func && !_config) { + qWarning("GfxEasing: Unknown easing curve '%s'", + curve.toLatin1().constData()); + _func = &easeNone; + } +} + +/*! + Construct a copy of \a other. + */ +GfxEasing::GfxEasing(const GfxEasing &other) +: _config(0), _func(other._func), _b(other._b), _c(other._c), _d(other._d) +{ + if(other._config) _config = other._config->copy(); +} + +/*! + Copy \a other. + */ +GfxEasing &GfxEasing::operator=(const GfxEasing &other) +{ + if(_config) { delete _config; _config = 0; } + if(other._config) _config = other._config->copy(); + + _func = other._func; + _b = other._b; + _c = other._c; + _d = other._d; + + return *this; +} + +/*! + Returns true if this is a linear easing object. + */ +bool GfxEasing::isLinear() const +{ + return !_config && _func == &easeNone; +} + +/*! + Return the starting value for the easing curve. By default this is 0. + */ +qreal GfxEasing::from() const +{ + return _b; +} + +/*! + Set the starting value for the easing curve to \a from. + */ +void GfxEasing::setFrom(qreal from) +{ + _b = from; +} + +/*! + Return the final value for the easing curve. By default this is 0. + */ +qreal GfxEasing::to() const +{ + return _c; +} + +/*! + Set the final value for the easing curve to \a to. + */ +void GfxEasing::setTo(qreal to) +{ + _c = to; +} + +/*! + Return the length of the easing curve, in milliseconds. By default this + is 1. + */ +qreal GfxEasing::length() const +{ + return _d; +} + +/*! + Set the \a length of the easing curve, in milliseconds. + */ +void GfxEasing::setLength(qreal length) +{ + Q_ASSERT(length > 0); + _d = length; +} + +/*! + Return the value for the easing curve at time \a t milliseconds, based on + the parameters returned by GfxEasing::from(), GfxEasing::to() and + GfxEasing::length(). + + \a t is clamped to (0, GfxEasing::length()). + */ +qreal GfxEasing::valueAt(qreal t) const +{ + if(t < 0) t = 0; + else if(t > length()) t = length(); + + if(_config) + return _config->value(t, _b, _c - _b, _d); + else + return _func(t, _b, _c - _b, _d); +} + +/* + Return the value for the easing curve at time \a t milliseconds, based on + the provided parameters \a from, \a to and \a length. The values returned + from the object's GfxEasing::from(), GfxEasing::to() and GfxEasing::length() + methods are ignored. + + \a t is clamped to (0, \a length). + */ +qreal GfxEasing::valueAt(qreal t, qreal from, qreal to, qreal length) const +{ + if(t < 0) t = 0; + else if(t > length) t = length; + + if(_config) + return _config->value(t, from, to - from, length); + else + return _func(t, from, to - from, length); +} + +/*! + \internal + */ +QStringList GfxEasing::curves() +{ + return nameFunctionMap()->keys(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/timeline/gfxeasing.h b/src/declarative/timeline/gfxeasing.h new file mode 100644 index 0000000..c15aadc --- /dev/null +++ b/src/declarative/timeline/gfxeasing.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 GFXEASING_H +#define GFXEASING_H + +#include <qfxglobal.h> +#include <QList> +#include <QPair> +#include <QWidget> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class GfxEasingFunction; +class Q_DECLARATIVE_EXPORT GfxEasing +{ +public: + enum Curve { + None, + 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 + }; + + GfxEasing(); + GfxEasing(Curve); + GfxEasing(const QString &); + GfxEasing(const GfxEasing &); + GfxEasing &operator=(const GfxEasing &); + + bool isLinear() const; + + qreal from() const; + void setFrom(qreal); + qreal to() const; + void setTo(qreal); + qreal length() const; + void setLength(qreal); + + qreal valueAt(qreal t) const; + qreal valueAt(qreal t, qreal from, qreal to, qreal length) const; + + static QStringList curves(); + + typedef float (*Function)(float t, float b, float c, float d); +private: + GfxEasingFunction *_config; + Function _func; + + qreal _b, _c, _d; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/timeline/gfxtimeline.cpp b/src/declarative/timeline/gfxtimeline.cpp new file mode 100644 index 0000000..36b18d0 --- /dev/null +++ b/src/declarative/timeline/gfxtimeline.cpp @@ -0,0 +1,946 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "gfxtimeline.h" +#include <QDebug> +#include <QMutex> +#include <QThread> +#include <QWaitCondition> +#include <QEvent> +#include <QCoreApplication> +#include "gfxeasing.h" +#include <QTime> + +QT_BEGIN_NAMESPACE + +#define TIMETICK 5 + +// +// Timeline stuff +// + +struct Update { + Update(GfxValue *_g, qreal _v) + : g(_g), v(_v) {} + Update(const GfxEvent &_e) + : g(0), v(0), e(_e) {} + + GfxValue *g; + qreal v; + GfxEvent e; +}; + +struct QmlTimeLinePrivate +{ + QmlTimeLinePrivate(QmlTimeLine *); + + struct Op { + enum Type { + Pause, + Set, + Move, + MoveBy, + Accel, + AccelDistance, + Execute + }; + Op() {} + Op(Type t, int l, qreal v, qreal v2, int o, + const GfxEvent &ev = GfxEvent(), const GfxEasing &es = GfxEasing()) + : type(t), length(l), value(v), value2(v2), order(o), event(ev), + easing(es) {} + Op(const Op &o) + : type(o.type), length(o.length), value(o.value), value2(o.value2), + order(o.order), event(o.event), easing(o.easing) {} + Op &operator=(const Op &o) { + type = o.type; length = o.length; value = o.value; + value2 = o.value2; order = o.order; event = o.event; + easing = o.easing; + return *this; + } + + Type type; + int length; + qreal value; + qreal value2; + + int order; + GfxEvent event; + GfxEasing easing; + }; + struct TimeLine + { + TimeLine() : length(0), consumedOpLength(0), base(0.) {} + QList<Op> ops; + int length; + int consumedOpLength; + qreal base; + }; + + int length; + int syncPoint; + typedef QHash<QmlTimeLineObject *, TimeLine> Ops; + Ops ops; + QmlTimeLine *q; + + void add(QmlTimeLineObject &, const Op &); + qreal value(const Op &op, int time, qreal base, bool *) const; + + int advance(int); + + bool clockRunning; + int prevTime; + + int order; + + QmlTimeLine::SyncMode syncMode; + int syncAdj; + QList<QPair<int, Update> > *updateQueue; +}; + +QmlTimeLinePrivate::QmlTimeLinePrivate(QmlTimeLine *parent) +: length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QmlTimeLine::LocalSync), syncAdj(0), updateQueue(0) +{ +} + +void QmlTimeLinePrivate::add(QmlTimeLineObject &g, const Op &o) +{ + if(g._t && g._t != q) { + qWarning() << "QmlTimeLine: Cannot modify a GfxValue owned by" + << "another timeline."; + return; + } + g._t = q; + + Ops::Iterator iter = ops.find(&g); + if(iter == ops.end()) { + iter = ops.insert(&g, TimeLine()); + if(syncPoint > 0) + q->pause(g, syncPoint); + } + if(!iter->ops.isEmpty() && + o.type == Op::Pause && + iter->ops.last().type == Op::Pause) { + iter->ops.last().length += o.length; + iter->length += o.length; + } else { + iter->ops.append(o); + iter->length += o.length; + } + + if(iter->length > length) + length = iter->length; + + if(!clockRunning) { + q->stop(); + prevTime = 0; + clockRunning = true; + + if(syncMode == QmlTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + q->start(); +/* q->tick(0); + if(syncMode == QmlTimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + */ + } +} + +qreal QmlTimeLinePrivate::value(const Op &op, int time, qreal base, bool *changed) const +{ + Q_ASSERT(time >= 0); + Q_ASSERT(time <= op.length); + *changed = true; + + switch(op.type) { + case Op::Pause: + *changed = false; + return base; + case Op::Set: + return op.value; + case Op::Move: + if(time == 0) { + return base; + } else if(time == (op.length)) { + return op.value; + } else { + if(op.easing.isLinear()) { + qreal delta = op.value - base; + qreal pTime = (qreal)(time) / (qreal)op.length; + return base + delta * pTime; + } else { + return op.easing.valueAt(time, base, op.value, op.length); + } + } + case Op::MoveBy: + if(time == 0) { + return base; + } else if(time == (op.length)) { + return base + op.value; + } else { + if(op.easing.isLinear()) { + qreal delta = op.value; + qreal pTime = (qreal)(time) / (qreal)op.length; + return base + delta * pTime; + } else { + return op.easing.valueAt(time, base, base + op.value, op.length); + } + } + case Op::Accel: + if(time == 0) { + return base; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal delta = op.value * t + 0.5f * op.value2 * t * t; + return base + delta; + } + case Op::AccelDistance: + if(time == 0) { + return base; + } else if(time == (op.length)) { + return base + op.value2; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal accel = -1.0f * 1000.0f * op.value / (qreal)op.length; + qreal delta = op.value * t + 0.5f * accel * t * t; + return base + delta; + + } + case Op::Execute: + op.event.execute(); + *changed = false; + return -1; + } + + return base; +} + +/*! + \class QmlTimeLine + \ingroup animation + \brief The QmlTimeLine class provides a timeline for controlling animations. + + QmlTimeLine is similar to QTimeLine except: + \list + \i It updates GfxValue instances directly, rather than maintaining a single + current value. + + For example, the following animates a simple value over 200 milliseconds: + \code + GfxValue v(<starting value>); + QmlTimeLine tl; + tl.move(v, 100., 200); + tl.start() + \endcode + + If your program needs to know when values are changed, it can either + connect to the QmlTimeLine's updated() signal, or inherit from GfxValue + and reimplement the GfxValue::setValue() method. + + \i Supports multiple GfxValue, arbitrary start and end values and allows + animations to be strung together for more complex effects. + + For example, the following animation moves the x and y coordinates of + an object from wherever they are to the position (100, 100) in 50 + milliseconds and then further animates them to (100, 200) in 50 + milliseconds: + + \code + GfxValue x(<starting value>); + GfxValue y(<starting value>); + + QmlTimeLine tl; + tl.start(); + + tl.move(x, 100., 50); + tl.move(y, 100., 50); + tl.move(y, 200., 50); + \endcode + + \i All QmlTimeLine instances share a single, synchronized clock. + + Actions scheduled within the same event loop tick are scheduled + synchronously against each other, regardless of the wall time between the + scheduling. Synchronized scheduling applies both to within the same + QmlTimeLine and across separate QmlTimeLine's within the same process. + + \endlist + + Currently easing functions are not supported. +*/ + + +/*! + Construct a new QmlTimeLine with the specified \a parent. +*/ +QmlTimeLine::QmlTimeLine(QObject *parent) +: QAbstractAnimation(parent) +{ + d = new QmlTimeLinePrivate(this); +} + +/*! + Destroys the time line. Any inprogress animations are canceled, but not + completed. +*/ +QmlTimeLine::~QmlTimeLine() +{ + for(QmlTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + iter.key()->_t = 0; + + delete d; d = 0; +} + +/*! + \enum QmlTimeLine::SyncMode + */ + +/*! + Return the timeline's synchronization mode. + */ +QmlTimeLine::SyncMode QmlTimeLine::syncMode() const +{ + return d->syncMode; +} + +/*! + Set the timeline's synchronization mode to \a syncMode. + */ +void QmlTimeLine::setSyncMode(SyncMode syncMode) +{ + d->syncMode = syncMode; +} + +/*! + Pause \a gfxValue for \a time milliseconds. +*/ +void QmlTimeLine::pause(QmlTimeLineObject &obj, int time) +{ + if(time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Pause, time, 0., 0., d->order++); + d->add(obj, op); +} + +/*! + Execute the \a event. + */ +void QmlTimeLine::execute(const GfxEvent &event) +{ + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, event); + d->add(*event.eventObject(), op); +} + +/*! + Set the \a value of \a gfxValue. +*/ +void QmlTimeLine::set(GfxValue &gfxValue, qreal value) +{ + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Set, 0, value, 0., d->order++); + d->add(gfxValue, op); +} + +/*! + Decelerate \a gfxValue from the starting \a velocity to zero at the + given \a acceleration rate. Although the \a acceleration is technically + a deceleration, it should always be positive. The QmlTimeLine will ensure + that the deceleration is in the opposite direction to the initial velocity. +*/ +int QmlTimeLine::accel(GfxValue &gfxValue, qreal velocity, qreal acceleration) +{ + if((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast<int>(-1000 * velocity / acceleration); + + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(gfxValue, op); + + return time; +} + +/*! + \overload + + Decelerate \a gfxValue from the starting \a velocity to zero at the + given \a acceleration rate over a maximum distance of maxDistance. + + If necessary, QmlTimeLine will reduce the acceleration to ensure that the + entire operation does not require a move of more than \a maxDistance. + \a maxDistance should always be positive. +*/ +int QmlTimeLine::accel(GfxValue &gfxValue, qreal velocity, qreal acceleration, qreal maxDistance) +{ + Q_ASSERT(acceleration >= 0.0f && maxDistance >= 0.0f); + + qreal maxAccel = (velocity * velocity) / (2.0f * maxDistance); + if(maxAccel > acceleration) + acceleration = maxAccel; + + if((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast<int>(-1000 * velocity / acceleration); + + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(gfxValue, op); + + return time; +} + +/*! + Decelerate \a gfxValue from the starting \a velocity to zero over the given + \a distance. This is like accel(), but the QmlTimeLine calculates the exact + deceleration to use. + + \a distance should be positive. +*/ +int QmlTimeLine::accelDistance(GfxValue &gfxValue, qreal velocity, qreal distance) +{ + if (distance == 0.0f || velocity == 0.0f) + return -1; + Q_ASSERT((distance >= 0.0f) == (velocity >= 0.0f)); + + int time = static_cast<int>(1000 * (2.0f * distance) / velocity); + + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::AccelDistance, time, velocity, distance, d->order++); + d->add(gfxValue, op); + + return time; +} + +/*! + Linearly change the \a gfxValue from its current value to the given + \a destination value over \a time milliseconds. +*/ +void QmlTimeLine::move(GfxValue &gfxValue, qreal destination, int time) +{ + if(time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++); + d->add(gfxValue, op); +} + +/*! + Change the \a gfxValue from its current value to the given \a destination + value over \a time milliseconds using the \a easing curve. + */ +void QmlTimeLine::move(GfxValue &gfxValue, qreal destination, const GfxEasing &easing, int time) +{ + if(time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, GfxEvent(), easing); + d->add(gfxValue, op); +} + +/*! + Linearly change the \a gfxValue from its current value by the \a change amount + over \a time milliseconds. +*/ +void QmlTimeLine::moveBy(GfxValue &gfxValue, qreal change, int time) +{ + if(time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++); + d->add(gfxValue, op); +} + +/*! + Change the \a gfxValue from its current value by the \a change amount over + \a time milliseconds using the \a easing curve. + */ +void QmlTimeLine::moveBy(GfxValue &gfxValue, qreal change, const GfxEasing &easing, int time) +{ + if(time <= 0) return; + QmlTimeLinePrivate::Op op(QmlTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, GfxEvent(), easing); + d->add(gfxValue, op); +} + +/*! + Cancel (but don't complete) all scheduled actions for \a gfxValue. +*/ +void QmlTimeLine::reset(GfxValue &gfxValue) +{ + if(!gfxValue._t) + return; + if(gfxValue._t != this) { + qWarning() << "QmlTimeLine: Cannot reset a GfxValue owned by another timeline."; + return; + } + remove(&gfxValue); + gfxValue._t = 0; +} + +int QmlTimeLine::duration() const +{ + return -1; +} + +/*! + Synchronize the end point of \a gfxValue to the endpoint of \a syncTo + within this timeline. + + Following operations on \a gfxValue in this timeline will be scheduled after + all the currently scheduled actions on \a syncTo are complete. In + psuedo-code this is equivalent to: + \code + QmlTimeLine::pause(gfxValue, min(0, length_of(syncTo) - length_of(gfxValue))) + \endcode +*/ +void QmlTimeLine::sync(GfxValue &gfxValue, GfxValue &syncTo) +{ + QmlTimeLinePrivate::Ops::Iterator iter = d->ops.find(&syncTo); + if(iter == d->ops.end()) + return; + int length = iter->length; + + iter = d->ops.find(&gfxValue); + if(iter == d->ops.end()) { + pause(gfxValue, length); + } else { + int glength = iter->length; + pause(gfxValue, length - glength); + } +} + +/*! + Synchronize the end point of \a gfxValue to the endpoint of the longest + action currently scheduled in the timeline. + + In psuedo-code, this is equivalent to: + \code + QmlTimeLine::pause(gfxValue, length_of(timeline) - length_of(gfxValue)) + \endcode +*/ +void QmlTimeLine::sync(GfxValue &gfxValue) +{ + QmlTimeLinePrivate::Ops::Iterator iter = d->ops.find(&gfxValue); + if(iter == d->ops.end()) { + pause(gfxValue, d->length); + } else { + pause(gfxValue, d->length - iter->length); + } +} + +/*! + Synchronize all currently and future scheduled values in this timeline to + the longest action currently scheduled. + + For example: + \code + value1->setValue(0.); + value2->setValue(0.); + value3->setValue(0.); + QmlTimeLine tl; + ... + tl.move(value1, 10, 200); + tl.move(value2, 10, 100); + tl.sync(); + tl.move(value2, 20, 100); + tl.move(value3, 20, 100); + \endcode + + will result in: + + \table + \header \o \o 0ms \o 50ms \o 100ms \o 150ms \o 200ms \o 250ms \o 300ms + \row \o value1 \o 0 \o 2.5 \o 5.0 \o 7.5 \o 10 \o 10 \o 10 + \row \o value2 \o 0 \o 5.0 \o 10.0 \o 10.0 \o 10.0 \o 15.0 \o 20.0 + \row \o value2 \o 0 \o 0 \o 0 \o 0 \o 0 \o 10.0 \o 20.0 + \endtable +*/ + +/*void QmlTimeLine::sync() +{ + for(QmlTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + pause(*iter.key(), d->length - iter->length); + d->syncPoint = d->length; +}*/ + +/*! + \internal + + Temporary hack. + */ +void QmlTimeLine::setSyncPoint(int sp) +{ + d->syncPoint = sp; +} + +/*! + \internal + + Temporary hack. + */ +int QmlTimeLine::syncPoint() const +{ + return d->syncPoint; +} + +/*! + Returns true if the timeline is active. An active timeline is one where + GfxValue actions are still pending. +*/ +bool QmlTimeLine::isActive() const +{ + return !d->ops.isEmpty(); +} + +/*! + Completes the timeline. All queued actions are played to completion, and then discarded. For example, + \code + GfxValue v(0.); + QmlTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.complete(); + // v.value() == 100. + \endcode +*/ +void QmlTimeLine::complete() +{ + d->advance(d->length); +} + +/*! + Resets the timeline. All queued actions are discarded and GfxValue's retain their current value. For example, + \code + GfxValue v(0.); + QmlTimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.clear(); + // v.value() == 50. + \endcode +*/ +void QmlTimeLine::clear() +{ + for(QmlTimeLinePrivate::Ops::ConstIterator iter = d->ops.begin(); iter != d->ops.end(); ++iter) + iter.key()->_t = 0; + d->ops.clear(); + d->length = 0; + d->syncPoint = 0; + //XXX need stop here? +} + +int QmlTimeLine::time() const +{ + return d->prevTime; +} + +/*! + \fn void QmlTimeLine::updated() + + Emitted each time the timeline modifies GfxValues. Even if multiple + GfxValues are changed, this signal is only emitted once for each clock tick. +*/ + +void QmlTimeLine::updateCurrentTime(int v) +{ + if(d->syncAdj == -1) + d->syncAdj = v; + v -= d->syncAdj; + + int timeChanged = v - d->prevTime; +#if 0 + if(!timeChanged) + return; +#endif + d->prevTime = v; + d->advance(timeChanged); + emit updated(); + + // Do we need to stop the clock? + if(d->ops.isEmpty()) { + stop(); + d->prevTime = 0; + d->clockRunning = false; + emit completed(); + } /*else if(pauseTime > 0) { + GfxClock::cancelClock(); + d->prevTime = 0; + GfxClock::pauseFor(pauseTime); + d->syncAdj = 0; + d->clockRunning = false; + }*/ else if(/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + d->syncAdj = 0; + start(); + } +} + +bool operator<(const QPair<int, Update> &lhs, + const QPair<int, Update> &rhs) +{ + return lhs.first < rhs.first; +} + +int QmlTimeLinePrivate::advance(int t) +{ + int pauseTime = -1; + + // XXX - surely there is a more efficient way? + do { + pauseTime = -1; + // Minimal advance time + int advanceTime = t; + for(Ops::Iterator iter = ops.begin(); iter != ops.end(); ++iter) { + TimeLine &tl = *iter; + Op &op = tl.ops.first(); + int length = op.length - tl.consumedOpLength; + + if(length < advanceTime) { + advanceTime = length; + if(advanceTime == 0) + break; + } + } + t -= advanceTime; + + // Process until then. A zero length advance time will only process + // sets. + QList<QPair<int, Update> > updates; + + for(Ops::Iterator iter = ops.begin(); iter != ops.end(); ) { + GfxValue *v = static_cast<GfxValue *>(iter.key()); + TimeLine &tl = *iter; + Q_ASSERT(!tl.ops.isEmpty()); + + do { + Op &op = tl.ops.first(); + if(advanceTime == 0 && op.length != 0) + continue; + + if(tl.consumedOpLength == 0 && + op.type != Op::Pause && + op.type != Op::Execute) + tl.base = v->value(); + + if((tl.consumedOpLength + advanceTime) == op.length) { + if(op.type == Op::Execute) { + updates << qMakePair(op.order, Update(op.event)); + } else { + bool changed = false; + qreal val = value(op, op.length, tl.base, &changed); + if(changed) + updates << qMakePair(op.order, Update(v, val)); + } + tl.length -= qMin(advanceTime, tl.length); + tl.consumedOpLength = 0; + tl.ops.removeFirst(); + } else { + tl.consumedOpLength += advanceTime; + bool changed = false; + qreal val = value(op, tl.consumedOpLength, tl.base, &changed); + if(changed) + updates << qMakePair(op.order, Update(v, val)); + tl.length -= qMin(advanceTime, tl.length); + break; + } + + } while(!tl.ops.isEmpty() && advanceTime == 0 && tl.ops.first().length == 0); + + + if(tl.ops.isEmpty()) { + iter = ops.erase(iter); + v->_t = 0; + } else { + if(tl.ops.first().type == Op::Pause && pauseTime != 0) { + int opPauseTime = tl.ops.first().length - tl.consumedOpLength; + if(pauseTime == -1 || opPauseTime < pauseTime) + pauseTime = opPauseTime; + } else { + pauseTime = 0; + } + ++iter; + } + } + + length -= qMin(length, advanceTime); + syncPoint -= advanceTime; + + qSort(updates.begin(), updates.end()); + updateQueue = &updates; + for(int ii = 0; ii < updates.count(); ++ii) { + const Update &v = updates.at(ii).second; + if(v.g) + v.g->setValue(v.v); + else + v.e.execute(); + } + updateQueue = 0; + } while(t); + + return pauseTime; +} + +void QmlTimeLine::remove(QmlTimeLineObject *v) +{ + QmlTimeLinePrivate::Ops::Iterator iter = d->ops.find(v); + Q_ASSERT(iter != d->ops.end()); + + int len = iter->length; + d->ops.erase(iter); + if(len == d->length) { + // We need to recalculate the length + d->length = 0; + for(QmlTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) { + + if(iter->length > d->length) + d->length = iter->length; + + } + } + if(d->ops.isEmpty()) { + stop(); + d->clockRunning = false; + } else if(/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + + if(d->syncMode == QmlTimeLine::LocalSync) { + d->syncAdj = -1; + } else { + d->syncAdj = 0; + } + start(); + } + + if(d->updateQueue) { + for(int ii = 0; ii < d->updateQueue->count(); ++ii) { + if(d->updateQueue->at(ii).second.g == v || + d->updateQueue->at(ii).second.e.eventObject() == v) { + d->updateQueue->removeAt(ii); + --ii; + } + } + } + + +} + +/*! + \class GfxValue + \ingroup animation + \brief The GfxValue class is modified by QmlTimeLine. +*/ + +/*! + \fn GfxValue::GfxValue(qreal value = 0) + + Construct a new GfxValue with an initial \a value. +*/ + +/*! + \fn qreal GfxValue::value() const + + Return the current value. +*/ + +/*! + \fn void GfxValue::setValue(qreal value) + + Set the current \a value. +*/ + +/*! + \fn QmlTimeLine *GfxValue::timeLine() const + + If a QmlTimeLine is operating on this value, return a pointer to it, + otherwise return null. +*/ + + +QmlTimeLineObject::QmlTimeLineObject() +: _t(0) +{ +} + +QmlTimeLineObject::~QmlTimeLineObject() +{ + if(_t) { + _t->remove(this); + _t = 0; + } +} + +GfxEvent::GfxEvent() +: d0(0), d1(0), d2(0) +{ +} + +GfxEvent::GfxEvent(const GfxEvent &o) +: d0(o.d0), d1(o.d1), d2(o.d2) +{ +} + +GfxEvent &GfxEvent::operator=(const GfxEvent &o) +{ + d0 = o.d0; + d1 = o.d1; + d2 = o.d2; + return *this; +} + +void GfxEvent::execute() const +{ + d0(d1); +} + +QmlTimeLineObject *GfxEvent::eventObject() const +{ + return d2; +} + +QT_END_NAMESPACE diff --git a/src/declarative/timeline/gfxtimeline.h b/src/declarative/timeline/gfxtimeline.h new file mode 100644 index 0000000..0bdddaf --- /dev/null +++ b/src/declarative/timeline/gfxtimeline.h @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 GFXTIMELINE_H +#define GFXTIMELINE_H + +#include <QObject> +#include <qfxglobal.h> +#include <QAbstractAnimation> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class GfxEasing; +class GfxValue; +class GfxEvent; +struct QmlTimeLinePrivate; +class QmlTimeLineObject; +class Q_DECLARATIVE_EXPORT QmlTimeLine : public QAbstractAnimation +{ +Q_OBJECT +public: + QmlTimeLine(QObject *parent = 0); + ~QmlTimeLine(); + + enum SyncMode { LocalSync, GlobalSync }; + SyncMode syncMode() const; + void setSyncMode(SyncMode); + + void pause(QmlTimeLineObject &, int); + void execute(const GfxEvent &); + void set(GfxValue &, qreal); + + int accel(GfxValue &, qreal velocity, qreal accel); + int accel(GfxValue &, qreal velocity, qreal accel, qreal maxDistance); + int accelDistance(GfxValue &, qreal velocity, qreal distance); + + void move(GfxValue &, qreal destination, int time = 500); + void move(GfxValue &, qreal destination, const GfxEasing &, int time = 500); + void moveBy(GfxValue &, qreal change, int time = 500); + void moveBy(GfxValue &, qreal change, const GfxEasing &, int time = 500); + + void sync(); + void setSyncPoint(int); + int syncPoint() const; + + void sync(GfxValue &); + void sync(GfxValue &, GfxValue &); + + void reset(GfxValue &); + + void complete(); + void clear(); + bool isActive() const; + + int time() const; + + virtual int duration() const; +Q_SIGNALS: + void updated(); + void completed(); + +protected: + virtual void updateCurrentTime(int); + +private: + void remove(QmlTimeLineObject *); + friend class QmlTimeLineObject; + friend struct QmlTimeLinePrivate; + QmlTimeLinePrivate *d; +}; + +class Q_DECLARATIVE_EXPORT QmlTimeLineObject +{ +public: + QmlTimeLineObject(); + virtual ~QmlTimeLineObject(); + +protected: + friend class QmlTimeLine; + friend struct QmlTimeLinePrivate; + QmlTimeLine *_t; +}; + +class Q_DECLARATIVE_EXPORT GfxValue : public QmlTimeLineObject +{ +public: + GfxValue(qreal v = 0.) : _v(v) {} + + qreal value() const { return _v; } + virtual void setValue(qreal v) { _v = v; } + + QmlTimeLine *timeLine() const { return _t; } + + operator qreal() const { return _v; } + GfxValue &operator=(qreal v) { setValue(v); return *this; } +private: + friend class QmlTimeLine; + friend struct QmlTimeLinePrivate; + qreal _v; +}; + +class Q_DECLARATIVE_EXPORT GfxEvent +{ +public: + GfxEvent(); + GfxEvent(const GfxEvent &o); + + template<class T, void (T::*method)()> + GfxEvent(QmlTimeLineObject *b, T *c) + { + d0 = &callFunc<T, method>; + d1 = (void *)c; + d2 = b; + } + + template<class T, void (T::*method)()> + static GfxEvent gfxEvent(QmlTimeLineObject *b, T *c) + { + GfxEvent rv; + rv.d0 = &callFunc<T, method>; + rv.d1 = (void *)c; + rv.d2 = b; + return rv; + } + + GfxEvent &operator=(const GfxEvent &o); + void execute() const; + QmlTimeLineObject *eventObject() const; + +private: + typedef void (*CallFunc)(void *c); + + template <class T, void (T::*method)()> + static void callFunc(void *c) + { + T *cls = (T *)c; + (cls->*method)(); + } + CallFunc d0; + void *d1; + QmlTimeLineObject *d2; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/timeline/gfxvalueproxy.h b/src/declarative/timeline/gfxvalueproxy.h new file mode 100644 index 0000000..1eaf44e --- /dev/null +++ b/src/declarative/timeline/gfxvalueproxy.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 QtDeclarative 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 GFXVALUEPROXY_H +#define GFXVALUEPROXY_H +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +template<class T> +class GfxValueProxy : public GfxValue +{ +public: + GfxValueProxy(T *cls, void (T::*func)(qreal), qreal v = 0.) + : GfxValue(v), _class(cls), _setFunctionReal(func), _setFunctionInt(0) + { + Q_ASSERT(_class); + } + + GfxValueProxy(T *cls, void (T::*func)(int), qreal v = 0.) + : GfxValue(v), _class(cls), _setFunctionReal(0), _setFunctionInt(func) + { + Q_ASSERT(_class); + } + + virtual void setValue(qreal v) + { + GfxValue::setValue(v); + if(_setFunctionReal) (_class->*_setFunctionReal)(v); + else if(_setFunctionInt) (_class->*_setFunctionInt)((int)v); + } + +private: + T *_class; + void (T::*_setFunctionReal)(qreal); + void (T::*_setFunctionInt)(int); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif//GFXVALUEPROXY diff --git a/src/declarative/timeline/timeline.pri b/src/declarative/timeline/timeline.pri new file mode 100644 index 0000000..8994c78 --- /dev/null +++ b/src/declarative/timeline/timeline.pri @@ -0,0 +1,9 @@ +SOURCES += \ + timeline/gfxeasing.cpp \ + timeline/gfxtimeline.cpp \ + +HEADERS += \ + timeline/gfxeasing.h \ + timeline/gfxtimeline.h \ + timeline/gfxvalueproxy.h \ + diff --git a/src/declarative/util/qbindablemap.cpp b/src/declarative/util/qbindablemap.cpp new file mode 100644 index 0000000..aea2a2c --- /dev/null +++ b/src/declarative/util/qbindablemap.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qbindablemap.h" +#include <qmlopenmetaobject.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +//QBindableMapMetaObject lets us listen for changes coming from QML +//so we can emit the changed signal. +class QBindableMapMetaObject : public QmlOpenMetaObject +{ +public: + QBindableMapMetaObject(QBindableMap *obj) : QmlOpenMetaObject(obj) + { + map = obj; + } + +protected: + virtual void propertyWrite(int index) + { + map->emitChanged(QLatin1String(name(index))); + } + +private: + QBindableMap *map; +}; + +/*! + \class QBindableMap + \brief The QBindableMap class allows you to set key-value pairs that can be used in bindings. + + QBindableMap provides a convenient way to expose domain data to the UI layer. + The following example shows how you might declare data in C++ and then + access it in QML. + + Setup in C++: + \code + //create our data + QBindableMap ownerData; + ownerData.setValue("name", QVariant(QString("John Smith"))); + ownerData.setValue("phone", QVariant(QString("555-5555"))); + + //expose it to the UI layer + QmlContext *ctxt = view->bindContext(); + ctxt->setProperty("owner", &data); + \endcode + + Then, in QML: + \code + <Text text="{owner.name}"/> + <Text text="{owner.phone}"/> + \endcode + + The binding is dynamic - whenever a key's value is updated, anything bound to that + key will be updated as well. + + To detect value changes made in the UI layer you can connect to the changed() signal. + However, note that changed() is \b NOT emitted when changes are made by calling setValue() + or clearValue() - it is only emitted when a value is updated from QML. +*/ + +// is there a more efficient way to store/return keys? +// (or should we just provide an iterator or something else instead?) +// can we provide a way to clear keys? +// do we want to make any claims regarding key ordering? +// should we have signals for insertion and and deletion -- becoming more model like +// should we emit change for our own changes as well? +// Bug or Feature?: values can be created in QML (owner.somethingElse = "Hello") will create somethingElse property. (need to verify if this is actually the case) +// Bug or Feature?: all values are read-write (there are no read-only values) + +/*! + Constructs a bindable map with parent object \a parent. +*/ +QBindableMap::QBindableMap(QObject *parent) +: QObject(parent) +{ + m_mo = new QBindableMapMetaObject(this); +} + +QBindableMap::~QBindableMap() +{ +} + +/*! + Clears the value (if any) associated with \a key. +*/ +void QBindableMap::clearValue(const QString &key) +{ + //m_keys.remove(); //### + m_mo->setValue(key.toLatin1(), QVariant()); + //emit changed(key); +} + +/*! + Returns the value associated with \a key. + + If no value has been set for this key (or if the value has been cleared), + an invalid QVariant is returned. +*/ +QVariant QBindableMap::value(const QString &key) const +{ + return m_mo->value(key.toLatin1()); +} + +/*! + Sets the value associated with \a key to \a value. + + If the key doesn't exist, it is automatically created. +*/ +void QBindableMap::setValue(const QString &key, QVariant value) +{ + if (!m_keys.contains(key)) + m_keys.append(key); + m_mo->setValue(key.toLatin1(), value); + //emit changed(key); +} + +/*! + Returns the list of keys. + + Keys that have been cleared will still appear in this list, even though their + associated values are invalid QVariants. +*/ +QStringList QBindableMap::keys() const +{ + return m_keys; +} + +/*! + \fn void QBindableMap::changed(const QString &key) + This signal is emitted whenever one of the values in the map is changed. \a key + is the key corresponding to the value that was changed. + */ + +void QBindableMap::emitChanged(const QString &key) +{ + emit changed(key); +} +QT_END_NAMESPACE diff --git a/src/declarative/util/qbindablemap.h b/src/declarative/util/qbindablemap.h new file mode 100644 index 0000000..d617867 --- /dev/null +++ b/src/declarative/util/qbindablemap.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 QtDeclarative 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 QBINDABLEMAP_H +#define QBINDABLEMAP_H + +#include <qfxglobal.h> +#include <QObject> +#include <QHash> +#include <QStringList> +#include <QVariant> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QBindableMapMetaObject; +class Q_DECLARATIVE_EXPORT QBindableMap : public QObject +{ + Q_OBJECT +public: + QBindableMap(QObject *parent = 0); + virtual ~QBindableMap(); + + QVariant value(const QString &key) const; + void setValue(const QString &key, QVariant value); + void clearValue(const QString &key); + + Q_INVOKABLE QStringList keys() const; + +Q_SIGNALS: + void changed(const QString &key); + +private: + Q_DISABLE_COPY(QBindableMap) + void emitChanged(const QString &key); + QBindableMapMetaObject *m_mo; + QStringList m_keys; + friend class QBindableMapMetaObject; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/util/qfxglobal.h b/src/declarative/util/qfxglobal.h new file mode 100644 index 0000000..52cf021 --- /dev/null +++ b/src/declarative/util/qfxglobal.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QFXGLOBAL_H +#define QFXGLOBAL_H + +#include <qglobal.h> +#include <QObject> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +#if defined(QT_OPENGL_ES_1) +#define QFX_CONFIGURATION_OPENGL1 +#elif defined(QT_OPENGL_ES_2) +#define QFX_CONFIGURATION_OPENGL2 +#else +#define QFX_CONFIGURATION_SOFTWARE +#endif + +/* + The choices of renderer are: + QFX_RENDER_QPAINTER + QFX_RENDER_OPENGL1 + QFX_RENDER_OPENGL2 + To simplify code, if either of the OpenGL renderers are used, + QFX_RENDER_OPENGL is also defined. +*/ + +#if defined(QFX_CONFIGURATION_OPENGL2) + +#define QFX_RENDER_OPENGL +#define QFX_RENDER_OPENGL2 + +#elif defined(QFX_CONFIGURATION_OPENGL1) + +#define QFX_RENDER_OPENGL +#define QFX_RENDER_OPENGL1 + +#elif defined(QFX_CONFIGURATION_SOFTWARE) + +#define QFX_RENDER_QPAINTER + +#endif + +#define DEFINE_BOOL_CONFIG_OPTION(name, var) \ + static bool name() \ + { \ + static enum { Yes, No, Unknown } status = Unknown; \ + if(status == Unknown) { \ + QByteArray v = qgetenv(#var); \ + bool value = !v.isEmpty() && v != "0" && v != "false"; \ + if(value) status = Yes; \ + else status = No; \ + } \ + return status == Yes; \ + } + +struct QFx_DerivedObject : public QObject +{ + void setParent_noEvent(QObject *parent) { + bool sce = d_ptr->sendChildEvents; + d_ptr->sendChildEvents = false; + setParent(parent); + d_ptr->sendChildEvents = sce; + } +}; + +/*! + Makes the \a object a child of \a parent. Note that when using this method, + neither \a parent nor the object's previous parent (if it had one) will + receive ChildRemoved or ChildAdded events. +*/ +inline void QFx_setParent_noEvent(QObject *object, QObject *parent) +{ + static_cast<QFx_DerivedObject *>(object)->setParent_noEvent(parent); +} + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QFXGLOBAL_H diff --git a/src/declarative/util/qfxperf.cpp b/src/declarative/util/qfxperf.cpp new file mode 100644 index 0000000..5ce8646 --- /dev/null +++ b/src/declarative/util/qfxperf.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 QtDeclarative 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 "qfxperf.h" + + +QT_BEGIN_NAMESPACE +Q_DEFINE_PERFORMANCE_LOG(QFxPerf, "QFx") { + Q_DEFINE_PERFORMANCE_METRIC(XmlParsing, "XML Parsing"); + Q_DEFINE_PERFORMANCE_METRIC(Compile, "XML Compilation"); + Q_DEFINE_PERFORMANCE_METRIC(CompileRun, "XML Compilation Run"); + Q_DEFINE_PERFORMANCE_METRIC(CssParsing, "CSS Parsing"); + Q_DEFINE_PERFORMANCE_METRIC(CreateComponent, "Component creation"); + Q_DEFINE_PERFORMANCE_METRIC(BindInit, "BindValue Initialization"); + Q_DEFINE_PERFORMANCE_METRIC(BindCompile, "BindValue compile"); + Q_DEFINE_PERFORMANCE_METRIC(BindValue, "BindValue execution"); + Q_DEFINE_PERFORMANCE_METRIC(BindValueSSE, "BindValue execution SSE"); + Q_DEFINE_PERFORMANCE_METRIC(BindValueQt, "BindValue execution QtScript"); + Q_DEFINE_PERFORMANCE_METRIC(BindableValueUpdate, "QmlBindableValue::update"); + Q_DEFINE_PERFORMANCE_METRIC(PixmapLoad, "Pixmap loading"); + Q_DEFINE_PERFORMANCE_METRIC(MetaProperty, "Meta property resolution"); + Q_DEFINE_PERFORMANCE_METRIC(PathCache, "Path cache"); + Q_DEFINE_PERFORMANCE_METRIC(CreateParticle, "Particle creation"); + Q_DEFINE_PERFORMANCE_METRIC(FontDatabase, "Font database creation"); + Q_DEFINE_PERFORMANCE_METRIC(ItemComponentComplete, "QFxItem::componentComplete"); + Q_DEFINE_PERFORMANCE_METRIC(ImageComponentComplete, "QFxImage::componentComplete"); + Q_DEFINE_PERFORMANCE_METRIC(ComponentInstanceComponentComplete, "QFxComponentInstance::componentComplete"); + Q_DEFINE_PERFORMANCE_METRIC(BaseLayoutComponentComplete, "QFxBaseLayout::componentComplete"); + Q_DEFINE_PERFORMANCE_METRIC(TextComponentComplete, "QFxText::componentComplete"); + Q_DEFINE_PERFORMANCE_METRIC(ContextQuery, "QtScript: Query Context"); + Q_DEFINE_PERFORMANCE_METRIC(ContextProperty, "QtScript: Context Property"); + Q_DEFINE_PERFORMANCE_METRIC(ObjectQuery, "QtScript: Query Object"); + Q_DEFINE_PERFORMANCE_METRIC(ObjectProperty, "QtScript: Object Property"); + Q_DEFINE_PERFORMANCE_METRIC(ObjectSetProperty, "QtScript: Set Object Property"); + Q_DEFINE_PERFORMANCE_METRIC(QFxText_setText, "QFxText::setText"); +} +QT_END_NAMESPACE diff --git a/src/declarative/util/qfxperf.h b/src/declarative/util/qfxperf.h new file mode 100644 index 0000000..b1f9bd0 --- /dev/null +++ b/src/declarative/util/qfxperf.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 QtDeclarative 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 _QFXPERF_H_ +#define _QFXPERF_H_ + +#include "qperformancelog.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +Q_DECLARE_PERFORMANCE_LOG(QFxPerf) { + Q_DECLARE_PERFORMANCE_METRIC(XmlParsing); + Q_DECLARE_PERFORMANCE_METRIC(Compile); + Q_DECLARE_PERFORMANCE_METRIC(CompileRun); + Q_DECLARE_PERFORMANCE_METRIC(CssParsing); + Q_DECLARE_PERFORMANCE_METRIC(CreateComponent); + Q_DECLARE_PERFORMANCE_METRIC(BindInit); + Q_DECLARE_PERFORMANCE_METRIC(BindCompile); + Q_DECLARE_PERFORMANCE_METRIC(BindValue); + Q_DECLARE_PERFORMANCE_METRIC(BindValueSSE); + Q_DECLARE_PERFORMANCE_METRIC(BindValueQt); + Q_DECLARE_PERFORMANCE_METRIC(BindableValueUpdate); + Q_DECLARE_PERFORMANCE_METRIC(PixmapLoad); + Q_DECLARE_PERFORMANCE_METRIC(MetaProperty); + Q_DECLARE_PERFORMANCE_METRIC(PathCache); + Q_DECLARE_PERFORMANCE_METRIC(CreateParticle); + Q_DECLARE_PERFORMANCE_METRIC(FontDatabase); + Q_DECLARE_PERFORMANCE_METRIC(ItemComponentComplete); + Q_DECLARE_PERFORMANCE_METRIC(ImageComponentComplete); + Q_DECLARE_PERFORMANCE_METRIC(ComponentInstanceComponentComplete); + Q_DECLARE_PERFORMANCE_METRIC(BaseLayoutComponentComplete); + Q_DECLARE_PERFORMANCE_METRIC(TextComponentComplete); + Q_DECLARE_PERFORMANCE_METRIC(ContextQuery); + Q_DECLARE_PERFORMANCE_METRIC(ContextProperty); + Q_DECLARE_PERFORMANCE_METRIC(ObjectQuery); + Q_DECLARE_PERFORMANCE_METRIC(ObjectProperty); + Q_DECLARE_PERFORMANCE_METRIC(ObjectSetProperty); + Q_DECLARE_PERFORMANCE_METRIC(QFxText_setText); +} + +#endif // _QFXPERF_H_ + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qfxview.cpp b/src/declarative/util/qfxview.cpp new file mode 100644 index 0000000..3fb30e9 --- /dev/null +++ b/src/declarative/util/qfxview.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qscriptvalueiterator.h" +#include "qdebug.h" +#include "qtimer.h" +#include "qevent.h" +#include "qdir.h" +#include "qcoreapplication.h" +#include "qfontdatabase.h" +#include "qicon.h" +#include "qurl.h" +#include "qboxlayout.h" + +#include "qmlbindablevalue.h" +#include "qml.h" +#include "qfxitem.h" +#include "qperformancelog.h" +#include "qfxperf.h" + +#include "qfxview.h" +#include <QtDeclarative/qmlengine.h> +#include <QtDeclarative/qmlcontext.h> + + +QT_BEGIN_NAMESPACE +DEFINE_BOOL_CONFIG_OPTION(itemTreeDump, ITEMTREE_DUMP); + +static QVariant stringToPixmap(const QString &str) +{ + //XXX need to use correct paths + return QVariant(QPixmap(str)); +} + +static QVariant stringToIcon(const QString &str) +{ + //XXX need to use correct paths + return QVariant(QIcon(str)); +} + +static QVariant stringToKeySequence(const QString &str) +{ + return QVariant::fromValue(QKeySequence(str)); +} + +static QVariant stringToUrl(const QString &str) +{ + return QVariant(QUrl(str)); +} + +class QFxViewPrivate +{ +public: + QFxViewPrivate(QFxView *w) + : q(w), root(0), component(0) {} + + QFxView *q; + QFxItem *root; + + QUrl source; + QString xml; + + QmlEngine engine; + QmlComponent *component; + void init(); +}; + +/*! + \class QFxView + \brief The QFxView class provides a widget for displaying a Qt Declarative user interface. + + QFxView currently provides a minimal interface for displaying Qml files, and + connecting between QML and C++ Qt objects. + + Typcial usage looks something like this: + \code + ... + QFxView *view = new QFxView(this); + vbox->addWidget(view); + + QFile file(fileName); + file.open(QFile::ReadOnly); + QString xml = file.readAll(); + view->setXml(xml, fileName); + + QFileInfo fi(file); + view->setPath(fi.path()); + + view->execute(); + ... + \endcode +*/ + +QFxView::QFxView(QWidget *parent) +: QSimpleCanvas(parent), d(new QFxViewPrivate(this)) +{ + d->init(); +} + +QFxView::QFxView(QSimpleCanvas::CanvasMode mode, QWidget *parent) +: QSimpleCanvas(mode, parent), d(new QFxViewPrivate(this)) +{ + d->init(); +} + +void QFxViewPrivate::init() +{ + // XXX: These need to be put in a central location for this kind of thing + qRegisterMetaType<QFxAnchorLine>("QFxAnchorLine"); + + QmlMetaType::registerCustomStringConverter(QVariant::Pixmap, &stringToPixmap); + QmlMetaType::registerCustomStringConverter(QVariant::Icon, &stringToIcon); + QmlMetaType::registerCustomStringConverter(QVariant::KeySequence, &stringToKeySequence); + QmlMetaType::registerCustomStringConverter(QVariant::Url, &stringToUrl); + +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer<QFxPerf::FontDatabase> perf; +#endif + QFontDatabase database; +} + +QFxView::~QFxView() +{ + clearItems(); + delete d; d = 0; +} + +void QFxView::setUrl(const QUrl& url) +{ + d->source = url; + d->xml = QString(); +} + +void QFxView::setXml(const QString &xml, const QString &filename) +{ + d->source = QUrl::fromLocalFile(filename); + d->xml = xml; +} + +QString QFxView::xml() const +{ + return d->xml; +} + +QmlEngine* QFxView::engine() +{ + return &d->engine; +} + +QmlContext* QFxView::rootContext() +{ + return d->engine.rootContext(); +} + +void QFxView::execute() +{ + rootContext()->activate(); + + if (d->xml.isEmpty()) { + d->component = new QmlComponent(&d->engine, d->source, this); + } else { + d->component = new QmlComponent(&d->engine, d->xml.toUtf8(), d->source); + } + + if(d->component->isReady()) { + continueExecute(); + } else { + connect(d->component, SIGNAL(readyChanged()), this, SLOT(continueExecute())); + } +} + +void QFxView::continueExecute() +{ + disconnect(d->component, SIGNAL(readyChanged()), this, SLOT(continueExecute())); + + if(!d->component){ + qWarning() << "Error in loading" << d->source; + return; + } + + QObject *obj = d->component->create(); + rootContext()->deactivate(); + if(obj) { + if(QFxItem *item = qobject_cast<QFxItem *>(obj)) { + item->QSimpleCanvasItem::setParent(QSimpleCanvas::root()); + + if(itemTreeDump()) + item->dump(); + + QPerformanceLog::displayData(); + QPerformanceLog::clear(); + d->root = item; + emit sceneResized(QSize(item->width(), item->height())); + } else if(QWidget *wid = qobject_cast<QWidget *>(obj)) { + window()->setAttribute(Qt::WA_OpaquePaintEvent, false); + window()->setAttribute(Qt::WA_NoSystemBackground, false); + if (!layout()) { + setLayout(new QVBoxLayout); + } else if (layout()->count()) { + // Hide the QGraphicsView in GV mode. + QLayoutItem *item = layout()->itemAt(0); + if (item->widget()) + item->widget()->hide(); + } + layout()->addWidget(wid); + emit sceneResized(wid->size()); + } + } +} + +QFxItem* QFxView::addItem(const QString &xml, QFxItem* parent) +{ + if(!d->root) + return 0; + + QmlComponent component(&d->engine, xml.toUtf8(), QUrl()); + QObject *obj = component.create(); + if(obj){ + QFxItem *item = static_cast<QFxItem *>(obj); + if(!parent) + parent = d->root; + + item->setItemParent(parent); + return item; + } + return 0; +} + +void QFxView::reset() +{ + clearItems(); + d->engine.clearComponentCache(); +} + +void QFxView::clearItems() +{ + if(!d->root) + return; + delete d->root; + d->root = 0; +} + +QFxItem *QFxView::root() const +{ + return d->root; +} + +void QFxView::resizeEvent(QResizeEvent *e) +{ + if(d->root) { + d->root->setWidth(width()); + d->root->setHeight(height()); + } + QSimpleCanvas::resizeEvent(e); +} + +void QFxView::focusInEvent(QFocusEvent *) +{ + // Do nothing (do not call QWidget::update()) +} + +void QFxView::focusOutEvent(QFocusEvent *) +{ + // Do nothing (do not call QWidget::update()) +} + + +void QFxView::dumpRoot() +{ + root()->dump(); +} +QT_END_NAMESPACE diff --git a/src/declarative/util/qfxview.h b/src/declarative/util/qfxview.h new file mode 100644 index 0000000..b5592b8 --- /dev/null +++ b/src/declarative/util/qfxview.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 _QFXVIEW_H_ +#define _QFXVIEW_H_ + +#include <qfxglobal.h> +#include <QtCore/qdatetime.h> +#include <QtGui/qgraphicssceneevent.h> +#include <QtGui/qwidget.h> +#include <qsimplecanvas.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QFxItem; +class QmlEngine; +class QmlContext; +class Canvas; + +class QFxViewPrivate; +class Q_DECLARATIVE_EXPORT QFxView : public QSimpleCanvas +{ +Q_OBJECT +public: + explicit QFxView(QWidget *parent = 0); + QFxView(QSimpleCanvas::CanvasMode mode, QWidget* parent = 0); + + virtual ~QFxView(); + + void setUrl(const QUrl&); + void setXml(const QString &xml, const QString &filename=QString()); + QString xml() const; + QmlEngine* engine(); + QmlContext* rootContext(); + virtual void execute(); + virtual void reset(); + + virtual QFxItem* addItem(const QString &xml, QFxItem* parent=0); + virtual void clearItems(); + + virtual QFxItem *root() const; + + void dumpRoot(); + +Q_SIGNALS: + void sceneResized(QSize size); + +private Q_SLOTS: + void continueExecute(); + +protected: + virtual void resizeEvent(QResizeEvent *); + void focusInEvent(QFocusEvent *); + void focusOutEvent(QFocusEvent *); + +private: + friend class QFxViewPrivate; + QFxViewPrivate *d; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // _QFXVIEW_H_ diff --git a/src/declarative/util/qmlanimation.cpp b/src/declarative/util/qmlanimation.cpp new file mode 100644 index 0000000..803f2b2 --- /dev/null +++ b/src/declarative/util/qmlanimation.cpp @@ -0,0 +1,2292 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlanimation.h" +#include "gfxtimeline.h" +#include "qvariant.h" +#include "qcolor.h" +#include "qfile.h" +#include "gfxeasing.h" +#include "qmlpropertyvaluesource.h" +#include "qml.h" +#include "qmlanimation_p.h" +#include "gfxtimeline.h" +#include "qmlbehaviour.h" +#include <QParallelAnimationGroup> +#include <QSequentialAnimationGroup> +#include <QtCore/qset.h> +#include <QtDeclarative/qmlexpression.h> +#include <private/qmlstringconverters_p.h> + +/* TODO: + Check for any memory leaks + easing should be a QEasingCurve-type property + All other XXXs +*/ + +QT_BEGIN_NAMESPACE + +QEasingCurve stringToCurve(const QString &curve) +{ + QEasingCurve easingCurve; + + QString normalizedCurve = curve; + bool hasParams = curve.contains(QLatin1Char('(')); + QStringList props; + + if(hasParams) { + QString easeName = curve.trimmed(); + if(!easeName.endsWith(QLatin1Char(')'))) { + qWarning("QEasingCurve: Unmatched perenthesis in easing function '%s'", + curve.toLatin1().constData()); + return easingCurve; + } + + int idx = easeName.indexOf(QLatin1Char('(')); + QString prop_str = + easeName.mid(idx + 1, easeName.length() - 1 - idx - 1); + normalizedCurve = easeName.left(idx); + + props = prop_str.split(QLatin1Char(',')); + } + + normalizedCurve = normalizedCurve.mid(4); + //XXX optimize? + int index = QEasingCurve::staticMetaObject.indexOfEnumerator("Type"); + QMetaEnum me = QEasingCurve::staticMetaObject.enumerator(index); + + int value = me.keyToValue(normalizedCurve.toLatin1().constData()); + if (value < 0) { + //XXX print line number + qWarning("QEasingCurve: Unknown easing curve '%s'", + curve.toLatin1().constData()); + value = 0; + } + easingCurve.setType((QEasingCurve::Type)value); + + if (hasParams) { + foreach(QString str, props) { + int sep = str.indexOf(QLatin1Char(':')); + + if(sep == -1) { + qWarning("QEasingCurve: Improperly specified property in easing function '%s'", + curve.toLatin1().constData()); + return easingCurve; + } + + QString propName = str.left(sep).trimmed(); + bool isOk; + qreal propValue = str.mid(sep + 1).trimmed().toDouble(&isOk); + + if(propName.isEmpty() || !isOk) { + qWarning("QEasingCurve: Improperly specified property in easing function '%s'", + curve.toLatin1().constData()); + return easingCurve; + } + + //XXX optimize + if (propName == QLatin1String("amplitude")) { + easingCurve.setAmplitude(propValue); + } else if (propName == QLatin1String("period")) { + easingCurve.setPeriod(propValue); + } else if (propName == QLatin1String("overshoot")) { + easingCurve.setOvershoot(propValue); + } + } + } + + return easingCurve; +} + +QML_DEFINE_NOCREATE_TYPE(QmlAbstractAnimation); + +/*! + \qmlclass Animation + \brief The Animation element is the base of all QML animations. + + The Animation element cannot be used directly in a QML file. It exists + to provide a set of common properties and methods, available across all the + other animation types that inherit from it. Attempting to use the Animation + element directly will result in an error. +*/ + +/*! + \class QmlAbstractAnimation + \internal +*/ + +QmlAbstractAnimation::QmlAbstractAnimation(QObject *parent) +: QmlPropertyValueSource(*(new QmlAbstractAnimationPrivate), parent) +{ +} + +QmlAbstractAnimation::~QmlAbstractAnimation() +{ +} + +QmlAbstractAnimation::QmlAbstractAnimation(QmlAbstractAnimationPrivate &dd, QObject *parent) +: QmlPropertyValueSource(dd, parent) +{ +} + +/*! + \qmlproperty bool Animation::running + This property holds whether the animation is currently running. + + The \c running property can be set to declaratively control whether or not + an animation is running. The following example will animate a rectangle + whenever the \l MouseRegion is pressed. + + \code + <Rect width="100" height="100"> + <x> + <NumericAnimation running="{MyMouse.pressed}" from="0" to="100" /> + </x> + <MouseRegion id="MyMouse" /> + </Rect> + \endcode + + Likewise, the \c running property can be read to determine if the animation + is running. In the following example the text element will indicate whether + or not the animation is running. + + \code + <NumericAnimation id="MyAnimation" /> + <Text text="{MyAnimation.running?'Animation is running':'Animation is not running'}" /> + \endcode + + Animations can also be started and stopped imperatively from JavaScript + using the \c start() and \c stop() methods. + + By default, animations are not running. +*/ +bool QmlAbstractAnimation::isRunning() const +{ + Q_D(const QmlAbstractAnimation); + return d->running; +} + +void QmlAbstractAnimationPrivate::commence() +{ + Q_Q(QmlAbstractAnimation); + + q->prepare(userProperty.value); + q->qtAnimation()->start(); + if(!q->qtAnimation()->state() == QAbstractAnimation::Running) { + running = false; + emit q->completed(); + } +} + +void QmlAbstractAnimation::setRunning(bool r) +{ + Q_D(QmlAbstractAnimation); + if(d->running == r) + return; + + if(d->group) { + qWarning("QmlAbstractAnimation: setRunning() cannot be used on non-root animation nodes"); + return; + } + + d->running = r; + if(d->running) { + if(!d->connectedTimeLine) { + QObject::connect(qtAnimation(), SIGNAL(finished()), + this, SLOT(timelineComplete())); + d->connectedTimeLine = true; + } + if (d->componentComplete) + d->commence(); + else + d->startOnCompletion = true; + emit started(); + } else { + if(!d->finishPlaying) + qtAnimation()->stop(); + emit completed(); + } + + emit runningChanged(d->running); +} + +void QmlAbstractAnimation::classBegin() +{ + Q_D(QmlAbstractAnimation); + d->componentComplete = false; +} + +void QmlAbstractAnimation::componentComplete() +{ + Q_D(QmlAbstractAnimation); + if (d->startOnCompletion) + d->commence(); + d->componentComplete = true; +} + +/*! + \qmlproperty bool Animation::finishPlaying + This property holds whether the animation should finish playing when it is stopped. + + If this true the animation will complete its current iteration when it + is stopped - either by setting the \c running property to false, or by + calling the \c stop() method. The \c complete() method is not effected + by this value. + + This behaviour is most useful when the \c repeat property is set, as the + animation will finish playing normally but not restart. + + By default, the finishPlaying property is not set. +*/ +bool QmlAbstractAnimation::finishPlaying() const +{ + Q_D(const QmlAbstractAnimation); + return d->finishPlaying; +} + +void QmlAbstractAnimation::setFinishPlaying(bool f) +{ + Q_D(QmlAbstractAnimation); + if(d->finishPlaying == f) + return; + + d->finishPlaying = f; + emit finishPlayingChanged(f); +} + +/*! + \qmlproperty bool Animation::repeat + This property holds whether the animation should repeat. + + If set, the animation will continuously repeat until it is explicitly + stopped - either by setting the \c running property to false, or by calling + the \c stop() method. + + In the following example, the rectangle will spin indefinately. + + \code + <Rect> + <rotation> + <NumericAnimation running="true" repeat="true" from="0" to="360" /> + </rotation> + </Rect> + \endcode +*/ +bool QmlAbstractAnimation::repeat() const +{ + Q_D(const QmlAbstractAnimation); + return d->repeat; +} + +void QmlAbstractAnimation::setRepeat(bool r) +{ + Q_D(QmlAbstractAnimation); + if(r == d->repeat) + return; + + d->repeat = r; + int ic = r ? -1 : 1; + qtAnimation()->setIterationCount(ic); + emit repeatChanged(r); +} + +QmlAnimationGroup *QmlAbstractAnimation::group() const +{ + Q_D(const QmlAbstractAnimation); + return d->group; +} + +void QmlAbstractAnimation::setGroup(QmlAnimationGroup *g) +{ + Q_D(QmlAbstractAnimation); + if(d->group == g) + return; + if(d->group) + static_cast<QmlAnimationGroupPrivate *>(d->group->d_ptr)->animations.removeAll(this); + + d->group = g; + + if(d->group && !static_cast<QmlAnimationGroupPrivate *>(d->group->d_ptr)->animations.contains(this)) + static_cast<QmlAnimationGroupPrivate *>(d->group->d_ptr)->animations.append(this); + + if (d->group) + ((QAnimationGroup*)d->group->qtAnimation())->addAnimation(qtAnimation()); + + //if(g) //if removed from a group, then the group should no longer be the parent + setParent(g); +} + +/*! + \qmlproperty Object Animation::target + This property holds an explicit target object to animate. + + The exact effect of the \c target property depends on how the animation + is being used. Refer to the \l animation documentation for details. +*/ +QObject *QmlAbstractAnimation::target() const +{ + Q_D(const QmlAbstractAnimation); + return d->target; +} + +void QmlAbstractAnimation::setTarget(QObject *o) +{ + Q_D(QmlAbstractAnimation); + if(d->target == o) + return; + + d->target = o; + if(d->target && !d->propertyName.isEmpty()) { + d->userProperty = QmlMetaProperty(d->target, d->propertyName); + } else { + d->userProperty.invalidate(); + } + + emit targetChanged(d->target, d->propertyName); +} + +/*! + \qmlproperty string Animation::property + This property holds an explicit property to animated. + + The exact effect of the \c property property depends on how the animation + is being used. Refer to the \l animation documentation for details. +*/ +QString QmlAbstractAnimation::property() const +{ + Q_D(const QmlAbstractAnimation); + return d->propertyName; +} + +void QmlAbstractAnimation::setProperty(const QString &n) +{ + Q_D(QmlAbstractAnimation); + if(d->propertyName == n) + return; + + d->propertyName = n; + if(d->target && !d->propertyName.isEmpty()) { + d->userProperty = QmlMetaProperty(d->target, d->propertyName); + } else { + d->userProperty.invalidate(); + } + + emit targetChanged(d->target, d->propertyName); +} + +/*! + \qmlmethod Animation::start() + \brief Starts the animation. + + If the animation is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QmlAbstractAnimation::start() +{ + setRunning(true); +} + +/*! + \qmlmethod Animation::stop() + \brief Stops the animation. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). + + Normally \c stop() stops the animation immediately, and the animation has + no further influence on property values. In this example animation + \code + <Rect> + <x> + <NumericAnimation from="0" to="100" duration="500" /> + </x> + </Rect> + \endcode + was stopped at time 250ms, the \c x property will have a value of 50. + + However, if the \c finishPlaying property is set, the animation will + continue running until it completes and then stop. The \c running property + will still become false immediately. +*/ +void QmlAbstractAnimation::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod Animation::restart() + \brief Restarts the animation. + + This is a convenience method, and is equivalent to calling \c stop() and + then \c start(). +*/ +void QmlAbstractAnimation::restart() +{ + stop(); + start(); +} + +/*! + \qmlmethod Animation::complete() + \brief Stops the animation, jumping to the final property values. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c complete(). + + Unlike \c stop(), \c complete() immediately fast-forwards the animation to + its end. In the following example, + \code + <Rect> + <x> + <NumericAnimation from="0" to="100" duration="500" /> + </x> + </Rect> + \endcode + calling \c stop() at time 250ms will result in the \c x property having + a value of 50, while calling \c complete() will set the \c x property to + 100, exactly as though the animation had played the whole way through. +*/ +void QmlAbstractAnimation::complete() +{ + if(isRunning()) { + qtAnimation()->setCurrentTime(qtAnimation()->duration()); + } +} + +void QmlAbstractAnimation::setTarget(const QmlMetaProperty &p) +{ + Q_D(QmlAbstractAnimation); + if(d->userProperty.isNull) + d->userProperty = p; +} + +//prepare is called before an animation begins +//(when an animation is used as a simple animation, and not as part of a transition) +void QmlAbstractAnimation::prepare(QmlMetaProperty &) +{ +} + +void QmlAbstractAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(actions); + Q_UNUSED(modified); + Q_UNUSED(direction); +} + +void QmlAbstractAnimation::timelineComplete() +{ + setRunning(false); +} + +/*! + \qmlclass PauseAnimation QmlPauseAnimation + \inherits Animation + \brief The PauseAnimation provides a pause for an animation. + + When used in a SequentialAnimation, PauseAnimation is a step when + nothing happens, for a specified duration. + + A 500ms animation sequence, with a 100ms pause between two animations: + \code + <SequentialAnimation> + <NumericAnimation ... duration="200"/> + <PauseAnimation duration="100"/> + <NumericAnimation ... duration="200"/> + </SequentialAnimation> + \endcode +*/ +/*! + \internal + \class QmlPauseAnimation + \ingroup animation states + \brief The QmlPauseAnimation class provides a pause for an animation. + + When used in a QmlSequentialAnimation, QmlPauseAnimation is a step when + nothing happens, for a specified duration. + + A QmlPauseAnimation object can be instantiated in Qml using the tag + \ref xmlPauseAnimation "<PauseAnimation>". +*/ + +QML_DEFINE_TYPE(QmlPauseAnimation,PauseAnimation); +QmlPauseAnimation::QmlPauseAnimation(QObject *parent) +: QmlAbstractAnimation(*(new QmlPauseAnimationPrivate), parent) +{ + Q_D(QmlPauseAnimation); + d->init(); +} + +QmlPauseAnimation::~QmlPauseAnimation() +{ +} + +void QmlPauseAnimationPrivate::init() +{ + Q_Q(QmlPauseAnimation); + pa = new QPauseAnimation(q); +} + +/*! + \qmlproperty int PauseAnimation::duration + This property holds the duration of the pause in milliseconds + + The default value is 250. +*/ +/*! + \property QmlPauseAnimation::duration + \brief the duration of the pause in milliseconds + + The default value is 250. +*/ +int QmlPauseAnimation::duration() const +{ + Q_D(const QmlPauseAnimation); + return d->pa->duration(); +} + +void QmlPauseAnimation::setDuration(int duration) +{ + if(duration < 0) { + qWarning("QmlPauseAnimation: Cannot set a duration of < 0"); + return; + } + + Q_D(QmlPauseAnimation); + if(d->pa->duration() == duration) + return; + d->pa->setDuration(duration); + emit durationChanged(duration); +} + +void QmlPauseAnimation::prepare(QmlMetaProperty &p) +{ + Q_D(QmlPauseAnimation); + if(d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; +} + +QAbstractAnimation *QmlPauseAnimation::qtAnimation() +{ + Q_D(QmlPauseAnimation); + return d->pa; +} + +/*! + \qmlclass ColorAnimation QmlColorAnimation + \inherits Animation + \brief The ColorAnimation allows you to animate color changes. + + \code + <ColorAnimation from="white" to="#c0c0c0" duration="100"/> + \endcode + + The default property animated is \c color, but like other animations, + this can be changed by setting \c property. The \c color property will + still animate. XXX is this a bug? +*/ +/*! + \internal + \class QmlColorAnimation + \ingroup animation states + \brief The QmlColorAnimation class allows you to animate color changes. + + A QmlColorAnimation object can be instantiated in Qml using the tag + \ref xmlColorAnimation "<ColorAnimation>". +*/ + +QmlColorAnimation::QmlColorAnimation(QObject *parent) +: QmlAbstractAnimation(*(new QmlColorAnimationPrivate), parent) +{ + Q_D(QmlColorAnimation); + d->init(); +} + +QmlColorAnimation::~QmlColorAnimation() +{ +} + +void QmlColorAnimationPrivate::init() +{ + Q_Q(QmlColorAnimation); + ca = new GfxValueAnimator(q); + ca->setStartValue(QVariant(0.0f)); + ca->setEndValue(QVariant(1.0f)); +} + +/*! + \qmlproperty int ColorAnimation::duration + This property holds the duration of the color transition, in milliseconds. + + The default value is 250. +*/ +/*! + \property QmlColorAnimation::duration + \brief the duration of the transition, in milliseconds. + + The default value is 250. +*/ +int QmlColorAnimation::duration() const +{ + Q_D(const QmlColorAnimation); + return d->ca->duration(); +} + +void QmlColorAnimation::setDuration(int duration) +{ + if(duration < 0) { + qWarning("QmlColorAnimation: Cannot set a duration of < 0"); + return; + } + + Q_D(QmlColorAnimation); + if(d->ca->duration() == duration) + return; + d->ca->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty color ColorAnimation::from + This property holds the starting color. +*/ +/*! + \property QmlColorAnimation::from + \brief the starting color. +*/ +QColor QmlColorAnimation::from() const +{ + Q_D(const QmlColorAnimation); + return d->fromValue; +} + +void QmlColorAnimation::setFrom(const QColor &f) +{ + Q_D(QmlColorAnimation); + if(d->fromValue.isValid() && f == d->fromValue) + return; + d->fromValue = f; + emit fromChanged(f); +} + +/*! + \qmlproperty color ColorAnimation::from + This property holds the ending color. +*/ +/*! + \property QmlColorAnimation::to + \brief the ending color. +*/ +QColor QmlColorAnimation::to() const +{ + Q_D(const QmlColorAnimation); + return d->toValue; +} + +void QmlColorAnimation::setTo(const QColor &t) +{ + Q_D(QmlColorAnimation); + if(d->toValue.isValid() && t == d->toValue) + return; + d->toValue = t; + emit toChanged(t); +} + +/*! + \qmlproperty string ColorAnimation::easing + This property holds the easing curve used for the transition. + + Each channel of the color is eased using the same easing curve. + See NumericAnimation::easing for a full discussion of easing, + and a list of available curves. +*/ +QString QmlColorAnimation::easing() const +{ + Q_D(const QmlColorAnimation); + return d->easing; +} + +void QmlColorAnimation::setEasing(const QString &e) +{ + Q_D(QmlColorAnimation); + if(d->easing == e) + return; + + d->easing = e; + d->ca->setEasingCurve(stringToCurve(d->easing)); + emit easingChanged(e); +} + +/*! + \qmlproperty list<Item> ColorAnimation::filter + This property holds the items selected to be affected by this animation (all if not set). + \sa exclude +*/ +QList<QObject *> *QmlColorAnimation::filter() +{ + Q_D(QmlColorAnimation); + return &d->filter; +} + +/*! + \qmlproperty list<Item> ColorAnimation::exclude + This property holds the items not to be affected by this animation. + \sa filter +*/ +QList<QObject *> *QmlColorAnimation::exclude() +{ + Q_D(QmlColorAnimation); + return &d->exclude; +} + +void QmlColorAnimation::prepare(QmlMetaProperty &p) +{ + Q_D(QmlColorAnimation); + if(d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + d->fromSourced = false; + d->value.GfxValue::setValue(0.); + d->ca->setAnimValue(&d->value, QAbstractAnimation::KeepWhenStopped); +} + +QAbstractAnimation *QmlColorAnimation::qtAnimation() +{ + Q_D(QmlColorAnimation); + return d->ca; +} + +void QmlColorAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlColorAnimation); + Q_UNUSED(direction); + + struct NTransitionData : public GfxValue + { + QmlStateActions actions; + void write(QmlMetaProperty &property, const QColor &color) + { + if(property.propertyType() == qMetaTypeId<QColor>()) { + property.write(color); + } + } + + void setValue(qreal v) + { + GfxValue::setValue(v); + for(int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + + QColor to(action.toValue.value<QColor>()); + + if(v == 1.) { + write(action.property, to); + } else { + if(action.fromValue.isNull()) { + action.fromValue = action.property.read(); + if(action.fromValue.isNull()) + action.fromValue = QVariant(QColor()); + } + + QColor from(action.fromValue.value<QColor>()); + + //XXX consolidate somewhere + uint red = uint(qreal(from.red()) + v * (qreal(to.red()) - qreal(from.red()))); + uint green = uint(qreal(from.green()) + v * (qreal(to.green()) - qreal(from.green()))); + uint blue = uint(qreal(from.blue()) + v * (qreal(to.blue()) - qreal(from.blue()))); + uint alpha = uint(qreal(from.alpha()) + v * (qreal(to.alpha()) - qreal(from.alpha()))); + + write(action.property, QColor(red, green, blue, alpha)); + } + } + } + }; + + //XXX should we get rid of this? + QStringList props; + props << QLatin1String("color"); + if(!d->propertyName.isEmpty() && !props.contains(d->propertyName)) + props.append(d->propertyName); + + NTransitionData *data = new NTransitionData; + + QSet<QObject *> objs; + for(int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + + if((d->filter.isEmpty() || d->filter.contains(obj)) && + (!d->exclude.contains(obj)) && props.contains(propertyName) && + (!target() || target() == obj)) { + objs.insert(obj); + Action myAction = action; + if(d->fromValue.isValid()) + myAction.fromValue = QVariant(d->fromValue); + if(d->toValue.isValid()) + myAction.toValue = QVariant(d->toValue); + + modified << action.property; + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if(d->toValue.isValid() && target() && !objs.contains(target())) { + QObject *obj = target(); + for(int jj = 0; jj < props.count(); ++jj) { + Action myAction; + myAction.property = QmlMetaProperty(obj, props.at(jj)); + + if(d->fromValue.isValid()) + myAction.fromValue = QVariant(d->fromValue); + + myAction.toValue = QVariant(d->toValue); + myAction.bv = 0; + myAction.event = 0; + data->actions << myAction; + } + } + + if(data->actions.count()) + d->ca->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + else + delete data; +} + + +void QmlColorAnimationPrivate::valueChanged(qreal v) +{ + if(!fromSourced) { + if(!fromValue.isValid()) { + fromValue = QColor(qvariant_cast<QColor>(property.read())); + } + fromSourced = true; + } + + //XXX consolidate somewhere + uint red = uint(qreal(fromValue.red()) + v * (qreal(toValue.red()) - qreal(fromValue.red()))); + uint green = uint(qreal(fromValue.green()) + v * (qreal(toValue.green()) - qreal(fromValue.green()))); + uint blue = uint(qreal(fromValue.blue()) + v * (qreal(toValue.blue()) - qreal(fromValue.blue()))); + uint alpha = uint(qreal(fromValue.alpha()) + v * (qreal(toValue.alpha()) - qreal(fromValue.alpha()))); + + if(property.propertyType() == qMetaTypeId<QColor>()) { + property.write(QColor(red, green, blue, alpha)); + } +} +QML_DEFINE_TYPE(QmlColorAnimation,ColorAnimation); + +/*! + \qmlclass RunScriptAction QmlRunScriptAction + \inherits Animation + \brief The RunScripAction allows scripts to be run during transitions. + +*/ +/*! + \internal + \class QmlRunScriptAction + \brief The QmlRunScriptAction class allows scropts to be run during transitions + + \ref xmlRunScriptAction +*/ +QmlRunScriptAction::QmlRunScriptAction(QObject *parent) + :QmlAbstractAnimation(*(new QmlRunScriptActionPrivate), parent) +{ + Q_D(QmlRunScriptAction); + d->init(); +} + +QmlRunScriptAction::~QmlRunScriptAction() +{ +} + +void QmlRunScriptActionPrivate::init() +{ + Q_Q(QmlRunScriptAction); + rsa = new QActionAnimation(&proxy, q); +} + +/*! + \qmlproperty QString RunScript::script + This property holds the script to run. +*/ +QString QmlRunScriptAction::script() const +{ + Q_D(const QmlRunScriptAction); + return d->script; +} + +void QmlRunScriptAction::setScript(const QString &script) +{ + Q_D(QmlRunScriptAction); + if(script == d->script) + return; + d->script = script; + emit scriptChanged(script); +} + +/*! + \qmlproperty QString RunScript::script + This property holds the file containing the script to run. +*/ +QString QmlRunScriptAction::file() const +{ + Q_D(const QmlRunScriptAction); + return d->file; +} + +void QmlRunScriptAction::setFile(const QString &file) +{ + Q_D(QmlRunScriptAction); + if(file == d->file) + return; + d->file = file; + emit fileChanged(file); +} + +void QmlRunScriptActionPrivate::execute() +{ + Q_Q(QmlRunScriptAction); + QString scriptStr = script; + if(!file.isEmpty()){ + QFile scriptFile(file); + if(scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)){ + scriptStr = QString::fromUtf8(scriptFile.readAll()); + } + } + + if(!scriptStr.isEmpty()) { + QmlExpression expr(ctxt, scriptStr, q); + expr.setTrackChange(false); + expr.value(); + } +} + +QAbstractAnimation *QmlRunScriptAction::qtAnimation() +{ + Q_D(QmlRunScriptAction); + return d->rsa; +} + +QML_DEFINE_TYPE(QmlRunScriptAction, RunScriptAction); + +/*! + \qmlclass SetPropertyAction QmlSetPropertyAction + \inherits Animation + \brief The SetPropertyAction allows property changes during transitions. + + Explicitly set \c theimage.smooth=true during a transition: + \code + <SetPropertyAction target="{theimage}" property="smooth" value="true"/> + \endcode + + Set \c thewebview.url to the value set for the destination state: + \code + <SetPropertyAction target="{thewebview}" property="url"/> + \endcode + + The SetPropertyAction is immediate - + the target property is not animated to the selected value in any way. +*/ +/*! + \internal + \class QmlSetPropertyAction + \brief The QmlSetPropertyAction class allows property changes during transitions. + + A QmlSetPropertyAction object can be instantiated in Qml using the tag + \ref xmlSetPropertyAction "<SetPropertyAction>". +*/ +QmlSetPropertyAction::QmlSetPropertyAction(QObject *parent) +: QmlAbstractAnimation(*(new QmlSetPropertyActionPrivate), parent) +{ + Q_D(QmlSetPropertyAction); + d->init(); +} + +QmlSetPropertyAction::~QmlSetPropertyAction() +{ +} + +void QmlSetPropertyActionPrivate::init() +{ + Q_Q(QmlSetPropertyAction); + spa = new QActionAnimation(q); +} + +/*! + \qmlproperty string SetPropertyAction::properties + This property holds the properties to be immediately set, comma-separated. +*/ +QString QmlSetPropertyAction::properties() const +{ + Q_D(const QmlSetPropertyAction); + return d->properties; +} + +void QmlSetPropertyAction::setProperties(const QString &p) +{ + Q_D(QmlSetPropertyAction); + if(d->properties == p) + return; + d->properties = p; + emit propertiesChanged(p); +} + +/*! + \qmlproperty list<Item> SetPropertyAction::filter + This property holds the items selected to be affected by this animation (all if not set). + \sa exclude +*/ +QList<QObject *> *QmlSetPropertyAction::filter() +{ + Q_D(QmlSetPropertyAction); + return &d->filter; +} + +/*! + \qmlproperty list<Item> SetPropertyAction::exclude + This property holds the items not to be affected by this animation. + \sa filter +*/ +QList<QObject *> *QmlSetPropertyAction::exclude() +{ + Q_D(QmlSetPropertyAction); + return &d->exclude; +} + +/*! + \qmlproperty any SetPropertyAction::value + This property holds the value to be set on the property. + If not set, then the value defined for the end state of the transition. +*/ +QVariant QmlSetPropertyAction::value() const +{ + Q_D(const QmlSetPropertyAction); + return d->value; +} + +void QmlSetPropertyAction::setValue(const QVariant &v) +{ + Q_D(QmlSetPropertyAction); + if(d->value.isNull || d->value != v) { + d->value = v; + emit valueChanged(v); + } +} + +void QmlSetPropertyActionPrivate::doAction() +{ + property.write(value); +} + +QAbstractAnimation *QmlSetPropertyAction::qtAnimation() +{ + Q_D(QmlSetPropertyAction); + return d->spa; +} + +void QmlSetPropertyAction::prepare(QmlMetaProperty &p) +{ + Q_D(QmlSetPropertyAction); + + if(d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + + d->spa->setAnimAction(&d->proxy, QAbstractAnimation::KeepWhenStopped); +} + +void QmlSetPropertyAction::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlSetPropertyAction); + Q_UNUSED(direction); + + struct QmlSetPropertyAnimationAction : public QAbstractAnimationAction + { + QmlStateActions actions; + virtual void doAction() + { + for(int ii = 0; ii < actions.count(); ++ii) { + const Action &action = actions.at(ii); + QmlBehaviour::_ignore = true; + action.property.write(action.toValue); + QmlBehaviour::_ignore = false; + } + } + }; + + QStringList props = d->properties.split(QLatin1Char(',')); + for(int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if(!d->propertyName.isEmpty() && !props.contains(d->propertyName)) + props.append(d->propertyName); + + QmlSetPropertyAnimationAction *data = new QmlSetPropertyAnimationAction; + + QSet<QObject *> objs; + for(int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + + if((d->filter.isEmpty() || d->filter.contains(obj)) && + (!d->exclude.contains(obj)) && props.contains(propertyName) && + (!target() || target() == obj)) { + objs.insert(obj); + Action myAction = action; + + if(d->value.isValid()) + myAction.toValue = d->value; + + modified << action.property; + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if(d->value.isValid() && target() && !objs.contains(target())) { + QObject *obj = target(); + for(int jj = 0; jj < props.count(); ++jj) { + Action myAction; + myAction.property = QmlMetaProperty(obj, props.at(jj)); + myAction.toValue = d->value; + data->actions << myAction; + } + } + + if(data->actions.count()) { + d->spa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +QML_DEFINE_TYPE(QmlSetPropertyAction,SetPropertyAction); + +/*! + \qmlclass ParentChangeAction QmlParentChangeAction + \inherits Animation + \brief The ParentChangeAction allows parent changes during transitions. + + The ParentChangeAction is immediate - it is not animated in any way. +*/ + +QmlParentChangeAction::QmlParentChangeAction(QObject *parent) +: QmlAbstractAnimation(*(new QmlParentChangeActionPrivate), parent) +{ + Q_D(QmlParentChangeAction); + d->init(); +} + +QmlParentChangeAction::~QmlParentChangeAction() +{ +} + +void QmlParentChangeActionPrivate::init() +{ + Q_Q(QmlParentChangeAction); + cpa = new QActionAnimation(q); +} + +void QmlParentChangeActionPrivate::doAction() +{ + //XXX property.write(value); +} + +void QmlParentChangeAction::prepare(QmlMetaProperty &p) +{ + Q_D(QmlParentChangeAction); + + if(d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + + //XXX +} + +QAbstractAnimation *QmlParentChangeAction::qtAnimation() +{ + Q_D(QmlParentChangeAction); + return d->cpa; +} + +void QmlParentChangeAction::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlParentChangeAction); + Q_UNUSED(direction); + + struct QmlParentChangeActionData : public QAbstractAnimationAction + { + QmlStateActions actions; + virtual void doAction() + { + for(int ii = 0; ii < actions.count(); ++ii) { + const Action &action = actions.at(ii); + QmlBehaviour::_ignore = true; + action.property.write(action.toValue); + QmlBehaviour::_ignore = false; + } + } + }; + + QmlParentChangeActionData *data = new QmlParentChangeActionData; + + QSet<QObject *> objs; + for(int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + + if((!target() || target() == obj) && propertyName == QString(QLatin1String("moveToParent"))) { + objs.insert(obj); + Action myAction = action; + + /*if(d->value.isValid()) + myAction.toValue = d->value;*/ + + modified << action.property; + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + /*if(d->value.isValid() && target() && !objs.contains(target())) { + QObject *obj = target(); + for(int jj = 0; jj < props.count(); ++jj) { + Action myAction; + myAction.property = QmlMetaProperty(obj, props.at(jj)); + myAction.toValue = d->value; + data->actions << myAction; + } + }*/ + + if(data->actions.count()) { + d->cpa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +QML_DEFINE_TYPE(QmlParentChangeAction,ParentChangeAction); + +/*! + \qmlclass NumericAnimation QmlNumericAnimation + \inherits Animation + \brief The NumericAnimation allows you to animate changes in properties of type qreal. + + Animate a set of properties over 200ms, from their values in the start state to + their values in the end state of the transition: + \code + <NumericAnimation properties="x,y,scale" duration="200"/> + \endcode +*/ + +/*! + \internal + \class QmlNumericAnimation + \ingroup animation states + \brief The QmlNumericAnimation class allows you to animate changes in properties of type qreal. + + A QmlNumericAnimation object can be instantiated in Qml using the tag + \ref xmlNumericAnimation "<NumericAnimation>". +*/ + +QmlNumericAnimation::QmlNumericAnimation(QObject *parent) +: QmlAbstractAnimation(*(new QmlNumericAnimationPrivate), parent) +{ + Q_D(QmlNumericAnimation); + d->init(); +} + +QmlNumericAnimation::~QmlNumericAnimation() +{ +} + +void QmlNumericAnimationPrivate::init() +{ + Q_Q(QmlNumericAnimation); + na = new GfxValueAnimator(q); + na->setStartValue(QVariant(0.0f)); + na->setEndValue(QVariant(1.0f)); +} + +/*! + \qmlproperty int NumericAnimation::duration + This property holds the duration of the transition, in milliseconds. + + The default value is 250. +*/ +/*! + \property QmlNumericAnimation::duration + \brief the duration of the transition, in milliseconds. + + The default value is 250. +*/ +int QmlNumericAnimation::duration() const +{ + Q_D(const QmlNumericAnimation); + return d->na->duration(); +} + +void QmlNumericAnimation::setDuration(int duration) +{ + if(duration < 0) { + qWarning("QmlNumericAnimation: Cannot set a duration of < 0"); + return; + } + + Q_D(QmlNumericAnimation); + if(d->na->duration() == duration) + return; + d->na->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty real NumericAnimation::from + This property holds the starting value. + If not set, then the value defined in the start state of the transition. +*/ +/*! + \property QmlNumericAnimation::from + \brief the starting value. +*/ +qreal QmlNumericAnimation::from() const +{ + Q_D(const QmlNumericAnimation); + return d->from; +} + +void QmlNumericAnimation::setFrom(qreal f) +{ + Q_D(QmlNumericAnimation); + if(!d->from.isNull && f == d->from) + return; + d->from = f; + emit fromChanged(f); +} + +/*! + \qmlproperty real NumericAnimation::to + This property holds the ending value. + If not set, then the value defined in the end state of the transition. +*/ +/*! + \property QmlNumericAnimation::to + \brief the ending value. +*/ +qreal QmlNumericAnimation::to() const +{ + Q_D(const QmlNumericAnimation); + return d->to; +} + +void QmlNumericAnimation::setTo(qreal t) +{ + Q_D(QmlNumericAnimation); + if(!d->to.isNull && t == d->to) + return; + d->to = t; + emit toChanged(t); +} + +/* XML docs in GfxEasing */ +/*! + \property QmlNumericAnimation::easing + This property holds the easing curve to use. + + \sa QEasingCurve +*/ +QString QmlNumericAnimation::easing() const +{ + Q_D(const QmlNumericAnimation); + return d->easing; +} + +void QmlNumericAnimation::setEasing(const QString &e) +{ + Q_D(QmlNumericAnimation); + if(d->easing == e) + return; + + d->easing = e; + d->na->setEasingCurve(stringToCurve(d->easing)); + emit easingChanged(e); +} + +/*! + \qmlproperty string NumericAnimation::properties + This property holds the properties this animation should be applied to. + + This is a comma-separated list of properties that should use + this animation when they change. +*/ +/*! + \property QmlNumericAnimation::properties + \brief the properties this animation should be applied to. + + properties holds a comma-separated list of properties that should use + this animation when they change. +*/ +QString QmlNumericAnimation::properties() const +{ + Q_D(const QmlNumericAnimation); + return d->properties; +} + +void QmlNumericAnimation::setProperties(const QString &prop) +{ + Q_D(QmlNumericAnimation); + if(d->properties == prop) + return; + + d->properties = prop; + emit propertiesChanged(prop); +} + +/*! + \qmlproperty list<Item> NumericAnimation::filter + This property holds the items selected to be affected by this animation (all if not set). + \sa exclude +*/ +QList<QObject *> *QmlNumericAnimation::filter() +{ + Q_D(QmlNumericAnimation); + return &d->filter; +} + +/*! + \qmlproperty list<Item> NumericAnimation::exclude + This property holds the items not to be affected by this animation. + \sa filter +*/ +QList<QObject *> *QmlNumericAnimation::exclude() +{ + Q_D(QmlNumericAnimation); + return &d->exclude; +} + +void QmlNumericAnimationPrivate::valueChanged(qreal r) +{ + if(!fromSourced) { + if(from.isNull) { + fromValue = qvariant_cast<qreal>(property.read()); + } else { + fromValue = from; + } + fromSourced = true; + } + + if(r == 1.) { + property.write(to.value); + } else { + qreal val = fromValue + (to-fromValue) * r; + property.write(val); + } +} + +void QmlNumericAnimation::prepare(QmlMetaProperty &p) +{ + Q_D(QmlNumericAnimation); + if(d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + d->fromSourced = false; + d->value.GfxValue::setValue(0.); + d->na->setAnimValue(&d->value, QAbstractAnimation::KeepWhenStopped); +} + +QAbstractAnimation *QmlNumericAnimation::qtAnimation() +{ + Q_D(QmlNumericAnimation); + return d->na; +} + +void QmlNumericAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlNumericAnimation); + Q_UNUSED(direction); + + struct NTransitionData : public GfxValue + { + QmlStateActions actions; + void setValue(qreal v) + { + GfxValue::setValue(v); + for(int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + + QmlBehaviour::_ignore = true; + if(v == 1.) + action.property.write(action.toValue.toDouble()); + else { + if(action.fromValue.isNull()) { + action.fromValue = action.property.read(); + if(action.fromValue.isNull()) { + action.fromValue = QVariant(0.); + } + } + qreal start = action.fromValue.toDouble(); + qreal end = action.toValue.toDouble(); + qreal val = start + (end-start) * v; + action.property.write(val); + } + QmlBehaviour::_ignore = false; + } + } + }; + + QStringList props = d->properties.split(QLatin1Char(',')); + for(int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if(!d->propertyName.isEmpty() && !props.contains(d->propertyName)) + props.append(d->propertyName); + + NTransitionData *data = new NTransitionData; + + QSet<QObject *> objs; + for(int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + + if((d->filter.isEmpty() || d->filter.contains(obj)) && + (!d->exclude.contains(obj)) && props.contains(propertyName) && + (!target() || target() == obj)) { + objs.insert(obj); + Action myAction = action; + if(d->from.isValid()) { + myAction.fromValue = QVariant(d->from); + } else { + myAction.fromValue = QVariant(); + } + if(d->to.isValid()) + myAction.toValue = QVariant(d->to); + + modified << action.property; + + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if(d->to.isValid() && target() && !objs.contains(target())) { + QObject *obj = target(); + for(int jj = 0; jj < props.count(); ++jj) { + Action myAction; + myAction.property = QmlMetaProperty(obj, props.at(jj)); + + if(d->from.isValid()) + myAction.fromValue = QVariant(d->from); + + myAction.toValue = QVariant(d->to); + myAction.bv = 0; + myAction.event = 0; + data->actions << myAction; + } + } + + if(data->actions.count()) { + d->na->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +QML_DEFINE_TYPE(QmlNumericAnimation,NumericAnimation); + +QmlAnimationGroup::QmlAnimationGroup(QObject *parent) +: QmlAbstractAnimation(*(new QmlAnimationGroupPrivate), parent) +{ +} + +QmlAnimationGroup::~QmlAnimationGroup() +{ +} + +QmlList<QmlAbstractAnimation *> *QmlAnimationGroup::animations() +{ + Q_D(QmlAnimationGroup); + return &d->animations; +} + +/*! + \qmlclass SequentialAnimation QmlSequentialAnimation + \inherits Animation + \brief The SequentialAnimation allows you to run animations sequentially. + + Animations controlled in SequentialAnimation will be run one after the other. + + The following example chains two numeric animations together. The \c MyItem + object will animate from its current x position to 100, and then back to 0. + + \code + <SequentialAnimation> + <NumericAnimation target="{MyItem}" property="x" to="100" /> + <NumericAnimation target="{MyItem}" property="x" to="0" /> + <SequentialAnimation> + \endcode + + \sa ParallelAnimation +*/ + +QmlSequentialAnimation::QmlSequentialAnimation(QObject *parent) : + QmlAnimationGroup(parent) +{ + Q_D(QmlAnimationGroup); + d->ag = new QSequentialAnimationGroup(this); +} + +QmlSequentialAnimation::~QmlSequentialAnimation() +{ +} + +void QmlSequentialAnimation::prepare(QmlMetaProperty &p) +{ + Q_D(QmlAnimationGroup); + if(d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + + for (int i = 0; i < d->animations.size(); ++i) + d->animations.at(i)->prepare(d->property); +} + +QAbstractAnimation *QmlSequentialAnimation::qtAnimation() +{ + Q_D(QmlAnimationGroup); + return d->ag; +} + +void QmlSequentialAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlAnimationGroup); + + int inc = 1; + int from = 0; + if(direction == Backward) { + inc = -1; + from = d->animations.count() - 1; + } + + //XXX removing and readding isn't ideal; we do it to get around the problem mentioned below. + for (int i = d->ag->animationCount()-1; i >= 0; --i) + d->ag->takeAnimationAt(i); + + for(int ii = from; ii < d->animations.count() && ii >= 0; ii += inc) { + d->animations.at(ii)->transition(actions, modified, direction); + d->ag->addAnimation(d->animations.at(ii)->qtAnimation()); + } + + //XXX changing direction means all the animations play in reverse, while we only want the ordering reversed. + //d->ag->setDirection(direction == Backward ? QAbstractAnimation::Backward : QAbstractAnimation::Forward); +} + +QML_DEFINE_TYPE(QmlSequentialAnimation,SequentialAnimation); + +/*! + \qmlclass ParallelAnimation QmlParallelAnimation + \inherits Animation + \brief The ParallelAnimation allows you to run animations in parallel. + + Animations contained in ParallelAnimation will be run at the same time. + + The following animation demonstrates animating the \c MyItem item + to (100,100) by animating the x and y properties in parallel. + + \code + <ParallelAnimation> + <NumericAnimation target="{MyItem}" property="x" to="100" /> + <NumericAnimation target="{MyItem}" property="y" to="100" /> + </ParallelAnimation> + \endcode + + \sa SequentialAnimation +*/ +/*! + \internal + \class QmlParallelAnimation + \ingroup animation states + \brief The QmlParallelAnimation class allows you to run animations in parallel. + + Animations controlled by QmlParallelAnimation will be run at the same time. + + \sa QmlSequentialAnimation + + A QmlParallelAnimation object can be instantiated in Qml using the tag + \ref xmlParallelAnimation "<ParallelAnimation>". +*/ + +QmlParallelAnimation::QmlParallelAnimation(QObject *parent) : + QmlAnimationGroup(parent) +{ + Q_D(QmlAnimationGroup); + d->ag = new QParallelAnimationGroup(this); +} + +QmlParallelAnimation::~QmlParallelAnimation() +{ +} + +void QmlParallelAnimation::prepare(QmlMetaProperty &p) +{ + Q_D(QmlAnimationGroup); + if(d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + + for (int i = 0; i < d->animations.size(); ++i) + d->animations.at(i)->prepare(d->property); +} + +QAbstractAnimation *QmlParallelAnimation::qtAnimation() +{ + Q_D(QmlAnimationGroup); + return d->ag; +} + +void QmlParallelAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlAnimationGroup); + + for(int ii = 0; ii < d->animations.count(); ++ii) { + d->animations.at(ii)->transition(actions, modified, direction); + } +} + +QML_DEFINE_TYPE(QmlParallelAnimation,ParallelAnimation); + +//XXX it would be good to use QVariantAnimation's interpolators if possible +QVariant QmlVariantAnimationPrivate::interpolateVariant(const QVariant &from, const QVariant &to, qreal progress) +{ + if (from.userType() != to.userType()) + return QVariant(); + + QVariant res; + switch (from.userType()) { + case QVariant::Int: { + int f = from.toInt(); + int t = to.toInt(); + res = f + (t - f) * progress; + break; + } + case QVariant::Double: { + double f = from.toDouble(); + double t = to.toDouble(); + res = f + (t - f) * progress; + break; + } + case QMetaType::Float: { + float f = from.toDouble(); + float t = to.toDouble(); + res = f + (t - f) * progress; + break; + } + case QVariant::Color: { + QColor f = from.value<QColor>(); + QColor t = to.value<QColor>(); + uint red = uint(qreal(f.red()) + progress * (qreal(t.red()) - qreal(f.red()))); + uint green = uint(qreal(f.green()) + progress * (qreal(t.green()) - qreal(f.green()))); + uint blue = uint(qreal(f.blue()) + progress * (qreal(t.blue()) - qreal(f.blue()))); + res = QColor(red,green,blue); + break; + } + case QVariant::Rect: { + QRect f = from.value<QRect>(); + QRect t = to.value<QRect>(); + int x = f.x() + (t.x() - f.x()) * progress; + int y = f.y() + (t.y() - f.y()) * progress; + int w = f.width() + (t.width() - f.width()) * progress; + int h = f.height() + (t.height() - f.height()) * progress; + res = QRect(x, y, w, h); + break; + } + case QVariant::RectF: { + QRectF f = from.value<QRectF>(); + QRectF t = to.value<QRectF>(); + qreal x = f.x() + (t.x() - f.x()) * progress; + qreal y = f.y() + (t.y() - f.y()) * progress; + qreal w = f.width() + (t.width() - f.width()) * progress; + qreal h = f.height() + (t.height() - f.height()) * progress; + res = QRectF(x, y, w, h); + break; + } + case QVariant::Point: { + QPoint f = from.value<QPoint>(); + QPoint t = to.value<QPoint>(); + int x = f.x() + (t.x() - f.x()) * progress; + int y = f.y() + (t.y() - f.y()) * progress; + res = QPointF(x, y); + break; + } + case QVariant::PointF: { + QPointF f = from.value<QPointF>(); + QPointF t = to.value<QPointF>(); + qreal x = f.x() + (t.x() - f.x()) * progress; + qreal y = f.y() + (t.y() - f.y()) * progress; + res = QPointF(x, y); + break; + } + case QVariant::Size: { + QSize f = from.value<QSize>(); + QSize t = to.value<QSize>(); + int w = f.width() + (t.width() - f.width()) * progress; + int h = f.height() + (t.height() - f.height()) * progress; + res = QSize(w, h); + break; + } + case QVariant::SizeF: { + QSizeF f = from.value<QSizeF>(); + QSizeF t = to.value<QSizeF>(); + qreal w = f.width() + (t.width() - f.width()) * progress; + qreal h = f.height() + (t.height() - f.height()) * progress; + res = QSizeF(w, h); + break; + } + default: + res = to; + break; + } + + return res; +} + +//convert a variant from string type to another animatable type +void QmlVariantAnimationPrivate::convertVariant(QVariant &variant, QVariant::Type type) +{ + if (variant.type() != QVariant::String) { + variant.convert(type); + return; + } + + switch (type) { + case QVariant::Rect: { + variant.setValue(QmlStringConverters::rectFFromString(variant.toString()).toRect()); + break; + } + case QVariant::RectF: { + variant.setValue(QmlStringConverters::rectFFromString(variant.toString())); + break; + } + case QVariant::Point: { + variant.setValue(QmlStringConverters::pointFFromString(variant.toString()).toPoint()); + break; + } + case QVariant::PointF: { + variant.setValue(QmlStringConverters::pointFFromString(variant.toString())); + break; + } + case QVariant::Size: { + variant.setValue(QmlStringConverters::sizeFFromString(variant.toString()).toSize()); + break; + } + case QVariant::SizeF: { + variant.setValue(QmlStringConverters::sizeFFromString(variant.toString())); + break; + } + case QVariant::Color: { + variant.setValue(QmlStringConverters::colorFromString(variant.toString())); + break; + } + default: + variant.convert(type); + break; + } +} + +/*! + \qmlclass VariantAnimation QmlVariantAnimation + \inherits Animation + \brief The VariantAnimation allows you to animate changes in properties of type QVariant. + + Animate a size property over 200ms, from its current size to 20-by-20: + \code + <VariantAnimation property="size" to="20x20" duration="200"/> + \endcode +*/ + +QmlVariantAnimation::QmlVariantAnimation(QObject *parent) +: QmlAbstractAnimation(*(new QmlVariantAnimationPrivate), parent) +{ + Q_D(QmlVariantAnimation); + d->init(); +} + +QmlVariantAnimation::~QmlVariantAnimation() +{ +} + +void QmlVariantAnimationPrivate::init() +{ + Q_Q(QmlVariantAnimation); + va = new GfxValueAnimator(q); + va->setStartValue(QVariant(0.0f)); + va->setEndValue(QVariant(1.0f)); +} + +/*! + \qmlproperty int VariantAnimation::duration + This property holds the duration of the transition, in milliseconds. + + The default value is 250. +*/ +/*! + \property QmlVariantAnimation::duration + \brief the duration of the transition, in milliseconds. + + The default value is 250. +*/ +int QmlVariantAnimation::duration() const +{ + Q_D(const QmlVariantAnimation); + return d->va->duration(); +} + +void QmlVariantAnimation::setDuration(int duration) +{ + if(duration < 0) { + qWarning("QmlVariantAnimation: Cannot set a duration of < 0"); + return; + } + + Q_D(QmlVariantAnimation); + if(d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty real VariantAnimation::from + This property holds the starting value. + If not set, then the value defined in the start state of the transition. +*/ +/*! + \property QmlVariantAnimation::from + \brief the starting value. +*/ +QVariant QmlVariantAnimation::from() const +{ + Q_D(const QmlVariantAnimation); + return d->from; +} + +void QmlVariantAnimation::setFrom(const QVariant &f) +{ + Q_D(QmlVariantAnimation); + if(!d->from.isNull && f == d->from) + return; + d->from = f; + emit fromChanged(f); +} + +/*! + \qmlproperty real VariantAnimation::to + This property holds the ending value. + If not set, then the value defined in the end state of the transition. +*/ +/*! + \property QmlVariantAnimation::to + \brief the ending value. +*/ +QVariant QmlVariantAnimation::to() const +{ + Q_D(const QmlVariantAnimation); + return d->to; +} + +void QmlVariantAnimation::setTo(const QVariant &t) +{ + Q_D(QmlVariantAnimation); + if(!d->to.isNull && t == d->to) + return; + d->to = t; + emit toChanged(t); +} + +/*! + \qmlproperty string VariantAnimation::easing + This property holds the easing curve used for the transition. + + See NumericAnimation::easing for a full discussion of easing, + and a list of available curves. +*/ + +/*! + \property QmlVariantAnimation::easing + \brief the easing curve to use. + + \sa QEasingCurve +*/ +QString QmlVariantAnimation::easing() const +{ + Q_D(const QmlVariantAnimation); + return d->easing; +} + +void QmlVariantAnimation::setEasing(const QString &e) +{ + Q_D(QmlVariantAnimation); + if(d->easing == e) + return; + + d->easing = e; + d->va->setEasingCurve(stringToCurve(d->easing)); + emit easingChanged(e); +} + +/*! + \qmlproperty string VariantAnimation::properties + This property holds the properties this animation should be applied to. + + This is a comma-separated list of properties that should use + this animation when they change. +*/ +/*! + \property QmlVariantAnimation::properties + \brief the properties this animation should be applied to + + properties holds a copy separated list of properties that should use + this animation when they change. +*/ +QString QmlVariantAnimation::properties() const +{ + Q_D(const QmlVariantAnimation); + return d->properties; +} + +void QmlVariantAnimation::setProperties(const QString &prop) +{ + Q_D(QmlVariantAnimation); + if(d->properties == prop) + return; + + d->properties = prop; + emit propertiesChanged(prop); +} + +/*! + \qmlproperty list<Item> VariantAnimation::filter + This property holds the items selected to be affected by this animation (all if not set). + \sa exclude +*/ +QList<QObject *> *QmlVariantAnimation::filter() +{ + Q_D(QmlVariantAnimation); + return &d->filter; +} + +/*! + \qmlproperty list<Item> VariantAnimation::exclude + This property holds the items not to be affected by this animation. + \sa filter +*/ +QList<QObject *> *QmlVariantAnimation::exclude() +{ + Q_D(QmlVariantAnimation); + return &d->exclude; +} + +void QmlVariantAnimationPrivate::valueChanged(qreal r) +{ + if(!fromSourced) { + if(from.isNull) { + fromValue = property.read(); + } else { + fromValue = from; + } + fromSourced = true; + } + + if(r == 1.) { + property.write(to.value); + } else { + QVariant val = interpolateVariant(fromValue, to.value, r); + property.write(val); + } +} + +QAbstractAnimation *QmlVariantAnimation::qtAnimation() +{ + Q_D(QmlVariantAnimation); + return d->va; +} + +void QmlVariantAnimation::prepare(QmlMetaProperty &p) +{ + Q_D(QmlVariantAnimation); + if(d->userProperty.isNull) + d->property = p; + else + d->property = d->userProperty; + + d->convertVariant(d->to.value, (QVariant::Type)d->property.propertyType()); + if (!d->from.isNull) + d->convertVariant(d->from.value, (QVariant::Type)d->property.propertyType()); + + d->fromSourced = false; + d->value.GfxValue::setValue(0.); + d->va->setAnimValue(&d->value, QAbstractAnimation::KeepWhenStopped); +} + +void QmlVariantAnimation::transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction) +{ + Q_D(QmlVariantAnimation); + Q_UNUSED(direction); + + struct NTransitionData : public GfxValue + { + QmlStateActions actions; + void setValue(qreal v) + { + GfxValue::setValue(v); + for(int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + + if(v == 1.) + action.property.write(action.toValue); + else { + if(action.fromValue.isNull()) { + action.fromValue = action.property.read(); + /*if(action.fromValue.isNull()) + action.fromValue = QVariant(0.);*/ //XXX can we give a default value for any type? + } + QVariant val = QmlVariantAnimationPrivate::interpolateVariant(action.fromValue, action.toValue, v); + action.property.write(val); + } + } + } + }; + + QStringList props = d->properties.split(QLatin1Char(',')); + for(int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if(!d->propertyName.isEmpty() && !props.contains(d->propertyName)) + props.append(d->propertyName); + + NTransitionData *data = new NTransitionData; + + QSet<QObject *> objs; + for(int ii = 0; ii < actions.count(); ++ii) { + Action &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + + if((d->filter.isEmpty() || d->filter.contains(obj)) && + (!d->exclude.contains(obj)) && props.contains(propertyName) && + (!target() || target() == obj)) { + objs.insert(obj); + Action myAction = action; + + if(d->from.isValid()) + myAction.fromValue = QVariant(d->from); + if(d->to.isValid()) + myAction.toValue = QVariant(d->to); + + d->convertVariant(myAction.fromValue, (QVariant::Type)myAction.property.propertyType()); + d->convertVariant(myAction.toValue, (QVariant::Type)myAction.property.propertyType()); + + modified << action.property; + + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if(d->to.isValid() && target() && !objs.contains(target())) { + QObject *obj = target(); + for(int jj = 0; jj < props.count(); ++jj) { + Action myAction; + myAction.property = QmlMetaProperty(obj, props.at(jj)); + + if(d->from.isValid()) { + d->convertVariant(d->from.value, (QVariant::Type)myAction.property.propertyType()); + myAction.fromValue = QVariant(d->from); + } + + d->convertVariant(d->to.value, (QVariant::Type)myAction.property.propertyType()); + myAction.toValue = QVariant(d->to); + myAction.bv = 0; + myAction.event = 0; + data->actions << myAction; + } + } + + if(data->actions.count()) { + d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +//XXX whats the best name for this? (just Animation?) +QML_DEFINE_TYPE(QmlVariantAnimation,VariantAnimation); + + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlanimation.h b/src/declarative/util/qmlanimation.h new file mode 100644 index 0000000..578631c --- /dev/null +++ b/src/declarative/util/qmlanimation.h @@ -0,0 +1,453 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLANIMATION_H +#define QMLANIMATION_H + +#include <QtCore/qvariant.h> +#include <QtGui/qcolor.h> +#include <qmltransition.h> +#include <qmlpropertyvaluesource.h> +#include <qmlstate.h> +#include <qml.h> +#include <QAbstractAnimation> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlAbstractAnimationPrivate; +class QmlAnimationGroup; +class QmlAbstractAnimation : public QmlPropertyValueSource, public QmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlAbstractAnimation) + + Q_INTERFACES(QmlParserStatus); + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged); + Q_PROPERTY(bool finishPlaying READ finishPlaying WRITE setFinishPlaying NOTIFY finishPlayingChanged()); + Q_PROPERTY(bool repeat READ repeat WRITE setRepeat NOTIFY repeatChanged); + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged); + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY targetChanged); + Q_CLASSINFO("DefaultMethod", "start()"); + Q_INTERFACES(QmlParserStatus) + +public: + QmlAbstractAnimation(QObject *parent=0); + virtual ~QmlAbstractAnimation(); + + bool isRunning() const; + void setRunning(bool); + bool finishPlaying() const; + void setFinishPlaying(bool); + bool repeat() const; + void setRepeat(bool); + + QmlAnimationGroup *group() const; + void setGroup(QmlAnimationGroup *); + + QObject *target() const; + void setTarget(QObject *); + QString property() const; + void setProperty(const QString &); + + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void started(); + void completed(); + void runningChanged(bool); + void repeatChanged(bool); + void targetChanged(QObject *, const QString &); + void finishPlayingChanged(bool); + +public Q_SLOTS: + void restart(); + void start(); + void stop(); + void complete(); + +protected: + virtual void setTarget(const QmlMetaProperty &); + QmlAbstractAnimation(QmlAbstractAnimationPrivate &dd, QObject *parent); + +public: + enum TransitionDirection { Forward, Backward }; + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual void prepare(QmlMetaProperty &); + virtual QAbstractAnimation *qtAnimation() = 0; + +private Q_SLOTS: + void timelineComplete(); +}; + +QML_DECLARE_TYPE(QmlAbstractAnimation); + +class QmlPauseAnimationPrivate; +class QmlPauseAnimation : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPauseAnimation); + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged); + +public: + QmlPauseAnimation(QObject *parent=0); + virtual ~QmlPauseAnimation(); + + int duration() const; + void setDuration(int); + +Q_SIGNALS: + void durationChanged(int); + +protected: + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); +}; +QML_DECLARE_TYPE(QmlPauseAnimation); + +class QmlColorAnimationPrivate; +class QmlColorAnimation : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlColorAnimation); + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged); + Q_PROPERTY(QColor from READ from WRITE setFrom NOTIFY fromChanged); + Q_PROPERTY(QColor to READ to WRITE setTo NOTIFY toChanged); + Q_PROPERTY(QString easing READ easing WRITE setEasing NOTIFY easingChanged); + Q_PROPERTY(QList<QObject *>* filter READ filter); + Q_PROPERTY(QList<QObject *>* exclude READ exclude); + +public: + QmlColorAnimation(QObject *parent=0); + virtual ~QmlColorAnimation(); + + int duration() const; + void setDuration(int); + + QColor from() const; + void setFrom(const QColor &); + + QColor to() const; + void setTo(const QColor &); + + QString easing() const; + void setEasing(const QString &); + + QList<QObject *> *filter(); + + QList<QObject *> *exclude(); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); + +Q_SIGNALS: + void durationChanged(int); + void fromChanged(const QColor &); + void toChanged(const QColor &); + void easingChanged(const QString &); +}; +QML_DECLARE_TYPE(QmlColorAnimation); + +class QmlRunScriptActionPrivate; +class QmlRunScriptAction : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlRunScriptAction); + + Q_PROPERTY(QString script READ script WRITE setScript NOTIFY scriptChanged); + Q_PROPERTY(QString file READ file WRITE setFile NOTIFY fileChanged); + +public: + QmlRunScriptAction(QObject *parent=0); + virtual ~QmlRunScriptAction(); + + QString script() const; + void setScript(const QString &); + + QString file() const; + void setFile(const QString &); + +Q_SIGNALS: + void fileChanged(const QString &); + void scriptChanged(const QString &); + +protected: + virtual QAbstractAnimation *qtAnimation(); +}; +QML_DECLARE_TYPE(QmlRunScriptAction); + +class QmlSetPropertyActionPrivate; +class QmlSetPropertyAction : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlSetPropertyAction); + + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged); + Q_PROPERTY(QList<QObject *>* filter READ filter); + Q_PROPERTY(QList<QObject *>* exclude READ exclude); + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged); + +public: + QmlSetPropertyAction(QObject *parent=0); + virtual ~QmlSetPropertyAction(); + + QString properties() const; + void setProperties(const QString &); + + QList<QObject *> *filter(); + QList<QObject *> *exclude(); + + QVariant value() const; + void setValue(const QVariant &); + +Q_SIGNALS: + void valueChanged(const QVariant &); + void propertiesChanged(const QString &); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); +}; +QML_DECLARE_TYPE(QmlSetPropertyAction); + +class QmlParentChangeActionPrivate; +class QmlParentChangeAction : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlParentChangeAction); + + //XXX should have parent property as well for when it isn't part of a transition + +public: + QmlParentChangeAction(QObject *parent=0); + virtual ~QmlParentChangeAction(); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); +}; +QML_DECLARE_TYPE(QmlParentChangeAction); + +class QmlNumericAnimationPrivate; +class QmlNumericAnimation : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlNumericAnimation); + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged); + Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged); + Q_PROPERTY(qreal to READ to WRITE setTo NOTIFY toChanged); + Q_PROPERTY(QString easing READ easing WRITE setEasing NOTIFY easingChanged); + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged); + Q_PROPERTY(QList<QObject *>* filter READ filter); + Q_PROPERTY(QList<QObject *>* exclude READ exclude); + +public: + QmlNumericAnimation(QObject *parent=0); + virtual ~QmlNumericAnimation(); + + int duration() const; + void setDuration(int); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); + + QString easing() const; + void setEasing(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QList<QObject *> *filter(); + QList<QObject *> *exclude(); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); + +Q_SIGNALS: + void durationChanged(int); + void fromChanged(qreal); + void toChanged(qreal); + void easingChanged(const QString &); + void propertiesChanged(const QString &); +}; +QML_DECLARE_TYPE(QmlNumericAnimation); + +#if 0 +class QmlDiscreteAnimation : public QmlAbstractAnimation +{ +Q_OBJECT +}; +#endif + +class QmlAnimationGroupPrivate; +class QmlAnimationGroup : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlAnimationGroup); + + Q_CLASSINFO("DefaultProperty", "animations"); + Q_PROPERTY(QmlList<QmlAbstractAnimation *> *animations READ animations); + +public: + QmlAnimationGroup(QObject *parent); + virtual ~QmlAnimationGroup(); + + QmlList<QmlAbstractAnimation *>* animations(); +}; + +class QmlSequentialAnimation : public QmlAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlAnimationGroup); + +public: + QmlSequentialAnimation(QObject *parent=0); + virtual ~QmlSequentialAnimation(); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); +}; +QML_DECLARE_TYPE(QmlSequentialAnimation); + +class QmlParallelAnimation : public QmlAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlAnimationGroup); + +public: + QmlParallelAnimation(QObject *parent=0); + virtual ~QmlParallelAnimation(); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); +}; +QML_DECLARE_TYPE(QmlParallelAnimation); + +class QmlVariantAnimationPrivate; +class QmlVariantAnimation : public QmlAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlVariantAnimation); + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged); + Q_PROPERTY(QVariant from READ from WRITE setFrom NOTIFY fromChanged); + Q_PROPERTY(QVariant to READ to WRITE setTo NOTIFY toChanged); + Q_PROPERTY(QString easing READ easing WRITE setEasing NOTIFY easingChanged); + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged); + Q_PROPERTY(QList<QObject *>* filter READ filter); + Q_PROPERTY(QList<QObject *>* exclude READ exclude); + +public: + QmlVariantAnimation(QObject *parent=0); + virtual ~QmlVariantAnimation(); + + int duration() const; + void setDuration(int); + + QVariant from() const; + void setFrom(const QVariant &); + + QVariant to() const; + void setTo(const QVariant &); + + QString easing() const; + void setEasing(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QList<QObject *> *filter(); + QList<QObject *> *exclude(); + +protected: + virtual void transition(QmlStateActions &actions, + QmlMetaProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + virtual void prepare(QmlMetaProperty &); + +Q_SIGNALS: + void durationChanged(int); + void fromChanged(QVariant); + void toChanged(QVariant); + void easingChanged(const QString &); + void propertiesChanged(const QString &); +}; +QML_DECLARE_TYPE(QmlVariantAnimation); + +#endif // QMLANIMATION_H + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qmlanimation_p.h b/src/declarative/util/qmlanimation_p.h new file mode 100644 index 0000000..db7cb18 --- /dev/null +++ b/src/declarative/util/qmlanimation_p.h @@ -0,0 +1,374 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLANIMATION_P_H +#define QMLANIMATION_P_H + +#include <private/qobject_p.h> +#include <private/qmlnullablevalue_p.h> +#include <qmlanimation.h> +#include <qml.h> +#include <qmlcontext.h> +#include <private/qvariantanimation_p.h> +#include <QPauseAnimation> +#include <QVariantAnimation> +#include <QAnimationGroup> +#include <QColor> +#include <gfxvalueproxy.h> + +QT_BEGIN_NAMESPACE + +//interface for classes that provide animation actions for QActionAnimation +class QAbstractAnimationAction +{ +public: + virtual ~QAbstractAnimationAction() {} + virtual void doAction() = 0; +}; + +//templated animation action +//allows us to specify an action that calls a function of a class. +//(so that class doesn't have to inherit QmlAbstractAnimationAction) +template<class T, void (T::*method)()> +class QAnimationActionProxy : public QAbstractAnimationAction +{ +public: + QAnimationActionProxy(T *p) : m_p(p) {} + virtual void doAction() { (m_p->*method)(); } + +private: + T *m_p; +}; + +//performs an action of type QAbstractAnimationAction +class QActionAnimation : public QAbstractAnimation +{ +public: + QActionAnimation(QObject *parent = 0) : QAbstractAnimation(parent), animAction(0), policy(KeepWhenStopped) {} + QActionAnimation(QAbstractAnimationAction *action, QObject *parent = 0) + : QAbstractAnimation(parent), animAction(action), policy(KeepWhenStopped) {} + virtual int duration() const { return 0; } + void setAnimAction(QAbstractAnimationAction *action, DeletionPolicy p) + { + if (state() == Running) + stop(); + animAction = action; + policy = p; + } +protected: + virtual void updateCurrentTime(int) {} + + virtual void updateState(State /*oldState*/, State newState) + { + if (newState == Running) { + if (animAction) + animAction->doAction(); + } else if (newState == Stopped && policy == DeleteWhenStopped) { + delete animAction; + animAction = 0; + } + } + +private: + QAbstractAnimationAction *animAction; + DeletionPolicy policy; +}; + +//animates GfxValue (assumes start and end values will be reals or compatible) +class GfxValueAnimator : public QVariantAnimation +{ +public: + GfxValueAnimator(QObject *parent = 0) : QVariantAnimation(parent), animValue(0), policy(KeepWhenStopped) {} + GfxValueAnimator(GfxValue *value, QObject *parent = 0) : QVariantAnimation(parent), animValue(value), policy(KeepWhenStopped) {} + void setAnimValue(GfxValue *value, DeletionPolicy p) + { + if (state() == Running) + stop(); + animValue = value; + policy = p; + } +protected: + virtual void updateCurrentValue(const QVariant &value) + { + if (animValue) + animValue->setValue(value.toDouble()); + } + virtual void updateState(State oldState, State newState) + { + QVariantAnimation::updateState(oldState, newState); + if (newState == Stopped && policy == DeleteWhenStopped) { + delete animValue; + animValue = 0; + } + } + +private: + GfxValue *animValue; + DeletionPolicy policy; +}; + +//an animation that just gives a tick +template<class T, void (T::*method)(int)> +class QTickAnimationProxy : public QAbstractAnimation +{ +public: + QTickAnimationProxy(T *p, QObject *parent = 0) : QAbstractAnimation(parent), m_p(p) {} + virtual int duration() const { return -1; } +protected: + virtual void updateCurrentTime(int msec) { (m_p->*method)(msec); } + +private: + T *m_p; +}; + +class QmlAbstractAnimationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlAbstractAnimation); +public: + QmlAbstractAnimationPrivate() + : running(false), finishPlaying(false), repeat(false), + connectedTimeLine(false), componentComplete(true), startOnCompletion(false), + target(0), group(0) {} + + bool running; + bool finishPlaying; + bool repeat; + bool connectedTimeLine; + + bool componentComplete; + bool startOnCompletion; + + void commence(); + + QmlNullableValue<QmlMetaProperty> userProperty; + QObject *target; + QString propertyName; + + QmlMetaProperty property; + QmlAnimationGroup *group; +}; + +class QmlPauseAnimationPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlPauseAnimation); +public: + QmlPauseAnimationPrivate() + : QmlAbstractAnimationPrivate(), pa(0) {} + + void init(); + + QPauseAnimation *pa; +}; + +class QmlColorAnimationPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlColorAnimation); +public: + QmlColorAnimationPrivate() + : QmlAbstractAnimationPrivate(), fromSourced(false), ca(0), value(this, &QmlColorAnimationPrivate::valueChanged) {} + + void init(); + + QString easing; + + QList<QObject *> filter; + QList<QObject *> exclude; + bool fromSourced; + QColor fromValue; + QColor toValue; + GfxValueAnimator *ca; + virtual void valueChanged(qreal); + + GfxValueProxy<QmlColorAnimationPrivate> value; +}; + +class QmlRunScriptActionPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlRunScriptAction); +public: + QmlRunScriptActionPrivate() + : QmlAbstractAnimationPrivate(), ctxt(QmlContext::activeContext()), proxy(this), rsa(0) {} + + void init(); + + QString script; + QString file; + QmlContext* ctxt; + + void execute(); + + QAnimationActionProxy<QmlRunScriptActionPrivate, + &QmlRunScriptActionPrivate::execute> proxy; + QActionAnimation *rsa; +}; + +class QmlSetPropertyActionPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlSetPropertyAction); +public: + QmlSetPropertyActionPrivate() + : QmlAbstractAnimationPrivate(), proxy(this), spa(0) {} + + void init(); + + QString properties; + QList<QObject *> filter; + QList<QObject *> exclude; + + QmlNullableValue<QVariant> value; + + void doAction(); + + QAnimationActionProxy<QmlSetPropertyActionPrivate, + &QmlSetPropertyActionPrivate::doAction> proxy; + QActionAnimation *spa; +}; + +class QmlParentChangeActionPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlParentChangeAction); +public: + QmlParentChangeActionPrivate() + : QmlAbstractAnimationPrivate() {} + + void init(); + + void doAction(); + QActionAnimation *cpa; +}; + +class QmlNumericAnimationPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlNumericAnimation); +public: + QmlNumericAnimationPrivate() + : QmlAbstractAnimationPrivate(), fromSourced(false), na(0), value(this, &QmlNumericAnimationPrivate::valueChanged) {} + + void init(); + + QmlNullableValue<qreal> from; + QmlNullableValue<qreal> to; + + QString easing; + + QString properties; + QList<QObject *> filter; + QList<QObject *> exclude; + + bool fromSourced; + qreal fromValue; + GfxValueAnimator *na; + virtual void valueChanged(qreal); + + GfxValueProxy<QmlNumericAnimationPrivate> value; +}; + +class QmlAnimationGroupPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlAnimationGroup); +public: + QmlAnimationGroupPrivate() + : QmlAbstractAnimationPrivate(), animations(this), ag(0) {} + + struct AnimationList : public QmlConcreteList<QmlAbstractAnimation *> + { + AnimationList(QmlAnimationGroupPrivate *p) + : anim(p) {} + virtual void append(QmlAbstractAnimation *a) { + QmlConcreteList<QmlAbstractAnimation *>::append(a); + a->setGroup(anim->q_func()); + } + virtual void clear() + { + for (int i = 0; i < count(); ++i) + at(i)->setGroup(0); + QmlConcreteList<QmlAbstractAnimation *>::clear(); + } + virtual void removeAt(int i) + { + at(i)->setGroup(0); + QmlConcreteList<QmlAbstractAnimation *>::removeAt(i); + } + virtual void insert(int i, QmlAbstractAnimation *a) + { + QmlConcreteList<QmlAbstractAnimation *>::insert(i, a); + a->setGroup(anim->q_func()); + } + + QmlAnimationGroupPrivate *anim; + }; + + AnimationList animations; + QAnimationGroup *ag; +}; + +class QmlVariantAnimationPrivate : public QmlAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QmlVariantAnimation); +public: + QmlVariantAnimationPrivate() + : QmlAbstractAnimationPrivate(), fromSourced(false), va(0), value(this, &QmlVariantAnimationPrivate::valueChanged) {} + + void init(); + + QmlNullableValue<QVariant> from; + QmlNullableValue<QVariant> to; + + QString easing; + + QString properties; + QList<QObject *> filter; + QList<QObject *> exclude; + + bool fromSourced; + QVariant fromValue; + GfxValueAnimator *va; + virtual void valueChanged(qreal); + + GfxValueProxy<QmlVariantAnimationPrivate> value; + + static QVariant interpolateVariant(const QVariant &from, const QVariant &to, qreal progress); + static void convertVariant(QVariant &variant, QVariant::Type type); +}; + +#endif // QMLANIMATION_P_H + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlbehaviour.cpp b/src/declarative/util/qmlbehaviour.cpp new file mode 100644 index 0000000..3169f63 --- /dev/null +++ b/src/declarative/util/qmlbehaviour.cpp @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <private/qobject_p.h> +#include "qmlanimation.h" +#include "qmltransition.h" +#include "qmlbehaviour.h" +#include <QtDeclarative/qmlcontext.h> + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QmlBehaviour,Behaviour); + + +class QmlBehaviourData : public QObject +{ +Q_OBJECT +public: + QmlBehaviourData(QObject *parent) + : QObject(parent) {} + + Q_PROPERTY(QVariant endValue READ endValue NOTIFY valuesChanged); + Q_PROPERTY(QVariant startValue READ startValue NOTIFY valuesChanged); + QVariant endValue() const { return e; } + QVariant startValue() const { return s; } + + QVariant e; + QVariant s; + +Q_SIGNALS: + void valuesChanged(); + +private: + friend class QmlBehaviour; +}; + +class QmlBehaviourPrivate : public QObjectPrivate +{ +public: + QmlBehaviourPrivate() + : context(0), oldContext(0), valueData(0), operations(this) {} + QmlMetaProperty property; + QVariant currentValue; + + QVariant fromValue; + QVariant toValue; + QmlContext *context; + QmlContext *oldContext; + QmlBehaviourData *valueData; + class AnimationList : public QmlConcreteList<QmlAbstractAnimation *> + { + public: + AnimationList(QmlBehaviourPrivate *parent) : _parent(parent) {} + virtual void append(QmlAbstractAnimation *a) + { + QmlConcreteList<QmlAbstractAnimation *>::append(a); + _parent->group->addAnimation(a->qtAnimation()); + } + virtual void clear() { QmlConcreteList<QmlAbstractAnimation *>::clear(); } //### + private: + QmlBehaviourPrivate *_parent; + }; + AnimationList operations; + QSequentialAnimationGroup *group; +}; + +/*! + \qmlclass Behaviour QmlBehaviour + \brief The Behaviour element allows you to specify a default animation for a property change. + + In example below, Rect1 will use a bounce easing curve over 200 millisecond for any changes to its y property: + \code + <Rect id="Rect1" y="200" width="20" height="20" color="#00ff00"> + <y> + <Behaviour> + <NumericAnimation easing="easeOutBounce(amplitude:100)" duration="200" /> + </Behaviour> + </y> + </Rect> + \endcode +*/ + +QmlBehaviour::QmlBehaviour(QObject *parent) +: QmlPropertyValueSource(*(new QmlBehaviourPrivate), parent) +{ + Q_D(QmlBehaviour); + d->valueData = new QmlBehaviourData(this); + d->context = new QmlContext(QmlContext::activeContext(), this); + d->context->addDefaultObject(d->valueData); + d->group = new QSequentialAnimationGroup(this); +} + +/*! + \qmlproperty QVariant Behaviour::fromValue + This property holds a selector specifying a starting value for the behaviour + + If you only want the behaviour to apply when the change starts at a + specific value you can specify fromValue. This selector is used in conjunction + with the toValue selector. +*/ + +QVariant QmlBehaviour::fromValue() const +{ + Q_D(const QmlBehaviour); + return d->fromValue; +} + +void QmlBehaviour::setFromValue(const QVariant &v) +{ + Q_D(QmlBehaviour); + d->fromValue = v; +} + +/*! + \qmlproperty QVariant Behaviour::toValue + This property holds a selector specifying a ending value for the behaviour + + If you only want the behaviour to apply when the change ends at a + specific value you can specify toValue. This selector is used in conjunction + with the fromValue selector. +*/ + +QVariant QmlBehaviour::toValue() const +{ + Q_D(const QmlBehaviour); + return d->toValue; +} + +void QmlBehaviour::setToValue(const QVariant &v) +{ + Q_D(QmlBehaviour); + d->toValue = v; +} + +QmlList<QmlAbstractAnimation *>* QmlBehaviour::operations() +{ + Q_D(QmlBehaviour); + return &d->operations; +} + +QmlBehaviour::~QmlBehaviour() +{ + //### do we need any other cleanup here? +} + +bool QmlBehaviour::_ignore = false; +void QmlBehaviour::propertyValueChanged() +{ + Q_D(QmlBehaviour); + if(_ignore) + return; + + QVariant newValue = d->property.read(); + + if((!fromValue().isValid() || fromValue() == d->currentValue) && + (!toValue().isValid() || toValue() == newValue)) { + + //### does this clean up everything needed? + d->group->stop(); + + d->valueData->e = newValue; + d->valueData->s = d->currentValue; + emit d->valueData->valuesChanged(); + + QmlStateOperation::ActionList actions; + Action action; + action.property = d->property; + action.fromValue = d->currentValue; + action.toValue = newValue; + actions << action; + + _ignore = true; + d->property.write(d->currentValue); + + QList<QmlMetaProperty> after; + for(int ii = 0; ii < d->operations.count(); ++ii) { + d->operations.at(ii)->transition(actions, after, QmlAbstractAnimation::Forward); + } + d->group->start(); + if(!after.contains(d->property)) + d->property.write(newValue); + _ignore = false; + } + + d->currentValue = newValue; +} + +void QmlBehaviour::setTarget(const QmlMetaProperty &property) +{ + Q_D(QmlBehaviour); + d->property = property; + d->currentValue = property.read(); + d->property.connectNotifier(this, SLOT(propertyValueChanged())); +} + +void QmlBehaviour::classBegin() +{ + Q_D(QmlBehaviour); + d->context->activate(); +} + +void QmlBehaviour::classComplete() +{ + Q_D(QmlBehaviour); + d->context->deactivate(); +} + +#include "qmlbehaviour.moc" + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlbehaviour.h b/src/declarative/util/qmlbehaviour.h new file mode 100644 index 0000000..080423a --- /dev/null +++ b/src/declarative/util/qmlbehaviour.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLBEHAVIOUR_H +#define QMLBEHAVIOUR_H + +#include <qmlpropertyvaluesource.h> +#include <qml.h> +#include <qmlstate.h> +#include <gfxtimeline.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlAbstractAnimation; +class QmlBehaviourPrivate; +class Q_DECLARATIVE_EXPORT QmlBehaviour : public QmlPropertyValueSource, + public QmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlBehaviour) + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QVariant from READ fromValue WRITE setFromValue); + Q_PROPERTY(QVariant to READ toValue WRITE setToValue); + Q_CLASSINFO("DefaultProperty", "operations"); + Q_PROPERTY(QmlList<QmlAbstractAnimation *>* operations READ operations); + +public: + QmlBehaviour(QObject *parent=0); + ~QmlBehaviour(); + + QVariant fromValue() const; + void setFromValue(const QVariant &); + QVariant toValue() const; + void setToValue(const QVariant &); + virtual void setTarget(const QmlMetaProperty &); + + QmlList<QmlAbstractAnimation *>* operations(); + + static bool _ignore; + +protected: + virtual void classBegin(); + virtual void classComplete(); + +private Q_SLOTS: + void propertyValueChanged(); +}; +QML_DECLARE_TYPE(QmlBehaviour); + + +#endif // QMLBEHAVIOUR_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qmlbind.cpp b/src/declarative/util/qmlbind.cpp new file mode 100644 index 0000000..d71d711 --- /dev/null +++ b/src/declarative/util/qmlbind.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 QtDeclarative 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 <qmlbindablevalue.h> +#include <QtDeclarative/qmlengine.h> +#include <QtDeclarative/qmlcontext.h> +#include <private/qobject_p.h> +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtScript/qscriptvalue.h> +#include <QtScript/qscriptcontext.h> +#include <QtScript/qscriptengine.h> +#include <private/qmlnullablevalue_p.h> +#include "qmlbind.h" + +QT_BEGIN_NAMESPACE +class QmlBindPrivate : public QObjectPrivate +{ +public: + QmlBindPrivate() : when(true), obj(0) {} + + bool when; + QObject *obj; + QString prop; + QmlNullableValue<QVariant> value; +}; + +QML_DEFINE_TYPE(QmlBind,Bind); +/*! + \qmlclass Bind QmlBind + \brief The Bind element allows arbitrary property bindings to be created. + + Sometimes it is necessary to bind to a property of an object that wasn't + directly instantiated by QML - generally a property of a class exported + to QML by C++. In these cases, regular property binding doesn't work. Bind + allows you to bind any value to any property. + + For example, imagine a C++ application that maps an "app.enteredText" + property into QML. You could use Bind to update the enteredText property + like this. + \code + <LineEdit id="myTextField" text="Please type here..." /> + <Bind target="{app}" property="enteredText" value="{myTextField.text}" /> + \endcode + Whenever the text in the LineEdit is updated, the C++ property will be + updated also. + + If the bind target or bind property is changed, the bound value is + immediately pushed onto the new target. + + \sa {qmlforcpp}{Qt Declarative Markup Language For C++ Programmers} + */ +/*! + \internal + \class QmlBind + \ingroup utility + \brief The QmlBind class allows arbitrary property bindings to be created. + + Simple bindings are usually earier to do in-place rather than creating a + QmlBind item. For that reason, QmlBind is usually used to transfer property information + from Qml to C++. + + \sa cppqml + */ +QmlBind::QmlBind(QObject *parent) + : QObject(*(new QmlBindPrivate), parent) +{ +} + +QmlBind::~QmlBind() +{ +} + +bool QmlBind::when() const +{ + Q_D(const QmlBind); + return d->when; +} + +void QmlBind::setWhen(bool v) +{ + Q_D(QmlBind); + d->when = v; + eval(); +} + +/*! + \qmlproperty Object Bind::target + + The object to be updated. + */ +/*! + \property QmlBind::target + \brief the object to be updated. +*/ +QObject *QmlBind::object() +{ + Q_D(const QmlBind); + return d->obj; +} + +void QmlBind::setObject(QObject *obj) +{ + Q_D(QmlBind); + d->obj = obj; + eval(); +} + +/*! + \qmlproperty string Bind::property + + The property to be updated. + */ +/*! + \property QmlBind::property + \brief the property of the target to be updated. +*/ +QString QmlBind::property() const +{ + Q_D(const QmlBind); + return d->prop; +} + +void QmlBind::setProperty(const QString &p) +{ + Q_D(QmlBind); + d->prop = p; + eval(); +} + +/*! + \qmlproperty any Bind::value + + The value to be set on the target object and property. This can be a + constant (which isn't very useful), or a bound expression. + */ +/*! + \property QmlBind::value + \brief the value to bind to. +*/ +QVariant QmlBind::value() const +{ + Q_D(const QmlBind); + return d->value.value; +} + +void QmlBind::setValue(const QVariant &v) +{ + Q_D(QmlBind); + d->value.value = v; + d->value.isNull = false; + eval(); +} + +void QmlBind::eval() +{ + Q_D(QmlBind); + if(!d->obj || d->value.isNull || !d->when) + return; + + QmlMetaProperty prop(d->obj, d->prop); + prop.write(d->value.value); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlbind.h b/src/declarative/util/qmlbind.h new file mode 100644 index 0000000..355edfd --- /dev/null +++ b/src/declarative/util/qmlbind.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 QtDeclarative 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 QMLBIND_H +#define QMLBIND_H + +#include <qfxglobal.h> +#include <QtCore/qobject.h> +#include "qml.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlBindPrivate; +class Q_DECLARATIVE_EXPORT QmlBind : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlBind); + + Q_PROPERTY(QObject *target READ object WRITE setObject); + Q_PROPERTY(QString property READ property WRITE setProperty); + Q_PROPERTY(QVariant value READ value WRITE setValue); + +public: + QmlBind(QObject *parent=0); + ~QmlBind(); + + Q_PROPERTY(bool when READ when WRITE setWhen); + bool when() const; + void setWhen(bool); + + QObject *object(); + void setObject(QObject *); + + QString property() const; + void setProperty(const QString &); + + QVariant value() const; + void setValue(const QVariant &); + +private: + void eval(); +}; +QML_DECLARE_TYPE(QmlBind); + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/util/qmlconnection.cpp b/src/declarative/util/qmlconnection.cpp new file mode 100644 index 0000000..df45a31 --- /dev/null +++ b/src/declarative/util/qmlconnection.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlconnection.h" +#include <QtDeclarative/qmlexpression.h> +#include "private/qmlboundsignal_p.h" +#include "private/qobject_p.h" +#include <QtDeclarative/qmlcontext.h> +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE +class QmlConnectionPrivate : public QObjectPrivate +{ +public: + QmlConnectionPrivate() : ctxt(0), boundsignal(0), signalSender(0), componentcomplete(false) {} + + QmlContext *ctxt; + QmlBoundSignal *boundsignal; + QObject *signalSender; + QString script; + QString signal; + bool componentcomplete; +}; + +/*! + \qmlclass Connection QmlConnection + \brief The Connection element describes generalized connections to signals. + + JavaScript-in-HTML style \l {qmlformatsignalscpp}{signal properties} do not allow: + \list + \i connecting to signals with the same name but different parameters + \i conformance checking that parameters are correctly named + \i multiple connections to the same signal + \i connections outside the scope of the signal sender + \i signals in classes with coincidentally-named on<Signal> properties + \endlist + + When any of these is needed, the Connection element can be used instead. + Where a signal could be connected like this: + + \code + <MouseRegion onClicked="foo(x+123,y+456)" /> + \endcode + + An equivalent binding can be made with a Connection element: + + \code + <MouseRegion> + <Connection signal="clicked(x,y)" script="foo(x+123,y+456)" /> + </MouseRegion> + \endcode + + More generally, the Connection element can be a child of some other element than + the sender of the signal, and the script is the default attribute: + + \code + <MouseRegion id="mr"/> + ... + <Connection sender="{mr}" signal="clicked(x,y)"> + foo(x+123,y+456) + </Connection> + \endcode +*/ + +/*! + \internal + \class QmlConnection + \brief The QmlConnection class describes generalized connections to signals. + + QmlSetProperties is a mechanism for connecting a script to be run when + some object sends a signal. +*/ +QmlConnection::QmlConnection(QObject *parent) : + QObject(*(new QmlConnectionPrivate), parent) +{ + Q_D(QmlConnection); + d->ctxt = QmlContext::activeContext(); +} + +QmlConnection::~QmlConnection() +{ + Q_D(QmlConnection); + delete d->boundsignal; +} + +/*! + \qmlproperty Object Connection::sender + This property holds the object that sends the signal. + + By default, the sender is assumed to be the parent of the Connection. +*/ + +/*! + \property QmlConnection::sender + \brief the object that sends the signal. + + By default, the sender is assumed to be the parent of the Connection. + + Note that the set/get methods are setSignalSender() and signalSender(), + due to the pre-existence of QObject::sender(). +*/ +QObject *QmlConnection::signalSender() const +{ + Q_D(const QmlConnection); + return d->signalSender ? d->signalSender : parent(); +} + +void QmlConnection::setSignalSender(QObject *obj) +{ + Q_D(QmlConnection); + if (d->signalSender == obj) + return; + disconnectIfValid(); + d->signalSender = obj; + connectIfValid(); +} + +void QmlConnection::connectIfValid() +{ + Q_D(QmlConnection); + if (!d->componentcomplete) + return; + // boundsignal must not exist + if ((d->signalSender || parent()) && !d->signal.isEmpty() && !d->script.isEmpty()) { + // create + // XXX scope? + int sigIdx = -1; + int lparen = d->signal.indexOf(QLatin1Char('(')); + QList<QByteArray> sigparams; + if (lparen >= 0 && d->signal.length() > lparen+2) { + QStringList l = d->signal.mid(lparen+1,d->signal.length()-lparen-2).split(QLatin1Char(',')); + foreach (QString s, l) { + sigparams.append(s.toLatin1()); + } + } + QString signalname = d->signal.left(lparen); + QObject *sender = d->signalSender ? d->signalSender : parent(); + const QMetaObject *mo = sender->metaObject(); + int methods = mo->methodCount(); + for(int ii = 0; ii < methods; ++ii) { + QMetaMethod method = mo->method(ii); + QString methodName = QLatin1String(method.signature()); + int idx = methodName.indexOf(QLatin1Char('(')); + methodName = methodName.left(idx); + if(methodName == signalname && (lparen<0 || method.parameterNames() == sigparams)) { + sigIdx = ii; + break; + } + } + if (sigIdx < 0) { + qWarning() << "signal" << d->signal << "not found"; + return; + } + + if (sigparams.isEmpty()) + d->boundsignal = new QmlBoundSignal(d->ctxt, d->script, sender, sigIdx, this); + else + d->boundsignal = new QmlBoundSignalProxy(new QmlContext(d->ctxt,this), d->script, sender, sigIdx, this); + } +} + +void QmlConnection::disconnectIfValid() +{ + Q_D(QmlConnection); + if (!d->componentcomplete) + return; + if ((d->signalSender || parent()) && !d->signal.isEmpty() && !d->script.isEmpty()) { + // boundsignal must exist + // destroy + delete d->boundsignal; + d->boundsignal = 0; + } +} + +void QmlConnection::componentComplete() +{ + Q_D(QmlConnection); + d->componentcomplete=true; + connectIfValid(); +} + + +/*! + \qmlproperty string Connection::script + This property holds the JavaScript executed whenever the signal is sent. + + This is the default attribute of Connection. +*/ + +/*! + \property QmlConnection::script + \brief the JavaScript executed whenever the signal is sent. +*/ +QString QmlConnection::script() const +{ + Q_D(const QmlConnection); + return d->script; +} + +void QmlConnection::setScript(const QString& script) +{ + Q_D(QmlConnection); + if ((d->signalSender || parent()) && !d->signal.isEmpty()) { + if (d->script.isEmpty()) { + // mustn't exist - create + d->script = script; + connectIfValid(); + } else { + // must exist - update + d->script = script; + d->boundsignal->setExpression(script); + } + } else { + d->script = script; + } +} + +/*! + \qmlproperty string Connection::signal + This property holds the signal from the sender to which the script is attached. + + The signal must have its formal parameter names given in parentheses: + + \code + <Connection signal="clicked(x,y)" ... /> + \endcode +*/ + +/*! + \property QmlConnection::signal + \brief the signal from the sender to which the script is attached. +*/ +QString QmlConnection::signal() const +{ + Q_D(const QmlConnection); + return d->signal; +} + +void QmlConnection::setSignal(const QString& sig) +{ + Q_D(QmlConnection); + if (d->signal == sig) + return; + disconnectIfValid(); + d->signal = sig; + connectIfValid(); +} + +QML_DEFINE_TYPE(QmlConnection,Connection); + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlconnection.h b/src/declarative/util/qmlconnection.h new file mode 100644 index 0000000..c943092 --- /dev/null +++ b/src/declarative/util/qmlconnection.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 QtDeclarative 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 QMLCONNECTION_H +#define QMLCONNECTION_H + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtDeclarative/qml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlBoundSignal; +class QmlContext; +class QmlConnectionPrivate; +class Q_DECLARATIVE_EXPORT QmlConnection : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlConnection) + + Q_INTERFACES(QmlParserStatus); + Q_CLASSINFO("DefaultProperty", "script"); + Q_PROPERTY(QObject *sender READ signalSender WRITE setSignalSender); + Q_PROPERTY(QString script READ script WRITE setScript); + Q_PROPERTY(QString signal READ signal WRITE setSignal); + +public: + QmlConnection(QObject *parent=0); + ~QmlConnection(); + + QObject *signalSender() const; + void setSignalSender(QObject *); + QString script() const; + void setScript(const QString&); + QString signal() const; + void setSignal(const QString&); + +private: + void disconnectIfValid(); + void connectIfValid(); + void componentComplete(); +}; +QML_DECLARE_TYPE(QmlConnection); + +#endif + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qmldatetimeformatter.cpp b/src/declarative/util/qmldatetimeformatter.cpp new file mode 100644 index 0000000..138f68b --- /dev/null +++ b/src/declarative/util/qmldatetimeformatter.cpp @@ -0,0 +1,368 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmldatetimeformatter.h" +#include "private/qobject_p.h" +#include <QtCore/qlocale.h> + +QT_BEGIN_NAMESPACE +//TODO: may need optimisation as the QDateTime member may not be needed? +// be able to set a locale? + +class QmlDateTimeFormatterPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlDateTimeFormatter) +public: + QmlDateTimeFormatterPrivate() : locale(QLocale::system()), longStyle(false), classComplete(true) {} + + void updateText(); + + QDateTime dateTime; + QDate date; + QTime time; + QLocale locale; + QString dateTimeText; + QString dateText; + QString timeText; + QString dateTimeFormat; //set for convienience? + QString dateFormat; + QString timeFormat; + bool longStyle; + bool classComplete; +}; + +/*! + \qmlclass DateTimeFormatter QmlDateTimeFormatter + \brief The DateTimeFormatter allows you to control the format of a date string. + + \code + <DateTimeFormatter id="Formatter" date="{System.date}"/> + <Text text="{Formatter.dateText}"/> + \endcode + + By default, the text properties (dateText, timeText, and dateTimeText) will return the + date and time using the current system locale's format. +*/ + +/*! + \internal + \class QmlDateTimeFormatter + \ingroup utility + \brief The QmlDateTimeFormatter class allows you to format a date string. +*/ + +QmlDateTimeFormatter::QmlDateTimeFormatter(QObject *parent) +: QObject(*(new QmlDateTimeFormatterPrivate), parent) +{ +} + +QmlDateTimeFormatter::~QmlDateTimeFormatter() +{ +} + +/*! + \qmlproperty string DateTimeFormatter::dateText + \qmlproperty string DateTimeFormatter::timeText + \qmlproperty string DateTimeFormatter::dateTimeText + + Formatted text representations of the \c date, \c time, + and \c {date and time}, respectively. + + If there is no explictly specified format the DateTimeFormatter + will use the system locale's default 'short' setting. + + \code + <!-- specify source date (assuming today is February 19, 2009) --> + <DateTimeFormatter id="formatter" dateTime="{Today.date}"/> + + <!-- display the full date and time --> + <Text text="{formatter.dateText}"/> + \endcode + + Would be equivalent to the following for a US English locale: + + \code + <!-- display the date --> + <Text text="2/19/09"/> + \endcode +*/ +QString QmlDateTimeFormatter::dateTimeText() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateTimeText; +} + +QString QmlDateTimeFormatter::dateText() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateText; +} + +QString QmlDateTimeFormatter::timeText() const +{ + Q_D(const QmlDateTimeFormatter); + return d->timeText; +} + +/*! + \qmlproperty date DateTimeFormatter::date + \qmlproperty time DateTimeFormatter::time + \qmlproperty datetime DateTimeFormatter::dateTime + + The source date and time to be used by the formatter. + + \code + <!-- setting the date and time --> + <DateTimeFormatter date="{System.date}" time="{System.time}"/> + \endcode + + For convienience it is possible to set the datetime property to set both the date and the time. + \code + <!-- setting the datetime --> + <DateTimeFormatter dateTime="{System.dateTime}"/> + \endcode + + There can only be one instance of date and time per formatter; if date, time, and dateTime are all + set the actual date and time used is not guaranteed. + + \note If no date is set, dateTimeText will be just the date; + If no time is set, the dateTimeText will be just the time. + +*/ +QDate QmlDateTimeFormatter::date() const +{ + Q_D(const QmlDateTimeFormatter); + return d->date; +} + +QTime QmlDateTimeFormatter::time() const +{ + Q_D(const QmlDateTimeFormatter); + return d->time; +} + +QDateTime QmlDateTimeFormatter::dateTime() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateTime; +} + +/*! + \qmlproperty string DateTimeFormatter::dateFormat + \qmlproperty string DateTimeFormatter::timeFormat + \qmlproperty string DateTimeFormatter::dateTimeFormat + + Specifies a custom format which the DateTime Formatter can use. + + If there is no explictly specified format the DateTimeFormatter + will use the system locale's default 'short' setting. + + The text's format may be modified by setting: + \list + \i \c dateFormat + \i \c timeFormat + \i \c dateTimeFormat + \endlist + + If only the format for date is defined, the time and dateTime formats will be defined + as the system locale default and likewise for the others. + + Syntax for the format is based on the QDateTime::toString() formatting options. + + \code + <!-- Format the date such that the dateText is: '1997-12-12'> + <DateFormatter id="formatter" dateTime="{Today.dateTime}" formatDate="yyyy-MM-d"/> + \endcode + + Assigning an empty string to a particular format will reset it. +*/ +QString QmlDateTimeFormatter::dateTimeFormat() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateTimeFormat; +} + +QString QmlDateTimeFormatter::dateFormat() const +{ + Q_D(const QmlDateTimeFormatter); + return d->dateFormat; +} + +QString QmlDateTimeFormatter::timeFormat() const +{ + Q_D(const QmlDateTimeFormatter); + return d->timeFormat; +} + +/*! + \qmlproperty bool DateTimeFormatter::longStyle + + This property causes the formatter to use the system locale's long format rather than short format + by default. + + This setting is off by default. +*/ +bool QmlDateTimeFormatter::longStyle() const +{ + Q_D(const QmlDateTimeFormatter); + return d->longStyle; +} + +void QmlDateTimeFormatter::setDateTime(const QDateTime &dateTime) +{ + Q_D(QmlDateTimeFormatter); + if (d->dateTime == dateTime) + return; + d->dateTime = dateTime; + d->date = d->dateTime.date(); + d->time = d->dateTime.time(); + d->updateText(); +} + +void QmlDateTimeFormatter::setTime(const QTime &time) +{ + Q_D(QmlDateTimeFormatter); + if (d->dateTime.time() == time) + return; + d->time = time; + d->dateTime.setTime(time); + d->updateText(); +} + +void QmlDateTimeFormatter::setDate(const QDate &date) +{ + Q_D(QmlDateTimeFormatter); + if (d->dateTime.date() == date) + return; + d->date = date; + bool clearTime = d->dateTime.time().isValid() ? false : true; //because setting date generates default time + d->dateTime.setDate(date); + if (clearTime) + d->dateTime.setTime(QTime()); + d->updateText(); +} + +//DateTime formatting may be a combination of date and time? +void QmlDateTimeFormatter::setDateTimeFormat(const QString &format) +{ + Q_D(QmlDateTimeFormatter); + //no format checking + d->dateTimeFormat = format; + d->updateText(); +} + +void QmlDateTimeFormatter::setDateFormat(const QString &format) +{ + Q_D(QmlDateTimeFormatter); + //no format checking + d->dateFormat = format; + d->updateText(); +} + +void QmlDateTimeFormatter::setTimeFormat(const QString &format) +{ + Q_D(QmlDateTimeFormatter); + //no format checking + d->timeFormat = format; + d->updateText(); +} + +void QmlDateTimeFormatter::setLongStyle(bool longStyle) +{ + Q_D(QmlDateTimeFormatter); + d->longStyle = longStyle; + d->updateText(); +} + +void QmlDateTimeFormatterPrivate::updateText() +{ + Q_Q(QmlDateTimeFormatter); + if (!classComplete) + return; + + QString str; + QString str1; + QString str2; + + Qt::DateFormat defaultFormat = longStyle ? Qt::SystemLocaleLongDate : Qt::SystemLocaleShortDate; + + if (dateFormat.isEmpty()) + str1 = date.toString(defaultFormat); + else + str1 = date.toString(dateFormat); + + if (timeFormat.isEmpty()) + str2 = time.toString(defaultFormat); + else + str2 = time.toString(timeFormat); + + if (dateTimeFormat.isEmpty()) + str = dateTime.toString(defaultFormat); + //else if (!formatTime.isEmpty() && !formatDate.isEmpty()) + // str = str1 + QLatin1Char(' ') + str2; + else + str = dateTime.toString(dateTimeFormat); + + if (dateTimeText == str && dateText == str1 && timeText == str2) + return; + + dateTimeText = str; + dateText = str1; + timeText = str2; + + emit q->textChanged(); +} + +void QmlDateTimeFormatter::classBegin() +{ + Q_D(QmlDateTimeFormatter); + d->classComplete = false; +} + +void QmlDateTimeFormatter::classComplete() +{ + Q_D(QmlDateTimeFormatter); + d->classComplete = true; + d->updateText(); +} + +QML_DEFINE_TYPE(QmlDateTimeFormatter, DateTimeFormatter); +QT_END_NAMESPACE diff --git a/src/declarative/util/qmldatetimeformatter.h b/src/declarative/util/qmldatetimeformatter.h new file mode 100644 index 0000000..3421f8c --- /dev/null +++ b/src/declarative/util/qmldatetimeformatter.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLDATETIMEFORMATTER_H +#define QMLDATETIMEFORMATTER_H + +#include <QtCore/qdatetime.h> +#include <QtDeclarative/qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlDateTimeFormatterPrivate; +class Q_DECLARATIVE_EXPORT QmlDateTimeFormatter : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(QString dateText READ dateText NOTIFY textChanged) + Q_PROPERTY(QString timeText READ timeText NOTIFY textChanged) + Q_PROPERTY(QString dateTimeText READ dateTimeText NOTIFY textChanged) + Q_PROPERTY(QDate date READ date WRITE setDate) + Q_PROPERTY(QTime time READ time WRITE setTime) + Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime) + Q_PROPERTY(QString dateFormat READ dateFormat WRITE setDateFormat) + Q_PROPERTY(QString timeFormat READ timeFormat WRITE setTimeFormat) + Q_PROPERTY(QString dateTimeFormat READ dateTimeFormat WRITE setDateTimeFormat) + Q_PROPERTY(bool longStyle READ longStyle WRITE setLongStyle) +public: + QmlDateTimeFormatter(QObject *parent=0); + ~QmlDateTimeFormatter(); + + QString dateTimeText() const; + QString dateText() const; + QString timeText() const; + + QDate date() const; + void setDate(const QDate &); + + QTime time() const; + void setTime(const QTime &); + + QDateTime dateTime() const; + void setDateTime(const QDateTime &); + + QString dateTimeFormat() const; + void setDateTimeFormat(const QString &); + + QString dateFormat() const; + void setDateFormat(const QString &); + + QString timeFormat() const; + void setTimeFormat(const QString &); + + bool longStyle() const; + void setLongStyle(bool); + + virtual void classBegin(); + virtual void classComplete(); + +Q_SIGNALS: + void textChanged(); + +private: + Q_DISABLE_COPY(QmlDateTimeFormatter) + Q_DECLARE_PRIVATE(QmlDateTimeFormatter) +}; + +QML_DECLARE_TYPE(QmlDateTimeFormatter); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif diff --git a/src/declarative/util/qmlfollow.cpp b/src/declarative/util/qmlfollow.cpp new file mode 100644 index 0000000..c841b85 --- /dev/null +++ b/src/declarative/util/qmlfollow.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 QtDeclarative 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 <limits.h> +#include <QtCore/qdebug.h> +#include <gfxtimeline.h> +#include "private/qobject_p.h" +#include "qmlfollow.h" +#include "private/qmlanimation_p.h" + + +QT_BEGIN_NAMESPACE +QML_DEFINE_TYPE(QmlFollow,Follow); + +class QmlFollowPrivate : public QObjectPrivate +{ +public: + QmlFollowPrivate() + : sourceValue(0), maxVelocity(0), lastTime(0) + , mass(1.0), spring(0.), damping(0.), velocity(0), enabled(true), mode(Track), clock(this) {} + + QmlMetaProperty property; + qreal currentValue; + qreal sourceValue; + qreal maxVelocity; + qreal velocityms; + int lastTime; + qreal mass; + qreal spring; + qreal damping; + qreal velocity; + bool enabled; + + enum Mode { + Track, + Velocity, + Spring + }; + Mode mode; + + void tick(int); + void updateMode(); + void start(); + void stop(); + + QTickAnimationProxy<QmlFollowPrivate, &QmlFollowPrivate::tick> clock; +}; + +void QmlFollowPrivate::tick(int time) +{ + int elapsed = time - lastTime; + if (!elapsed) + return; + if (mode == Spring) { + if (elapsed < 10) // capped at 100fps. + return; + // Real men solve the spring DEs using RK4. + // We'll do something much simpler which gives a result that looks fine. + int count = (elapsed+5) / 10; + for (int i = 0; i < count; ++i) { + qreal diff = sourceValue - currentValue; + velocity = velocity + spring * diff - damping * velocity; + // The following line supports mass. Not sure its worth the extra divisions. + // velocity = velocity + spring / mass * diff - damping / mass * velocity; + if (maxVelocity > 0.) { + // limit velocity + if (velocity > maxVelocity) + velocity = maxVelocity; + else if (velocity < -maxVelocity) + velocity = -maxVelocity; + } + currentValue += velocity * 10.0 / 1000.0; + } + if (qAbs(velocity) < 0.5 && qAbs(sourceValue - currentValue) < 0.5) { + velocity = 0.0; + currentValue = sourceValue; + clock.stop(); + } + lastTime = time - (elapsed - count * 10); + } else { + qreal moveBy = elapsed * velocityms; + qreal diff = sourceValue - currentValue; + if (diff > 0) { + currentValue += moveBy; + if (currentValue > sourceValue) { + currentValue = sourceValue; + clock.stop(); + } + } else { + currentValue -= moveBy; + if (currentValue < sourceValue) { + currentValue = sourceValue; + clock.stop(); + } + } + lastTime = time; + } + property.write(currentValue); +} + +void QmlFollowPrivate::updateMode() +{ + if (spring == 0. && maxVelocity == 0.) + mode = Track; + else if (spring > 0.) + mode = Spring; + else + mode = Velocity; +} + +void QmlFollowPrivate::start() +{ + if (!enabled) + return; + + if (mode == QmlFollowPrivate::Track) { + currentValue = sourceValue; + property.write(currentValue); + } else if (sourceValue != currentValue && clock.state() != QAbstractAnimation::Running) { + lastTime = 0; + clock.start(); // infinity?? + } +} + +void QmlFollowPrivate::stop() +{ + clock.stop(); +} + +/*! + \qmlclass Follow QmlFollow + \brief The Follow element allows a property to track a value. + + In example below, Rect2 will follow Rect1 moving with a velocity of up to 200: + \code + <Rect id="Rect1" y="{200}" width="20" height="20" color="#00ff00"> + <y> + <SequentialAnimation running="true" repeat="true"> + <NumericAnimation to="{200}" easing="easeOutBounce(amplitude:100)" duration="2000" /> + <PauseAnimation duration="1000" /> + </SequentialAnimation> + </y> + </Rect> + <Rect id="Rect2" x="{Rect1.width}" width="20" height="20" color="#ff0000"> + <y> + <Follow source="{Rect1.y}" velocity="200"/> + </y> + </Rect> + \endcode +*/ + +QmlFollow::QmlFollow(QObject *parent) +: QmlPropertyValueSource(*(new QmlFollowPrivate),parent) +{ +} + +QmlFollow::~QmlFollow() +{ +} + +void QmlFollow::setTarget(const QmlMetaProperty &property) +{ + Q_D(QmlFollow); + d->property = property; + d->currentValue = property.read().toDouble(); +} + +qreal QmlFollow::sourceValue() const +{ + Q_D(const QmlFollow); + return d->sourceValue; +} + +/*! + \qmlproperty qreal Follow::source + This property holds the source value which will be tracked. + + Bind to a property in order to track its changes. +*/ + +void QmlFollow::setSourceValue(qreal value) +{ + Q_D(QmlFollow); + d->sourceValue = value; + d->start(); +} + +/*! + \qmlproperty qreal Follow::velocity + This property holds the maximum velocity allowed when tracking the source. +*/ + +qreal QmlFollow::velocity() const +{ + Q_D(const QmlFollow); + return d->maxVelocity; +} + +void QmlFollow::setVelocity(qreal velocity) +{ + Q_D(QmlFollow); + d->maxVelocity = velocity; + d->velocityms = velocity / 1000.0; + d->updateMode(); +} + +/*! + \qmlproperty qreal Follow::spring + This property holds the spring constant + + The spring constant describes how strongly the target is pulled towards the + source. Setting spring to 0 turns off spring tracking. Useful values 0 - 5.0 + + When a spring constant is set and the velocity property is greater than 0, + velocity limits the maximum speed. +*/ +qreal QmlFollow::spring() const +{ + Q_D(const QmlFollow); + return d->spring; +} + +void QmlFollow::setSpring(qreal spring) +{ + Q_D(QmlFollow); + d->spring = spring; + d->updateMode(); +} + +/*! + \qmlproperty qreal Follow::damping + This property holds the spring damping constant + + The damping constant describes how quickly a sprung follower comes to rest. + Useful range is 0 - 1.0 +*/ +qreal QmlFollow::damping() const +{ + Q_D(const QmlFollow); + return d->damping; +} + +void QmlFollow::setDamping(qreal damping) +{ + Q_D(QmlFollow); + if (damping > 1.) + damping = 1.; + + d->damping = damping; +} + +/*! + \qmlproperty bool Follow::enabled + This property holds whether the target will track the source. +*/ +bool QmlFollow::enabled() const +{ + Q_D(const QmlFollow); + return d->enabled; +} + +void QmlFollow::setEnabled(bool enabled) +{ + Q_D(QmlFollow); + d->enabled = enabled; + if (enabled) + d->start(); + else + d->stop(); +} +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlfollow.h b/src/declarative/util/qmlfollow.h new file mode 100644 index 0000000..a609305 --- /dev/null +++ b/src/declarative/util/qmlfollow.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLFOLLOW_H +#define QMLFOLLOW_H + +#include <qmlpropertyvaluesource.h> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlFollowPrivate; +class Q_DECLARATIVE_EXPORT QmlFollow : public QmlPropertyValueSource, + public QmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlFollow) + Q_INTERFACES(QmlParserStatus) + + Q_PROPERTY(qreal source READ sourceValue WRITE setSourceValue); + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity); + Q_PROPERTY(qreal spring READ spring WRITE setSpring); + Q_PROPERTY(qreal damping READ damping WRITE setDamping); + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled); + +public: + QmlFollow(QObject *parent=0); + ~QmlFollow(); + + virtual void setTarget(const QmlMetaProperty &); + + qreal sourceValue() const; + void setSourceValue(qreal value); + qreal velocity() const; + void setVelocity(qreal velocity); + qreal spring() const; + void setSpring(qreal spring); + qreal damping() const; + void setDamping(qreal damping); + bool enabled() const; + void setEnabled(bool enabled); +}; + +QML_DECLARE_TYPE(QmlFollow); + + +#endif // QFXFOLLOW_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qmlfont.cpp b/src/declarative/util/qmlfont.cpp new file mode 100644 index 0000000..ad91edd --- /dev/null +++ b/src/declarative/util/qmlfont.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "private/qobject_p.h" +#include "qfont.h" +#include "qmlfont.h" + + +QT_BEGIN_NAMESPACE +class QmlFontPrivate : public QObjectPrivate +{ +public: + QFont font; +}; + +QML_DEFINE_TYPE(QmlFont,Font); + +/*! + \internal + \class QmlFont + \ingroup utility + \brief The QmlFont class provides a font used for drawing text on a QFxView. +*/ +QmlFont::QmlFont(QObject *parent) + : QObject(*(new QmlFontPrivate), parent) +{ +} + +QmlFont::~QmlFont() +{ +} + +/*! + \property QmlFont::family + \brief the family of the font. +*/ +QString QmlFont::family() const +{ + Q_D(const QmlFont); + return d->font.family(); +} + +void QmlFont::setFamily(const QString &family) +{ + Q_D(QmlFont); + d->font.setFamily(family); + emit updated(); +} + +/*! + \property QmlFont::bold + \brief whether the font should be bold. +*/ +bool QmlFont::bold() const +{ + Q_D(const QmlFont); + return d->font.bold(); +} + +void QmlFont::setBold(bool b) +{ + Q_D(QmlFont); + d->font.setBold(b); + emit updated(); +} + +/*! + \property QmlFont::italic + \brief whether the font should be italic. +*/ +bool QmlFont::italic() const +{ + Q_D(const QmlFont); + return d->font.italic(); +} + +void QmlFont::setItalic(bool b) +{ + Q_D(QmlFont); + d->font.setItalic(b); + emit updated(); +} + +/*! + \property QmlFont::size + \brief the size of the font in points. +*/ +qreal QmlFont::size() const +{ + Q_D(const QmlFont); + return d->font.pointSizeF(); +} + +void QmlFont::setSize(qreal size) +{ + Q_D(QmlFont); + d->font.setPointSizeF(size); + emit updated(); +} + +/*! + \brief Returns a QFont representation of the font. +*/ +QFont QmlFont::font() const +{ + Q_D(const QmlFont); + return d->font; +} +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlfont.h b/src/declarative/util/qmlfont.h new file mode 100644 index 0000000..b6bce7c --- /dev/null +++ b/src/declarative/util/qmlfont.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 QtDeclarative 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 QMLFONT_H +#define QMLFONT_H + +#include <QtCore/qobject.h> +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlFontPrivate; +class Q_DECLARATIVE_EXPORT QmlFont : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlFont) + + Q_PROPERTY(QString family READ family WRITE setFamily) + Q_PROPERTY(bool bold READ bold WRITE setBold) + Q_PROPERTY(bool italic READ italic WRITE setItalic) + Q_PROPERTY(qreal size READ size WRITE setSize) + +public: + QmlFont(QObject *parent=0); + ~QmlFont(); + + QString family() const; + void setFamily(const QString &); + + bool bold() const; + void setBold(bool b); + + bool italic() const; + void setItalic(bool b); + + qreal size() const; + void setSize(qreal size); + + QFont font() const; + +Q_SIGNALS: + void updated(); +}; +QML_DECLARE_TYPE(QmlFont); + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QMLFONT_H diff --git a/src/declarative/util/qmllistaccessor.cpp b/src/declarative/util/qmllistaccessor.cpp new file mode 100644 index 0000000..9387bbc --- /dev/null +++ b/src/declarative/util/qmllistaccessor.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmllistaccessor.h" +#include <QStringList> +#include <qmlmetatype.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +QmlListAccessor::QmlListAccessor() +: type(Invalid) +{ +} + +QmlListAccessor::~QmlListAccessor() +{ +} + +QVariant QmlListAccessor::list() const +{ + return d; +} + +void QmlListAccessor::setList(const QVariant &v) +{ + d = v; + + if(!d.isValid()) { + type = Invalid; + } else if(d.type() == QVariant::StringList) { + type = StringList; + } else if(d.type() != QVariant::UserType) { + type = Instance; + } else if(QmlMetaType::isObject(d.userType())) { + QObject *data = 0; + data = *(QObject **)v.constData(); + d = QVariant::fromValue(data); + type = Instance; + } else if(QmlMetaType::isQmlList(d.userType())) { + type = QmlList; + } else if(QmlMetaType::isList(d.userType())) { + type = QList; + } else { + type = Invalid; + d = QVariant(); + } +} + +int QmlListAccessor::count() const +{ + switch(type) { + case Invalid: + return 0; + case StringList: + return qvariant_cast<QStringList>(d).count(); + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + return li->count(); + } + case QList: + return QmlMetaType::listCount(d); + case Instance: + return 1; + } + + return 0; +} + +QVariant QmlListAccessor::at(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < count()); + switch(type) { + case Invalid: + return QVariant(); + case StringList: + return QVariant::fromValue(qvariant_cast<QStringList>(d).at(idx)); + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + void *ptr[1]; + li->at(idx, ptr); + return QmlMetaType::fromObject((QObject*)ptr[0], li->type()); //XXX only handles QObject-derived types + } + case QList: + return QmlMetaType::listAt(d, idx); + case Instance: + return d; + } + + return QVariant(); +} + +void QmlListAccessor::append(const QVariant &value) +{ + switch(type) { + case Invalid: + break; + case StringList: + { + const QString &str = value.toString(); + qvariant_cast<QStringList>(d).append(str); + break; + } + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + li->append(const_cast<void *>(value.constData())); //XXX + break; + } + case QList: + QmlMetaType::append(d, value); + break; + case Instance: + //do nothing + break; + } +} + +void QmlListAccessor::insert(int index, const QVariant &value) +{ + switch(type) { + case Invalid: + break; + case StringList: + { + const QString &str = value.toString(); + qvariant_cast<QStringList>(d).insert(index, str); + break; + } + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + li->insert(index, const_cast<void *>(value.constData())); //XXX + break; + } + case QList: + //XXX needs implementation + qWarning() << "insert function not yet implemented for QLists"; + break; + case Instance: + //XXX do nothing? + if (index == 0) + setList(value); + break; + } +} + +void QmlListAccessor::removeAt(int index) +{ + switch(type) { + case Invalid: + break; + case StringList: + qvariant_cast<QStringList>(d).removeAt(index); + break; + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + li->removeAt(index); + break; + } + case QList: + //XXX needs implementation + qWarning() << "removeAt function not yet implemented for QLists"; + break; + case Instance: + //XXX do nothing? + if (index == 0) + setList(QVariant()); + break; + } +} + +void QmlListAccessor::clear() +{ + switch(type) { + case Invalid: + break; + case StringList: + qvariant_cast<QStringList>(d).clear(); + break; + case QmlList: + { + QmlPrivate::ListInterface *li = *(QmlPrivate::ListInterface **)d.constData(); + li->clear(); + break; + } + case QList: + QmlMetaType::clear(d); + break; + case Instance: + //XXX what should we do here? + setList(QVariant()); + break; + } +} + +bool QmlListAccessor::isValid() const +{ + return type != Invalid; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmllistaccessor.h b/src/declarative/util/qmllistaccessor.h new file mode 100644 index 0000000..29f910d --- /dev/null +++ b/src/declarative/util/qmllistaccessor.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLLISTACCESSOR_H +#define QMLLISTACCESSOR_H + +#include <QVariant> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QmlListAccessor +{ +public: + QmlListAccessor(); + virtual ~QmlListAccessor(); + + QVariant list() const; + void setList(const QVariant &); + + bool isValid() const; + + int count() const; + QVariant at(int) const; + + virtual void append(const QVariant &); + virtual void insert(int, const QVariant &); + virtual void removeAt(int); + virtual void clear(); + +private: + enum Type { Invalid, StringList, QmlList, QList, Instance }; + Type type; + QVariant d; +}; + +#endif // QMLLISTACCESSOR_H + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qmllistmodel.cpp b/src/declarative/util/qmllistmodel.cpp new file mode 100644 index 0000000..992185a --- /dev/null +++ b/src/declarative/util/qmllistmodel.cpp @@ -0,0 +1,721 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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/qdebug.h> +#include <QtCore/qstack.h> +#include <QXmlStreamReader> +#include "qmlcustomparser.h" +#include "qmlopenmetaobject.h" +#include <qmlcontext.h> +#include <qmlbindablevalue.h> +#include "qmllistmodel.h" + +QT_BEGIN_NAMESPACE + +#define DATA_ROLE_ID 1 +#define DATA_ROLE_NAME "data" + +Q_DECLARE_METATYPE(QListModelInterface *); +class QmlListModelPrivate +{ +public: + QmlListModelPrivate(QmlListModel *m) + : q(m), + type(QmlListModel::Invalid), + listModelInterface(0), + singleObject(0), + roleCacheValid(false) + { + } + + void clear() + { + type = QmlListModel::Invalid; + model = QVariant(); + if(listModelInterface) + listModelInterface->disconnect(q); + listModelInterface = 0; + singleObject = 0; + roleCacheValid = false; + roleCache.clear(); + } + + void updateRoleCache() + { + if(roleCacheValid) + return; + + roleCacheValid = true; + if(type == QmlListModel::SingleObject) + roleCache = QmlMetaProperty::properties(singleObject); + } + + QmlListModel *q; + + QmlListModel::ModelType type; + + QVariant model; + QListModelInterface *listModelInterface; + QObject *singleObject; + + bool roleCacheValid; + QStringList roleCache; +}; + +/*! + \qmlclass ListModel QmlListModel + \brief The ListModel element defines a free-form list data source. + + The ListModel is a simple XML heirarchy of items containing data roles. + For example: + + \code + <ListModel id="FruitModel"> + <Fruit> + <name>Apple</name> + <cost>2.45</cost> + <Fruit> + <Fruit> + <name>Orange</name> + <cost>3.25</cost> + </Fruit> + <Fruit> + <name>Banana</name> + <cost>1.95</cost> + </Fruit> + </ListModel> + \endcode + + Elements beginning with a capital are items. Elements beginning + with lower-case are the data roles. The above example defines a + ListModel containing three items, with the roles "name" and "cost". + + The defined model can be used in views such as ListView: + \code + <Component id="FruitDelegate"> + <Item width="200" height="50"> + <Text text="{name}"/> + <Text text="{'$'+cost}" anchors.right="{parent.right}"/> + </Item> + </Component> + + <ListView model="{FruitModel}" delegate="{FruitDelegate}" anchors.fill="{parent}"/> + \endcode +*/ +/*! + \internal + \class QmlListModel +*/ +QmlListModel::QmlListModel(QObject *parent) +: QListModelInterface(parent), d(new QmlListModelPrivate(this)) +{ +} + +QmlListModel::~QmlListModel() +{ + delete d; d = 0; +} + +QmlListModel::ModelType QmlListModel::modelType() const +{ + return d->type; +} + +bool QmlListModel::setModel(const QVariant &model) +{ + d->clear(); + + QListModelInterface *iface = qvariant_cast<QListModelInterface *>(model); + if(iface) { + QObject::connect(iface, SIGNAL(itemsInserted(int,int)), + this, SIGNAL(itemsInserted(int,int))); + QObject::connect(iface, SIGNAL(itemsRemoved(int,int)), + this, SIGNAL(itemsRemoved(int,int))); + QObject::connect(iface, SIGNAL(itemsMoved(int,int,int)), + this, SIGNAL(itemsMoved(int,int,int))); + QObject::connect(iface, SIGNAL(itemsChanged(int,int,QList<int>)), + this, SIGNAL(itemsChanged(int,int,QList<int>))); + d->listModelInterface = iface; + d->type = ListInterface; + d->model = model; + return true; + } + + QObject *object = qvariant_cast<QObject *>(model); + if(object) { + d->singleObject = object; + d->type = SingleObject; + d->model = model; + return true; + } + + if(QmlMetaType::isList(model)) { + d->type = SimpleList; + d->model = model; + return true; + } + + return false; +} + +QVariant QmlListModel::model() const +{ + return d->model; +} + +QList<int> QmlListModel::roles() const +{ + d->updateRoleCache(); + switch(modelType()) { + case Invalid: + return QList<int>(); + case SimpleList: + return QList<int>() << DATA_ROLE_ID; + case ListInterface: + return d->listModelInterface->roles(); + case SingleObject: + { + QList<int> rv; + for(int ii = 0; ii < d->roleCache.count(); ++ii) + rv << ii; + return rv; + } + break; + }; + return QList<int>(); +} + +QString QmlListModel::toString(int role) const +{ + d->updateRoleCache(); + switch(modelType()) { + case Invalid: + return QString(); + case SimpleList: + if(role == DATA_ROLE_ID) + return QLatin1String(DATA_ROLE_NAME); + else + return QString(); + case ListInterface: + return d->listModelInterface->toString(role); + case SingleObject: + if(role >= d->roleCache.count()) + return QString(); + else + return d->roleCache.at(role); + }; + return QString(); +} + +/*! + \qmlproperty int ListModel::count + This property holds the number of items in the list. +*/ +int QmlListModel::count() const +{ + switch(modelType()) { + case Invalid: + return 0; + case SimpleList: + return QmlMetaType::listCount(model()); + case ListInterface: + return d->listModelInterface->count(); + case SingleObject: + return 1; + } + return 0; +} + +QHash<int,QVariant> QmlListModel::data(int index, const QList<int> &roles) const +{ + d->updateRoleCache(); + QHash<int, QVariant> rv; + switch(modelType()) { + case Invalid: + break; + case SimpleList: + if(roles.contains(DATA_ROLE_ID)) + rv.insert(DATA_ROLE_ID, QmlMetaType::listAt(d->model, index)); + break; + case ListInterface: + return d->listModelInterface->data(index, roles); + case SingleObject: + { + for(int ii = 0; ii < roles.count(); ++ii) { + QmlMetaProperty prop(d->singleObject, toString(roles.at(ii))); + rv.insert(roles.at(ii), prop.read()); + } + } + break; + }; + + return rv; +} + + + +struct ModelNode; +class ListModel : public QListModelInterface +{ + Q_OBJECT +public: + ListModel(QObject *parent=0); + + virtual QList<int> roles() const; + virtual QString toString(int role) const; + Q_PROPERTY(int count READ count); + virtual int count() const; + virtual QHash<int,QVariant> data(int index, const QList<int> &roles = (QList<int>())) const; + +private: + QVariant valueForNode(ModelNode *) const; + mutable QStringList roleStrings; + friend class ListModelParser; + friend struct ModelNode; + + void checkRoles() const; + void addRole(const QString &) const; + mutable bool _rolesOk; + ModelNode *_root; +}; + +class ModelObject : public QObject +{ + Q_OBJECT +public: + ModelObject(ModelNode *); + + void setValue(const QByteArray &name, const QVariant &val) + { + _mo->setValue(name, val); + } + +private: + ModelNode *_node; + bool _haveProperties; + QmlOpenMetaObject *_mo; +}; + +struct ModelNode +{ + ModelNode(); + ~ModelNode(); + QString className; + + QList<QVariant> values; + QHash<QString, ModelNode *> properties; + + ListModel *model() { + if(!modelCache) { + modelCache = new ListModel; + modelCache->_root = this; + } + return modelCache; + } + + ModelObject *object() { + if(!objectCache) { + objectCache = new ModelObject(this); + QHash<QString, ModelNode *>::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + if (!(*it)->values.isEmpty()) + objectCache->setValue(it.key().toLatin1(), (*it)->values.first()); + } + } + return objectCache; + } + + ListModel *modelCache; + ModelObject *objectCache; +}; +Q_DECLARE_METATYPE(ModelNode *); + +ModelObject::ModelObject(ModelNode *node) +: _node(node), _haveProperties(false), _mo(new QmlOpenMetaObject(this)) +{ +} + +QML_DECLARE_TYPE(ListModel); +QML_DEFINE_TYPE(ListModel,ListModel); +ListModel::ListModel(QObject *parent) +: QListModelInterface(parent), _rolesOk(false), _root(0) +{ +} + +void ListModel::checkRoles() const +{ + if(_rolesOk) + return; + + for(int ii = 0; ii < _root->values.count(); ++ii) { + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(ii)); + if(node) { + foreach(QString role, node->properties.keys()) + addRole(role); + } + } + + _rolesOk = true; +} + +void ListModel::addRole(const QString &role) const +{ + if(!roleStrings.contains(role)) + roleStrings << role; +} + +QList<int> ListModel::roles() const +{ + checkRoles(); + QList<int> rv; + for(int ii = 0; ii < roleStrings.count(); ++ii) + rv << ii; + return rv; +} + +QString ListModel::toString(int role) const +{ + checkRoles(); + if(role < roleStrings.count()) + return roleStrings.at(role); + else + return QString(); +} + +QVariant ListModel::valueForNode(ModelNode *node) const +{ + QObject *rv = 0; + + if(!node->properties.isEmpty()) { + // Object + rv = node->object(); + } else if(node->values.count() == 0) { + // Invalid + return QVariant(); + } else if(node->values.count() == 1) { + // Value + QVariant &var = node->values[0]; + ModelNode *valueNode = qvariant_cast<ModelNode *>(var); + if(valueNode) { + if(!valueNode->properties.isEmpty()) + rv = valueNode->object(); + else + rv = valueNode->model(); + } else { + return var; + } + } else if(node->values.count() > 1) { + // List + rv = node->model(); + } + + if(rv) + return QVariant::fromValue(rv); + else + return QVariant(); +} + +QHash<int,QVariant> ListModel::data(int index, const QList<int> &roles) const +{ + checkRoles(); + QHash<int, QVariant> rv; + if(index >= count()) + return rv; + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if(!node) + return rv; + + for(int ii = 0; ii < roles.count(); ++ii) { + const QString &roleString = roleStrings.at(roles.at(ii)); + + QHash<QString, ModelNode *>::ConstIterator iter = + node->properties.find(roleString); + if(iter != node->properties.end()) { + ModelNode *row = *iter; + rv.insert(roles.at(ii), valueForNode(row)); + } + } + + return rv; +} + +int ListModel::count() const +{ + if(!_root) return 0; + return _root->values.count(); +} + +struct ListInstruction +{ + enum { Push, Pop, Value, Set } type; + int dataIdx; +}; + +class ListModelParser : public QmlCustomParser +{ +public: + virtual QByteArray compile(QXmlStreamReader& reader, bool *); + virtual QVariant create(const QByteArray &); +}; +QML_DEFINE_CUSTOM_PARSER(ListModel, ListModelParser); + +static void dump(ModelNode *node, int ind) +{ + QByteArray indentBa(ind * 4, ' '); + const char *indent = indentBa.constData(); + + for(int ii = 0; ii < node->values.count(); ++ii) { + ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii)); + if(subNode) { + qWarning().nospace() << indent << "Sub-node " << ii << ": class " << subNode->className; + dump(subNode, ind + 1); + } else { + qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString(); + } + } + + for(QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) { + qWarning().nospace() << indent << "Property " << iter.key() << ":"; + dump(iter.value(), ind + 1); + } +} + +ModelNode::ModelNode() +: modelCache(0), objectCache(0) +{ +} + +ModelNode::~ModelNode() +{ + qDeleteAll(properties); + for(int ii = 0; ii < values.count(); ++ii) { + ModelNode *node = qvariant_cast<ModelNode *>(values.at(ii)); + if(node) { delete node; node = 0; } + } + if(modelCache) { delete modelCache; modelCache = 0; } +} + +struct ListModelData +{ + int dataOffset; + int id; + int instrCount; + ListInstruction *instructions() const { return (ListInstruction *)((char *)this + sizeof(ListModelData)); } +}; + +QByteArray ListModelParser::compile(QXmlStreamReader& reader, bool *ok) +{ + *ok = true; + + QByteArray id; + QByteArray data; + QList<ListInstruction> instr; + int depth=0; + + while(!reader.atEnd() && depth >= 0) { + switch(reader.readNext()) { + case QXmlStreamReader::StartElement: + { + QStringRef name = reader.name(); + bool isType = name.at(0).isUpper(); + + if (isType) { + ListInstruction li; + li.type = ListInstruction::Push; + li.dataIdx = -1; + instr << li; + + for (int i = 0; i < reader.attributes().count(); ++i) { + const QXmlStreamAttribute &attr = reader.attributes().at(i); + QStringRef attrName = attr.name(); + QStringRef attrValue = attr.value(); + + ListInstruction li; + int ref = data.count(); + data.append(attrName.toString().toLatin1()); + data.append('\0'); + li.type = ListInstruction::Set; + li.dataIdx = ref; + instr << li; + + ref = data.count(); + data.append(attrValue.toString().toLatin1()); + data.append('\0'); + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + } else { + ListInstruction li; + int ref = data.count(); + data.append(name.toString().toLatin1()); + data.append('\0'); + li.type = ListInstruction::Set; + li.dataIdx = ref; + instr << li; + } + } + ++depth; + break; + case QXmlStreamReader::EndElement: + { + ListInstruction li; + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + --depth; + } + break; + case QXmlStreamReader::Characters: + if (!reader.isWhitespace()) { + int ref = data.count(); + QByteArray d = reader.text().toString().toLatin1(); + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + } + break; + + case QXmlStreamReader::Invalid: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + + if (reader.hasError()) + *ok = true; + + if (!*ok) + return QByteArray(); + + int size = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction) + + data.count(); + + QByteArray rv; + rv.resize(size); + + ListModelData *lmd = (ListModelData *)rv.data(); + if(id.count()) + lmd->id = 0; + else + lmd->id = -1; + lmd->dataOffset = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction); + lmd->instrCount = instr.count(); + for(int ii = 0; ii < instr.count(); ++ii) + lmd->instructions()[ii] = instr.at(ii); + ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); + + return rv; +} + +QVariant ListModelParser::create(const QByteArray &d) +{ + ListModel *rv = new ListModel; + ModelNode *root = new ModelNode; + rv->_root = root; + QStack<ModelNode *> nodes; + nodes << root; + + const ListModelData *lmd = (const ListModelData *)d.constData(); + const char *data = ((const char *)lmd) + lmd->dataOffset; + + for(int ii = 0; ii < lmd->instrCount; ++ii) { + const ListInstruction &instr = lmd->instructions()[ii]; + + switch(instr.type) { + case ListInstruction::Push: + { + ModelNode *n = nodes.top(); + ModelNode *n2 = new ModelNode; + n->values << qVariantFromValue(n2); + nodes.push(n2); + } + break; + + case ListInstruction::Pop: + nodes.pop(); + break; + + case ListInstruction::Value: + { + ModelNode *n = nodes.top(); + n->values.append(QByteArray(data + instr.dataIdx)); + } + break; + + case ListInstruction::Set: + { + ModelNode *n = nodes.top(); + ModelNode *n2 = new ModelNode; + n->properties.insert(QLatin1String(data + instr.dataIdx), n2); + nodes.push(n2); + } + break; + } + } + + if(lmd->id != -1) { + QmlContext *ctxt = QmlContext::activeContext(); + ctxt->setContextProperty(QLatin1String(data + lmd->id), rv); + } + + return QVariant::fromValue(rv); +} + +QT_END_NAMESPACE +#include "qmllistmodel.moc" diff --git a/src/declarative/util/qmllistmodel.h b/src/declarative/util/qmllistmodel.h new file mode 100644 index 0000000..3dcac4f --- /dev/null +++ b/src/declarative/util/qmllistmodel.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 QtDeclarative 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 QMLLISTMODEL_H +#define QMLLISTMODEL_H + +#include <QObject> +#include <qfxglobal.h> +#include <QStringList> +#include <QHash> +#include <QList> +#include <QVariant> +#include <qml.h> +#include <qlistmodelinterface.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlListModelPrivate; +class Q_DECLARATIVE_EXPORT QmlListModel : public QListModelInterface +{ +Q_OBJECT +public: + QmlListModel(QObject *parent = 0); + virtual ~QmlListModel(); + + enum ModelType { + Invalid, + SimpleList, + ListInterface, + SingleObject + }; + + ModelType modelType() const; + bool setModel(const QVariant &); + QVariant model() const; + + virtual QList<int> roles() const; + virtual QString toString(int role) const; + + Q_PROPERTY(int count READ count); + virtual int count() const; + virtual QHash<int,QVariant> + data(int index, const QList<int> &roles = (QList<int>())) const; +private: + QmlListModelPrivate *d; +}; +QML_DECLARE_TYPE(QmlListModel); + +#endif // QMLLISTMODEL_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qmlnullablevalue_p.h b/src/declarative/util/qmlnullablevalue_p.h new file mode 100644 index 0000000..f16ddd6 --- /dev/null +++ b/src/declarative/util/qmlnullablevalue_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLNULLABLEVALUE_P_H +#define QMLNULLABLEVALUE_P_H + +template<typename T> +struct QmlNullableValue +{ + QmlNullableValue() + : isNull(true), value(T()) {} + QmlNullableValue(const QmlNullableValue<T> &o) + : isNull(o.isNull), value(o.value) {} + QmlNullableValue(const T &t) + : isNull(false), value(t) {} + QmlNullableValue<T> &operator=(const T &t) + { isNull = false; value = t; return *this; } + QmlNullableValue<T> &operator=(const QmlNullableValue<T> &o) + { isNull = o.isNull; value = o.value; return *this; } + operator T() const { return value; } + + void invalidate() { isNull = true; } + bool isValid() const { return !isNull; } + bool isNull; + T value; +}; + +#endif // QMLNULLABLEVALUE_P_H + diff --git a/src/declarative/util/qmlopenmetaobject.cpp b/src/declarative/util/qmlopenmetaobject.cpp new file mode 100644 index 0000000..09d71bd --- /dev/null +++ b/src/declarative/util/qmlopenmetaobject.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlopenmetaobject.h" +#include <QDebug> + + +QT_BEGIN_NAMESPACE +QmlOpenMetaObject::QmlOpenMetaObject(QObject *obj, bool automatic) +: autoCreate(automatic), parent(0), mem(0), _object(obj) +{ + mob.setSuperClass(obj->metaObject()); + mob.setFlags(QMetaObjectBuilder::DynamicMetaObject); + + QObjectPrivate *op = QObjectPrivate::get(obj); + if(op->metaObject) + mob.setSuperClass(op->metaObject); + + mem = mob.toMetaObject(); + *static_cast<QMetaObject *>(this) = *mem; + op->metaObject = this; + _propertyOffset = propertyOffset(); + _signalOffset = methodOffset(); +} + +QmlOpenMetaObject::~QmlOpenMetaObject() +{ + if(parent) + delete parent; + qFree(mem); +} + +int QmlOpenMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +{ + if(( c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) + && id >= _propertyOffset) { + int propId = id - _propertyOffset; + if(c == QMetaObject::ReadProperty) { + propertyRead(propId); + *reinterpret_cast<QVariant *>(a[0]) = data[propId]; + } else if(c == QMetaObject::WriteProperty) { + if(data[propId] != *reinterpret_cast<QVariant *>(a[0])) { + propertyWrite(propId); + data[propId] = *reinterpret_cast<QVariant *>(a[0]); + activate(_object, _signalOffset + propId, 0); + } + } + return -1; + } else { + if(parent) + return parent->metaCall(c, id, a); + else + return _object->qt_metacall(c, id, a); + } +} + +QVariant QmlOpenMetaObject::value(int id) const +{ + Q_ASSERT(id >= 0 && id < data.count()); + return data.at(id); +} + +void QmlOpenMetaObject::setValue(int id, const QVariant &value) +{ + Q_ASSERT(id >= 0 && id < data.count()); + data[id] = value; + activate(_object, id + _signalOffset, 0); +} + +QVariant QmlOpenMetaObject::value(const QByteArray &name) const +{ + QHash<QByteArray, int>::ConstIterator iter = names.find(name); + if(iter == names.end()) + return QVariant(); + + return data.at(*iter); +} + +void QmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) +{ + QHash<QByteArray, int>::ConstIterator iter = names.find(name); + + int id = -1; + if(iter == names.end()) { + id = doCreateProperty(name.constData()) - _propertyOffset; + } else { + id = *iter; + } + + if(data[id] == val) + return; + + data[id] = val; + activate(_object, id + _signalOffset, 0); +} + +int QmlOpenMetaObject::createProperty(const char *name, const char *) +{ + if(autoCreate) + return doCreateProperty(name); + else + return -1; +} + +int QmlOpenMetaObject::doCreateProperty(const char *name) +{ + int id = mob.propertyCount(); + mob.addSignal("__" + QByteArray::number(id) + "()"); + QMetaPropertyBuilder build = mob.addProperty(name, "QVariant", id); + build.setDynamic(true); + data << propertyCreated(id, build); + qFree(mem); + mem = mob.toMetaObject(); + *static_cast<QMetaObject *>(this) = *mem; + names.insert(name, id); + + return _propertyOffset + id; +} + +void QmlOpenMetaObject::propertyRead(int) +{ +} + +void QmlOpenMetaObject::propertyWrite(int) +{ +} + +QVariant QmlOpenMetaObject::propertyCreated(int, QMetaPropertyBuilder &) +{ + return QVariant(); +} + +int QmlOpenMetaObject::count() const +{ + return data.count(); +} + +QByteArray QmlOpenMetaObject::name(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < data.count()); + + return mob.property(idx).name(); +} + +QObject *QmlOpenMetaObject::object() const +{ + return _object; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlopenmetaobject.h b/src/declarative/util/qmlopenmetaobject.h new file mode 100644 index 0000000..17cecd87 --- /dev/null +++ b/src/declarative/util/qmlopenmetaobject.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 QtDeclarative 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 QMLOPENMETAOBJECT_H +#define QMLOPENMETAOBJECT_H + +#include <QMetaObject> +#include "private/qmetaobjectbuilder_p.h" +#include <private/qobject_p.h> +#include <QObject> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_EXPORT QmlOpenMetaObject : public QAbstractDynamicMetaObject +{ +public: + QmlOpenMetaObject(QObject *, bool = true); + ~QmlOpenMetaObject(); + + QVariant value(const QByteArray &) const; + void setValue(const QByteArray &, const QVariant &); + QVariant value(int) const; + void setValue(int, const QVariant &); + + int count() const; + QByteArray name(int) const; + + QObject *object() const; +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + virtual int createProperty(const char *, const char *); + + virtual void propertyRead(int); + virtual void propertyWrite(int); + virtual QVariant propertyCreated(int, QMetaPropertyBuilder &); + +private: + int doCreateProperty(const char *); + bool autoCreate; + QAbstractDynamicMetaObject *parent; + int _propertyOffset; + int _signalOffset; + QList<QVariant> data; + QHash<QByteArray, int> names; + QMetaObjectBuilder mob; + QMetaObject *mem; + QObject *_object; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER +#endif // QMLOPENMETAOBJECT_H diff --git a/src/declarative/util/qmlpackage.cpp b/src/declarative/util/qmlpackage.cpp new file mode 100644 index 0000000..264d186 --- /dev/null +++ b/src/declarative/util/qmlpackage.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "private/qobject_p.h" +#include "qmlpackage.h" + + +QT_BEGIN_NAMESPACE +class QmlPackagePrivate : public QObjectPrivate +{ +public: + QmlPackagePrivate() {} + + QmlConcreteList<QObject *> dataList; +}; + +class QmlPackageAttached : public QObject +{ +Q_OBJECT +public: + QmlPackageAttached(QObject *parent); + virtual ~QmlPackageAttached(); + + Q_PROPERTY(QString name READ name WRITE setName); + QString name() const; + void setName(const QString &n); + + static QHash<QObject *, QmlPackageAttached *> attached; +private: + QString _name; +}; + +QHash<QObject *, QmlPackageAttached *> QmlPackageAttached::attached; + +QmlPackageAttached::QmlPackageAttached(QObject *parent) +: QObject(parent) +{ + attached.insert(parent, this); +} + +QmlPackageAttached::~QmlPackageAttached() +{ + attached.remove(parent()); +} + +QString QmlPackageAttached::name() const +{ + return _name; +} + +void QmlPackageAttached::setName(const QString &n) +{ + _name = n; +} + +QmlPackage::QmlPackage(QObject *parent) + : QObject(*(new QmlPackagePrivate), parent) +{ +} + +QmlPackage::~QmlPackage() +{ +} + +QmlList<QObject *> *QmlPackage::data() +{ + Q_D(QmlPackage); + return &d->dataList; +} + +bool QmlPackage::hasPart(const QString &name) +{ + Q_D(QmlPackage); + for(int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QmlPackageAttached *a = QmlPackageAttached::attached.value(obj); + if(a && a->name() == name) + return true; + } + return false; +} + +QObject *QmlPackage::part(const QString &name) +{ + Q_D(QmlPackage); + if(name.isEmpty() && !d->dataList.isEmpty()) + return d->dataList.at(0); + + for(int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QmlPackageAttached *a = QmlPackageAttached::attached.value(obj); + if(a && a->name() == name) + return obj; + } + + if(name == QLatin1String("default") && !d->dataList.isEmpty()) + return d->dataList.at(0); + + return 0; +} + +QObject *QmlPackage::qmlAttachedProperties(QObject *o) +{ + return new QmlPackageAttached(o); +} + +QML_DEFINE_TYPE(QmlPackage, Package); + +QT_END_NAMESPACE +#include "qmlpackage.moc" diff --git a/src/declarative/util/qmlpackage.h b/src/declarative/util/qmlpackage.h new file mode 100644 index 0000000..6652b98 --- /dev/null +++ b/src/declarative/util/qmlpackage.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 QtDeclarative 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 QMLPACKAGE_H +#define QMLPACKAGE_H + +#include <qml.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +/***************************************************************************** + ***************************************************************************** + XXX Experimental + ***************************************************************************** +*****************************************************************************/ + +class QmlPackagePrivate; +class QmlPackage : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlPackage) + + Q_CLASSINFO("DefaultProperty", "data"); + Q_PROPERTY(QmlList<QObject *> *data READ data SCRIPTABLE false); + +public: + QmlPackage(QObject *parent=0); + virtual ~QmlPackage(); + + QmlList<QObject *> *data(); + + QObject *part(const QString & = QString()); + bool hasPart(const QString &); + + static QObject *qmlAttachedProperties(QObject *); +}; +QML_DECLARE_TYPE(QmlPackage); + +#endif // QMLPACKAGE_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qmlscript.cpp b/src/declarative/util/qmlscript.cpp new file mode 100644 index 0000000..a24c427 --- /dev/null +++ b/src/declarative/util/qmlscript.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 QtDeclarative 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 <qmlbindablevalue.h> +#include <QtDeclarative/qmlengine.h> +#include <QtDeclarative/qmlcontext.h> +#include <private/qobject_p.h> +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtScript/qscriptvalue.h> +#include <QtScript/qscriptcontext.h> +#include <QtScript/qscriptengine.h> +#include <private/qmlnullablevalue_p.h> +#include <private/qmlengine_p.h> +#include <private/qmlcontext_p.h> +#include "qmlscript.h" +#include <QNetworkReply> +#include <QNetworkRequest> +#include <QtDeclarative/qmlinfo.h> + +QT_BEGIN_NAMESPACE + + + +class QmlScriptPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlScript); + +public: + QmlScriptPrivate() : reply(0), ctxt(0) {} + + void addScriptToEngine(const QString &, const QString &fileName=QString()); + + QString script; + QString source; + QNetworkReply *reply; + QUrl url; + QmlContext *ctxt; +}; + +/*! + \qmlclass Script QmlScript + \brief The Script element adds JavaScript snippets. + \ingroup utility + + QmlScript is used to add convenient JavaScript "glue" methods to + your Qt Declarative application or component. While you can have any JavaScript code + within a QmlScript, it is best to limit yourself to defining functions. + + \qml + <Script> + function debugMyComponent() { + print(text.text); + print(otherinterestingitem.property); + } + </Script> + <MouseRegion onClicked="debugMyComponent()" /> + \endqml + + \note QmlScript executes JavaScript as soon as it is specified. + When defining a component, this may be before the execution context is + fully specified. As a result some properties or items may not be + accessible. By limiting your JavaScript to defining functions that are + only executed later once the context is fully defined, this problem is + avoided. +*/ + +QML_DEFINE_TYPE(QmlScript,Script); +QmlScript::QmlScript(QObject *parent) : QObject(*(new QmlScriptPrivate), parent) +{ + Q_D(QmlScript); + d->ctxt = QmlContext::activeContext(); +} + +/*! + \qmlproperty string Script::script + \default + JavaScript code to execute. +*/ +/*! + \property QmlScript::script + \brief a script snippet. +*/ +QString QmlScript::script() const +{ + Q_D(const QmlScript); + return d->script; +} + +void QmlScript::setScript(const QString &script) +{ + Q_D(QmlScript); + d->script = script; + d->addScriptToEngine(d->script); +} + +/*! + \qmlproperty string Script::source + + Setting this property causes the Script element to read JavaScript code from + the file specified. + */ +/*! + \property QmlScript::source + \brief the path to a script file. +*/ +QString QmlScript::source() const +{ + Q_D(const QmlScript); + return d->source; +} + +void QmlScript::setSource(const QString &source) +{ + Q_D(QmlScript); + if (d->source == source) + return; + d->source = source; + d->url = d->ctxt->resolvedUrl(source); + QNetworkRequest req(d->url); + req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + d->reply = d->ctxt->engine()->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), + this, SLOT(replyFinished())); +} + +void QmlScript::replyFinished() +{ + Q_D(QmlScript); + if (!d->reply->error()) { + QByteArray ba = d->reply->readAll(); + d->addScriptToEngine(QString::fromUtf8(ba), d->source); + } + d->reply->deleteLater(); + d->reply = 0; +} + +void QmlScriptPrivate::addScriptToEngine(const QString &script, const QString &fileName) +{ + Q_Q(QmlScript); + QmlEngine *engine = ctxt->engine(); + QScriptEngine *scriptEngine = engine->scriptEngine(); + + QScriptContext *currentContext = engine->scriptEngine()->currentContext(); + QScriptValueList oldScopeChain = currentContext->scopeChain(); + QScriptValue oldact = currentContext->activationObject(); + + for (int i = 0; i < oldScopeChain.size(); ++i) { + currentContext->popScope(); + } + for (int i = ctxt->d_func()->scopeChain.size() - 1; i > -1; --i) { + currentContext->pushScope(ctxt->d_func()->scopeChain.at(i)); + } + + currentContext->setActivationObject(ctxt->d_func()->scopeChain.at(0)); + + QScriptValue val = scriptEngine->evaluate(script, fileName); + if (scriptEngine->hasUncaughtException()) { + if (scriptEngine->uncaughtException().isError()){ + QScriptValue exception = scriptEngine->uncaughtException(); + if(!exception.property(QLatin1String("fileName")).toString().isEmpty()){ + qWarning() << exception.property(QLatin1String("fileName")).toString() + << scriptEngine->uncaughtExceptionLineNumber() + << exception.toString(); + + } else { + qmlInfo(q) << exception.toString(); + } + } + } + + currentContext->setActivationObject(oldact); + + for (int i = 0; i < ctxt->d_func()->scopeChain.size(); ++i) + currentContext->popScope(); + + for (int i = oldScopeChain.size() - 1; i > -1; --i) + currentContext->pushScope(oldScopeChain.at(i)); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlscript.h b/src/declarative/util/qmlscript.h new file mode 100644 index 0000000..fc038b4 --- /dev/null +++ b/src/declarative/util/qmlscript.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLSCRIPT_H +#define QMLSCRIPT_H + +#include <qfxglobal.h> +#include <QtCore/qobject.h> +#include "qml.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlScriptPrivate; +class Q_DECLARATIVE_EXPORT QmlScript : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlScript); + + Q_PROPERTY(QString script READ script WRITE setScript); + Q_PROPERTY(QString src READ source WRITE setSource); + Q_CLASSINFO("DefaultProperty", "script"); + +public: + QmlScript(QObject *parent=0); + + QString script() const; + void setScript(const QString &); + + QString source() const; + void setSource(const QString &); + +private Q_SLOTS: + void replyFinished(); +}; +QML_DECLARE_TYPE(QmlScript); + +QT_END_NAMESPACE +QT_END_HEADER +#endif diff --git a/src/declarative/util/qmlsetproperties.cpp b/src/declarative/util/qmlsetproperties.cpp new file mode 100644 index 0000000..f385391 --- /dev/null +++ b/src/declarative/util/qmlsetproperties.cpp @@ -0,0 +1,247 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "private/qobject_p.h" +#include "qmlopenmetaobject.h" +#include "qmlsetproperties.h" +#include <QtCore/qdebug.h> +#include <QtDeclarative/qmlinfo.h> + + +QT_BEGIN_NAMESPACE +class QmlSetPropertiesMetaObject : public QmlOpenMetaObject +{ +public: + QmlSetPropertiesMetaObject(QObject *); + +protected: + virtual void propertyRead(int); + virtual void propertyWrite(int); +}; + +class QmlSetPropertiesProxyObject : public QObject +{ +Q_OBJECT +public: + QmlSetPropertiesProxyObject(QObject *); + + QmlSetPropertiesMetaObject *fxMetaObject() const { return _mo; } +private: + QmlSetPropertiesMetaObject *_mo; +}; + +QmlSetPropertiesProxyObject::QmlSetPropertiesProxyObject(QObject *parent) +: QObject(parent), _mo(new QmlSetPropertiesMetaObject(this)) +{ +} + +QmlSetPropertiesMetaObject::QmlSetPropertiesMetaObject(QObject *obj) +: QmlOpenMetaObject(obj) +{ +} + +void QmlSetPropertiesMetaObject::propertyRead(int id) +{ + if(!value(id).isValid()) + setValue(id, QVariant::fromValue((QObject *)new QmlSetPropertiesProxyObject(object()))); + + QmlOpenMetaObject::propertyRead(id); +} + +void QmlSetPropertiesMetaObject::propertyWrite(int id) +{ + if(value(id).userType() == qMetaTypeId<QObject *>()) { + QObject *val = qvariant_cast<QObject *>(value(id)); + QmlSetPropertiesProxyObject *proxy = qobject_cast<QmlSetPropertiesProxyObject *>(val); + if(proxy) { + setValue(id, QVariant()); + delete proxy; + } + } + QmlOpenMetaObject::propertyWrite(id); +} + +/*! + \qmlclass SetProperties QmlSetProperties + \brief The SetProperties element describes new property values for a state. + + SetProperties is a convenience element for changing many properties on a single + object. It allows you to specify the property names and values similar to how + you normally would specify them for the actual item: + + \code + <SetProperties target="{myRect}" x="52" y="300" width="48"/> + \endcode + + \c target is a property of \c SetProperties, so if the property you want to change + is named \e target you will have to use \l SetProperty instead. You should also + use \l SetProperty if you want to update the binding for a property, + as SetProperties does not support this. +*/ + +/*! + \internal + \class QmlSetProperties + \brief The QmlSetProperties class describes new property values for a state. + + \ingroup states + + QmlSetProperties is a convenience class for changing many properties on a single + object. It allows you to specify the property names and values similar to how + you normally would specify them for the actual item: + + \code + <SetProperties target="{myRect}" x="52" y="300" width="48"/> + \endcode + + \c target is a property of \c SetProperties, so if the property you want to change + is named \e target you will have to use QmlSetProperty instead. You should also use QmlSetProperty + if you want to update the binding for a property, as QmlSetProperties does not support this. + + \sa QmlSetProperty +*/ + +class QmlSetPropertiesPrivate : public QObjectPrivate +{ +public: + QmlSetPropertiesPrivate() : obj(0), mo(0) {} + + QObject *obj; + QmlSetPropertiesMetaObject *mo; +}; + +QML_DEFINE_TYPE(QmlSetProperties,SetProperties); +QmlSetProperties::QmlSetProperties() + : QmlStateOperation(*(new QmlSetPropertiesPrivate)) +{ + Q_D(QmlSetProperties); + d->mo = new QmlSetPropertiesMetaObject(this); +} + +QmlSetProperties::QmlSetProperties(QObject *parent) + : QmlStateOperation(*(new QmlSetPropertiesPrivate), parent) +{ + Q_D(QmlSetProperties); + d->mo = new QmlSetPropertiesMetaObject(this); +} + +QmlSetProperties::~QmlSetProperties() +{ +} + +/*! + \qmlproperty Object SetProperties::target + This property holds the object that the properties to change belong to +*/ + +/*! + \property QmlSetProperties::target + \brief the object that the properties to change belong to +*/ +QObject *QmlSetProperties::object() +{ + Q_D(QmlSetProperties); + return d->obj; +} + +void QmlSetProperties::setObject(QObject *o) +{ + Q_D(QmlSetProperties); + d->obj = o; +} + +QmlSetProperties::ActionList +QmlSetProperties::doAction(QmlSetPropertiesMetaObject *metaObject, + QObject *object) +{ + ActionList list; + + for (int ii = 0; ii < metaObject->count(); ++ii) { + + QByteArray name = metaObject->name(ii); + QVariant value = metaObject->value(ii); + + QmlSetPropertiesProxyObject *po = qobject_cast<QmlSetPropertiesProxyObject *>(qvariant_cast<QObject *>(value)); + + QmlMetaProperty prop(object, QLatin1String(name)); + + if(po) { + QObject *objVal = QmlMetaType::toQObject(prop.read()); + if(!objVal) { + qmlInfo(this) << object->metaObject()->className() + << "has no object property named" << name; + continue; + } + + list << doAction(po->fxMetaObject(), objVal); + } else if (!prop.isValid()) { + qmlInfo(this) << object->metaObject()->className() + << "has no property named" << name; + continue; + } else if (!prop.isWritable()) { + qmlInfo(this) << object->metaObject()->className() + << name << "is not writable, and cannot be set."; + continue; + } else { + //append action + Action a; + a.property = prop; + a.fromValue = prop.read(); + a.toValue = value; + + list << a; + } + } + + return list; +} + +QmlSetProperties::ActionList QmlSetProperties::actions() +{ + Q_D(QmlSetProperties); + if (!d->obj) + return ActionList(); + + return doAction(d->mo, d->obj); +} + +QT_END_NAMESPACE +#include "qmlsetproperties.moc" diff --git a/src/declarative/util/qmlsetproperties.h b/src/declarative/util/qmlsetproperties.h new file mode 100644 index 0000000..456b672 --- /dev/null +++ b/src/declarative/util/qmlsetproperties.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 QtDeclarative 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 QMLSETPROPERTIES_H +#define QMLSETPROPERTIES_H + +#include <qmlstateoperations.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlSetPropertiesMetaObject; +class QmlSetPropertiesPrivate; +class Q_DECLARATIVE_EXPORT QmlSetProperties : public QmlStateOperation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlSetProperties); + + Q_PROPERTY(QObject *target READ object WRITE setObject); + +public: + QmlSetProperties(); + QmlSetProperties(QObject *parent); + ~QmlSetProperties(); + + QObject *object(); + void setObject(QObject *); + + virtual ActionList actions(); + +private: + ActionList doAction(QmlSetPropertiesMetaObject *, QObject *); + //QmlSetProperties::ActionList appendDotActions(const QVariant &, const QVariant &); +}; +QML_DECLARE_TYPE(QmlSetProperties); + +#endif // QMLSETPROPERTIES_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qmlstate.cpp b/src/declarative/util/qmlstate.cpp new file mode 100644 index 0000000..50ae6b5 --- /dev/null +++ b/src/declarative/util/qmlstate.cpp @@ -0,0 +1,469 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <gfxeasing.h> +#include "qmltransition.h" +#include "qmlstategroup.h" +#include "qmlstate_p.h" +#include "qmlbindablevalue.h" +#include "qmlstateoperations.h" +#include "qmlanimation.h" +#include "qmlanimation_p.h" +#include "qmlstate.h" +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +Action::Action() : bv(0), event(0), actionDone(false) +{ +} + +ActionEvent::~ActionEvent() +{ +} + +QString ActionEvent::name() const +{ + return QString(); +} + +void ActionEvent::execute() +{ +} + +/*! + \internal +*/ +QmlStateOperation::QmlStateOperation(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + \qmlclass State + \brief The State element defines configurations of objects and properties. + + A state is specified as a set of batched changes from the default configuration. + + Note that setting the state of an object from within another state of the same object is + inadvisible. Not only would this have the same effect as going directly to the second state + it may cause the program to crash. + + \sa {states-transitions}{States and Transitions} +*/ + +/*! + \internal + \class QmlState + \brief The QmlState class allows you to define configurations of objects and properties. + + \ingroup states + + QmlState allows you to specify a state as a set of batched changes from the default + configuration. + + \sa {states-transitions}{States and Transitions} +*/ + +QML_DEFINE_TYPE(QmlState,State); +QmlState::QmlState(QObject *parent) +: QObject(*(new QmlStatePrivate), parent) +{ +} + +QmlState::~QmlState() +{ +} + +/*! + \qmlproperty string State::name + This property holds the name of the state + + Each state should have a unique name. +*/ + +/*! + \property QmlState::name + \brief the name of the state + + Each state should have a unique name. +*/ +QString QmlState::name() const +{ + Q_D(const QmlState); + return d->name; +} + +void QmlState::setName(const QString &n) +{ + Q_D(QmlState); + d->name = n; +} + +bool QmlState::isWhenKnown() const +{ + Q_D(const QmlState); + return d->when != 0; +} + +/*! + \qmlproperty bool State::when + This property holds when the state should be applied + + This should be set to an expression that evaluates to true when you want the state to + be applied. +*/ + +/*! + \property QmlState::when + \brief when the state should be applied + + This should be set to an expression that evaluates to true when you want the state to + be applied. +*/ +QmlBindableValue *QmlState::when() const +{ + Q_D(const QmlState); + return d->when; +} + +void QmlState::setWhen(QmlBindableValue *when) +{ + Q_D(QmlState); + d->when = when; + if(d->group) + d->group->updateAutoState(); +} + +/*! + \advanced + \qmlproperty string State::extends + This property holds the state that this state extends + + The state being extended is treated as the base state in regards to + the changes specified by the extending state. +*/ + +/*! + \property QmlState::extends + \brief the state that this state extends + + The state being extended is treated as the base state in regards to + the changes specified by the extending state. + + \sa operations +*/ +QString QmlState::extends() const +{ + Q_D(const QmlState); + return d->extends; +} + +void QmlState::setExtends(const QString &extends) +{ + Q_D(QmlState); + d->extends = extends; +} + +/*! + \qmlproperty list<StateOperation> State::operations + This property holds the changes to apply for this state + \default + + By default these changes are applied against the default state. If the state + extends another state, then the changes are applied against the state being + extended. +*/ + +/*! + \property QmlState::operations + \brief the changes to apply for this state + + By default these changes are applied against the default state. If the state + extends another state, then the changes are applied against the state being + extended. +*/ +QmlList<QmlStateOperation *> *QmlState::operations() +{ + Q_D(QmlState); + return &d->operations; +} + +QmlState &QmlState::operator<<(QmlStateOperation *op) +{ + Q_D(QmlState); + d->operations.append(op); + return *this; +} + +#if 0 +static void dump(const QmlStateOperation::ActionList &list) +{ + if(!QString(getenv("STATE_DEBUG")).isEmpty()) + return; + + for(int ii = 0; ii < list.count(); ++ii) { + const Action &action = list.at(ii); + qWarning() << action.property.object << action.property.name << action.toValue; + } +} +#endif + +void QmlStatePrivate::complete() +{ + Q_Q(QmlState); + //apply bindings (now that all transitions are complete) + ////////////////////////////////////////////////////////// + foreach(const Action &action, bindingsList) { + if (action.bv && !action.toBinding.isEmpty()) { + action.bv->setExpression(action.toBinding); + } + } + ////////////////////////////////////////////////////////// + + for(int ii = 0; ii < reverting.count(); ++ii) { + for(int jj = 0; jj < revertList.count(); ++jj) { + if(revertList.at(jj).property == reverting.at(ii)) { + revertList.removeAt(jj); + break; + } + } + } + reverting.clear(); + + for(int ii = 0; ii < completeList.count(); ++ii) { + const QmlMetaProperty &prop = completeList.at(ii).property; + prop.write(completeList.at(ii).value); + } + + completeList.clear(); + transition = 0; + emit q->completed(); +} + +QmlStateOperation::ActionList QmlStatePrivate::generateActionList(QmlStateGroup *group) const +{ + QmlStateOperation::ActionList applyList; + if(inState) + return applyList; + + inState = true; + + if(!extends.isEmpty()) { + QList<QmlState *> states = group->states(); + for(int ii = 0; ii < states.count(); ++ii) + if(states.at(ii)->name() == extends) + applyList = static_cast<QmlStatePrivate*>(states.at(ii)->d_ptr)->generateActionList(group); + } + + foreach(QmlStateOperation *op, operations) + applyList << op->actions(); + + inState = false; + return applyList; +} + +QmlStateGroup *QmlState::stateGroup() const +{ + Q_D(const QmlState); + return d->group; +} + +void QmlState::setStateGroup(QmlStateGroup *group) +{ + Q_D(QmlState); + d->group = group; +} + +void QmlState::cancel() +{ + Q_D(QmlState); + if (d->transition) { + d->transition->stop(); //XXX this could potentially trigger a complete in rare circumstances + d->transition = 0; + } +} + +void QmlState::apply(QmlStateGroup *group, QmlTransition *trans, QmlState *revert) +{ + Q_D(QmlState); + + cancel(); + if(revert) + revert->cancel(); + d->revertList.clear(); + d->reverting.clear(); + d->bindingsList.clear(); + + if(revert) + d->revertList = static_cast<QmlStatePrivate*>(revert->d_ptr)->revertList; + QmlStateOperation::RevertActionList additionalReverts; + + QmlStateOperation::ActionList applyList = d->generateActionList(group); + + for(int ii = 0; ii < applyList.count(); ++ii) { + const Action &action = applyList.at(ii); + if(action.event) + continue; + + bool found = false; + for(int jj = 0; !found && jj < d->revertList.count(); ++jj) { + if(d->revertList.at(jj).property == action.property) + found = true; + } + if(!found) { + RevertAction r(action); + additionalReverts << r; + } + } + for(int ii = 0; ii < d->revertList.count(); ++ii) { + bool found = false; + for(int jj = 0; !found && jj < applyList.count(); ++jj) { + const Action &action = applyList.at(jj); + if(action.property == d->revertList.at(ii).property) + found = true; + } + if(!found) { + QVariant cur = d->revertList.at(ii).property.read(); + Action a; + a.property = d->revertList.at(ii).property; + a.fromValue = cur; + a.toValue = d->revertList.at(ii).value; + a.toBinding = d->revertList.at(ii).binding; + if (!a.toBinding.isEmpty()) { + a.fromBinding = d->revertList.at(ii).bv->expression(); //### relies on clearExpression not clearing string + a.bv = d->revertList.at(ii).bv; + } + applyList << a; + d->reverting << d->revertList.at(ii).property; + } + } + d->revertList << additionalReverts; + + //apply all changes, and work out any ending positions for bindings + //then rewind all changes and proceed as normal + //### 4 foreach loops! + //////////////////////////////////////////////////////////////////// + foreach(const Action &action, applyList) { + if(stateChangeDebug()) + qWarning() << " Action:" << action.property.object() << action.property.name() << action.toValue; + + if (action.bv && !action.toBinding.isEmpty()) { + d->bindingsList << action; + action.bv->clearExpression(); + } + } + + if (!d->bindingsList.isEmpty()) { + foreach(const Action &action, applyList) { + if (action.bv && !action.toBinding.isEmpty()) { + action.bv->setExpression(action.toBinding); + } else if(!action.event) { + action.property.write(action.toValue); + } + } + + for (int ii = 0; ii < applyList.size(); ++ii) { + Action *action = &applyList[ii]; + if(action->event) + continue; + + const QmlMetaProperty &prop = action->property; + if (action->bv && !action->toBinding.isEmpty()) { + action->toValue = prop.read(); + } + } + + foreach(const Action &action, applyList) { + if(action.event) + continue; + + if (action.bv && !action.toBinding.isEmpty()) + action.bv->clearExpression(); + action.property.write(action.fromValue); + } + } + //////////////////////////////////////////////////////////////////// + + QmlStateOperation::ActionList modList = applyList; + QList<QmlMetaProperty> touched; + d->completeList.clear(); + if(trans) { + d->transition = trans; + trans->prepare(modList, touched, this); + for(int ii = 0; ii < modList.count(); ++ii) { + const Action &action = modList.at(ii); + + if(action.event) { + if(action.actionDone) { + modList.removeAt(ii); + --ii; + } + } else { + if(action.toValue != action.fromValue) { + d->completeList << RevertAction(action, false); + } + + if(touched.contains(action.property)) { + modList.removeAt(ii); + --ii; + } + } + } + } + + foreach(const Action &action, modList) { + if(action.event) + action.event->execute(); + else + action.property.write(action.toValue); + } +} + +QML_DEFINE_TYPE(QmlStateOperation,StateOperation); +QmlStateOperation::ActionList QmlStateOperation::actions() +{ + return ActionList(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlstate.h b/src/declarative/util/qmlstate.h new file mode 100644 index 0000000..3e6727d --- /dev/null +++ b/src/declarative/util/qmlstate.h @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLSTATE_H +#define QMLSTATE_H + +#include <QtCore/qobject.h> +#include <qfxglobal.h> +#include <gfxtimeline.h> +#include <qml.h> +#include <QSequentialAnimationGroup> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class ActionEvent; +class QmlBindableValue; +class Action +{ +public: + Action(); + + QmlMetaProperty property; + QVariant fromValue; + QVariant toValue; + QString fromBinding; + QString toBinding; + QmlBindableValue *bv; + ActionEvent *event; + bool actionDone; +}; + +class ActionEvent +{ +public: + virtual ~ActionEvent(); + virtual QString name() const; + virtual void execute(); +}; + +class RevertAction +{ +public: + RevertAction(const Action &a, bool from = true) : bv(0) + { + property = a.property; + if (from) { + value = a.fromValue; + binding = a.fromBinding; + } else { + value = a.toValue; + binding = a.toBinding; + } + bv = a.bv; + } + + QmlMetaProperty property; + QVariant value; + QString binding; + QmlBindableValue *bv; +}; + +class QmlStateGroup; +class Q_DECLARATIVE_EXPORT QmlStateOperation : public QObject +{ + Q_OBJECT +public: + QmlStateOperation(QObject *parent = 0) + : QObject(parent) {} + typedef QList<Action> ActionList; + typedef QList<RevertAction> RevertActionList; + + virtual ActionList actions(); + +protected: + QmlStateOperation(QObjectPrivate &dd, QObject *parent = 0); +}; +QML_DECLARE_TYPE(QmlStateOperation); + +typedef QmlStateOperation::ActionList QmlStateActions; + +class QmlTransition; +class QmlTransitionPrivate; +class QmlStatePrivate; +class Q_DECLARATIVE_EXPORT QmlState : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName); + Q_PROPERTY(QmlBindableValue *when READ when WRITE setWhen); + Q_PROPERTY(QString extends READ extends WRITE setExtends); + Q_PROPERTY(QmlList<QmlStateOperation *>* operations READ operations); + Q_CLASSINFO("DefaultProperty", "operations"); + +public: + QmlState(QObject *parent=0); + virtual ~QmlState(); + + QString name() const; + void setName(const QString &); + + /*'when' is a QmlBindableValue to limit state changes oscillation + due to the unpredictable order of evaluation of bound expressions*/ + bool isWhenKnown() const; + QmlBindableValue *when() const; + void setWhen(QmlBindableValue *); + + QString extends() const; + void setExtends(const QString &); + + QmlList<QmlStateOperation *> *operations(); + QmlState &operator<<(QmlStateOperation *); + + void apply(QmlStateGroup *, QmlTransition *, QmlState *revert); + void cancel(); + + QmlStateGroup *stateGroup() const; + void setStateGroup(QmlStateGroup *); + +Q_SIGNALS: + void completed(); + +private: + Q_DECLARE_PRIVATE(QmlState) + Q_DISABLE_COPY(QmlState) + friend class QmlTransitionPrivate; +}; +QML_DECLARE_TYPE(QmlState); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLSTATE_H diff --git a/src/declarative/util/qmlstate_p.h b/src/declarative/util/qmlstate_p.h new file mode 100644 index 0000000..20d1c1a --- /dev/null +++ b/src/declarative/util/qmlstate_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLSTATE_P_H +#define QMLSTATE_P_H + +#include <qmlstate.h> +#include "private/qobject_p.h" +#include "private/qmlanimation_p.h" + +QT_BEGIN_NAMESPACE + +class QmlStatePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlState); + +public: + QmlStatePrivate() + : when(0), transition(0), inState(false), group(0) {} + + QString name; + QmlBindableValue *when; + QmlConcreteList<QmlStateOperation *> operations; + QmlTransition *transition; + QmlStateOperation::RevertActionList revertList; + QList<QmlMetaProperty> reverting; + QmlStateOperation::RevertActionList completeList; + QmlStateOperation::ActionList bindingsList; + QString extends; + mutable bool inState; + QmlStateGroup *group; + + QmlStateOperation::ActionList generateActionList(QmlStateGroup *) const; + void complete(); +}; + +QT_END_NAMESPACE + +#endif // QMLSTATE_P_H diff --git a/src/declarative/util/qmlstategroup.cpp b/src/declarative/util/qmlstategroup.cpp new file mode 100644 index 0000000..36fea4e --- /dev/null +++ b/src/declarative/util/qmlstategroup.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "private/qobject_p.h" +#include "qmlbindablevalue.h" +#include "qmlstategroup.h" +#include "qmltransition.h" +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +QML_DEFINE_TYPE(QmlStateGroup,StateGroup); + +class QmlStateGroupPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlStateGroup) +public: + QmlStateGroupPrivate(QmlStateGroup *p) + : nullState(0), states(p), classComplete(true), + componentComplete(true), ignoreTrans(false) {} + + QString currentState; + QmlState *nullState; + + struct StateList : public QmlConcreteList<QmlState *> + { + StateList(QmlStateGroup *g) + :group(g) {} + void append(QmlState *s) { + QmlConcreteList<QmlState *>::append(s); + if(s) s->setStateGroup(group); + } + private: + QmlStateGroup *group; + }; + StateList states; + + QmlConcreteList<QmlTransition *> transitions; + bool classComplete; + bool componentComplete; + bool ignoreTrans; + + QmlTransition *findTransition(const QString &from, const QString &to); + void setCurrentStateInternal(const QString &state, bool = false); + void updateAutoState(); +}; + +QmlStateGroup::QmlStateGroup(QObject *parent) + : QObject(*(new QmlStateGroupPrivate(this)), parent) +{ +} + +QmlStateGroup::~QmlStateGroup() +{ +} + +QList<QmlState *> QmlStateGroup::states() const +{ + Q_D(const QmlStateGroup); + return d->states; +} + +QmlList<QmlState *>* QmlStateGroup::statesProperty() +{ + Q_D(QmlStateGroup); + return &(d->states); +} + +QmlList<QmlTransition *>* QmlStateGroup::transitionsProperty() +{ + Q_D(QmlStateGroup); + return &(d->transitions); +} + +QString QmlStateGroup::state() const +{ + Q_D(const QmlStateGroup); + return d->currentState; +} + +void QmlStateGroup::setState(const QString &state) +{ + Q_D(QmlStateGroup); + if(d->currentState == state) + return; + + d->setCurrentStateInternal(state); + + d->currentState = state; + emit stateChanged(d->currentState); +} + +void QmlStateGroup::classBegin() +{ + Q_D(QmlStateGroup); + d->classComplete = false; + d->componentComplete = false; +} + +void QmlStateGroup::classComplete() +{ + Q_D(QmlStateGroup); + d->classComplete = true; +} + +void QmlStateGroup::updateAutoState() +{ + Q_D(QmlStateGroup); + d->updateAutoState(); +} + +void QmlStateGroupPrivate::updateAutoState() +{ + Q_Q(QmlStateGroup); + if(!classComplete) + return; + + bool revert = false; + for(int ii = 0; ii < states.count(); ++ii) { + QmlState *state = states.at(ii); + if(state->isWhenKnown()) { + if(!state->name().isEmpty()) { + if(state->when() && state->when()->value().toBool()) { + if(stateChangeDebug()) + qWarning() << "Setting auto state due to:" + << state->when()->expression(); + q->setState(state->name()); + return; + } else if(state->name() == currentState) { + revert = true; + } + } + } + } + if(revert) + q->setState(QString()); +} + +QmlTransition *QmlStateGroupPrivate::findTransition(const QString &from, const QString &to) +{ + QmlTransition *highest = 0; + int score = 0; + bool reversed = false; + bool done = false; + + for(int ii = 0; !done && ii < transitions.count(); ++ii) { + QmlTransition *t = transitions.at(ii); + for(int ii = 0; ii < 2; ++ii) + { + if(ii && (!t->reversible() || + (t->fromState() == QLatin1String("*") && + t->toState() == QLatin1String("*")))) + break; + QStringList fromState; + QStringList toState; + + fromState = t->fromState().split(QLatin1Char(',')); + toState = t->toState().split(QLatin1Char(',')); + if(ii == 1) + qSwap(fromState, toState); + int tScore = 0; + if(fromState.contains(from)) + tScore += 2; + else if(fromState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if(toState.contains(to)) + tScore += 2; + else if(toState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if(ii == 1) + reversed = true; + else + reversed = false; + + if(tScore == 4) { + highest = t; + done = true; + break; + } else if(tScore > score) { + score = tScore; + highest = t; + } + } + } + + if(highest) + highest->setReversed(reversed); + + return highest; +} + +void QmlStateGroupPrivate::setCurrentStateInternal(const QString &state, + bool ignoreTrans) +{ + Q_Q(QmlStateGroup); + if(!componentComplete) + return; + + QmlTransition *transition = (ignoreTrans || ignoreTrans) ? 0 : findTransition(currentState, state); + if(stateChangeDebug()) { + qWarning() << this << "Changing state. From" << currentState << ". To" << state; + if(transition) + qWarning() << " using transition" << transition->fromState() + << transition->toState(); + } + + QmlState *oldState = 0; + if(!currentState.isEmpty()) { + for(int ii = 0; ii < states.count(); ++ii) { + if(states.at(ii)->name() == currentState) { + oldState = states.at(ii); + break; + } + } + } + + currentState = state; + + QmlState *newState = 0; + for(int ii = 0; ii < states.count(); ++ii) { + if(states.at(ii)->name() == currentState) { + newState = states.at(ii); + break; + } + } + + if(oldState == 0 || newState == 0) { + if(!nullState) { nullState = new QmlState(q); } + if(!oldState) oldState = nullState; + if(!newState) newState = nullState; + } + + newState->apply(q, transition, oldState); +} + +void QmlStateGroup::componentComplete() +{ + Q_D(QmlStateGroup); + d->updateAutoState(); + d->componentComplete = true; + if(!d->currentState.isEmpty()) { + QString cs = d->currentState; + d->currentState = QString(); + d->setCurrentStateInternal(cs, true); + } +} + +QmlState *QmlStateGroup::findState(const QString &name) const +{ + Q_D(const QmlStateGroup); + for (int i = 0; i < d->states.count(); ++i) { + QmlState *state = d->states.at(i); + if (state->name() == name) + return state; + } + + return 0; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlstategroup.h b/src/declarative/util/qmlstategroup.h new file mode 100644 index 0000000..55b84eb --- /dev/null +++ b/src/declarative/util/qmlstategroup.h @@ -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 QtDeclarative 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 QMLSTATEGROUP_H +#define QMLSTATEGROUP_H + +#include <qmlstate.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QmlStateGroupPrivate; +class QmlStateGroup : public QObject, public QmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QmlParserStatus) + Q_DECLARE_PRIVATE(QmlStateGroup); + + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged); + Q_PROPERTY(QmlList<QmlState *>* states READ statesProperty DESIGNABLE false); + Q_PROPERTY(QmlList<QmlTransition *>* transitions READ transitionsProperty DESIGNABLE false); + +public: + QmlStateGroup(QObject * = 0); + virtual ~QmlStateGroup(); + + QString state() const; + void setState(const QString &); + + QmlList<QmlState *>* statesProperty(); + QList<QmlState *> states() const; + + QmlList<QmlTransition *>* transitionsProperty(); + + QmlState *findState(const QString &name) const; + + virtual void classBegin(); + virtual void classComplete(); + virtual void componentComplete(); +Q_SIGNALS: + void stateChanged(const QString &); + +private: + friend class QmlState; + void updateAutoState(); +}; +QML_DECLARE_TYPE(QmlStateGroup); + +#endif // QMLSTATEGROUP_H + + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/util/qmlstateoperations.cpp b/src/declarative/util/qmlstateoperations.cpp new file mode 100644 index 0000000..c2ba4b8 --- /dev/null +++ b/src/declarative/util/qmlstateoperations.cpp @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 <private/qobject_p.h> +#include <gfxeasing.h> +#include <qml.h> +#include <QtDeclarative/qmlcontext.h> +#include "qmlbindablevalue.h" +#include "qmlstateoperations.h" +#include <QtCore/qdebug.h> +#include <QtDeclarative/qmlinfo.h> + +QT_BEGIN_NAMESPACE + +class QmlParentChangePrivate : public QObjectPrivate +{ +public: + QmlParentChangePrivate() : target(0), parent(0) {} + + QObject *target; + QObject *parent; +}; + +/*! + \qmlclass ParentChange + \brief The ParentChange element allows you to reparent an object in a state. +*/ + +QML_DEFINE_TYPE(QmlParentChange,ParentChange); +QmlParentChange::QmlParentChange(QObject *parent) + : QmlStateOperation(*(new QmlParentChangePrivate), parent) +{ +} + +QmlParentChange::~QmlParentChange() +{ +} + +/*! + \qmlproperty Object ParentChange::target + This property holds the object to be reparented +*/ + +QObject *QmlParentChange::object() const +{ + Q_D(const QmlParentChange); + return d->target; +} +void QmlParentChange::setObject(QObject *target) +{ + Q_D(QmlParentChange); + d->target = target; +} + +/*! + \qmlproperty Object ParentChange::parent + This property holds the parent for the object in this state +*/ + +QObject *QmlParentChange::parent() const +{ + Q_D(const QmlParentChange); + return d->parent; +} + +void QmlParentChange::setParent(QObject *parent) +{ + Q_D(QmlParentChange); + d->parent = parent; +} + +QmlStateOperation::ActionList QmlParentChange::actions() +{ + Q_D(QmlParentChange); + if(!d->target || !d->parent) + return ActionList(); + + QString propName(QLatin1String("moveToParent")); + QmlMetaProperty prop(d->target, propName); + if (!prop.isValid()) { + qmlInfo(this) << d->target->metaObject()->className() + << "has no property named" << propName; + return ActionList(); + }else if(!prop.isWritable()){ + qmlInfo(this) << d->target->metaObject()->className() << propName + << "is not a writable property and cannot be set."; + return ActionList(); + } + QVariant cur = prop.read(); + + Action a; + a.property = prop; + a.fromValue = cur; + a.toValue = qVariantFromValue(d->parent); + + return ActionList() << a; +} + +class QmlRunScriptPrivate : public QObjectPrivate +{ +public: + QmlRunScriptPrivate() : ctxt(0) {} + + QmlContext *ctxt; + QString script; + QString name; +}; + +/*! + \qmlclass RunScript QmlRunScript + \brief The RunScript element allows you to run a script in a state. +*/ +QML_DEFINE_TYPE(QmlRunScript,RunScript); +QmlRunScript::QmlRunScript(QObject *parent) +: QmlStateOperation(*(new QmlRunScriptPrivate), parent) +{ + Q_D(QmlRunScript); + d->ctxt = QmlContext::activeContext(); +} + +QmlRunScript::~QmlRunScript() +{ +} + +/*! + \qmlproperty string RunScript::script + This property holds the script to run when the state is current. +*/ +QString QmlRunScript::script() const +{ + Q_D(const QmlRunScript); + return d->script; +} + +void QmlRunScript::setScript(const QString &s) +{ + Q_D(QmlRunScript); + d->script = s; +} + +QString QmlRunScript::name() const +{ + Q_D(const QmlRunScript); + return d->name; +} + +void QmlRunScript::setName(const QString &n) +{ + Q_D(QmlRunScript); + d->name = n; +} + +void QmlRunScript::execute() +{ + Q_D(QmlRunScript); + if(!d->script.isEmpty()) { + QmlExpression expr(d->ctxt, d->script, this); + expr.setTrackChange(false); + expr.value(); + } +} + +QmlRunScript::ActionList QmlRunScript::actions() +{ + ActionList rv; + Action a; + a.event = this; + rv << a; + return rv; +} + +/*! + \qmlclass SetProperty QmlSetProperty + \brief The SetProperty element describes a new property value or binding for a state. + + The code below changes the position of the Rect depending upon + the current state: + + \code + <Rect id="myrect" width="50" height="50" color="red"/> + + <states> + <State name="Position1"> + <SetProperty target="{myrect}" property="x" value="150"/> + <SetProperty target="{myrect}" property="y" value="50"/> + </State> + <State name="Position2"> + <SetProperty target="{myrect}" property="y" value="200"/> + </State> + </states> + \endcode + + \sa SetProperties +*/ + +/*! + \internal + \class QmlSetProperty + \brief The QmlSetProperty class describes a new property value or binding for a state. + + \ingroup states + + \sa QmlSetProperties +*/ + +class QmlSetPropertyPrivate : public QObjectPrivate +{ +public: + QmlSetPropertyPrivate() : obj(0) {} + + QObject *obj; + QString prop; + QVariant value; + QString binding; +}; + +QML_DEFINE_TYPE(QmlSetProperty,SetProperty); + +QmlSetProperty::QmlSetProperty(QObject *parent) + : QmlStateOperation(*(new QmlSetPropertyPrivate), parent) +{ +} + +QmlSetProperty::~QmlSetProperty() +{ +} + +/*! + \qmlproperty Object SetProperty::target + This property holds the object the property to change belongs to +*/ + +/*! + \property QmlSetProperty::target + \brief the object the property to change belongs to +*/ +QObject *QmlSetProperty::object() +{ + Q_D(QmlSetProperty); + return d->obj; +} + +void QmlSetProperty::setObject(QObject *o) +{ + Q_D(QmlSetProperty); + d->obj = o; +} + +/*! + \qmlproperty string SetProperty::property + This property holds the name of the property to change +*/ + +/*! + \property QmlSetProperty::property + \brief the name of the property to change +*/ +QString QmlSetProperty::property() const +{ + Q_D(const QmlSetProperty); + return d->prop; +} + +void QmlSetProperty::setProperty(const QString &p) +{ + Q_D(QmlSetProperty); + d->prop = p; +} + +/*! + \qmlproperty variant SetProperty::value + This property holds the value to assign to the property + + You should set either a \c value or a \c binding, but not both. +*/ + +/*! + \property QmlSetProperty::value + \brief the value to assign to the property + + You should set either a value or a binding, not both. +*/ +QVariant QmlSetProperty::value() const +{ + Q_D(const QmlSetProperty); + return d->value; +} + +void QmlSetProperty::setValue(const QVariant &v) +{ + Q_D(QmlSetProperty); + d->value = v; +} + +/*! + \qmlproperty string SetProperty::binding + This property holds the binding to assign to the property + + You should set either a \c value or a \c binding, but not both. +*/ + +/*! + \property QmlSetProperty::binding + \brief the binding to assign to the property + + You should set either a value or a binding, not both. +*/ +QString QmlSetProperty::binding() const +{ + Q_D(const QmlSetProperty); + return d->binding; +} + +void QmlSetProperty::setBinding(const QString &binding) +{ + Q_D(QmlSetProperty); + d->binding = binding; +} + +QmlSetProperty::ActionList QmlSetProperty::actions() +{ + Q_D(QmlSetProperty); + if(!d->obj) + return ActionList(); + + QObject *obj = d->obj; + QString propName = d->prop; + + if (d->prop.contains(QLatin1Char('.'))) { //handle dot properties + QStringList str = d->prop.split(QLatin1Char('.')); + for(int ii = 0; ii < str.count()-1; ++ii) { + const QString &s = str.at(ii); + QmlMetaProperty prop(obj, s); + if(!prop.isValid()) { + qmlInfo(this) << obj->metaObject()->className() + << "has no property named" << s; + return ActionList(); + } + QVariant v = prop.read(); + obj = QmlMetaType::toQObject(v); + if (!obj) { + qmlInfo(this) << "Unable to coerce value property" + << s << "into a QObject"; + return ActionList(); + } + } + propName = str.last(); + } + + QmlMetaProperty prop(obj, propName); + if (!prop.isValid()) { + qmlInfo(this) << obj->metaObject()->className() + << "has no property named" << propName; + return ActionList(); + }else if(!prop.isWritable()){ + qmlInfo(this) << obj->metaObject()->className() << propName + << "is not a writable property and cannot be set."; + return ActionList(); + } + QVariant cur = prop.read(); + + Action a; + a.property = prop; + a.fromValue = cur; + a.toValue = d->value; + if (!d->binding.isEmpty()) { + QmlBindableValue *bv = prop.binding(); + if(bv) { + a.fromBinding = bv->expression(); + a.bv = bv; + } + } + a.toBinding = d->binding; + + return ActionList() << a; +} + +QT_END_NAMESPACE diff --git a/src/declarative/util/qmlstateoperations.h b/src/declarative/util/qmlstateoperations.h new file mode 100644 index 0000000..8ecdcd2 --- /dev/null +++ b/src/declarative/util/qmlstateoperations.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QMLSTATEOPERATIONS_H +#define QMLSTATEOPERATIONS_H + +#include <qmlstate.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlParentChangePrivate; +class Q_DECLARATIVE_EXPORT QmlParentChange : public QmlStateOperation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlParentChange); + + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(QObject *parent READ parent WRITE setParent) +public: + QmlParentChange(QObject *parent=0); + ~QmlParentChange(); + + QObject *object() const; + void setObject(QObject *); + + QObject *parent() const; + void setParent(QObject *); + + virtual ActionList actions(); +}; +QML_DECLARE_TYPE(QmlParentChange); + +class QmlRunScriptPrivate; +class Q_DECLARATIVE_EXPORT QmlRunScript : public QmlStateOperation, public ActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlRunScript) + + Q_PROPERTY(QString script READ script WRITE setScript); + Q_PROPERTY(QString name READ name WRITE setName); + +public: + QmlRunScript(QObject *parent=0); + ~QmlRunScript(); + + virtual ActionList actions(); + + QString script() const; + void setScript(const QString &); + + virtual QString name() const; + void setName(const QString &); + + virtual void execute(); +}; +QML_DECLARE_TYPE(QmlRunScript); + +class QmlSetPropertyPrivate; +class Q_DECLARATIVE_EXPORT QmlSetProperty : public QmlStateOperation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlSetProperty); + + Q_PROPERTY(QObject *target READ object WRITE setObject); + Q_PROPERTY(QString property READ property WRITE setProperty); + Q_PROPERTY(QVariant value READ value WRITE setValue); + Q_PROPERTY(QString binding READ binding WRITE setBinding); + +public: + QmlSetProperty(QObject *parent=0); + ~QmlSetProperty(); + + QObject *object(); + void setObject(QObject *); + QString property() const; + void setProperty(const QString &); + QVariant value() const; + void setValue(const QVariant &); + QString binding() const; + void setBinding(const QString&); + + virtual ActionList actions(); +}; +QML_DECLARE_TYPE(QmlSetProperty); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLSTATEOPERATIONS_H diff --git a/src/declarative/util/qmltransition.cpp b/src/declarative/util/qmltransition.cpp new file mode 100644 index 0000000..fb09e32 --- /dev/null +++ b/src/declarative/util/qmltransition.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qmlstate.h" +#include "qmlstategroup.h" +#include "qmlstate_p.h" +#include "qmlbindablevalue.h" +#include "qmlstateoperations.h" +#include "qmlanimation.h" +#include "qmlanimation_p.h" +#include <QParallelAnimationGroup> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Transition QmlTransition + \brief The Transition element defines animated transitions that occur on state changes. + + \sa {states-transitions}{States and Transitions} +*/ + +/*! + \internal + \class QmlTransition + \brief The QmlTransition class allows you to define animated transitions that occur on state changes. + + \ingroup states +*/ + +//ParallelAnimationWrapperallows us to do a "callback" when the animation finishes, rather than connecting +//and disconnecting signals and slots frequently +class ParallelAnimationWrapper : public QParallelAnimationGroup +{ + Q_OBJECT +public: + ParallelAnimationWrapper(QObject *parent) : QParallelAnimationGroup(parent) {} + QmlTransitionPrivate *trans; +protected: + virtual void updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState); +}; + +class QmlTransitionPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlTransition) +public: + QmlTransitionPrivate() : fromState(QLatin1String("*")), toState(QLatin1String("*")) + , reversed(false), reversible(false), group(0), endState(0) + { + operations.parent = this; + } + + QString fromState; + QString toState; + bool reversed; + bool reversible; + ParallelAnimationWrapper *group; + QmlState *endState; + + void init() + { + Q_Q(QmlTransition); + group = new ParallelAnimationWrapper(q); + group->trans = this; + } + + void complete() + { + endState->d_func()->complete(); + } + + class AnimationList : public QmlConcreteList<QmlAbstractAnimation *> + { + public: + AnimationList() : parent(0) {} + virtual void append(QmlAbstractAnimation *a); + virtual void clear() { QmlConcreteList<QmlAbstractAnimation *>::clear(); } //XXX + + QmlTransitionPrivate *parent; + }; + AnimationList operations; +}; + +void QmlTransitionPrivate::AnimationList::append(QmlAbstractAnimation *a) +{ + QmlConcreteList<QmlAbstractAnimation *>::append(a); + parent->group->addAnimation(a->qtAnimation()); +} + +void ParallelAnimationWrapper::updateState(QAbstractAnimation::State oldState, QAbstractAnimation::State newState) +{ + QParallelAnimationGroup::updateState(oldState, newState); + //XXX not 100% guaranteed to be at end (if there are many zero duration animations at the end)? + if (newState == Stopped && currentTime() == duration()) + { + trans->complete(); + } +} + + +QML_DEFINE_TYPE(QmlTransition,Transition); +QmlTransition::QmlTransition(QObject *parent) + : QObject(*(new QmlTransitionPrivate), parent) +{ + Q_D(QmlTransition); + d->init(); +} + +QmlTransition::~QmlTransition() +{ +} + +void QmlTransition::stop() +{ + Q_D(QmlTransition); + d->group->stop(); +} + +void QmlTransition::setReversed(bool r) +{ + Q_D(QmlTransition); + d->reversed = r; +} + +void QmlTransition::prepare(QmlStateOperation::ActionList &actions, + QList<QmlMetaProperty> &after, + QmlState *endState) +{ + Q_D(QmlTransition); + + if(d->reversed) { + for(int ii = d->operations.count() - 1; ii >= 0; --ii) { + d->operations.at(ii)->transition(actions, after, QmlAbstractAnimation::Backward); + } + } else { + for(int ii = 0; ii < d->operations.count(); ++ii) { + d->operations.at(ii)->transition(actions, after, QmlAbstractAnimation::Forward); + } + } + + d->endState = endState; + d->group->start(); +} + +/*! + \qmlproperty string Transition::fromState + \qmlproperty string Transition::toState + These properties are selectors indicating which state changes should trigger the transition. + + fromState is used in conjunction with toState to determine when a transition should + be applied. By default fromState and toState are both "*" (any state). In the following example, + the transition is applied when changing from state1 to state2. + \code + <Transition fromState="state1" toState="state2"> + ... + </Transition> + \endcode +*/ + +/*! + \property QmlTransition::fromState + \brief a selector indicating which states, when left, should trigger the transition. + + fromState is used in conjunction with toState to determine when a transition should + be applied. The default value is "*" (any state). +*/ +QString QmlTransition::fromState() const +{ + Q_D(const QmlTransition); + return d->fromState; +} + +void QmlTransition::setFromState(const QString &f) +{ + Q_D(QmlTransition); + d->fromState = f; +} + +/*! + \qmlproperty bool Transition::reversible + This property holds whether the transition should be automatically reversed when the conditions that triggered this transition are reversed. + + The default value is false. +*/ + + +/*! + \property QmlTransition::reversible + \brief whether the transition should be automatically reversed when the conditions that triggered this transition are reversed. + + The default value is false. +*/ +bool QmlTransition::reversible() const +{ + Q_D(const QmlTransition); + return d->reversible; +} + +void QmlTransition::setReversible(bool r) +{ + Q_D(QmlTransition); + d->reversible = r; +} + +/*! + \property QmlTransition::toState + \brief a selector indicating which states, when entered, should trigger the transition. + + toState is used in conjunction with fromState to determine when a transition should + be applied. The default value is "*" (any state). +*/ +QString QmlTransition::toState() const +{ + Q_D(const QmlTransition); + return d->toState; +} + +void QmlTransition::setToState(const QString &t) +{ + Q_D(QmlTransition); + d->toState = t; +} + +/*! + \qmlproperty list<Animation> Transition::operations + This property holds a list of the animations to be run for this transition. + + The top-level animations in operations are run in parallel. + To run them sequentially, you can create a single <SequentialAnimation> + which contains all the animations, and assign that to operations. + \default +*/ + +/*! + \property QmlTransition::operations + \brief a list of the transition animations to be run. +*/ +QmlList<QmlAbstractAnimation *>* QmlTransition::operations() +{ + Q_D(QmlTransition); + return &d->operations; +} + +QT_END_NAMESPACE + +#include "qmltransition.moc" diff --git a/src/declarative/util/qmltransition.h b/src/declarative/util/qmltransition.h new file mode 100644 index 0000000..0d86b7d --- /dev/null +++ b/src/declarative/util/qmltransition.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 QtDeclarative 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 QMLTRANSITION_H +#define QMLTRANSITION_H + +#include <QtCore/qobject.h> +#include <qfxglobal.h> +#include <gfxtimeline.h> +#include <qmlstate.h> +#include <qml.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlAbstractAnimation; +class QmlTransitionPrivate; +class Q_DECLARATIVE_EXPORT QmlTransition : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlTransition) + + Q_PROPERTY(QString fromState READ fromState WRITE setFromState) + Q_PROPERTY(QString toState READ toState WRITE setToState) + Q_PROPERTY(bool reversible READ reversible WRITE setReversible) + Q_PROPERTY(QmlList<QmlAbstractAnimation *>* operations READ operations) + Q_CLASSINFO("DefaultProperty", "operations") + +public: + QmlTransition(QObject *parent=0); + ~QmlTransition(); + + QString fromState() const; + void setFromState(const QString &); + + QString toState() const; + void setToState(const QString &); + + bool reversible() const; + void setReversible(bool); + + QmlList<QmlAbstractAnimation *>* operations(); + + void prepare(QmlStateOperation::ActionList &actions, + QList<QmlMetaProperty> &after, + QmlState *endState); + + void setReversed(bool r); + void stop(); +}; +QML_DECLARE_TYPE(QmlTransition); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLTRANSITION_H diff --git a/src/declarative/util/qperformancelog.cpp b/src/declarative/util/qperformancelog.cpp new file mode 100644 index 0000000..52ccc0d --- /dev/null +++ b/src/declarative/util/qperformancelog.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "qperformancelog.h" +#include <QHash> +#include <QDebug> + + +#ifdef Q_ENABLE_PERFORMANCE_LOG + +struct QPerformanceLogData +{ + struct Log + { + Log() + : logDescription(0), maxId(-1) {} + + QHash<int, const char *> descriptions; + const char *logDescription; + int maxId; + }; + + typedef QHash<QPerformanceLog::LogData *, Log> Logs; + Logs logs; +}; +Q_GLOBAL_STATIC(QPerformanceLogData, performanceLogData); + +QPerformanceLog::LogData::LogData(const char *desc) +: sumTime(0), data(0) +{ + QPerformanceLogData *logData = performanceLogData(); + + QPerformanceLogData::Log log; + log.logDescription = desc; + logData->logs.insert(this, log); + + timer.start(); +} + +QPerformanceLog::LogMetric::LogMetric(LogData *l, int id, const char *desc) +{ + if(id < 0) + qFatal("QPerformanceLog: Invalid log id %d ('%s')", id, desc); + + QPerformanceLogData *logData = performanceLogData(); + + QPerformanceLogData::Logs::Iterator logIter = logData->logs.find(l); + if(logIter == logData->logs.end()) + qFatal("QPerformanceLog: Unable to locate log for metric '%s'", desc); + QPerformanceLogData::Log &log = *logIter; + if(log.descriptions.contains(id)) + qFatal("QPerformanceLog: Duplicate log metric %d ('%s')", id, desc); + log.descriptions.insert(id, desc); + + if(log.maxId < id) { + log.maxId = id; + if(l->data) delete [] l->data; + l->data = new unsigned int[2 * (log.maxId + 1)]; + ::memset(l->data, 0, 2 * (log.maxId + 1) * sizeof(unsigned int)); + } +} + +static void QPerformanceLog_clear(QPerformanceLog::LogData *l, const QPerformanceLogData::Log *pl) +{ + ::memset(l->data, 0, 2 * (pl->maxId + 1) * sizeof(unsigned int)); +} + +static void QPerformanceLog_displayData(const QPerformanceLog::LogData *l, const QPerformanceLogData::Log *pl) +{ + qWarning() << pl->logDescription << "performance data"; + unsigned int total = 0; + for(QHash<int, const char *>::ConstIterator iter = pl->descriptions.begin(); + iter != pl->descriptions.end(); + ++iter) { + + int id = iter.key(); + unsigned int ms = l->data[id * 2]; + total += ms; + unsigned int inst = l->data[id * 2 + 1]; + float pi = float(ms) / float(inst); + qWarning().nospace() << " " << *iter << ": " << ms << " ms over " + << inst << " instances (" << pi << " ms/instance)"; + } + qWarning().nospace() << " TOTAL: " << total; +} + +void QPerformanceLog::displayData() +{ + QPerformanceLogData *logData = performanceLogData(); + + for(QPerformanceLogData::Logs::ConstIterator iter = logData->logs.begin(); + iter != logData->logs.end(); + ++iter) { + QPerformanceLog_displayData(iter.key(), &(*iter)); + } +} + +void QPerformanceLog::clear() +{ + QPerformanceLogData *logData = performanceLogData(); + + for(QPerformanceLogData::Logs::ConstIterator iter = logData->logs.begin(); + iter != logData->logs.end(); + ++iter) { + QPerformanceLog_clear(iter.key(), &(*iter)); + } +} + +void QPerformanceLog::displayData(LogData *l) +{ + QPerformanceLogData *logData = performanceLogData(); + QPerformanceLogData::Logs::ConstIterator iter = logData->logs.find(l); + if(iter == logData->logs.end()) + qFatal("QPerformanceLog: Internal corruption - unable to locate log"); + + QPerformanceLog_displayData(iter.key(), &(*iter)); +} + +void QPerformanceLog::clear(LogData *l) +{ + QPerformanceLogData *logData = performanceLogData(); + QPerformanceLogData::Logs::ConstIterator iter = logData->logs.find(l); + if(iter == logData->logs.end()) + qFatal("QPerformanceLog: Internal corruption - unable to locate log"); + + QPerformanceLog_clear(iter.key(), &(*iter)); +} + +#else // Q_ENABLE_PERFORMANCE_LOG + +void QPerformanceLog::displayData() +{ +} + +void QPerformanceLog::clear() +{ +} + +#endif // Q_ENABLE_PERFORMANCE_LOG diff --git a/src/declarative/util/qperformancelog.h b/src/declarative/util/qperformancelog.h new file mode 100644 index 0000000..9d19bbd --- /dev/null +++ b/src/declarative/util/qperformancelog.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 QPERFORMANCELOG_H +#define QPERFORMANCELOG_H + +#include <QtCore/qdatetime.h> +namespace QPerformanceLog +{ + Q_DECLARATIVE_EXPORT void displayData(); + Q_DECLARATIVE_EXPORT void clear(); + +#ifdef Q_ENABLE_PERFORMANCE_LOG + struct LogData { + LogData(const char *); + QTime timer; + int sumTime; + unsigned int *data; + }; + + struct LogMetric { + LogMetric(LogData *, int, const char *); + }; + + // Internal + void displayData(LogData *); + void clear(LogData *); +#endif +}; + +#ifdef Q_ENABLE_PERFORMANCE_LOG + +#define Q_DECLARE_PERFORMANCE_METRIC(name) \ + enum { name = ValueChoice<0, ValueTracker<0, __LINE__>::value, __LINE__>::value }; \ + template<int L> \ + struct ValueTracker<name, L> \ + { \ + enum { value = name }; \ + }; \ + extern QPerformanceLog::LogMetric metric ## name; + +#define Q_DECLARE_PERFORMANCE_LOG(name) \ + namespace name { \ + extern QPerformanceLog::LogData log; \ + inline void displayData() { QPerformanceLog::displayData(&log); } \ + inline void clear() { QPerformanceLog::clear(&log); } \ + } \ + template<int N> \ + class name ## Timer { \ + public: \ + name ## Timer() { \ + lastSum = name::log.sumTime + name::log.timer.restart(); \ + name::log.sumTime = 0; \ + } \ + ~ name ## Timer() { \ + name::log.data[2 * N] += name::log.sumTime + name::log.timer.restart(); \ + ++name::log.data[2 * N + 1]; \ + name::log.sumTime = lastSum; \ + } \ + private: \ + int lastSum; \ + }; \ + namespace name { \ + template<int N, int L> \ + struct ValueTracker \ + { \ + enum { value = -1 }; \ + }; \ + template<int DefNextValue, int NextValue, int L> \ + struct ValueChoice \ + { \ + enum { value = ValueChoice<DefNextValue + 1, ValueTracker<DefNextValue + 1, L>::value, L>::value }; \ + }; \ + template<int DefNextValue, int L> \ + struct ValueChoice<DefNextValue, -1, L> \ + { \ + enum { value = DefNextValue }; \ + }; \ + } \ + namespace name + +#define Q_DEFINE_PERFORMANCE_LOG(name, desc) \ + QPerformanceLog::LogData name::log(desc); \ + namespace name + +#define Q_DEFINE_PERFORMANCE_METRIC(name, desc) \ + QPerformanceLog::LogMetric metrix ## name(&log, name, desc); + +#else // Q_ENABLE_PERFORMANCE_LOG + +#define Q_DECLARE_PERFORMANCE_METRIC(name) \ + enum { name = ValueChoice<0, ValueTracker<0, __LINE__>::value, __LINE__>::value }; \ + template<int L> \ + struct ValueTracker<name, L> \ + { \ + enum { value = name }; \ + }; \ + +#define Q_DECLARE_PERFORMANCE_LOG(name) \ + namespace name { \ + inline void displayData() { }; \ + inline void clear() { }; \ + } \ + template<int N> \ + class name ## Timer { \ + public: \ + name ## Timer() { \ + } \ + ~ name ## Timer() { \ + } \ + }; \ + namespace name { \ + template<int N, int L> \ + struct ValueTracker \ + { \ + enum { value = -1 }; \ + }; \ + template<int DefNextValue, int NextValue, int L> \ + struct ValueChoice \ + { \ + enum { value = ValueChoice<DefNextValue + 1, ValueTracker<DefNextValue + 1, L>::value, L>::value }; \ + }; \ + template<int DefNextValue, int L> \ + struct ValueChoice<DefNextValue, -1, L> \ + { \ + enum { value = DefNextValue }; \ + }; \ + } \ + namespace name + +#define Q_DEFINE_PERFORMANCE_LOG(name, desc) \ + namespace name + +#define Q_DEFINE_PERFORMANCE_METRIC(name, desc) + +#endif // Q_ENABLE_PERFORMANCE_LOG + +#endif // QPERFORMANCELOG_H diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri new file mode 100644 index 0000000..030a44e --- /dev/null +++ b/src/declarative/util/util.pri @@ -0,0 +1,49 @@ +SOURCES += \ + util/qfxview.cpp \ + util/qfxperf.cpp \ + util/qperformancelog.cpp \ + util/qmlconnection.cpp \ + util/qmlpackage.cpp \ + util/qmlscript.cpp \ + util/qmlanimation.cpp \ + util/qmlbehaviour.cpp \ + util/qmlfont.cpp \ + util/qmlfollow.cpp \ + util/qmlstate.cpp\ + util/qmlstateoperations.cpp \ + util/qmlsetproperties.cpp \ + util/qmlstategroup.cpp \ + util/qmltransition.cpp \ + util/qbindablemap.cpp \ + util/qmldatetimeformatter.cpp \ + util/qmllistmodel.cpp\ + util/qmllistaccessor.cpp \ + util/qmlopenmetaobject.cpp \ + util/qmlbind.cpp + +HEADERS += \ + util/qfxview.h \ + util/qfxperf.h \ + util/qfxglobal.h \ + util/qperformancelog.h \ + util/qmlconnection.h \ + util/qmlpackage.h \ + util/qmlscript.h \ + util/qmlanimation.h \ + util/qmlanimation_p.h \ + util/qmlbehaviour.h \ + util/qmlfont.h \ + util/qmlfollow.h \ + util/qmlstate.h\ + util/qmlstateoperations.h \ + util/qmlsetproperties.h \ + util/qmlstate_p.h\ + util/qmlstategroup.h \ + util/qmltransition.h \ + util/qbindablemap.h \ + util/qmldatetimeformatter.h \ + util/qmllistmodel.h\ + util/qmllistaccessor.h \ + util/qmlopenmetaobject.h \ + util/qmlnullablevalue_p.h \ + util/qmlbind.h diff --git a/src/declarative/widgets/graphicslayouts.cpp b/src/declarative/widgets/graphicslayouts.cpp new file mode 100644 index 0000000..1ecde71 --- /dev/null +++ b/src/declarative/widgets/graphicslayouts.cpp @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "graphicslayouts.h" +#include <QtGui/qgraphicswidget.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +QML_DEFINE_INTERFACE(QGraphicsLayoutItem); +QML_DEFINE_INTERFACE(QGraphicsLayout); + +class LinearLayoutAttached : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int stretchFactor READ stretchFactor WRITE setStretchFactor NOTIFY stretchChanged) + Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged) +public: + LinearLayoutAttached(QObject *parent) + : QObject(parent), _stretch(1), _alignment(Qt::AlignCenter) + { + } + + int stretchFactor() const { return _stretch; } + void setStretchFactor(int f) + { + if (_stretch == f) + return; + + _stretch = f; + emit stretchChanged(reinterpret_cast<QGraphicsLayoutItem*>(parent()), _stretch); + } + + Qt::Alignment alignment() const { return _alignment; } + void setAlignment(Qt::Alignment a) + { + if (_alignment == a) + return; + + _alignment = a; + emit alignmentChanged(reinterpret_cast<QGraphicsLayoutItem*>(parent()), _alignment); + } + +Q_SIGNALS: + void stretchChanged(QGraphicsLayoutItem*,int); + void alignmentChanged(QGraphicsLayoutItem*,Qt::Alignment); + +private: + int _stretch; + Qt::Alignment _alignment; +}; + +QML_DEFINE_TYPE(QGraphicsLinearLayoutStretchItemObject,QGraphicsLinearLayoutStretchItem); + +QGraphicsLinearLayoutStretchItemObject::QGraphicsLinearLayoutStretchItemObject(QObject *parent) + : QObject(parent) +{ +} + +QSizeF QGraphicsLinearLayoutStretchItemObject::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED(which); + Q_UNUSED(constraint); + return QSizeF(); +} + +QML_DEFINE_TYPE(QGraphicsLinearLayoutObject,QGraphicsLinearLayout); + +QGraphicsLinearLayoutObject::QGraphicsLinearLayoutObject(QObject *parent) +: QObject(parent), _children(this) +{ +} + +QGraphicsLinearLayoutObject::~QGraphicsLinearLayoutObject() +{ +} + +void QGraphicsLinearLayoutObject::insertLayoutItem(int index, QGraphicsLayoutItem *item) +{ + insertItem(index, item); + + //connect attached properties + if(QObject *obj = attachedProperties.value(item)) { + setStretchFactor(item, static_cast<LinearLayoutAttached *>(obj)->stretchFactor()); + setAlignment(item, static_cast<LinearLayoutAttached *>(obj)->alignment()); + QObject::connect(obj, SIGNAL(stretchChanged(QGraphicsLayoutItem*,int)), + this, SLOT(updateStretch(QGraphicsLayoutItem*,int))); + QObject::connect(obj, SIGNAL(alignmentChanged(QGraphicsLayoutItem*,Qt::Alignment)), + this, SLOT(updateAlignment(QGraphicsLayoutItem*,Qt::Alignment))); + //XXX need to disconnect when widget is removed? + } +} + +//XXX is there a better way to do this? +void QGraphicsLinearLayoutObject::clearChildren() +{ + for (int i = 0; i < count(); ++i) + removeAt(i); +} + +void QGraphicsLinearLayoutObject::updateStretch(QGraphicsLayoutItem *item, int stretch) +{ + QGraphicsLinearLayout::setStretchFactor(item, stretch); +} + +void QGraphicsLinearLayoutObject::updateAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment) +{ + QGraphicsLinearLayout::setAlignment(item, alignment); +} + +QHash<QGraphicsLayoutItem*, QObject*> QGraphicsLinearLayoutObject::attachedProperties; +QObject *QGraphicsLinearLayoutObject::qmlAttachedProperties(QObject *obj) +{ + if (!qobject_cast<QGraphicsLayoutItem*>(obj)) + return 0; + if(!attachedProperties.contains(qobject_cast<QGraphicsLayoutItem*>(obj))) { + LinearLayoutAttached *rv = new LinearLayoutAttached(obj); + /*if (QGraphicsLinearLayoutObject *lo = qobject_cast<QGraphicsLinearLayoutObject*>(obj->parent())) + QObject::connect(rv, SIGNAL(stretchChanged(QGraphicsLayoutItem*,int)), + lo, SLOT(updateStretch(QGraphicsLayoutItem*,int))); + QObject::connect(rv, SIGNAL(alignmentChanged(QGraphicsLayoutItem*,Qt::Alignment)), + lo, SLOT(updateAlignment(QGraphicsLayoutItem*,Qt::Alignment)));*/ + attachedProperties.insert(qobject_cast<QGraphicsLayoutItem*>(obj), rv); + } + return attachedProperties.value(qobject_cast<QGraphicsLayoutItem*>(obj)); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// QGraphicsGridLayout-related classes +////////////////////////////////////////////////////////////////////////////////////////////////////// +class GridLayoutAttached : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int row READ row WRITE setRow) + Q_PROPERTY(int column READ column WRITE setColumn) + Q_PROPERTY(int rowSpan READ rowSpan WRITE setRowSpan) + Q_PROPERTY(int columnSpan READ columnSpan WRITE setColumnSpan) + Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment) +public: + GridLayoutAttached(QObject *parent) + : QObject(parent), _row(-1), _column(-1), _rowspan(1), _colspan(1), _alignment(-1) + { + } + + int row() const { return _row; } + void setRow(int r) + { + if (_row == r) + return; + + _row = r; + //emit rowChanged(reinterpret_cast<QGraphicsLayoutItem*>(parent()), _row); + } + + int column() const { return _column; } + void setColumn(int c) + { + if (_column == c) + return; + + _column = c; + //emit columnChanged(reinterpret_cast<QGraphicsLayoutItem*>(parent()), _column); + } + + int rowSpan() const { return _rowspan; } + void setRowSpan(int rs) + { + if (_rowspan == rs) + return; + + _rowspan = rs; + //emit rowSpanChanged(reinterpret_cast<QGraphicsLayoutItem*>(parent()), _rowSpan); + } + + int columnSpan() const { return _colspan; } + void setColumnSpan(int cs) + { + if (_colspan == cs) + return; + + _colspan = cs; + //emit columnSpanChanged(reinterpret_cast<QGraphicsLayoutItem*>(parent()), _columnSpan); + } + + Qt::Alignment alignment() const { return _alignment; } + void setAlignment(Qt::Alignment a) + { + if (_alignment == a) + return; + + _alignment = a; + //emit alignmentChanged(reinterpret_cast<QGraphicsLayoutItem*>(parent()), _alignment); + } + +Q_SIGNALS: + //void rowChanged(QGraphicsLayoutItem*,int); + //void columnSpanChanged(QGraphicsLayoutItem*,int); + //void rowSpanChanged(QGraphicsLayoutItem*,int); + //void columnChanged(QGraphicsLayoutItem*,int); + //void alignmentChanged(QGraphicsLayoutItem*,Qt::Alignment); + +private: + int _row; + int _column; + int _rowspan; + int _colspan; + Qt::Alignment _alignment; +}; + +QML_DEFINE_TYPE(QGraphicsGridLayoutObject,QGraphicsGridLayout); + + +QGraphicsGridLayoutObject::QGraphicsGridLayoutObject(QObject *parent) +: QObject(parent), _children(this) +{ +} + +QGraphicsGridLayoutObject::~QGraphicsGridLayoutObject() +{ +} + +void QGraphicsGridLayoutObject::addWidget(QGraphicsWidget *wid) +{ + //use attached properties + if(QObject *obj = attachedProperties.value(qobject_cast<QGraphicsLayoutItem*>(wid))) { + int row = static_cast<GridLayoutAttached *>(obj)->row(); + int column = static_cast<GridLayoutAttached *>(obj)->column(); + int rowSpan = static_cast<GridLayoutAttached *>(obj)->rowSpan(); + int columnSpan = static_cast<GridLayoutAttached *>(obj)->columnSpan(); + if (row == -1 || column == -1) { + qWarning() << "Must set row and column for an item in a grid layout"; + return; + } + addItem(wid, row, column, rowSpan, columnSpan); + } +} + +void QGraphicsGridLayoutObject::addLayoutItem(QGraphicsLayoutItem *item) +{ + //use attached properties + if(QObject *obj = attachedProperties.value(item)) { + int row = static_cast<GridLayoutAttached *>(obj)->row(); + int column = static_cast<GridLayoutAttached *>(obj)->column(); + int rowSpan = static_cast<GridLayoutAttached *>(obj)->rowSpan(); + int columnSpan = static_cast<GridLayoutAttached *>(obj)->columnSpan(); + Qt::Alignment alignment = static_cast<GridLayoutAttached *>(obj)->alignment(); + if (row == -1 || column == -1) { + qWarning() << "Must set row and column for an item in a grid layout"; + return; + } + addItem(item, row, column, rowSpan, columnSpan); + if (alignment != -1) + setAlignment(item,alignment); + } +} + +//XXX is there a better way to do this? +void QGraphicsGridLayoutObject::clearChildren() +{ + for (int i = 0; i < count(); ++i) + removeAt(i); +} + +qreal QGraphicsGridLayoutObject::spacing() const +{ + if (verticalSpacing() == horizontalSpacing()) + return verticalSpacing(); + return -1; //XXX +} + +QHash<QGraphicsLayoutItem*, QObject*> QGraphicsGridLayoutObject::attachedProperties; +QObject *QGraphicsGridLayoutObject::qmlAttachedProperties(QObject *obj) +{ + if (!qobject_cast<QGraphicsLayoutItem*>(obj)) + return 0; + if(!attachedProperties.contains(qobject_cast<QGraphicsLayoutItem*>(obj))) { + GridLayoutAttached *rv = new GridLayoutAttached(obj); + attachedProperties.insert(qobject_cast<QGraphicsLayoutItem*>(obj), rv); + } + return attachedProperties.value(qobject_cast<QGraphicsLayoutItem*>(obj)); +} + +QT_END_NAMESPACE + +#include "graphicslayouts.moc" diff --git a/src/declarative/widgets/graphicslayouts.h b/src/declarative/widgets/graphicslayouts.h new file mode 100644 index 0000000..beb4b65 --- /dev/null +++ b/src/declarative/widgets/graphicslayouts.h @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 GRAPHICSLAYOUTS_H +#define GRAPHICSLAYOUTS_H + +#include "graphicswidgets.h" +#include <QGraphicsLinearLayout> +#include <QGraphicsGridLayout> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +QML_DECLARE_INTERFACE(QGraphicsLayoutItem); +QML_DECLARE_INTERFACE(QGraphicsLayout); + +class QGraphicsLinearLayoutStretchItemObject : public QObject, public QGraphicsLayoutItem +{ + Q_OBJECT + Q_INTERFACES(QGraphicsLayoutItem) +public: + QGraphicsLinearLayoutStretchItemObject(QObject *parent = 0); + + virtual QSizeF sizeHint(Qt::SizeHint, const QSizeF &) const; +}; +QML_DECLARE_TYPE(QGraphicsLinearLayoutStretchItemObject); + +//TODO: +// -content margins +// -per-item spacing (does this need to be exposed?) +// -per-item alignment +class LinearLayoutAttached; +class QGraphicsLinearLayoutObject : public QObject, public QGraphicsLinearLayout +{ + Q_OBJECT + Q_INTERFACES(QGraphicsLayout QGraphicsLayoutItem) + + Q_PROPERTY(QmlList<QGraphicsLayoutItem *> *children READ children) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing) + Q_CLASSINFO("DefaultProperty", "children") +public: + QGraphicsLinearLayoutObject(QObject * = 0); + ~QGraphicsLinearLayoutObject(); + + QmlList<QGraphicsLayoutItem *> *children() { return &_children; } + + static QObject *qmlAttachedProperties(QObject *); + +private Q_SLOTS: + void updateStretch(QGraphicsLayoutItem*,int); + void updateAlignment(QGraphicsLayoutItem*,Qt::Alignment); + +private: + friend class LinearLayoutAttached; + void clearChildren(); + void insertLayoutItem(int, QGraphicsLayoutItem *); + static QHash<QGraphicsLayoutItem*, QObject*> attachedProperties; + + class ChildList : public QmlList<QGraphicsLayoutItem *> + { + public: + ChildList(QGraphicsLinearLayoutObject *o) + : obj(o) {} + + virtual void append(QGraphicsLayoutItem *item) + { + insert(-1, item); + } + virtual void clear() { obj->clearChildren(); } + virtual int count() const { return obj->count(); } + virtual void removeAt(int i) { obj->removeAt(i); } + virtual QGraphicsLayoutItem *at(int i) const { return obj->itemAt(i); } + virtual void insert(int i, QGraphicsLayoutItem *item) { obj->insertLayoutItem(i, item); } + + private: + QGraphicsLinearLayoutObject *obj; + }; + + ChildList _children; +}; +QML_DECLARE_TYPE(QGraphicsLinearLayoutObject); + +//TODO: +// -content margins +// -column and row specific settings: +// -alignment +// -fixed/min/max/preferred width +// -spacing +// -stretch +class QGraphicsGridLayoutObject : public QObject, public QGraphicsGridLayout +{ + Q_OBJECT + Q_INTERFACES(QGraphicsLayout QGraphicsLayoutItem) + + Q_PROPERTY(QmlList<QGraphicsLayoutItem *> *children READ children) + Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing) + Q_PROPERTY(qreal verticalSpacing READ verticalSpacing WRITE setVerticalSpacing) + Q_PROPERTY(qreal horizontalSpacing READ horizontalSpacing WRITE setHorizontalSpacing) + Q_CLASSINFO("DefaultProperty", "children") +public: + QGraphicsGridLayoutObject(QObject * = 0); + ~QGraphicsGridLayoutObject(); + + QmlList<QGraphicsLayoutItem *> *children() { return &_children; } + + qreal spacing() const; + + static QObject *qmlAttachedProperties(QObject *); + +private: + friend class GraphicsLayoutAttached; + void addWidget(QGraphicsWidget *); + void clearChildren(); + void addLayoutItem(QGraphicsLayoutItem *); + static QHash<QGraphicsLayoutItem*, QObject*> attachedProperties; + + class ChildList : public QmlList<QGraphicsLayoutItem *> + { + public: + ChildList(QGraphicsGridLayoutObject *o) + : obj(o) {} + + virtual void append(QGraphicsLayoutItem *o) + { + obj->addLayoutItem(o); + } + virtual void clear() { obj->clearChildren(); } + virtual int count() const { return obj->count(); } + virtual void removeAt(int i) { obj->removeAt(i); } + virtual QGraphicsLayoutItem *at(int i) const { return obj->itemAt(i); } + //XXX GridLayout doesn't have an insert, so for now we treat it as an append. + // this is obviously potenitally dangerous -- perhaps should be a concrete + // list with no relation to layout index, etc at all. + virtual void insert(int, QGraphicsLayoutItem *item) { append(item); } + + private: + QGraphicsGridLayoutObject *obj; + }; + + ChildList _children; +}; +QML_DECLARE_TYPE(QGraphicsGridLayoutObject); + +#endif // GRAPHICSLAYOUTS_H + +QT_END_NAMESPACE + +QT_END_HEADER diff --git a/src/declarative/widgets/graphicswidgets.cpp b/src/declarative/widgets/graphicswidgets.cpp new file mode 100644 index 0000000..1d0e638 --- /dev/null +++ b/src/declarative/widgets/graphicswidgets.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 "graphicswidgets.h" + +QT_BEGIN_NAMESPACE + +//### the single (default) property could alternatively be added to graphics view directly +class QGraphicsViewDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QGraphicsScene *scene READ scene WRITE setScene) + Q_CLASSINFO("DefaultProperty", "scene") +public: + QGraphicsViewDeclarativeUI(QObject *other) : QObject(other) {} + + QGraphicsScene *scene() const { return static_cast<QGraphicsView *>(parent())->scene(); } + void setScene(QGraphicsScene *scene) + { + static_cast<QGraphicsView *>(parent())->setScene(scene); + } +}; +QML_DEFINE_EXTENDED_TYPE(QGraphicsView,QGraphicsView,QGraphicsViewDeclarativeUI); + +class QGraphicsSceneDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QmlList<QObject *> *children READ children) + Q_CLASSINFO("DefaultProperty", "children") +public: + QGraphicsSceneDeclarativeUI(QObject *other) : QObject(other), _children(other) {} + + QmlList<QObject *> *children() { return &_children; } + +private: + class Children : public QmlConcreteList<QObject *> + { + public: + Children(QObject *scene) : q(scene) {} + virtual void append(QObject *o) + { + insert(-1, o); + } + virtual void clear() + { + for (int i = 0; i < count(); ++i) + if(QGraphicsWidget *w = qobject_cast<QGraphicsWidget *>(at(i))) + static_cast<QGraphicsScene *>(q)->removeItem(w); + QmlConcreteList<QObject *>::clear(); + } + virtual void removeAt(int i) + { + if(QGraphicsWidget *w = qobject_cast<QGraphicsWidget *>(at(i))) + static_cast<QGraphicsScene *>(q)->removeItem(w); + QmlConcreteList<QObject *>::removeAt(i); + } + virtual void insert(int i, QObject *o) + { + QmlConcreteList<QObject *>::insert(i, o); + + //XXX are there any cases when insertion should be different from appension? + if(QGraphicsWidget *w = qobject_cast<QGraphicsWidget *>(o)) + static_cast<QGraphicsScene *>(q)->addItem(w); + //else if (QWidget *w = qobject_cast<QWidget *>(o)) + // static_cast<QGraphicsScene *>(q)->addWidget(w); + //else + // qWarning() << "Can't add" << o << "to a QGraphicsScene"; + } + private: + QObject *q; + }; + Children _children; +}; +QML_DEFINE_EXTENDED_TYPE(QGraphicsScene,QGraphicsScene,QGraphicsSceneDeclarativeUI); + +class QGraphicsWidgetDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QmlList<QObject *> *data READ data) + Q_PROPERTY(QmlList<QGraphicsItem *> *children READ children) + Q_PROPERTY(QGraphicsLayout *layout READ layout WRITE setLayout) + Q_CLASSINFO("DefaultProperty", "children") +public: + QGraphicsWidgetDeclarativeUI(QObject *other) : QObject(other), _widgets(this) {} + + QmlList<QObject *> *data() { return &_data; } + + QmlList<QGraphicsItem *> *children() { return &_widgets; } + + QGraphicsLayout *layout() const { return static_cast<QGraphicsWidget *>(parent())->layout(); } + void setLayout(QGraphicsLayout *lo) + { + static_cast<QGraphicsWidget *>(parent())->setLayout(lo); + } + +private: + friend class WidgetList; + void setItemParent(QGraphicsItem *wid) + { + wid->setParentItem(static_cast<QGraphicsWidget *>(parent())); + } + + //### + void clearWidget() + { + } + + class WidgetList : public QmlConcreteList<QGraphicsItem *> + { + public: + WidgetList(QGraphicsWidgetDeclarativeUI *o) + : obj(o) {} + + virtual void append(QGraphicsItem *w) { QmlConcreteList<QGraphicsItem *>::append(w); obj->setItemParent(w); } + virtual void clear() { QmlConcreteList<QGraphicsItem *>::clear(); obj->clearWidget(); } + virtual void removeAt(int i) { QmlConcreteList<QGraphicsItem *>::removeAt(i); } //XXX + virtual void insert(int i, QGraphicsItem *item) { QmlConcreteList<QGraphicsItem *>::insert(i, item); obj->setItemParent(item); } + + private: + QGraphicsWidgetDeclarativeUI *obj; + }; + WidgetList _widgets; + QmlConcreteList<QObject *> _data; +}; + +QML_DEFINE_EXTENDED_TYPE(QGraphicsWidget,QGraphicsWidget,QGraphicsWidgetDeclarativeUI); + +QML_DEFINE_INTERFACE(QGraphicsItem); + +#include "graphicswidgets.moc" + +QT_END_NAMESPACE diff --git a/src/declarative/widgets/graphicswidgets.h b/src/declarative/widgets/graphicswidgets.h new file mode 100644 index 0000000..47a753a --- /dev/null +++ b/src/declarative/widgets/graphicswidgets.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 GRAPHICSWIDGETS_H +#define GRAPHICSWIDGETS_H + +#include <qml.h> +#include <QGraphicsScene> +#include <QGraphicsView> +#include <QGraphicsWidget> +#include <QGraphicsItem> + +QT_BEGIN_NAMESPACE + +QML_DECLARE_TYPE(QGraphicsView); +QML_DECLARE_TYPE_HASMETATYPE(QGraphicsScene); +QML_DECLARE_TYPE(QGraphicsWidget); +QML_DECLARE_INTERFACE_HASMETATYPE(QGraphicsItem); + +QT_END_NAMESPACE + +#endif // GRAPHICSWIDGETS_H diff --git a/src/declarative/widgets/widgets.pri b/src/declarative/widgets/widgets.pri new file mode 100644 index 0000000..41c50d8 --- /dev/null +++ b/src/declarative/widgets/widgets.pri @@ -0,0 +1,10 @@ +SOURCES += \ + widgets/graphicswidgets.cpp \ + widgets/graphicslayouts.cpp \ + +HEADERS += \ + widgets/graphicswidgets.h \ + widgets/graphicslayouts.h \ + + + |