/***************************************************************************
                   kstviewobject.h: base class for view objects
                             -------------------
    begin                : Mar 11, 2004
    copyright            : (C) 2004 The University of Toronto
    email                :
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef KSTVIEWOBJECT_H
#define KSTVIEWOBJECT_H

#include <qcolor.h>
#include <qdom.h>

#include <kdeversion.h>
#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,0)
#include <kdemacros.h>
#endif

#include "kstobject.h"
#include "kstalignment.h"

class KPopupMenu;
class KstViewObject;
class KstViewWidget;
typedef KstSharedPtr<KstViewObject> KstViewObjectPtr;
typedef KstObjectList<KstViewObjectPtr> KstViewObjectList;
typedef KstViewObjectPtr (*KstViewObjectFactoryMethod)();

// KstPaintType - hints to paint to allow optimizations
// P_PAINT: Neither 'data' nor 'plot' needs to change
// P_PLOT: data didn't change
// P_DATA: data changed
enum KstPaintType { P_PAINT, P_DATA, P_PLOT, P_ZOOM, P_PRINT, P_EXPORT };

struct KstAspectRatio {
  KstAspectRatio() : x(0.0), y(0.0), w(0.0), h(0.0) {}
  double x, y, w, h;
  friend QDataStream& operator>>(QDataStream& str, KstAspectRatio& ar) {
    str >> ar.x >> ar.y >> ar.w >> ar.h;
    return str;
  }
  friend QDataStream& operator<<(QDataStream& str, KstAspectRatio& ar) {
    str << ar.x << ar.y << ar.w << ar.h;
    return str;
  }
};

class KstViewObject : public KstObject {
  Q_OBJECT
  protected:
    KstViewObject(const QString& type);
  public:
    KstViewObject(QDomElement& e);
    virtual ~KstViewObject();

    virtual UpdateType update(int = -1);
    virtual void save(QTextStream& ts, const QString& indent = QString::null);
    virtual void saveTag(QTextStream& ts, const QString& indent = QString::null);
    virtual void loadChildren(QDomElement& e);

    // If cols <= 0, optimal is chosen automatically
    virtual void cleanup(int cols = -1);
    virtual int columns() const;
    virtual void setColumns(int cols);
    virtual bool onGrid() const;
    virtual void setOnGrid(bool on_grid);

    virtual void resize(const QSize& size);
    virtual void resizeForPrint(const QSize& size);
    virtual void revertForPrint();
    virtual void resizeFromAspect(double x, double y, double w, double h);
    virtual QSize size() const;
    virtual void internalAlignment(QRect& plotRegion);
    virtual QPoint position() const;
    virtual const QRect& geometry() const;
    virtual QRect contentsRect() const;
    virtual void setContentsRect(QRect& rect);
    virtual void move(const QPoint& to);

    // Draw a focus highlight
    virtual void setFocus(bool focus);
    virtual bool focused() const;
    virtual void setHasFocus(bool hasFocus);
    virtual void removeFocus(QPainter& p);

    virtual void readLock() const;
    virtual void readUnlock() const;
    virtual void writeLock() const;
    virtual void writeUnlock() const;

    virtual void appendChild(KstViewObjectPtr obj, bool keepAspect = false);
    virtual void prependChild(KstViewObjectPtr obj, bool keepAspect = false);
    virtual bool removeChild(KstViewObjectPtr obj, bool recursive = false);
    virtual void insertChildAfter(const KstViewObjectPtr after, KstViewObjectPtr obj, bool keepAspect = false);
    virtual void clearChildren();
    const KstViewObjectList& children() const;
    KstViewObjectList& children();

    // can't be const due to KstViewObjectPtr?
    KstViewObjectPtr findChild(const QPoint& pos);
    KstViewObjectPtr findChild(const QString& name, bool recursive = true);
    bool contains(KstViewObjectPtr child);
    template<class T> KstObjectList<KstSharedPtr<T> > findChildrenType(bool recursive = false);

    virtual void setBackgroundColor(const QColor& color);
    virtual QColor backgroundColor() const;

    virtual void setForegroundColor(const QColor& color);
    virtual QColor foregroundColor() const;

    virtual void updateSelection(const QRect& region);

    void recursively(void (KstViewObject::*)(), bool self = false);
    template<class T, class U> void recursively(void (U::*)(T), T, bool self = false);
    template<class T, class U> void forEachChild(void (U::*)(T), T, bool self = false);
    // GCC bug: compiler crashes if forEachChild is used here too
    // note also: self=false
    template<class U> void forEachChild2(void (U::*)());

    virtual bool popupMenu(KPopupMenu *menu, const QPoint& pos, KstViewObjectPtr topParent);
    virtual bool layoutPopupMenu(KPopupMenu *menu, const QPoint& pos, KstViewObjectPtr topParent);

    // FIXME: split into layout and non-layout?
    enum StandardActions { Delete =          1,
                           Copy =            2,
                           Cut =             4,
                           Paste =           8,
                           Raise =          16,
                           Lower =          32,
                           RaiseToTop =     64,
                           LowerToBottom = 128,
                           Properties =    256,
                           Rename =        512,
                           Edit =         1024,
                           Zoom =         2048,
                           Pause =        4096,
                           MoveTo =       8192
                        };

    virtual void drawFocusRect(QPainter& p);
    virtual void drawSelectRect(QPainter& p);

    virtual void zoom(bool zoom);
    virtual bool isSelected() const;
    virtual void setSelected(bool selected);
    void selectAll();
    void unselectAll();
    
    virtual void recursivelyQuery(bool (KstViewObject::*method)() const, KstViewObjectList& list, bool matchRecurse = false);

    virtual void detach(); // remove from its parent

    const QString& type() const { return _type; }

    virtual bool mouseHandler() const;
    virtual void mouseMoveEvent(QWidget *view, QMouseEvent *e);
    virtual void mousePressEvent(QWidget *view, QMouseEvent *e);
    virtual void mouseReleaseEvent(QWidget *view, QMouseEvent *e);
    virtual void keyPressEvent(QWidget *view, QKeyEvent *e);
    virtual void keyReleaseEvent(QWidget *view, QKeyEvent *e);
    virtual void dragMoveEvent(QWidget *view, QDragMoveEvent *e);
    virtual void dragEnterEvent(QWidget *view, QDragEnterEvent *e);
    virtual void dropEvent(QWidget *view, QDropEvent *e);
    virtual void wheelEvent(QWidget *view, QWheelEvent *e);

    friend QDataStream& operator<<(QDataStream& str, KstViewObjectPtr obj);
    friend QDataStream& operator>>(QDataStream& str, KstViewObjectPtr obj);

    virtual void load(QDomElement& e);

  public slots:
    virtual void paint(KstPaintType type, QPainter& p);
    virtual void updateFromAspect();
    virtual void updateAspectSize();
    virtual void updateAspectPos();
    virtual void updateAspect();
    virtual void zoomToggle();

  protected slots:
    virtual void parentResized();
    virtual void parentResizedForPrint();
    virtual void parentRevertedForPrint();
    virtual void parentMoved(const QPoint& offset);

    /***********  Actions ************/
    virtual void deleteObject();
    virtual void raiseToTop();
    virtual void lowerToBottom();
    virtual void raise();
    virtual void lower();
    virtual void moveTo(int);
    virtual void rename();
    virtual void pauseToggle();

  protected:
    virtual KstViewObjectFactoryMethod factory() const;
    virtual void writeBinary(QDataStream& str);
    virtual void readBinary(QDataStream& str);

    KstViewObjectList _children;
    QRect _geom;
    QRect _geomOld;
    QColor _backgroundColor, _foregroundColor; // FIXME: use QColorGroup
    bool _hasFocus : 1;
    bool _focus : 1;
    bool _selected : 1;
    bool _onGrid : 1;
    bool _prevOnGrid : 1;
    bool _maximized : 1;
    int _columns : 6; // "64 columns ought to be enough for anyone"
    KstViewObject *_parent; // danger!!
    //KstViewObjectPtr _parent; // FIXME: this is bad and should be removed ASAP
                              //        It was introduced as a temporary hack
                              //        but is no longer needed as events now
                              //        pass the view pointer along.  Also use
                              //        of this member indicates a bug since
                              //        objects can have multiple parents.
    Q_UINT32 _standardActions, _layoutActions;
    KstAspectRatio _aspect;
    KstAspectRatio _aspectOldZoomedObject;
    QString _type;
    QRegion _lastClipRegion; // a sort of cache - might not always work
    QMap<int, QString> _moveToMap;
};


template<class U>
void KstViewObject::forEachChild2(void (U::*method)()) {
  for (KstViewObjectList::Iterator i = _children.begin(); i != _children.end(); ++i) {
    U *it = kst_cast<U>(*i);
    if (it) {
      (it->*method)();
    }
  }
}


template<class T, class U>
void KstViewObject::forEachChild(void (U::*method)(T), T arg, bool self) {
  if (self) {
    U *me = dynamic_cast<U*>(this);
    if (me) {
      (me->*method)(arg);
    }
  }
  for (KstViewObjectList::Iterator i = _children.begin(); i != _children.end(); ++i) {
    U *it = kst_cast<U>(*i);
    if (it) {
      (it->*method)(arg);
    }
  }
}


template<class T, class U>
void KstViewObject::recursively(void (U::*method)(T), T arg, bool self) {
  if (self) {
    U *me = dynamic_cast<U*>(this);
    if (me) {
      (me->*method)(arg);
    }
  }
  for (KstViewObjectList::Iterator i = _children.begin(); i != _children.end(); ++i) {
    (*i)->recursively<T>(method, arg, true);
  }
}


template<class T>
KstObjectList<KstSharedPtr<T> > KstViewObject::findChildrenType(bool recursive) {
  KstObjectList<KstSharedPtr<T> > rc;
  for (KstViewObjectList::Iterator i = _children.begin(); i != _children.end(); ++i) {
    T *o = kst_cast<T>(*i);
    if (o) {
      rc.append(o);
    }

    if (recursive) {
      rc += (*i)->findChildrenType<T>(recursive);
    }
  }

  return rc;
}

#endif
// vim: ts=2 sw=2 et
