/* This file is part of the KDE project
   Copyright (C) 2001, 2002, 2003 The Karbon Developers

   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 <tqcursor.h>
#include <tqpainter.h>
#include <tqpixmap.h>

#include "karbon_view.h"
#include "karbon_part.h"
#include "karbon_drag.h"
#include "vcanvas.h"
#include "vdocument.h"
#include "vpainter.h"
#include "vqpainter.h"
#include "vpainterfactory.h"
#include "vselection.h"
#include "vtoolcontroller.h"
#include "vtool.h"

#include <kdebug.h>
#include <tdelocale.h>
#include <kcolordrag.h>

int
VCanvas::pageOffsetX() const
{
	double zoomedWidth = m_part->document().width() * m_view->zoom();
	if( contentsWidth() < visibleWidth() )
		return int( 0.5 * ( visibleWidth() - zoomedWidth ) );
	else
		return int( 0.5 * ( contentsWidth() - zoomedWidth ) );
}

int
VCanvas::pageOffsetY() const
{
	double zoomedHeight = m_part->document().height() * m_view->zoom();
	if( contentsHeight() < visibleHeight() )
		return int( 0.5 * ( visibleHeight() - zoomedHeight ) );
	else
		return int( 0.5 * ( contentsHeight() - zoomedHeight ) );
}

KoPoint VCanvas::snapToGrid( const KoPoint &point )
{
	if( !m_part->document().grid().isSnap )
		return point;

	KoPoint p = point;

	KoSize dist = m_part->document().grid().snap;
	KoSize dxy = m_part->document().grid().freq;

	int dx = tqRound( p.x() / dxy.width() );
	int dy = tqRound( p.y() / dxy.height() );

	float distx = TQMIN( TQABS( p.x() - dxy.width() * dx ), TQABS( p.x() - dxy.width() * ( dx + 1 ) ) );
	float disty = TQMIN( TQABS( p.y() - dxy.height() * dy ), TQABS( p.y() - dxy.height() * ( dy + 1 ) ) );

	if( distx < dist.width() )
	{
		if( TQABS(p.x() - dxy.width() * dx ) < TQABS( p.x() - dxy.width() * ( dx + 1 ) ) )
			p.rx() = dxy.width() * dx;
		else
			p.rx() = dxy.width() * ( dx + 1 );
	}

	if( disty < dist.height() )
	{
		if( TQABS( p.y() - dxy.height() * dy ) < TQABS( p.y() - dxy.height() * ( dy + 1 ) ) )
			p.ry() = dxy.height() * dy;
		else
			p.ry() = dxy.height() * ( dy + 1 );
	}

	return p;
}


VCanvas::VCanvas( TQWidget *parent, KarbonView* view, KarbonPart* part )
    : TQScrollView( parent, "canvas", WStaticContents/*WNorthWestGravity*/ | WResizeNoErase  |
	  WRepaintNoErase ), m_part( part ), m_view( view )
{
	connect(this, TQT_SIGNAL( contentsMoving( int, int ) ), this, TQT_SLOT( slotContentsMoving( int, int ) ) );
	viewport()->setFocusPolicy( TQ_StrongFocus );

	viewport()->setMouseTracking( true );
	setMouseTracking( true );

	viewport()->setBackgroundColor( TQt::white );
	viewport()->setBackgroundMode( TQWidget::NoBackground );
	viewport()->installEventFilter( this );

	resizeContents( 800, 600 );
	m_pixmap = new TQPixmap( 800, 600 );

	setFocus();

	setAcceptDrops( true );
}

VCanvas::~VCanvas()
{
	delete m_pixmap;
	m_view = 0L;
	m_part = 0L;
}

void
VCanvas::setPos( const KoPoint& p )
{
	KoPoint p2 = toViewport( p );
	TQCursor::setPos( mapToGlobal( TQPoint( int(p2.x()), int(p2.y()) ) ) );
}

bool
VCanvas::eventFilter( TQObject* object, TQEvent* event )
{
	TQScrollView::eventFilter( object, event );

	if( event->type() == TQEvent::AccelOverride || event->type() == TQEvent::Accel )
		return TQScrollView::eventFilter( object, event );

	if( event->type() == TQEvent::KeyPress || event->type() == TQEvent::KeyRelease )
		return m_view->keyEvent( event );

	TQMouseEvent* mouseEvent = dynamic_cast<TQMouseEvent*>( event );

	if( mouseEvent && m_view )
	{
		KoPoint canvasCoordinate = toContents( KoPoint( mouseEvent->pos() ) );
		return m_view->mouseEvent( mouseEvent, canvasCoordinate );
	}

	return false;
}


// This causes a repaint normally, so just overwriting it omits the repainting
void
VCanvas::focusInEvent( TQFocusEvent * )
{
}

KoPoint
VCanvas::toViewport( const KoPoint &p ) const
{
	KoPoint p2 = p;
	p2.setX( ( p.x() * m_view->zoom() ) - contentsX() + pageOffsetX() );
	if( contentsHeight() > height() )
		p2.setY( ( contentsHeight() - ( p.y() * m_view->zoom() + contentsY() + pageOffsetY() ) ) );
	else
		p2.setY( ( height() - p.y() * m_view->zoom() + pageOffsetY() ) );
	return p2;
}

KoPoint
VCanvas::toContents( const KoPoint &p ) const
{
	KoPoint p2 = p;
	p2.setX( ( p.x() + contentsX() - pageOffsetX() ) / m_view->zoom() );
	if( contentsHeight() > height() )
		p2.setY( ( contentsHeight() - ( p.y() + contentsY() + pageOffsetY()) ) / m_view->zoom() );
	else
		p2.setY( ( height() - p.y() - pageOffsetY() ) / m_view->zoom() );
	return p2;
}

KoRect
VCanvas::boundingBox() const
{
	KoPoint p1( 0, 0 );
	KoPoint p2( width(), height() );
	if( !m_view->documentDeleted() )
	{
		p1 = toContents( p1 );
		p2 = toContents( p2 );
	}
	return KoRect( p1, p2 ).normalize();
}

void
VCanvas::setYMirroring( VPainter *p )
{
	TQWMatrix mat;

	mat.scale( 1, -1 );
	mat.translate( pageOffsetX(), pageOffsetY() );

	if( contentsHeight() > visibleHeight() )
		mat.translate( -contentsX(), contentsY() - contentsHeight() );
	else
		mat.translate( 0, -visibleHeight() );

	p->setWorldMatrix( mat );
}

void
VCanvas::viewportPaintEvent( TQPaintEvent *e )
{
	TQRect eventRect = e->rect();
	KoRect rect = KoRect::fromTQRect( eventRect );
	
	setYMirroring( m_view->painterFactory()->editpainter() );
	viewport()->setUpdatesEnabled( false );
	VPainter *p = m_view->painterFactory()->painter();

	// TODO : only update ROIs
	p->begin();
	p->clear( rect, TQColor( 195, 194, 193 ) );
	p->setZoomFactor( m_view->zoom() );
	setYMirroring( p );
	
	// TRICK : slightly adjust the matrix so libart AA looks better
	TQWMatrix mat = p->worldMatrix();
	p->setWorldMatrix( mat.translate( -.5, -.5 ) );

	// set up clippath
	p->newPath();
	p->moveTo( rect.topLeft() );
	p->lineTo( rect.topRight() );
	p->lineTo( rect.bottomRight() );
	p->lineTo( rect.bottomLeft() );
	p->lineTo( rect.topLeft() );
	p->setClipPath();

	m_part->document().drawPage( p, m_part->pageLayout(), m_view->showPageMargins() );
	KoRect bbox = boundingBox();
	m_part->document().draw( p, &bbox );

	p->resetClipPath();
	p->end();

	// draw handle:
	VTQPainter qpainter( p->device() );
	setYMirroring( &qpainter );
	qpainter.setZoomFactor( m_view->zoom() );
	m_part->document().selection()->draw( &qpainter, m_view->zoom() );

	if( m_view->toolController()->currentTool() )
		m_view->toolController()->currentTool()->draw( &qpainter );

	bitBlt( viewport(), eventRect.topLeft(), p->device(), eventRect );
	viewport()->setUpdatesEnabled( true );
}

void
VCanvas::setViewport( double centerX, double centerY )
{
	setContentsPos( int( centerX * contentsWidth() - 0.5 * visibleWidth() ),
					int( centerY * contentsHeight() - 0.5 * visibleHeight() ) );
}

void
VCanvas::setViewportRect( const KoRect &r )
{
	viewport()->setUpdatesEnabled( false );
	double zoomX = m_view->zoom() * ( ( visibleWidth() / m_view->zoom() ) / r.width() );
	double zoomY = m_view->zoom() * ( ( visibleHeight() / m_view->zoom() ) / r.height() );
	double pageOffX = ( contentsWidth() - ( m_part->document().width() * m_view->zoom() ) ) / 2.0;
	double centerX = double( ( r.center().x() ) * m_view->zoom() + pageOffX ) / double( contentsWidth() );
	double pageOffY = ( contentsHeight() - ( m_part->document().height() * m_view->zoom() ) ) / 2.0;
	double centerY = double( ( r.center().y() ) * m_view->zoom() + pageOffY ) / double( contentsHeight() );
	double zoom = zoomX < zoomY ? zoomX : zoomY;
	resizeContents( int( ( zoom / m_view->zoom() ) * contentsWidth() ),
					int( ( zoom / m_view->zoom() ) * contentsHeight() ) );
	setViewport( centerX, 1.0 - centerY );
	m_view->setZoomAt( zoom );
	viewport()->setUpdatesEnabled( true );
}

void
VCanvas::drawContents( TQPainter* painter, int clipx, int clipy,
	int clipw, int cliph  )
{
	drawDocument( painter, KoRect( clipx, clipy, clipw, cliph ) );
}

void
VCanvas::drawDocument( TQPainter* /*painter*/, const KoRect&, bool drawVObjects )
{
	setYMirroring( m_view->painterFactory()->editpainter() );

	VPainter* p = m_view->painterFactory()->painter();
	if( drawVObjects )
	{
		p->begin();
		p->clear( TQColor( 195, 194, 193 ) );
		p->setZoomFactor( m_view->zoom() );
		setYMirroring( p );
		// TRICK : slightly adjust the matrix so libart AA looks better
		TQWMatrix mat = p->worldMatrix();
		p->setWorldMatrix( mat.translate( -.5, -.5 ) );

		m_part->document().drawPage( p, m_part->pageLayout(), m_view->showPageMargins() );
		KoRect r2 = boundingBox();
		m_part->document().draw( p, &r2 );

		p->end();
	}

	// draw handle:
	VTQPainter qpainter( p->device() );
	setYMirroring( &qpainter );
	qpainter.setZoomFactor( m_view->zoom() );
	m_part->document().selection()->draw( &qpainter, m_view->zoom() );

	if( m_view->toolController()->currentTool() )
		m_view->toolController()->currentTool()->draw( &qpainter );

	bitBlt( viewport(), 0, 0, p->device(), 0, 0, width(), height() );
}

void
VCanvas::repaintAll( bool drawVObjects )
{
	drawDocument( 0, KoRect( 0, 0, width(), height() ), drawVObjects );
}

/// repaints just a rect area (no scrolling)
void
VCanvas::repaintAll( const KoRect &r )
{
	drawDocument( 0, r );
}

void
VCanvas::resizeEvent( TQResizeEvent* event )
{
	double centerX = double( contentsX() + 0.5 * visibleWidth() ) / double( contentsWidth() );
	double centerY = double( contentsY() + 0.5 * visibleHeight() ) / double( contentsHeight() );

	TQScrollView::resizeEvent( event );
	if( !m_pixmap )
		m_pixmap = new TQPixmap( width(), height() );
	else
		m_pixmap->resize( width(), height() );

	VPainter *p = m_view->painterFactory()->painter();
	p->resize( width(), height() );
	p->clear( TQColor( 195, 194, 193 ) );
	setViewport( centerX, centerY );
}

void
VCanvas::slotContentsMoving( int /*x*/, int /*y*/ )
{
	emit viewportChanged();
}

void
VCanvas::dragEnterEvent( TQDragEnterEvent *e )
{
	e->accept( KarbonDrag::canDecode( e ) || KColorDrag::canDecode( e ) );
}

void
VCanvas::dropEvent( TQDropEvent *e )
{
	m_view->dropEvent( e );
}

#include "vcanvas.moc"

