/* This file is part of the KDE project
   Copyright (C) 1998, 1999, 2000 Torben Weis <weis@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 as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   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., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "KoContainerHandler.h"
#include <KoView.h>
#include <math.h>
#include <kcursor.h>
#include <kdebug.h>

KoEventHandler::KoEventHandler( TQObject* target )
{
    m_target = target;

    m_target->installEventFilter( this );
}

KoEventHandler::~KoEventHandler()
{
}

TQObject* KoEventHandler::target()
{
    return m_target;
}

// ------------------------------------------------------

class KoPartResizeHandlerPrivate {
public:
    KoPartResizeHandlerPrivate( const TQWMatrix& matrix, KoView *view, KoChild* child,
                              KoChild::Gadget gadget, const TQPoint& point ) :
        m_gadget(gadget), m_view(view), m_child(child), m_parentMatrix(matrix) {

        m_geometryStart = child->geometry();
        m_matrix = child->matrix() * matrix;
        m_invertParentMatrix = matrix.invert();

        bool ok = true;
        m_invert = m_matrix.invert( &ok );
        Q_ASSERT( ok );
        m_mouseStart = m_invert.map( m_invertParentMatrix.map( point ) );
    }
    ~KoPartResizeHandlerPrivate() {}

    KoChild::Gadget m_gadget;
    TQPoint m_mouseStart;
    TQRect m_geometryStart;
    KoView* m_view;
    KoChild* m_child;
    TQWMatrix m_invert;
    TQWMatrix m_matrix;
    TQWMatrix m_parentMatrix;
    TQWMatrix m_invertParentMatrix;
};

KoPartResizeHandler::KoPartResizeHandler( TQWidget* widget, const TQWMatrix& matrix, KoView* view, KoChild* child,
                                      KoChild::Gadget gadget, const TQPoint& point )
    : KoEventHandler( widget )
{
    child->lock();
    d=new KoPartResizeHandlerPrivate(matrix, view, child, gadget, point);
}

KoPartResizeHandler::~KoPartResizeHandler()
{
    d->m_child->unlock();
    delete d;
    d=0L;
}

void KoPartResizeHandler::repaint(TQRegion &rgn)
{
  rgn = rgn.unite( d->m_child->frameRegion( d->m_parentMatrix, true ) );
 // rgn.translate(- d->m_view->canvasXOffset(), - d->m_view->canvasYOffset());
  ((TQWidget*)target())->repaint( rgn );
}

bool KoPartResizeHandler::eventFilter( TQObject*, TQEvent* ev )
{
    if ( ev->type() == TQEvent::MouseButtonRelease )
    {
        delete this;
        return true;
    }
    else if ( ev->type() == TQEvent::MouseMove )
    {
        TQMouseEvent* e = (TQMouseEvent*)ev;
        TQPoint p = d->m_invert.map( d->m_invertParentMatrix.map( e->pos() /*+ TQPoint(d->m_view->canvasXOffset(), d->m_view->canvasYOffset()) */ ) );
        TQRegion rgn( d->m_child->frameRegion( d->m_parentMatrix, true ) );

        double x1_x, x1_y, x2_x, x2_y;
        d->m_matrix.map( double( p.x() ), 0.0, &x1_x, &x1_y );
        d->m_matrix.map( double( d->m_mouseStart.x() ), 0.0, &x2_x, &x2_y );
        double y1_x, y1_y, y2_x, y2_y;
        d->m_matrix.map( 0.0, double( p.y() ), &y1_x, &y1_y );
        d->m_matrix.map( 0.0, double( d->m_mouseStart.y() ), &y2_x, &y2_y );

        double dx = x2_x - x1_x;
        double dy = x2_y - x1_y;
        int x = int( sqrt( dx * dx + dy * dy ) * ( d->m_mouseStart.x() < p.x() ? 1.0 : -1.0 ) );

        dx = y2_x - y1_x;
        dy = y2_y - y1_y;
        int y = int( sqrt( dx * dx + dy * dy ) * ( d->m_mouseStart.y() < p.y() ? 1.0 : -1.0 ) );

        switch( d->m_gadget )
        {
        case KoChild::TopLeft:
            {
                x = TQMIN( d->m_geometryStart.width() - 1, x );
                y = TQMIN( d->m_geometryStart.height() - 1, y );

                d->m_child->setGeometry( TQRect( d->m_geometryStart.x() + x, d->m_geometryStart.y() + y,
                                             d->m_geometryStart.width() - x, d->m_geometryStart.height() - y ) );
                repaint(rgn);
            }
            break;
        case KoChild::TopMid:
            {
                y = TQMIN( d->m_geometryStart.height() - 1, y );

                d->m_child->setGeometry( TQRect( d->m_geometryStart.x(), d->m_geometryStart.y() + y,
                                             d->m_geometryStart.width(), d->m_geometryStart.height() - y ) );
                repaint(rgn);
            }
            break;
        case KoChild::TopRight:
            {
                x = TQMAX( -d->m_geometryStart.width() + 1, x );
                y = TQMIN( d->m_geometryStart.height() - 1, y );

                d->m_child->setGeometry( TQRect( d->m_geometryStart.x(), d->m_geometryStart.y() + y,
                                             d->m_geometryStart.width() + x, d->m_geometryStart.height() - y ) );
                repaint(rgn);
            }
            break;
        case KoChild::MidLeft:
            {
                x = TQMIN( d->m_geometryStart.width() - 1, x );

                d->m_child->setGeometry( TQRect( d->m_geometryStart.x() + x, d->m_geometryStart.y(),
                                             d->m_geometryStart.width() - x, d->m_geometryStart.height() ) );
                repaint(rgn);
            }
            break;
        case KoChild::MidRight:
            {
                x = TQMAX( -d->m_geometryStart.width() + 1, x );

                d->m_child->setGeometry( TQRect( d->m_geometryStart.x(), d->m_geometryStart.y(),
                                             d->m_geometryStart.width() + x, d->m_geometryStart.height() ) );
                repaint(rgn);
            }
            break;
        case KoChild::BottomLeft:
            {
                x = TQMIN( d->m_geometryStart.width() - 1, x );
                y = TQMAX( -d->m_geometryStart.height() + 1, y );

                d->m_child->setGeometry( TQRect( d->m_geometryStart.x() + x, d->m_geometryStart.y(),
                                             d->m_geometryStart.width() - x, d->m_geometryStart.height() + y ) );
                repaint(rgn);
            }
            break;
        case KoChild::BottomMid:
            {
                y = TQMAX( -d->m_geometryStart.height() + 1, y );

                d->m_child->setGeometry( TQRect( d->m_geometryStart.x(), d->m_geometryStart.y(),
                                             d->m_geometryStart.width(), d->m_geometryStart.height() + y ) );
                repaint(rgn);
            }
            break;
        case KoChild::BottomRight:
            {
                x = TQMAX( -d->m_geometryStart.width() + 1, x );
                y = TQMAX( -d->m_geometryStart.height() + 1, y );

                d->m_child->setGeometry( TQRect( d->m_geometryStart.x(), d->m_geometryStart.y(),
                                             d->m_geometryStart.width() + x, d->m_geometryStart.height() + y ) );
                repaint(rgn);
            }
            break;
        default:
            Q_ASSERT( 0 );
        }
        return true;
    }
    return false;
}

// --------------------------------------------------------------

class KoPartMoveHandlerPrivate {
public:
    KoPartMoveHandlerPrivate( const TQWMatrix& matrix, KoView* view, KoChild* child,
                            const TQPoint& point) : m_view(view), m_dragChild(child),
                                                   m_parentMatrix(matrix) {
        m_invertParentMatrix = matrix.invert();
        m_mouseDragStart = m_invertParentMatrix.map( point );
        m_geometryDragStart = m_dragChild->geometry();
        m_rotationDragStart = m_dragChild->rotationPoint();
    }
    ~KoPartMoveHandlerPrivate() {}

    KoView* m_view;
    KoChild* m_dragChild;
    TQPoint m_mouseDragStart;
    TQRect m_geometryDragStart;
    TQPoint m_rotationDragStart;
    TQWMatrix m_invertParentMatrix;
    TQWMatrix m_parentMatrix;
};

KoPartMoveHandler::KoPartMoveHandler( TQWidget* widget, const TQWMatrix& matrix, KoView* view, KoChild* child,
                                  const TQPoint& point )
    : KoEventHandler( widget )
{
    child->lock();
    d=new KoPartMoveHandlerPrivate(matrix, view, child, point);
}

KoPartMoveHandler::~KoPartMoveHandler()
{
    d->m_dragChild->unlock();
    delete d;
    d=0L;
}

bool KoPartMoveHandler::eventFilter( TQObject*, TQEvent* ev )
{
    if ( ev->type() == TQEvent::MouseButtonRelease )
    {
        delete this;
        return true;
    }
    else if ( ev->type() == TQEvent::MouseMove )
    {
        TQMouseEvent* e = (TQMouseEvent*)ev;

        TQRegion bound = d->m_dragChild->frameRegion( d->m_parentMatrix, true );
        TQPoint pos = d->m_invertParentMatrix.map( e->pos() /* + TQPoint(d->m_view->canvasXOffset(), d->m_view->canvasYOffset()) */ );
        d->m_dragChild->setGeometry( TQRect( d->m_geometryDragStart.x() + pos.x() - d->m_mouseDragStart.x(),
                                             d->m_geometryDragStart.y() + pos.y() - d->m_mouseDragStart.y(),
                                             d->m_geometryDragStart.width(), d->m_geometryDragStart.height() ) );
        d->m_dragChild->setRotationPoint( TQPoint( d->m_rotationDragStart.x() + pos.x() - d->m_mouseDragStart.x(),
                                               d->m_rotationDragStart.y() + pos.y() - d->m_mouseDragStart.y() ) );
        bound = bound.unite( d->m_dragChild->frameRegion( d->m_parentMatrix, false ) );
      //  bound.translate(- d->m_view->canvasXOffset(), - d->m_view->canvasYOffset());
        ((TQWidget*)target())->repaint( bound );

        return true;
    }

    return false;
}

// -------------------------------------------------------

KoContainerHandler::KoContainerHandler( KoView* view, TQWidget* widget )
    : KoEventHandler( widget )
{
    m_view = view;
}

KoContainerHandler::~KoContainerHandler()
{
}

bool KoContainerHandler::eventFilter( TQObject*, TQEvent* ev )
{
    if ( ev->type() == TQEvent::KeyPress )
    {
	TQKeyEvent* keyEvent=(TQKeyEvent*)ev;

	KoChild* child=m_view->selectedChild();

	if (child != 0)
	{
		if (keyEvent->key()==TQt::Key_Delete)
			emit deleteChild(child);
	}
    }

    if ( ev->type() == TQEvent::MouseButtonPress )
    {
        KoChild::Gadget gadget;
        TQPoint pos;
        TQMouseEvent *e=static_cast<TQMouseEvent*>(ev);
        KoChild *ch=child(gadget, pos, e);

	if ( e->button() == TQt::RightButton && gadget != KoChild::NoGadget )
        {
	    emit popupMenu( ch, e->globalPos() );
            return true;
        }
        else if ( e->button() == TQt::LeftButton && gadget == KoChild::Move )
        {
            (void)new KoPartMoveHandler( static_cast<TQWidget*>(target()), m_view->matrix(), m_view, ch, pos );
            return true;
        }
        else if ( e->button() == TQt::LeftButton && gadget != KoChild::NoGadget )
        {
            (void)new KoPartResizeHandler( static_cast<TQWidget*>(target()), m_view->matrix(), m_view, ch, gadget, pos );
            return true;
        }
        return false;
    }
    else if ( ev->type() == TQEvent::MouseMove )
    {
        TQWidget *targetWidget = static_cast<TQWidget*>( target() );
        KoChild::Gadget gadget;
        TQPoint pos;
        TQMouseEvent *e=static_cast<TQMouseEvent*>(ev);
        child(gadget, pos, e);

        bool retval = true;
        if ( gadget == KoChild::NoGadget )
            retval = false;

        if ( gadget == KoChild::TopLeft || gadget == KoChild::BottomRight )
            targetWidget->setCursor( sizeFDiagCursor );
        else if ( gadget == KoChild::TopRight || gadget == KoChild::BottomLeft )
            targetWidget->setCursor( sizeBDiagCursor );
        else if ( gadget == KoChild::TopMid || gadget == KoChild::BottomMid )
            targetWidget->setCursor( sizeVerCursor );
        else if ( gadget == KoChild::MidLeft || gadget == KoChild::MidRight )
            targetWidget->setCursor( sizeHorCursor );
        else if ( gadget == KoChild::Move )
            targetWidget->setCursor( KCursor::handCursor() );
        else
        {
//            targetWidget->setCursor( arrowCursor );
            return false;
        }
        return retval;
    }
    return false;
}

KoChild *KoContainerHandler::child(KoChild::Gadget &gadget, TQPoint &pos, const TQMouseEvent *ev) {

    pos = ev->pos(); //+ TQPoint(m_view->canvasXOffset(), m_view->canvasYOffset());

    pos = m_view->reverseViewTransformations( pos );

    KoChild *child = 0;
    KoDocumentChild* docChild = m_view->selectedChild();
    gadget = KoChild::NoGadget;
    if ( docChild )
    {
        KoViewChild *viewChild = m_view->child( docChild->document() );

        if ( viewChild )
            child = viewChild;
        else
            child = docChild;

        gadget = child->gadgetHitTest( pos );
    }
    if ( gadget == KoChild::NoGadget )
    {
        docChild = m_view->activeChild();
        if ( docChild )
        {
            KoViewChild *viewChild = m_view->child( docChild->document() );

            if ( viewChild )
                child = viewChild;
            else
                child = docChild;

            gadget = child->gadgetHitTest( pos );
        }
    }
    return child;
}

#include "KoContainerHandler.moc"
