/* amorbubble.cpp
**
** Copyright (c) 1999 Martin R. Jones <mjones@kde.org>
**
*/

/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
** MA 02110-1301, USA.
*/

/*
** Bug reports and questions can be sent to kde-devel@kde.org
*/
#include "amorbubble.h"
#include "amorbubble.moc"
#include <tqpainter.h>
#include <ktextbrowser.h>
#include <tqtooltip.h>
#include <kstandarddirs.h>
#include <X11/Xlib.h>
#include <X11/extensions/shape.h>
#include <tqtimer.h>

#define ARROW_WIDTH     10
#define ARROW_HEIGHT    12
#define BORDER_SIZE     4
#define BUBBLE_OFFSET   16
#define BUBBLE_TIMEOUT  4000    // Minimum milliseconds to display a tip

//---------------------------------------------------------------------------
//
// Constructor
//
AmorBubble::AmorBubble()
	: TQWidget(0, 0, WStyle_Customize | WStyle_NoBorder | WX11BypassWM )
{
    mOriginX = 0;
    mOriginY = 0;
    mBrowser = new KTextBrowser( this );
    mBrowser->setFrameStyle( TQFrame::NoFrame | TQFrame::Plain );
    mBrowser->setMargin( 0 );

    mBrowser->setWrapPolicy(TQTextEdit::AtWordOrDocumentBoundary); // too long to fit in one line?

    TQColorGroup clgrp = mBrowser->colorGroup();
    clgrp.setColor(TQColorGroup::Text, TQt::black);
    //Laurent TQTextBrowser didn't have this function FIX me
    //mBrowser->setPaperColorGroup( clgrp );
    mBrowser->setPaper( TQToolTip::palette().active().brush( TQColorGroup::Background ) );
    mBrowser->setVScrollBarMode( TQTextBrowser::AlwaysOff );
    mBrowser->setHScrollBarMode( TQTextBrowser::AlwaysOff );
    mBrowser->viewport()->installEventFilter( this );

    mBrowser->mimeSourceFactory()->addFilePath(KGlobal::dirs()->findResourceDir("data", "kdewizard/pics")+"kdewizard/pics/");
    TQStringList icons = KGlobal::dirs()->resourceDirs("icon");
    TQStringList::Iterator it;
    for (it = icons.begin(); it != icons.end(); ++it)
	mBrowser->mimeSourceFactory()->addFilePath(*it);

    mMouseWithin = false;
}

//---------------------------------------------------------------------------
//
// Destructor
//
AmorBubble::~AmorBubble()
{
}

//---------------------------------------------------------------------------
//
// Set the message to display in the bubble.  Causes the geometry of the
// widget to be recalculated.
//
void AmorBubble::setMessage(const TQString& message)
{
    mMessage = TQString( "<html>%1</html>" ).arg( message );
    // hacks because heightForWidth() doesn't work.
    setGeometry( -1000, 0, 300, 1000 );
    show();
    mBrowser->setGeometry( 0, 0, 250, 1000 );
    mBrowser->setText( mMessage );
    calcGeometry();
}

//---------------------------------------------------------------------------
//
// Calculates the size, position and mask of the bubble
//
void AmorBubble::calcGeometry()
{
    mBound = TQRect( 0, 0, 250, 0 );
//    mBound.setHeight( mBrowser->heightForWidth( mBound.width() ) );
    mBound.setHeight( mBrowser->contentsHeight() );
    mBound.moveBy(ARROW_WIDTH+BORDER_SIZE, BORDER_SIZE);

    // initialise the default geometry of the bubble
    int w = mBound.width() + BORDER_SIZE * 2 + ARROW_WIDTH;
    int h = mBound.height() + BORDER_SIZE * 2;
    int xpos = mOriginX + BUBBLE_OFFSET;
    int ypos = mOriginY - BORDER_SIZE - ARROW_HEIGHT / 2;

    mArrowVert = Top;
    mArrowHorz = Left;

    // The actual position of the bubble may change if it is too close to
    // the desktop boundary.
    if (mOriginX + w > kapp->desktop()->width())
    {
        // source on right
        xpos = mOriginX - w - BUBBLE_OFFSET;
        mArrowHorz = Right;
	mBound.moveBy( -ARROW_WIDTH, 0 );
    }

    if (mOriginY + h > kapp->desktop()->height())
    {
        // source at bottom
        ypos = mOriginY - h + BORDER_SIZE + ARROW_HEIGHT / 2;
        mArrowVert = Bottom;
    }

    // Check for negative vertical bubble position (top of the screen)
    if ( ypos < 0 )
       ypos = 0;

    setGeometry(xpos, ypos, w, h);
    mBrowser->setGeometry( mBound );

    // create and apply the shape mask
    mMask.resize(w, h);
    mMask.fill(color0);
    TQPainter maskPainter(&mMask);
    maskPainter.setPen(color1);
    maskPainter.setBrush(color1);
    drawBubble(maskPainter);
    XShapeCombineMask( x11Display(), winId(), ShapeBounding, 0, 0,
                       mMask.handle(), ShapeSet );
}

//---------------------------------------------------------------------------
//
// Draw the bubble that text will be draw into using the current pen
// as the outline and the current brush as the fill.
//
void AmorBubble::drawBubble(TQPainter &p)
{
    TQPointArray pointArray(3);

    int left = ARROW_WIDTH;

    if (mArrowHorz == Left)
    {
        pointArray.setPoint(0, ARROW_WIDTH+1, 0);
        pointArray.setPoint(1, 0, -3);
        pointArray.setPoint(2, ARROW_WIDTH+1, ARROW_HEIGHT);
    }
    else
    {
        pointArray.setPoint(0, 0, 0);
        pointArray.setPoint(1, ARROW_WIDTH+1, -3);
        pointArray.setPoint(2, 0, ARROW_HEIGHT);
        pointArray.translate(width() - ARROW_WIDTH - 1, 0);
	left = 0;
    }

    if (mArrowVert == Top)
    {
        pointArray.translate(0, BORDER_SIZE + ARROW_HEIGHT / 2);
    }
    else
    {
        pointArray.translate(0, height() - BORDER_SIZE - ARROW_HEIGHT / 2);
    }

//    p.drawRoundRect(left, 0, width() - ARROW_WIDTH, height(), 10, 20);
    p.drawRect(left, 0, width() - ARROW_WIDTH, height());

    TQPen pen(p.pen());
    p.setPen(NoPen);
    p.drawPolygon(pointArray);

    p.setPen(pen);
    p.drawPolyline(pointArray, 0, 3);
}

//---------------------------------------------------------------------------
//
// Draw the message in a bubble
//
void AmorBubble::paintEvent(TQPaintEvent *)
{
    TQPainter painter(this);
    painter.setPen(black);
    painter.setBrush( TQToolTip::palette().active().brush( TQColorGroup::Background ) );
    drawBubble(painter);
}

//---------------------------------------------------------------------------
//
// The user clicked on the widget
//
void AmorBubble::mouseReleaseEvent(TQMouseEvent *)
{
    hide();
}

//---------------------------------------------------------------------------
//
bool AmorBubble::eventFilter( TQObject *, TQEvent *e )
{
    switch ( e->type() )
    {

// GP	case TQEvent::Enter:
// GP	    mBubbleTimer->stop();
// GP	    break;
// GP	case TQEvent::Leave:
// GP	    if ( isVisible() )
// GP		mBubbleTimer->start( 1000, true );
// GP	    break;
	case TQEvent::Enter:
	    mMouseWithin = true;
	    break;
	case TQEvent::Leave:
	    mMouseWithin = false;
	    break;
	case TQEvent::MouseButtonRelease:
	    hide();				// GP This is the only reason a bubble might posibly be created but hidden
	    break;
	default:
	    break;
    }

    return false;
}