/*
 *	$Id$
 *
 *	KWin client button tooltip support module
 *
 *  Copyright (c) 2001
 *	Karol Szwed <gallium@kde.org>
 * 
 *  Uses portions of QToolTip code 
 *  Copyright (c) TrollTech AS
 *
 *  Since Qt installs its own internal event filter when using
 *  a QToolTip, kwin doesn't obtain the necessary events and hence
 *  focus problems and loss of window frames occur if they are used
 *  for kwin styles. Thus, this small class implements a bare subset
 *  of QToolTip designed especially for kwin client buttons without 
 *  installing any event filters that confuse kwin.
 * 
 *  For all normal clients, simply use the KWinButton class instead
 *  of QButton, which will use a KWinToolTip internally. Other clients
 *  which rely on QToolButton or QWidget for client buttons may use the 
 *  KWinToolButton and KWinWidgetButton classes to automatically take
 *  care of tooltips.
 *
 */

#include <qpoint.h>
#include "kwinbutton.h"
#include "options.h"
#include "workspace.h"

// This is a hack to ensure that we use Qt fade and scroll effects

// This code is copied directly from qeffects_p.h
struct QEffects
{
    enum Direction {
        LeftScroll      = 0x0001,
        RightScroll     = 0x0002,
        UpScroll        = 0x0004,
        DownScroll      = 0x0008
    };

    typedef uint DirFlags;
};

extern void Q_EXPORT qScrollEffect( QWidget*, 
	QEffects::DirFlags dir = QEffects::DownScroll, int time = -1 );
extern void Q_EXPORT qFadeEffect( QWidget*, int time = -1 );
// end qeffects_p.h code

using namespace KWinInternal;


KWinToolTip::KWinToolTip( QWidget* parent, const QString& tip )
	: QLabel( NULL, "KWinToolTip", WStyle_StaysOnTop + 
			  WStyle_Customize + WStyle_NoBorder + WStyle_Tool )
{
	setMargin(1);
	setIndent(0);
	setFrameStyle( QFrame::Plain | QFrame::Box );
	setLineWidth(1);
	setAlignment( AlignLeft | AlignTop );
	setText(tip);
	adjustSize();

	// Use stock Qt tooltip colors as defined in qapplication.cpp
	QColorGroup clrGroup( Qt::black, QColor(255,255,220),
					QColor(96,96,96), Qt::black, Qt::black,
					Qt::black, QColor(255,255,220) );
	setPalette( QPalette( clrGroup, clrGroup, clrGroup ) );
           
	btn = parent;
	connect(&hideTimer, SIGNAL(timeout()), SLOT(hideTip()));
	connect(&showTimer, SIGNAL(timeout()), SLOT(showTip()));
}

KWinToolTip::~KWinToolTip()
{
}

void KWinToolTip::setTipText( const QString& tip )
{
	bool visible = isVisible();

	if (visible) 
		hide();

	setText(tip);
	adjustSize();
	positionTip();

	if (visible) 
		showTip();
}

void KWinToolTip::enterTip()
{
	// Show after 1 second
	showTimer.start( 1000, TRUE );
}

void KWinToolTip::leaveTip()
{
	if (hideTimer.isActive())
		hideTimer.stop();
	if (showTimer.isActive())
		showTimer.stop();
	if (isVisible())
		hide();
}

/* Internal */
void KWinToolTip::showTip()
{
	if (isVisible())
		return;

	// Ignore empty tooltips
	if (text().isEmpty())
		return;

	positionTip();

	// Show tooltips the Qt way
	if (options->fadeTooltips())
		qFadeEffect(this); 
	else if (options->animateTooltips()) 
		qScrollEffect(this);
	else
		show();
	raise();

	// Hide after 10 seconds
	hideTimer.start( 10000, TRUE );
}

/* Internal */
void KWinToolTip::hideTip()
{
	if (isVisible())
		hide();
}

/* Internal */
void KWinToolTip::positionTip()
{
	QPoint p = btn->mapToGlobal(btn->rect().bottomLeft()) + QPoint(0, 16);
    int screen = QApplication::desktop()->screenNumber(btn->mapToGlobal(btn->rect().center()));
	QRect screenGeom = QApplication::desktop()->screenGeometry(screen);

	// Ensure the tooltip is displayed within the current desktop
	if ( screenGeom.right() < p.x() + width() )
		p.setX( screenGeom.right() - width());
	if ( screenGeom.bottom() < p.y() + height() )
		p.setY( screenGeom.bottom() - height() );
	if (p.x() < screenGeom.x()) p.setX(screenGeom.x());
	if (p.y() < screenGeom.y()) p.setY(screenGeom.y()); 
	move(p); 

	// Ensure we don't get enter/leave event race conditions when a
	// tooltip is over a button.
	QRect btnGlobalRect( btn->mapToGlobal(btn->rect().topLeft()),
						 btn->mapToGlobal(btn->rect().bottomRight()) );
	QRect tipGlobalRect( mapToGlobal(rect().topLeft()),
						 mapToGlobal(rect().bottomRight()) );
	if (btnGlobalRect.intersects( tipGlobalRect ))
	{
		// Only intersects when button is at lower part of screen
		p.setY( btn->mapToGlobal(btn->rect().topLeft()).y() - height() - 5 );
		move(p);
	}
}


// KWinButton class
KWinButton::KWinButton(QWidget *parent, const char *name, const QString& tip)
    : QButton(parent, name, WStyle_Customize | WRepaintNoErase |
                            WResizeNoErase   | WStyle_NoBorder )
{
	buttonTip = options->showTooltips() ? new KWinToolTip(this, tip) : NULL;
}

KWinButton::~KWinButton()
{
    if (buttonTip)
        delete buttonTip;
}

void KWinButton::setTipText(const QString& newTip)
{
	if (buttonTip)
		buttonTip->setTipText( newTip );
}

void KWinButton::enterEvent(QEvent *e)
{
    if (buttonTip)
        buttonTip->enterTip();
	QButton::enterEvent( e );
}

void KWinButton::leaveEvent(QEvent *e)
{
    if (buttonTip)
        buttonTip->leaveTip();
	QButton::leaveEvent( e );
}

void KWinButton::mousePressEvent(QMouseEvent* e)
{
    // Hide tooltips if a button is pressed
    if (buttonTip)
        buttonTip->leaveTip();
	QButton::mousePressEvent( e );
}


// KWinToolButton class
KWinToolButton::KWinToolButton(QWidget *parent, const char *name, const QString& tip)
    : QToolButton(parent, name)
{
	buttonTip = options->showTooltips() ? new KWinToolTip(this, tip) : NULL;
}

KWinToolButton::~KWinToolButton()
{
    if (buttonTip)
        delete buttonTip;
}

void KWinToolButton::setTipText(const QString& newTip)
{
	if (buttonTip)
		buttonTip->setTipText( newTip );
}

void KWinToolButton::enterEvent(QEvent *e)
{
    if (buttonTip)
        buttonTip->enterTip();
	QToolButton::enterEvent( e );
}

void KWinToolButton::leaveEvent(QEvent *e)
{
    if (buttonTip)
        buttonTip->leaveTip();
	QToolButton::leaveEvent( e );
}

void KWinToolButton::mousePressEvent(QMouseEvent* e)
{
    // Hide tooltips if a button is pressed
    if (buttonTip)
        buttonTip->leaveTip();
	QToolButton::mousePressEvent( e );
}


// KWinWidgetButton class
KWinWidgetButton::KWinWidgetButton(QWidget *parent, const char *name, 
	  WFlags f, const QString& tip)
    : QWidget(parent, name, f)
{
	buttonTip = options->showTooltips() ? new KWinToolTip(this, tip) : NULL;
}

KWinWidgetButton::~KWinWidgetButton()
{
    if (buttonTip)
        delete buttonTip;
}

void KWinWidgetButton::setTipText(const QString& newTip)
{
	if (buttonTip)
		buttonTip->setTipText( newTip );
}

void KWinWidgetButton::enterEvent(QEvent *e)
{
    if (buttonTip)
        buttonTip->enterTip();
	QWidget::enterEvent( e );
}

void KWinWidgetButton::leaveEvent(QEvent *e)
{
    if (buttonTip)
        buttonTip->leaveTip();
	QWidget::leaveEvent( e );
}

void KWinWidgetButton::mousePressEvent(QMouseEvent* /*e*/)
{
    // Hide tooltips if a button is pressed
    if (buttonTip)
        buttonTip->leaveTip();
}


#include "kwinbutton.moc"
// vim: ts=4