/*-*-c++-*-
 * $Id: cgraphobject.cpp,v 1.1 2002/01/28 15:38:32 holzheu Exp $
 *
 * This file is part of qlcrash, a GUI for the dump-analysis tool lcrash.
 *
 * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *
 * Authors:
 * Michael Geselbracht (let@users.sourceforge.net)
 * Fritz Elfert (elfert@de.ibm.com)
 * Michael Holzheu (holzheu@de.ibm.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version. See the file COPYING for more
 * information.
 */
#include "cgraphobject.h"
#include "cgraphbutton.h"
#include "cgraphlabel.h"
#include "cgraphmanager.h"

#include "close.xpm"
#include "viewone.xpm"
#include "viewmany.xpm"
#include "disc.xpm"

#include <qcursor.h>
#include <qpixmap.h>
#include <qpainter.h>
#include <qlabel.h>
#include <qfontmetrics.h>
#include <qscrollview.h>

#include <assert.h>
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <iostream.h>

#define HEADER_HEIGHT 18
#define HEADER_COLOR QColor(216, 216, 216)
#define HEADER_CAPTION_COLOR QColor(0, 0, 0)
#define HEADER_FOCUS_COLOR QColor(150, 150, 200)
#define HEADER_FOCUS_CAPTION_COLOR QColor(180, 180, 255)

CGraphObject::CGraphObject(CGraphManager *parent, const char *name )
	: QFrame(parent, name)
	, oParent(0)
	, oWidget(0)
	, oIcon(0)
	, oCaption(0)
	, oMoveAction(None)
	, oManager(parent)
	, oHaveFocus(false)
	, oOneChild(0)
{
	initTitleBar();
	setFrameStyle(QFrame::WinPanel | QFrame::Raised);
	reparent(parent->mainWidget(), QPoint(), true);
	setMouseTracking(true);
	setBackgroundColor(QColor(100, 100, 100));
}

CGraphObject::~CGraphObject()
{
}

void
CGraphObject::paintEvent(QPaintEvent* e)
{
	// draw the frame
	QFrame::paintEvent(e);
	
	QPainter p(this);
	p.setPen(QPen::NoPen);
	const QColor& cb = (oHaveFocus ? HEADER_FOCUS_COLOR : HEADER_COLOR);
	const QColor& cc = (oHaveFocus ? HEADER_FOCUS_CAPTION_COLOR : HEADER_CAPTION_COLOR);
	p.setBrush(cb);
	p.drawRect(2, 1, width() - 3, HEADER_HEIGHT);
	if (oCaption != 0)
		oCaption->setBackgroundColor(cb);
}

void
CGraphObject::resizeEvent(QResizeEvent*)
{
	oCloseB->move(width() - oCloseB->width() - 2, 2);
	oDiscB->move(oCloseB->x() - oDiscB->width() - 2, oCloseB->y());
	oViewB->move(oDiscB->x() - oViewB->width() - 2, oCloseB->y());
	if (oIcon != 0) {
		oIcon->move(1, 1);
	}
	if (oCaption != 0) {
		oCaption->setGeometry(20, 1, width() - 20 - 3 - 3 * (oCloseB->width() + 2), HEADER_HEIGHT);
		QFontMetrics fm = oCaption->fontMetrics();
		int brw = fm.boundingRect(caption()).width();
		int cw = oCaption->width();
		if (brw > cw) {
			QString str = caption();
			int n = oCaption->width() * str.length() / fm.boundingRect(str).width() - 1;
			int d = str.length() - n;
			str.replace(n / 2 - d / 2, d, "...");
			oCaption->setText(str);
		}
		else {
			oCaption->setText(caption());
		}
	}
	
	if (oWidget != 0) {
		oWidget->setGeometry(3, HEADER_HEIGHT + 3, width() - 6, height() - 6 - HEADER_HEIGHT);
	}
}

void
CGraphObject::initTitleBar()
{
	oCloseB = new CGraphButton(this);
	oCloseB->setPixmap(QPixmap(close_xpm));
	oCloseB->resizeButton();
	
	oDiscB = new CGraphButton(this);
	oDiscB->setPixmap(QPixmap(disc_xpm));
	oDiscB->resizeButton();
	
	oViewB = new CGraphButton(this);
	oViewB->setToggle(true);
	oViewB->setOnPixmap(QPixmap(viewone_xpm));
	oViewB->setOffPixmap(QPixmap(viewmany_xpm));
	oViewB->setPixmap(QPixmap(viewmany_xpm));
	oViewB->resizeButton();
	
	connect(oCloseB, SIGNAL(clicked()), SLOT(slotClose()));
	connect(oDiscB, SIGNAL(clicked()), SLOT(slotDisc()));
	connect(oViewB, SIGNAL(toggled(bool)), SLOT(slotView(bool)));
}

void
CGraphObject::setWidget(QWidget* w)
{
	QPoint p;
	
	delete oWidget;
	oWidget = w;
	
	w->reparent(this, p, true);
	resize(w->width() + 4, HEADER_HEIGHT + w->height() + 4);
	w->move(2, HEADER_HEIGHT + 2);
	
	oWidget->installEventFilter(this);
}

void
CGraphObject::replaceWidget(QWidget* w, bool keepSize)
{
	if (keepSize && oWidget != 0) { // resize new widget to the old widget size
		// this is buggy
		w->resize(oWidget->size());
	}
	
	// delete all child objects
	CGraphObject* walker = oChildList.first();
	while (walker != 0) {
		walker->slotClose();
		walker = oChildList.next();
	}
	
	setWidget(w);
}

void
CGraphObject::addChild(CGraphObject* child)
{
	assert(child != 0);
	oChildList.append(child);
	child->setParent(this);
}

void
CGraphObject::removeChild(CGraphObject* c)
{
	CGraphObject* walker = oChildList.first();
	while (walker != 0) {
		if (walker == c) {
			oChildList.remove();
			break;
		}
		
		walker = oChildList.next();
	}
	
	if (oOneChild == c) {
		oOneChild = 0;
	}
	
	c->setParent(0);
}

void
CGraphObject::mousePressEvent(QMouseEvent* e)
{
	const int x = e->x();
	const int y = e->y();
	const int bo = 3;
	
	// resize or move ?
	if (x <= bo || width() - x <= bo ||
		y <= bo || height() - y <= bo
	) {
		oMoveAction = Resize;
	}
	else {
		emit sigClicked(this);
		
		oMoveAction = Move;
	}
	
	QPoint pos =e->pos();
	oOldX = pos.x();
	oOldY = pos.y();
}

void
CGraphObject::mouseReleaseEvent(QMouseEvent*)
{
	oMoveAction = None;
	oManager->scrollEnd();
}

void
CGraphObject::mouseMoveEvent(QMouseEvent* e)
{
	if (oMoveAction == None) {
		int x = e->x();
		int y = e->y();
		int xd = width() - x;
		int yd = height() - y;
		const int bo = 2;
		QCursor curs;
		
		if (x < 0)
			x = 10;
		if (y < 0)
			y = 10;
		if (xd < 0)
			xd = 10;
		if (yd < 0)
			yd = 10;
		
		if (x <= bo) {
			if (y <= bo) {
				curs.setShape(SizeFDiagCursor);
				oResizeAction = ResizeType(VerTop | HorLeft);
			}
			else if (yd <= bo) {
				curs.setShape(SizeBDiagCursor);
				oResizeAction = ResizeType(VerBot | HorLeft);
			}
			else {
				curs.setShape(SizeHorCursor);
				oResizeAction = HorLeft;
			}
		}
		else if (xd <= bo) {
			if (y <= bo) {
				curs.setShape(SizeBDiagCursor);
				oResizeAction = ResizeType(VerTop | HorRight);
			}
			else if (yd <= bo) {
				curs.setShape(SizeFDiagCursor);
				oResizeAction = ResizeType(VerBot | HorRight);
			}
			else {
				curs.setShape(SizeHorCursor);
				oResizeAction = HorRight;
			}
		}
		else if (y <= bo) {
			curs.setShape(SizeVerCursor);
			oResizeAction = VerTop;
		}
		else if (yd <= bo) {
			curs.setShape(SizeVerCursor);
			oResizeAction = VerBot;
		}
		else {
			oResizeAction = NoResize;
		}
		
		if (oResizeAction == NoResize) {
			releaseMouse();
		}
		else {		
			grabMouse(curs);
		}
		
		// change pointer shape even if grabMouse() failed
		if (curs.shape() == ArrowCursor) {
			unsetCursor();
		}
		else {
			setCursor(curs);
		}
	}
	else if (oMoveAction == Move) {
		QPoint p = parentWidget()->mapFromGlobal(e->globalPos());
		if (p.x() < 0)
			p.rx() = 0;
		if (p.x() > parentWidget()->width())
			p.rx() = parentWidget()->width();
		if (p.y() > parentWidget()->height())
			p.ry() = parentWidget()->height();
		p = p - QPoint(oOldX, oOldY);
		if (p.y() < 0)
			p.ry() = 0;
		move(p);
		
		// move viewport if mouse pointer is near a border
		moveViewport(e);
		
		emit sigMoved(this);
	}
	else if (oMoveAction == Resize) {
		int x = this->x();
		int y = this->y();
		int w = width();
		int h = height();
		
		int newX = parentWidget()->mapFromGlobal(e->globalPos()).x();
		int newY = parentWidget()->mapFromGlobal(e->globalPos()).y();
		
		if (oResizeAction & HorLeft) {
			w -= newX - x - oOldX;
			x = newX - oOldX;
		}
		else if (oResizeAction & HorRight) {
			w += newX - (x + w);
		}
		if (oResizeAction & VerTop) {
			h -= newY - y - oOldY;
			y = newY - oOldY;
		}
		else if (oResizeAction & VerBot) {
				h += newY - (y + h);
		}
		
		if (w < oWidget->minimumSize().width() + 6) {
			w = oWidget->minimumSize().width() + 6;
		}
		if (h < oWidget->minimumSize().height() + HEADER_HEIGHT + 6) {
			h = oWidget->minimumSize().height() + HEADER_HEIGHT + 6;
		}
		
		setGeometry(x, y, w, h);
		
		moveViewport(e);
		
		emit sigMoved(this);
	}
}

void
CGraphObject::leaveEvent(QMouseEvent*)
{
	oResizeAction = NoResize;
	oMoveAction = None;
	releaseMouse();
}

void
CGraphObject::setIcon(const QPixmap& pix)
{
	if (pix.isNull()) {
		delete oIcon;
		oIcon = 0;
	}
	else {
		delete oIcon;
		oIcon = new QLabel(this);
		oIcon->setBackgroundColor(HEADER_COLOR);
		oIcon->setPixmap(pix);
		oIcon->setGeometry(1, 1, 16, 16);
		resizeEvent(0);
	}
}

void
CGraphObject::setCaption(const QString& capt)
{	
	delete oCaption;
	oCaption = new CGraphLabel(this);
	QFont f = oCaption->font();
	f.setBold(true);
	oCaption->setFont(f);
	oCaption->setBackgroundColor(HEADER_COLOR);
	oCaption->setGeometry(1, 18, width() - 20 - 16, HEADER_HEIGHT - 2);
	oCaption->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
	oCaption->setText(capt);
	oCaption->setFocusProxy(this);
	oCaption->setAutoResize(false);
	oCaption->show();
	QWidget::setCaption(oCaption->text());
	resizeEvent(0);
	
	connect(oCaption, SIGNAL(sigMoveEvent(QMouseEvent*)), SLOT(slotLabelMoved(QMouseEvent*)));
	connect(oCaption, SIGNAL(sigPressEvent(QMouseEvent*)), SLOT(slotLabelPressed(QMouseEvent*)));
	connect(oCaption, SIGNAL(sigReleaseEvent(QMouseEvent*)), SLOT(slotLabelReleased(QMouseEvent*)));
}



void
CGraphObject::slotLabelMoved(QMouseEvent* e)
{
	mouseMoveEvent(e);
}

void
CGraphObject::slotLabelPressed(QMouseEvent* e)
{
	mousePressEvent(e);
}

void
CGraphObject::slotLabelReleased(QMouseEvent* e)
{
	mouseReleaseEvent(e);
}

void
CGraphObject::slotClose()
{
	emit sigClose(this);
}

void
CGraphObject::slotDisc()
{
	emit sigDisc(this);
}

void
CGraphObject::slotView(bool)
{
	oOneChild = 0;
}

void
CGraphObject::moveViewport(QMouseEvent* e)
{
	QPoint pos = oManager->viewport()->mapFromGlobal(e->globalPos());
	CGraphManager::Direction d = CGraphManager::None;
	
	if (pos.x() < 20 && oManager->contentsX() > 0) {
		d = CGraphManager::Left;
	}
	else if (oManager->viewport()->width() - pos.x() < 20) {
		d = CGraphManager::Right;
	}
	if (pos.y() < 20 && oManager->contentsY() > 0) {
		d = CGraphManager::Up;
	}
	else if (oManager->viewport()->height() - pos.y() < 20) {
		d = CGraphManager::Down;
	}
	
	oManager->scrollStart(d);
}

void
CGraphObject::setPaintFocus(bool w)
{
	if (w != oHaveFocus) {
		oHaveFocus = w;
		repaint();
	}
}

bool
CGraphObject::eventFilter(QObject*, QEvent* e)
{
	if (e->type() == QEvent::FocusIn) {
		emit sigClicked(this);
	}
	
	return false;
}

void
CGraphObject::setCursor(const QCursor& c)
{
	if (oWidget != 0) {
		oWidget->setCursor(c);
	}
	QFrame::setCursor(c);
}

void
CGraphObject::unsetCursor()
{
	if (oWidget != 0) {
		oWidget->unsetCursor();
	}
	QFrame::unsetCursor();
}

bool
CGraphObject::isViewMany() const
{
	return oViewB->isOn();
}
