/* This file is part of the KDE project
   Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
   Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>

   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 "kexiscrollview.h"

#include <tqcursor.h>
#include <tqobjectlist.h>
#include <tqpainter.h>
#include <tqpixmap.h>

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

#include <utils/kexirecordnavigator.h>
#include <core/kexi.h>
#include <kexiutils/utils.h>

//! @internal
class KexiScrollViewData
{
	public:
		TQPixmap horizontalOuterAreaPixmapBuffer;
		TQPixmap verticalOuterAreaPixmapBuffer;
};

// @todo warning: not reentrant!
static KStaticDeleter<KexiScrollViewData> KexiScrollView_data_deleter;
KexiScrollViewData* KexiScrollView_data = 0;

KexiScrollView::KexiScrollView(TQWidget *parent, bool preview)
 : TQScrollView(parent, "kexiscrollview", WStaticContents)
 , m_widget(0)
 , m_helpFont(font())
 , m_preview(preview)
 , m_scrollViewNavPanel(0)
{
	setFrameStyle(TQFrame::WinPanel | TQFrame::Sunken);
	viewport()->setPaletteBackgroundColor(colorGroup().mid());
	TQColor fc = palette().active().foreground(),
		bc = viewport()->paletteBackgroundColor();
	m_helpColor = KexiUtils::blendedColors(fc, bc, 1, 2);
//	m_helpColor = TQColor((fc.red()+bc.red()*2)/3, (fc.green()+bc.green()*2)/3, 
//		(fc.blue()+bc.blue()*2)/3);
	m_helpFont.setPointSize( m_helpFont.pointSize() * 3 );

	setFocusPolicy(TQWidget::WheelFocus);

	//initial resize mode is always manual;
	//will be changed on show(), if needed
	setResizePolicy(Manual);

	viewport()->setMouseTracking(true);
	m_resizing = false;
	m_enableResizing = true;
	m_snapToGrid = false;
	m_gridSize = 0;
	m_outerAreaVisible = true;

	connect(&m_delayedResize, TQ_SIGNAL(timeout()), this, TQ_SLOT(refreshContentsSize()));
	m_smodeSet = false;
	if (m_preview) {
		refreshContentsSizeLater(true, true);
//! @todo allow to hide navigator
		updateScrollBars();
		m_scrollViewNavPanel = new KexiRecordNavigator(this, leftMargin(), "nav");
		m_scrollViewNavPanel->setSizePolicy(TQSizePolicy::Minimum,TQSizePolicy::Preferred);
	}
}

KexiScrollView::~KexiScrollView()
{
}

void
KexiScrollView::setWidget(TQWidget *w)
{
	addChild(w);
	m_widget = w;
}

void
KexiScrollView::setRecordNavigatorVisible(bool visible)
{
	if(/*m_scrollViewNavPanel->isVisible() &&*/ !visible)
		m_scrollViewNavPanel->hide();
	else if(visible)  {
		m_scrollViewNavPanel->show();
		updateNavPanelGeometry();
	}
}

void
KexiScrollView::setSnapToGrid(bool enable, int gridSize)
{
	m_snapToGrid = enable;
	if(enable) {
		m_gridSize = gridSize;
	}
}

void
KexiScrollView::refreshContentsSizeLater(bool horizontal, bool vertical)
{
	Q_UNUSED( horizontal );
	Q_UNUSED( vertical );

	if (!m_smodeSet) {
		m_smodeSet = true;
		m_vsmode = vScrollBarMode();
		m_hsmode = hScrollBarMode();
	}
//	if (vertical)
		setVScrollBarMode(TQScrollView::AlwaysOff);
	//if (horizontal)
		setHScrollBarMode(TQScrollView::AlwaysOff);
	updateScrollBars();
	m_delayedResize.start( 100, true );
}

void
KexiScrollView::refreshContentsSize()
{
	if(!m_widget)
		return;
	if (m_preview) {
		resizeContents(m_widget->width(), m_widget->height());
//		kdDebug() << "KexiScrollView::refreshContentsSize(): ( " 
	//		<< m_widget->width() <<", "<< m_widget->height() << endl;
		setVScrollBarMode(m_vsmode);
		setHScrollBarMode(m_hsmode);
		m_smodeSet = false;
		updateScrollBars();
	}
	else {
		// Ensure there is always space to resize Form
		int w = contentsWidth(), h = contentsHeight();
		bool change = false;
		const int delta_x = TQMAX( (KexiScrollView_data ? 
			KexiScrollView_data->verticalOuterAreaPixmapBuffer.width() : 0), 300);
		const int delta_y = TQMAX( (KexiScrollView_data ? 
			KexiScrollView_data->horizontalOuterAreaPixmapBuffer.height() : 0), 300);
		if((m_widget->width() + delta_x * 2 / 3) > w) {
			w = m_widget->width() + delta_x;
			change = true;
		}
		else if((w - m_widget->width()) > delta_x) {
			w = m_widget->width() + delta_x;
			change = true;
		}
		if((m_widget->height() + delta_y * 2 / 3) > h) {
			h = m_widget->height() + delta_y;
			change = true;
		}
		else if((h - m_widget->height()) > delta_y) {
			h = m_widget->height() + delta_y;
			change = true;
		}
		if (change) {
	repaint();
	viewport()->repaint();
	repaintContents();
	updateContents(0, 0, 2000,2000);
	clipper()->repaint();

			resizeContents(w, h);
		}
//		kdDebug() << "KexiScrollView::refreshContentsSize(): ( " 
	//		<< contentsWidth() <<", "<< contentsHeight() << endl;
		updateScrollBars();
		setVScrollBarMode(Auto);
		setHScrollBarMode(Auto);
	}
	updateContents();
	updateScrollBars();
}

void
KexiScrollView::updateNavPanelGeometry()
{
	if (m_scrollViewNavPanel)
		m_scrollViewNavPanel->updateGeometry(leftMargin());
}

void
KexiScrollView::contentsMousePressEvent(TQMouseEvent *ev)
{
	if(!m_widget)
		return;

	TQRect r3(0, 0, m_widget->width() + 4, m_widget->height() + 4);
	if(!r3.contains(ev->pos())) // clicked outside form
		//m_form->resetSelection();
		emit outerAreaClicked();

	if(!m_enableResizing)
		return;

	TQRect r(m_widget->width(),  0, 4, m_widget->height() + 4); // right limit
	TQRect r2(0, m_widget->height(), m_widget->width() + 4, 4); // bottom limit
	if(r.contains(ev->pos()) || r2.contains(ev->pos()))
	{
		m_resizing = true;
		emit resizingStarted();
	}
}

void
KexiScrollView::contentsMouseReleaseEvent(TQMouseEvent *)
{
	if(m_resizing) {
		m_resizing = false;
		emit resizingEnded();
	}

	unsetCursor();
}

void
KexiScrollView::contentsMouseMoveEvent(TQMouseEvent *ev)
{
	if(!m_widget || !m_enableResizing)
		return;

	if(m_resizing) // resize widget
	{
		int tmpx = ev->x(), tmpy = ev->y();
		const int exceeds_x = (tmpx - contentsX() + 5) - clipper()->width();
		const int exceeds_y = (tmpy - contentsY() + 5) - clipper()->height();
		if (exceeds_x > 0)
			tmpx -= exceeds_x;
		if (exceeds_y > 0)
			tmpy -= exceeds_y;
		if ((tmpx - contentsX()) < 0)
			tmpx = contentsX();
		if ((tmpy - contentsY()) < 0)
			tmpy = contentsY();

		// we look for the max widget right() (or bottom()), which would be the limit for form resizing (not to hide widgets)
		TQObjectList *list = m_widget->queryList("TQWidget", 0, true, false /* not recursive*/);
		for(TQObject *o = list->first(); o; o = list->next())
		{
			TQWidget *w = (TQWidget*)o;
			tmpx = TQMAX(tmpx, (w->geometry().right() + 10));
			tmpy = TQMAX(tmpy, (w->geometry().bottom() + 10));
		}
		delete list;

		int neww = -1, newh;
		if(cursor().shape() == TQCursor::SizeHorCursor)
		{
			if(m_snapToGrid)
				neww = int( float(tmpx) / float(m_gridSize) + 0.5 ) * m_gridSize;
			else
				neww = tmpx;
			newh = m_widget->height();
		}
		else if(cursor().shape() == TQCursor::SizeVerCursor)
		{
			neww = m_widget->width();
			if(m_snapToGrid)
				newh = int( float(tmpy) / float(m_gridSize) + 0.5 ) * m_gridSize;
			else
				newh = tmpy;
		}
		else if(cursor().shape() == TQCursor::SizeFDiagCursor)
		{
			if(m_snapToGrid) {
				neww = int( float(tmpx) / float(m_gridSize) + 0.5 ) * m_gridSize;
				newh = int( float(tmpy) / float(m_gridSize) + 0.5 ) * m_gridSize;
			} else {
				neww = tmpx;
				newh = tmpy;
			}
		}
		//needs update?
		if (neww!=-1 && m_widget->size() != TQSize(neww, newh)) {
			m_widget->resize( neww, newh );
			refreshContentsSize();
			updateContents();
		}
	}
	else // update mouse cursor
	{
		TQPoint p = ev->pos();
		TQRect r(m_widget->width(),  0, 4, m_widget->height()); // right
		TQRect r2(0, m_widget->height(), m_widget->width(), 4); // bottom
		TQRect r3(m_widget->width(), m_widget->height(), 4, 4); // bottom-right corner

		if(r.contains(p))
			setCursor(TQCursor::SizeHorCursor);
		else if(r2.contains(p))
			setCursor(TQCursor::SizeVerCursor);
		else if(r3.contains(p))
			setCursor(TQCursor::SizeFDiagCursor);
		else
			unsetCursor();
	}
}

void 
KexiScrollView::setupPixmapBuffer(TQPixmap& pixmap, const TQString& text, int lines)
{
	Q_UNUSED( lines );

	TQFontMetrics fm(m_helpFont);
	const int flags = TQt::AlignCenter|TQt::AlignTop;
	TQRect rect(fm.boundingRect(0,0,1000,1000,flags,text));
	const int txtw = rect.width(), txth = rect.height();//fm.width(text), txth = fm.height()*lines;
	pixmap = TQPixmap(txtw, txth);
	if (!pixmap.isNull()) {
		//create pixmap once
		pixmap.fill( viewport()->paletteBackgroundColor() );
		TQPainter pb(&pixmap, this);
		pb.setPen(m_helpColor);
		pb.setFont(m_helpFont);
		pb.drawText(0, 0, txtw, txth, TQt::AlignCenter|TQt::AlignTop, text);
	}
}

void
KexiScrollView::drawContents( TQPainter * p, int clipx, int clipy, int clipw, int cliph ) 
{
	TQScrollView::drawContents(p, clipx, clipy, clipw, cliph);
	if (m_widget) {
		if(m_preview || !m_outerAreaVisible)
			return;

		//draw right and bottom borders
		const int wx = childX(m_widget);
		const int wy = childY(m_widget);
		p->setPen(palette().active().foreground());
		p->drawLine(wx+m_widget->width(), wy, wx+m_widget->width(), wy+m_widget->height());
		p->drawLine(wx, wy+m_widget->height(), wx+m_widget->width(), wy+m_widget->height());
//kdDebug() << "KexiScrollView::drawContents() " << wy+m_widget->height() << endl;

		if (!KexiScrollView_data) {
			KexiScrollView_data_deleter.setObject( KexiScrollView_data, new KexiScrollViewData() );

			//create flicker-less buffer
			setupPixmapBuffer( KexiScrollView_data->horizontalOuterAreaPixmapBuffer, i18n("Outer Area"), 1 );
			setupPixmapBuffer( KexiScrollView_data->verticalOuterAreaPixmapBuffer, i18n("Outer\nArea"), 2 );
		}
		if (!KexiScrollView_data->horizontalOuterAreaPixmapBuffer.isNull() 
			&& !KexiScrollView_data->verticalOuterAreaPixmapBuffer.isNull() 
			&& !m_delayedResize.isActive() /* only draw text if there's not pending delayed resize*/)
		{
			if (m_widget->height()>(KexiScrollView_data->verticalOuterAreaPixmapBuffer.height()+20)) {
				p->drawPixmap( 
					TQMAX( m_widget->width(), KexiScrollView_data->verticalOuterAreaPixmapBuffer.width() + 20 ) + 20,
					TQMAX( (m_widget->height() - KexiScrollView_data->verticalOuterAreaPixmapBuffer.height())/2, 20 ),
					KexiScrollView_data->verticalOuterAreaPixmapBuffer
				);
			}
			p->drawPixmap( 
				TQMAX( (m_widget->width() - KexiScrollView_data->horizontalOuterAreaPixmapBuffer.width())/2, 20 ),
				TQMAX( m_widget->height(), KexiScrollView_data->horizontalOuterAreaPixmapBuffer.height() + 20 ) + 20,
				KexiScrollView_data->horizontalOuterAreaPixmapBuffer
			);
		}
	}
}

void
KexiScrollView::leaveEvent( TQEvent *e )
{
	TQWidget::leaveEvent(e);
	m_widget->update(); //update form elements on too fast mouse move
}

void
KexiScrollView::setHBarGeometry( TQScrollBar & hbar, int x, int y, int w, int h )
{
/*todo*/
//	kdDebug(44021)<<"KexiScrollView::setHBarGeometry"<<endl;
	if (m_scrollViewNavPanel && m_scrollViewNavPanel->isVisible()) {
		m_scrollViewNavPanel->setHBarGeometry( hbar, x, y, w, h );
	}
	else {
		hbar.setGeometry( x, y, w, h );
	}
}

KexiRecordNavigator*
KexiScrollView::recordNavigator() const
{
	return m_scrollViewNavPanel;
}

#include "kexiscrollview.moc"

