[Kde-games-devel] New classes for libkdegames

Nicolas Hadacek kde-games-devel@mail.kde.org
Wed, 18 Dec 2002 23:13:50 +0100


--------------Boundary-00=_2R5CJPNB3BKNDASV1FOM
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 8bit

Hi there,

I'd like to propose two new set of classes for inclusion in libkdegames ; they 
are both used in kmines, ksirtet, kfouleggs and klickety games :

- "kgrid2d.h" : contains template classes for a bidimensionnal grid under the 
namespace KGrid2D. Square and hexagonal/triangle grids are implemented. Only 
the square class is used in kdegames at the moment but I think the hexagonal 
one can be useful (some algorithms are simple but not completely trivial).

- "kgamelcd.h/.cpp" : contains the classes KGameLCD, KGameLCDClock and 
KGameLCDList. KGameLCD is derived from QLCDNumber ; its background and 
foreground colors can be changed easily and it's possible to highlight it for 
a short time. KGameLCDClock implements a digital clock. KGameLCDList 
implements a list of QLCDNumber vertically layouted and with a title.

Have you any comment ?

see you,
Nicolas
--------------Boundary-00=_2R5CJPNB3BKNDASV1FOM
Content-Type: text/x-chdr;
  charset="us-ascii";
  name="kgrid2d.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="kgrid2d.h"

/*
    This file is part of the KDE games library
    Copyright (C) 2001-02 Nicolas Hadacek (hadacek@kde.org)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#ifndef __KGRID2D_H_
#define __KGRID2D_H_

#include <set>
#include <math.h>

#include <qvaluevector.h>
#include <qpoint.h>
#include <qsize.h>

#include <kglobal.h>


//-----------------------------------------------------------------------------
namespace KGrid2D
{
    /**
     * This type represents coordinates on a bidimensionnal grid.
     */
    typedef std::pair<int, int> Coord;
};

inline KGrid2D::Coord
operator +(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) {
    return KGrid2D::Coord(c1.first + c2.first, c1.second + c2.second);
}

inline KGrid2D::Coord
operator -(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) {
    return KGrid2D::Coord(c1.first - c2.first, c1.second - c2.second);
}

/**
 * @return the maximum of both coordinates.
 */
inline KGrid2D::Coord
maximum(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) {
    return KGrid2D::Coord(kMax(c1.first, c2.first), kMax(c1.second, c2.second));
}
/**
 * @return the minimum of both coordinates.
 */
inline KGrid2D::Coord
minimum(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) {
    return KGrid2D::Coord(kMin(c1.first, c2.first), kMin(c1.second, c2.second));
}

inline QTextStream &operator <<(QTextStream &s, const KGrid2D::Coord &c) {
    return s << '(' << c.second << ", " << c.first << ')';
}

inline QDataStream &operator <<(QDataStream &s, const KGrid2D::Coord &c) {
    return s << Q_UINT32(c.first) << Q_UINT32(c.second);
}

inline QDataStream &operator >>(QDataStream &s, KGrid2D::Coord &c) {
    Q_UINT32 first, second;
    s >> first >> second;
    c.first = first;
    c.second = second;
    return s;
}

//-----------------------------------------------------------------------------
namespace KGrid2D
{
    /**
     * This type represents a set of @ref Coord.
     */
    typedef std::set<Coord, std::less<Coord> > CoordSet;
};

inline QTextStream &operator <<(QTextStream &s, const KGrid2D::CoordSet &set) {
    for(KGrid2D::CoordSet::iterator i=set.begin(); i!=set.end(); ++i)
        s << *i;
	return s;
}

inline QDataStream &operator <<(QDataStream &s, const KGrid2D::CoordSet &set) {
    s << Q_UINT32(set.size());
    for(KGrid2D::CoordSet::iterator i=set.begin(); i!=set.end(); ++i)
        s << *i;
    return s;
}

inline QDataStream &operator >>(QDataStream &s, KGrid2D::CoordSet &set) {
    set.clear();
    Q_UINT32 nb;
    s >> nb;
    for (Q_UINT32 i=0; i<nb; i++) {
        KGrid2D::Coord c;
        s >> c;
        set.insert(c);
    }
    return s;
}

//-----------------------------------------------------------------------------
namespace KGrid2D
{
/**
 * This template class represents a generic bidimensionnal grid. Each node
 * contains an element of the template type.
 */
template <class Type>
class Generic
{
 public:
    /**
     * Constructor.
     */
    Generic(uint width = 0, uint height = 0) {
        resize(width, height);
    }

    virtual ~Generic() {}

    /**
     * Resize the grid.
     */
    void resize(uint width, uint height) {
        _width = width;
        _height = height;
        _vector.resize(width*height);
    }

    /**
     * Fill the nodes with the given value.
     */
    void fill(const Type &value) {
        std::fill(_vector.begin(), _vector.end(), value);
    }

    /**
     * @return the width.
     */
    uint width() const  { return _width; }
    /**
     * @return the height.
     */
    uint height() const { return _height; }
    /**
     * @return the number of nodes (ie width*height).
     */
    uint size() const   { return _width*_height; }

    /**
     * @return the linear index for the given coordinate.
     */
    uint index(const Coord &c) const {
        return c.first + c.second*_width;
    }

    /**
     * @return the coordinate corresponding to the linear index.
     */
    Coord coord(uint index) const {
        return Coord(index % _width, index / _width);
    }

    /**
     * @return the value at the given coordinate.
     */
    const Type &at(const Coord &c) const { return _vector[index(c)]; }
    /**
     * @return the value at the given coordinate.
     */
    Type &at(const Coord &c)             { return _vector[index(c)]; }
    /**
     * @return the value at the given coordinate.
     */
    const Type &operator [](const Coord &c) const { return _vector[index(c)]; }
    /**
     * @return the value at the given coordinate.
     */
    Type &operator [](const Coord &c)             { return _vector[index(c)]; }

    /**
     * @return the value at the given linear index.
     */
    const Type &at(uint index) const          { return _vector[index]; }
    /**
     * @return the value at the given linear index.
     */
    Type &at(uint index)                      { return _vector[index]; }
    /**
     * @return the value at the given linear index.
     */
    const Type &operator [](uint index) const { return _vector[index]; }
    /**
     * @return the value at the given linear index.
     */
    Type &operator [](uint index)             { return _vector[index]; }

    /**
     * @return if the given coordinate is inside the grid.
     */
    bool inside(const Coord &c) const {
        return ( c.first>=0 && c.first<(int)_width
                 && c.second>=0 && c.second<(int)_height );
    }

    /**
     * Bound the given coordinate with the grid dimensions.
     */
    void bound(Coord &c) const {
        c.first = kMax(kMin(c.first, (int)_width-1), 0);
        c.second = kMax(kMin(c.second, (int)_height-1), 0);
    }

 protected:
    uint               _width, _height;
    QValueVector<Type> _vector;
};
};

template <class Type>
QDataStream &operator <<(QDataStream &s, const KGrid2D::Generic<Type> &m) {
    s << (Q_UINT32)m.width() << (Q_UINT32)m.height();
    for (uint i=0; i<m.size(); i++) s << m[i];
    return s;
}

template <class Type>
QDataStream &operator >>(QDataStream &s, KGrid2D::Generic<Type> &m) {
    Q_UINT32 w, h;
    s >> w >> h;
    m.resize(w, h);
    for (uint i=0; i<m.size(); i++) s >> m[i];
    return s;
}


namespace KGrid2D
{

//-----------------------------------------------------------------------------
/**
 * This class contains static methods to manipulate coordinates for a
 * square bidimensionnal grid.
 */
class SquareBase
{
 public:
    /**
     * Identify the eight neighbours.
     */
    enum Neighbour { Left=0, Right, Up, Down, LeftUp, LeftDown,
                     RightUp, RightDown, Nb_Neighbour };

    /**
     * @return the trigonometric angle in radians for the given neighbour.
     */
    static double angle(Neighbour n) {
        switch (n) {
        case Left:      return M_PI;
        case Right:     return 0;
        case Up:        return M_PI_2;
        case Down:      return -M_PI_2;
        case LeftUp:    return 3.0*M_PI_4;
        case LeftDown:  return -3.0*M_PI_4;
        case RightUp:   return M_PI_4;
        case RightDown: return -M_PI_4;
        case Nb_Neighbour: Q_ASSERT(false);
        }
        return 0;
    }

    /**
     * @return the opposed neighbour.
     */
    static Neighbour opposed(Neighbour n) {
        switch (n) {
        case Left:      return Right;
        case Right:     return Left;
        case Up:        return Down;
        case Down:      return Up;
        case LeftUp:    return RightDown;
        case LeftDown:  return RightUp;
        case RightUp:   return LeftDown;
        case RightDown: return LeftUp;
        case Nb_Neighbour: Q_ASSERT(false);
        }
        return Nb_Neighbour;
    }

    /**
     * @return true if the neighbour is a direct one (ie is one of the four
     * nearest).
     */
    static bool isDirect(Neighbour n) { return n<LeftUp; }

    /**
     * @return the neighbour for the given coordinate.
     */
    static Coord neighbour(const Coord &c, Neighbour n) {
        switch (n) {
        case Left:      return c + Coord(-1,  0);
        case Right:     return c + Coord( 1,  0);
        case Up:        return c + Coord( 0, -1);
        case Down:      return c + Coord( 0,  1);
        case LeftUp:    return c + Coord(-1, -1);
        case LeftDown:  return c + Coord(-1,  1);
        case RightUp:   return c + Coord( 1, -1);
        case RightDown: return c + Coord( 1,  1);
        case Nb_Neighbour: Q_ASSERT(false);
        }
        return c;
    }
};

/**
 * This template is a @ref Generic implementation for a square bidimensionnal
 * grid (@ref SquareBase).
 */
template <class Type>
class Square : public Generic<Type>, public SquareBase
{
 public:
    /**
     * Constructor.
     */
    Square(uint width = 0, uint height = 0)
        : Generic<Type>(width, height) {}

    /**
     * @return the neighbours of coordinate @param c
     * to the given set of coordinates @param neighbours.
     *
     * @param insideOnly only add coordinates that are inside the grid.
     * @param directOnly only add the four nearest neighbours.
     */
    CoordSet neighbours(const Coord &c, bool insideOnly = true,
                        bool directOnly = false) const {
        CoordSet neighbours;
        for (uint i=0; i<(directOnly ? LeftUp : Nb_Neighbour); i++) {
            Coord n = neighbour(c, (Neighbour)i);
            if ( insideOnly && !inside(n) ) continue;
            neighbours.insert(n);
        }
        return neighbours;
    }

    /**
     * @return the "projection" of the given coordinate on the grid edges.
     *
     * @param n the direction of projection.
     */
    Coord toEdge(const Coord &c, Neighbour n) const {
        switch (n) {
        case Left:      return Coord(0, c.second);
        case Right:     return Coord(width()-1, c.second);
        case Up:        return Coord(c.first, 0);
        case Down:      return Coord(c.first, height()-1);
        case LeftUp:    return Coord(0, 0);
        case LeftDown:  return Coord(0, height()-1);
        case RightUp:   return Coord(width()-1, 0);
        case RightDown: return Coord(width()-1, height()-1);
        case Nb_Neighbour: Q_ASSERT(false);
        }
        return c;
    }
};

//-----------------------------------------------------------------------------
/**
 * This class contains static methods to manipulate coordinates on an
 * hexagonal grid where hexagons form horizontal lines:
 * <pre>
 * (0,0)   (0,1)   (0,2)
 *     (1,0)   (1,1)   (1,2)
 * (2,0)   (2,1)   (2,2)
 * </pre>
 */
class HexagonalBase
{
 public:
    /**
     * Identify the six neighbours.
     */
    enum Neighbour { Left = 0, Right, LeftUp, LeftDown,
                     RightUp, RightDown, Nb_Neighbour };

     /**
     * @return the trigonometric angle in radians for the given neighbour.
     */
    static double angle(Neighbour n) {
        switch (n) {
        case Left:      return M_PI;
        case Right:     return 0;
        case LeftUp:    return 2.0*M_PI/3;
        case LeftDown:  return -2.0*M_PI/3;
        case RightUp:   return M_PI/3;
        case RightDown: return -M_PI/3;
        case Nb_Neighbour: Q_ASSERT(false);
        }
        return 0;
    }

    /**
     * @return the opposed neighbour.
     */
    static Neighbour opposed(Neighbour n) {
        switch (n) {
        case Left:      return Right;
        case Right:     return Left;
        case LeftUp:    return RightDown;
        case LeftDown:  return RightUp;
        case RightUp:   return LeftDown;
        case RightDown: return LeftUp;
        case Nb_Neighbour: Q_ASSERT(false);
        }
        return Nb_Neighbour;
    }

    /**
     * @return the neighbour of the given coordinate.
     */
    static Coord neighbour(const Coord &c, Neighbour n) {
        bool oddRow = c.second%2;
        switch (n) {
        case Left:      return c + Coord(-1,  0);
        case Right:     return c + Coord( 1,  0);
        case LeftUp:    return c + (oddRow ? Coord( 0, -1) : Coord(-1, -1));
        case LeftDown:  return c + (oddRow ? Coord( 0,  1) : Coord(-1,  1));
        case RightUp:   return c + (oddRow ? Coord( 1, -1) : Coord( 0, -1));
        case RightDown: return c + (oddRow ? Coord( 1,  1) : Coord( 0,  1));
        case Nb_Neighbour: Q_ASSERT(false);
        }
        return c;
    }

    /**
    * @return the distance between the two coordinates in term of hexagons.
    */
    static uint distance(const Coord &c1, const Coord &c2) {
        return kAbs(c1.first - c2.first) + kAbs(c1.second - c2.second)
            + (c1.first==c2.first || c1.second==c2.second ? 0 : -1);
    }
};

/**
 * This template implements @Generic for an hexagonal grid
 * where hexagons form horizontal lines:
 * <pre>
 * (0,0)   (0,1)   (0,2)
 *     (1,0)   (1,1)   (1,2)
 * (2,0)   (2,1)   (2,2)
 * </pre>
 */
template <class Type>
class Hexagonal : public Generic<Type>, public HexagonalBase
{
 public:
    /**
     * Constructor.
     */
    Hexagonal(uint width = 0, uint height = 0)
        : Generic<Type>(width, height) {}

    /**
     * @return the neighbours of coordinate @param c
     * to the given set of coordinates @param neighbours.
     *
     * @param insideOnly only add coordinates that are inside the grid.
     */
    CoordSet neighbours(const Coord &c, bool insideOnly = true) const {
        CoordSet neighbours;
        for (uint i=0; i<Nb_Neighbour; i++) {
            Coord n = neighbour(c, (Neighbour)i);
            if ( insideOnly && !inside(n) ) continue;
            neighbours.insert(n);
        }
        return neighbours;
    }


    /**
     * @return the neighbours at distance @param distance of coordinate
     * @param c.
     *
     * @param distance distance to the neighbour (1 means at contact).
     * @param insideOnly only add coordinates that are inside the grid.
     * @param all returns all neighbours at distance equal and less than
     *        @param distance (the original coordinate is not included).
     */
    CoordSet neighbours(const Coord &c, uint distance, bool all,
                        bool insideOnly = true) const {
        // brute force algorithm -- you're welcome to make it more efficient :)
        CoordSet ring;
        if ( distance==0 ) return ring;
        ring = neighbours(c, insideOnly);
        if ( distance==1 ) return ring;
        CoordSet center;
        center.insert(c);
        for (uint i=1; i<distance; i++) {
            CoordSet newRing;
            CoordSet::const_iterator it;
            for (it=ring.begin(); it!=ring.end(); ++it) {
                CoordSet n = neighbours(*it, insideOnly);
                CoordSet::const_iterator it2;
                for (it2=n.begin(); it2!=n.end(); ++it2)
                    if ( center.find(*it2)==center.end()
                         && ring.find(*it2)==ring.end()
                         && newRing.find(*it2)==newRing.end() )
                        newRing.insert(*it2);
                center.insert(*it);
            }
            ring = newRing;
        }
        if ( !all ) return ring;
        CoordSet::const_iterator it;
        for (it=ring.begin(); it!=ring.end(); ++it)
            center.insert(*it);
        center.erase(c);
        return center;
    }
};

}; // namespace

#endif

--------------Boundary-00=_2R5CJPNB3BKNDASV1FOM
Content-Type: text/x-chdr;
  charset="us-ascii";
  name="kgamelcd.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="kgamelcd.h"

/*
    This file is part of the KDE games library
    Copyright (C) 2001-02 Nicolas Hadacek (hadacek@kde.org)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#ifndef __KGAMELCD_H
#define __KGAMELCD_H

#include <qlcdnumber.h>
#include <qptrvector.h>


class QLabel;
class QTimer;

//-----------------------------------------------------------------------------
/**
 * This class is a visually enhanced @ref QLCDNumber:
 * <ul>
 * <li> It can show an additionnal string before the integer being
 * displayed.</li>
 * <li> Its foreground and background colors can easily be modified. </li>
 * <li> It can be highlighted for a short time. </li>
 * </ul>
 */
class KGameLCD : public QLCDNumber
{
 Q_OBJECT
 public:
    KGameLCD(uint nbDigits, QWidget *parent = 0, const char *name = 0);

    /**
     * Set the default background color.
     */
    void setDefaultBackgroundColor(const QColor &color);

    /**
     * Set the default foreground color.
     */
    void setDefaultColor(const QColor &color);

    /**
     * Set highlight color.
     */
    void setHighlightColor(const QColor &color);

    /**
     * Set the string that will be displayed before the integer number to be
     * displayed. By default this string is null.
     */
    void setLeadingString(const QString &s);

    /**
     * Set the highlight duration in milliseconds. The default value is
     * 800 milliseconds.
     */
    void setHighlightTime(uint time);

    /**
     * Reset the foreground color to the default one.
     */
    void resetColor();

    /**
     * Set the foreground color.
     */
    void setColor(const QColor &color);

 public slots:
    /**
     * Highlight the LCD with the @ref QColorGourp::HighlightedText color
     * for a small time (@ref setHighlightTime).
     */
    void highlight();

    /**
     * Display the given integer with the (optionnal) leading string.
     */
    void displayInt(int value);

 private slots:
    void timeout() { highlight(false); }

 private:
    QColor   _fgColor, _hlColor;
    QString  _lead;
    uint     _htime;
    QTimer  *_timer;

    class KGameLCDPrivate;
    KGameLCDPrivate *d;

    void highlight(bool light);

};

//-----------------------------------------------------------------------------
/**
 * This class is a digital clock widget. It has a maximum duration of
 * 3599 seconds (one hour) and it gets updated every second.
 */
class KGameLCDClock : public KGameLCD
{
 Q_OBJECT
 public:
    KGameLCDClock(QWidget *parent = 0, const char *name = 0);

    /**
     * @return the total number of seconds elapsed.
     */
    uint seconds() const;

    /**
     * @return the time as a string to be displayed: "mm:ss".
     */
    QString pretty() const;

    /**
     * Set the time.
     */
    void setTime(uint seconds);

    /**
     * Set the time (format should be "mm:ss").
     */
    void setTime(const QString &s);

 public slots:
    /**
     * Stop the clock and reset it to zero.
     */
    virtual void reset();

    /**
     * Stop the clock but do not reset it to zero.
     */
	virtual void stop();

    /**
     * Start the clock from the current time.
     */
	virtual void start();

 protected slots:
    virtual void timeoutClock();

 private:
    QTimer *_timerClock;
	uint    _sec, _min;

    class KGameLCDClockPrivate;
    KGameLCDClockPrivate *d;

	void showTime();
};

//-----------------------------------------------------------------------------
/**
 * This widget holds a list of @ref QLCDNumber arranged in a vertical layout.
 * It also shows a label at the top of the list.
 */
class KGameLCDList : public QWidget
{
 Q_OBJECT
 public:
    /**
     * Constructor.
     *
     * @param title is the content of the top label.
     */
    KGameLCDList(const QString &title,
                 QWidget *parent = 0, const char *name = 0);
    KGameLCDList(QWidget *parent = 0, const char *name = 0);

    /**
     * Append a @ref QLCDNumber at the bottom of the list.
     */
    uint append(QLCDNumber *lcd);

    /**
     * @return the title label.
     */
    QLabel *title() const { return _title; }

    /**
     * @return the @ref QLCDNumber at index @param i.
     */
    QLCDNumber *lcd(uint i) const { return _lcds[i]; }

 private:
    QLabel                 *_title;
    QPtrVector<QLCDNumber>  _lcds;

    class KGameLCDListPrivate;
    KGameLCDListPrivate *d;

    void init(const QString &title);
};

#endif

--------------Boundary-00=_2R5CJPNB3BKNDASV1FOM
Content-Type: text/x-c++src;
  charset="us-ascii";
  name="kgamelcd.cpp"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="kgamelcd.cpp"

/*
    This file is part of the KDE games library
    Copyright (C) 2001-02 Nicolas Hadacek (hadacek@kde.org)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include "gmisc_ui.h"
#include "gmisc_ui.moc"

#include <qlayout.h>
#include <qlabel.h>
#include <qtimer.h>

#include <kglobal.h>


//-----------------------------------------------------------------------------
KGameLCD::KGameLCD(uint nbDigits, QWidget *parent, const char *name)
    : QLCDNumber(nbDigits, parent, name), _htime(800)
{
    const QPalette &p = palette();
    _fgColor = p.color(QPalette::Active, QColorGroup::Foreground);
    _hlColor = p.color(QPalette::Active, QColorGroup::HighlightedText);

    _timer = new QTimer(this);
    connect(_timer, SIGNAL(timeout()), SLOT(timeout()));

    setFrameStyle(Panel | Plain);
	setSegmentStyle(Flat);

    displayInt(0);
}

void KGameLCD::setDefaultBackgroundColor(const QColor &color)
{
    QPalette p = palette();
    p.setColor(QColorGroup::Background, color);
    setPalette(p);
}

void KGameLCD::setDefaultColor(const QColor &color)
{
    _fgColor = color;
    QPalette p = palette();
    p.setColor(QColorGroup::Foreground, color);
    setPalette(p);
}

void KGameLCD::setHighlightColor(const QColor &color)
{
    _hlColor = color;
}

void KGameLCD::setLeadingString(const QString &s)
{
    _lead = s;
    displayInt(0);
}

void KGameLCD::setHighlightTime(uint time)
{
    _htime = time;
}

void KGameLCD::resetColor()
{
    setColor(QColor());
}

void KGameLCD::setColor(const QColor &color)
{
    const QColor &c = (color.isValid() ? color : _fgColor);
    QPalette p = palette();
    p.setColor(QColorGroup::Foreground, c);
    setPalette(p);
}

void KGameLCD::displayInt(int v)
{
    int n = numDigits() - _lead.length();
    display(_lead + QString::number(v).rightJustify(n));
}

void KGameLCD::highlight()
{
    highlight(true);
    _timer->start(_htime, true);
}

void KGameLCD::highlight(bool light)
{
    if (light) setColor(_hlColor);
    else resetColor();
}

//-----------------------------------------------------------------------------
KGameLCDClock::KGameLCDClock(QWidget *parent, const char *name)
: KGameLCD(5, parent, name)
{
    _timerClock = new QTimer(this);
    connect(_timerClock, SIGNAL(timeout()), SLOT(timeoutClock()));
}

void KGameLCDClock::timeoutClock()
{
    // waiting an hour does not restart timer
    if ( _min==59 && _sec==59 ) return;
    _sec++;
    if (_sec==60) {
        _min++;
        _sec = 0;
    }
    showTime();
}

QString KGameLCDClock::pretty() const
{
    QString sec = QString::number(_sec).rightJustify(2, '0', true);
    QString min = QString::number(_min).rightJustify(2, '0', true);
    return min + ':' + sec;
}

void KGameLCDClock::showTime()
{
    display(pretty());
}

void KGameLCDClock::reset()
{
    _timerClock->stop();
	_sec = 0;
    _min = 0;
	showTime();
}

void KGameLCDClock::start()
{
    _timerClock->start(1000); // 1 second
}

void KGameLCDClock::stop()
{
    _timerClock->stop();
}

uint KGameLCDClock::seconds() const
{
    return _min*60 + _sec;
}

void KGameLCDClock::setTime(uint sec)
{
    Q_ASSERT( sec<3600 );
    _sec = sec % 60;
    _min = sec / 60;
    showTime();
}

void KGameLCDClock::setTime(const QString &s)
{
    Q_ASSERT( s.length()==5 && s[2]==':' );
    uint min = kMin(s.section(':', 0, 0).toUInt(), uint(59));
    uint sec = kMin(s.section(':', 1, 1).toUInt(), uint(59));
    setTime(sec + min*60);
}


//-----------------------------------------------------------------------------
KGameLCDList::KGameLCDList(const QString &title, QWidget *parent, const char *name)
    : QWidget(parent, name)
{
    init(title);
}

KGameLCDList::KGameLCDList(QWidget *parent, const char *name)
    : QWidget(parent, name)
{
    init(QString::null);
}

void KGameLCDList::init(const QString &title)
{
    QVBoxLayout *top = new QVBoxLayout(this, 5);

    _title = new QLabel(title, this);
    _title->setAlignment(AlignCenter);
    top->addWidget(_title, AlignCenter);
}

uint KGameLCDList::append(QLCDNumber *lcd)
{
    uint n = _lcds.size();
    _lcds.resize(n+1);
    _lcds.insert(n, lcd);
    static_cast<QVBoxLayout *>(layout())->addWidget(lcd);
    return n;
}

--------------Boundary-00=_2R5CJPNB3BKNDASV1FOM--