/***************************************************************************
    begin                : Thu Dec 23 1999
    copyright            : (C) 1999 by John Birch
    email                : jbb@kdevelop.org
	
                          Adapted for ruby debugging
                          --------------------------
    begin                : Mon Nov 1 2004
    copyright            : (C) 2004 by Richard Dale
    email                : Richard_Dale@tipitina.demon.co.uk
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "dbgtoolbar.h"
#include "debuggerpart.h"
#include "dbgcontroller.h"

#include <kdockwindow.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <tdepopupmenu.h>
#include <kstandarddirs.h>
#include <twin.h>
#include <twinmodule.h>

#include <tqapplication.h>
#include <tqcursor.h>
#include <tqframe.h>
#include <tqlayout.h>
#include <tqpainter.h>
#include <tqpushbutton.h>
#include <tqtooltip.h>
#include <tqwhatsthis.h>

// **************************************************************************
// **************************************************************************
// **************************************************************************

// Implements a floating toolbar for the debugger.

// Unfortunately, I couldn't get the TDEToolBar to work nicely when it
// was floating, so I was forced to write these classes. I'm not sure whether
// I didn't try hard enough or ... and I've forgotten what the problems were
// now.

// The problem with using this is that it will not dock as a normal toolbar.
// I'm not convince that this is a real problem though.

// So, if you can get it to work as a TDEToolBar, and it works well when the
// app is running, then all these classes can be removed.

// This code is very specific to the internal debugger in tdevelop.

namespace RDBDebugger
{

// **************************************************************************
// **************************************************************************
// **************************************************************************

// This just allows the user to click on the toolbar and drag it somewhere else.
// I would have preferred to use normal decoration on the toolbar and removed
// the iconify, close, etc buttons from the window title but again I kept running
// into problems. Instead, I used no decoration and this class. Also this looks
// similar to the TDEToolBar floating style.
class DbgMoveHandle : public TQFrame
{
public:
    DbgMoveHandle(DbgToolBar *parent=0, const char * name=0, WFlags f=0);
    virtual ~DbgMoveHandle();

    virtual void mousePressEvent(TQMouseEvent *e);
    virtual void mouseReleaseEvent(TQMouseEvent *e);
    virtual void mouseMoveEvent(TQMouseEvent *e);

private:
    DbgToolBar* toolBar_;
    TQPoint      offset_;
    bool        moving_;
};

// **************************************************************************

DbgMoveHandle::DbgMoveHandle(DbgToolBar *parent, const char * name, WFlags f)
    : TQFrame(parent, name, f),
      toolBar_(parent),
      offset_(TQPoint(0,0)),
      moving_(false)
{
    setFrameStyle(TQFrame::Panel|TQFrame::Raised);
    setFixedHeight(12);
}

// **************************************************************************

DbgMoveHandle::~DbgMoveHandle()
{
}

// **************************************************************************

void DbgMoveHandle::mousePressEvent(TQMouseEvent *e)
{
    TQFrame::mousePressEvent(e);
    if (moving_)
        return;

    if (e->button() == TQt::RightButton) {
        TDEPopupMenu *menu = new TDEPopupMenu(this);
	menu->insertTitle(i18n("Debug Toolbar"));
        menu->insertItem(i18n("Dock to Panel"),
                         parent(), TQ_SLOT(slotDock()));
        menu->insertItem(i18n("Dock to Panel && Iconify TDevelop"),
                         parent(), TQ_SLOT(slotIconifyAndDock()));
        menu->popup(e->globalPos());
    } else {
        moving_ = true;
        offset_ = parentWidget()->pos() - e->globalPos();
        setFrameStyle(TQFrame::Panel|TQFrame::Sunken);
        TQApplication::setOverrideCursor(TQCursor(sizeAllCursor));
        setPalette(TQPalette(colorGroup().background()));
        repaint();
    }
}

// **************************************************************************

void DbgMoveHandle::mouseReleaseEvent(TQMouseEvent *e)
{
    TQFrame::mouseReleaseEvent(e);
    moving_ = false;
    offset_ = TQPoint(0,0);
    setFrameStyle(TQFrame::Panel|TQFrame::Raised);
    TQApplication::restoreOverrideCursor();
    setPalette(TQPalette(colorGroup().background()));
    repaint();
}

// **************************************************************************

void DbgMoveHandle::mouseMoveEvent(TQMouseEvent *e)
{
    TQFrame::mouseMoveEvent(e);
    if (!moving_)
        return;

    toolBar_->move(e->globalPos() + offset_);
}

// **************************************************************************
// **************************************************************************
// **************************************************************************

// This class adds text _and_ a pixmap to a button. Why doesn't TQPushButton
// support that? It only allowed text _or_ pixmap.
class DbgButton : public TQPushButton
{
public:
    DbgButton(const TQPixmap &pixmap, const TQString &text,
              DbgToolBar *parent, const char *name=0);
    virtual ~DbgButton() {};
    void drawButtonLabel(TQPainter *painter);
    TQSize sizeHint() const;

private:
    TQPixmap pixmap_;
};

// **************************************************************************

DbgButton::DbgButton(const TQPixmap& pixmap, const TQString& text,
                     DbgToolBar* parent, const char* name)
    : TQPushButton(parent, name),
      pixmap_(pixmap)
{
    setText(text);
}

// **************************************************************************

void DbgButton::drawButtonLabel(TQPainter *painter)
{
    // We always have a pixmap (today...)
    // Centre it if there's no text

    bool hasText = !text().isEmpty();
    int x = ((hasText ? height() : width()) - pixmap_.width()) / 2;
    int y = (height() - pixmap_.height()) / 2;
    painter->drawPixmap(x, y, pixmap_);

    if (hasText) {
        painter->setPen(colorGroup().text());
        painter->drawText(height()+2, 0, width()-(height()+2), height(), AlignLeft|AlignVCenter, text());
    }
}

// **************************************************************************

TQSize DbgButton::sizeHint() const
{
    if (text().isEmpty())
        return pixmap_.size();
    else
        return TQPushButton::sizeHint();
}

// **************************************************************************
// **************************************************************************
// **************************************************************************

DbgDocker::DbgDocker(TQWidget* parent, DbgToolBar* toolBar, const TQPixmap& pixmap) :
    KSystemTray(parent, "DbgDocker"),
    toolBar_(toolBar)
{
    setPixmap(pixmap);
    TQToolTip::add( this, i18n("TDevelop ruby debugger: Click to execute one line of code (\"step\")") );
}

// **************************************************************************

void DbgDocker::mousePressEvent(TQMouseEvent *e)
{
    if (!rect().contains( e->pos()))
        return;

    switch (e->button()) {
    case TQt::LeftButton:
        {
            // Not really a click, but it'll hold for the time being !!!
            emit clicked();
            break;
        }
    case TQt::RightButton:
        {
            TDEPopupMenu* menu = new TDEPopupMenu(this);
	    menu->insertTitle(i18n("Debug Toolbar"));
            menu->insertItem(i18n("Activate"),                        toolBar_, TQ_SLOT(slotUndock()));
            menu->insertItem(i18n("Activate (TDevelop gets focus)"),  toolBar_, TQ_SLOT(slotActivateAndUndock()));
            menu->popup(e->globalPos());
            break;
        }
    default:
        break;
    }
}

// **************************************************************************
// **************************************************************************
// **************************************************************************

DbgToolBar::DbgToolBar(RubyDebuggerPart* part,
                       TQWidget* parent, const char* name)
    : TQFrame(0, name),
      part_(part),
      activeWindow_(0),
      winModule_(0),
      bKDevFocus_(0),
      bPrevFocus_(0),
      appIsActive_(false),
      docked_(false),
      docker_(0),
      dockWindow_(new KSystemTray(parent))
{
    winModule_  = new KWinModule(this);
    docker_ = new DbgDocker(parent, this, BarIcon("dbgnext"));
    connect(docker_, TQ_SIGNAL(clicked()), part_, TQ_SLOT(slotStepOver()));

    // Must have noFocus set so that we can see what window was active.
    // see slotDbgKdevFocus() for more comments
    // I do not want the user to be able to "close" this widget. If we have any
    // decoration then they can and that is bad.
    // This widget is closed when the debugger finishes i.e. they press "Stop"

    // Do we need NoFocus???
    KWin::setState(winId(), NET::StaysOnTop | NET::Modal | NET::SkipTaskbar);
//    KWin::setType(winId(), NET::Override);    // So it has no decoration
    KWin::setType(winId(), NET::Dock);

    setFocusPolicy(TQWidget::NoFocus);
    setFrameStyle( TQFrame::Box | TQFrame::Plain );
    setLineWidth(4);
    setMidLineWidth(0);

    TQBoxLayout* topLayout     = new TQVBoxLayout(this);

    TQBoxLayout* nextLayout    = new TQHBoxLayout();
    TQBoxLayout* stepLayout    = new TQHBoxLayout();
    TQBoxLayout* focusLayout   = new TQHBoxLayout();

    DbgMoveHandle*  moveHandle= new DbgMoveHandle(this);

    TQPushButton*  bRun        = new DbgButton(BarIcon("dbgrun"),        i18n("Run"),        this);
    TQPushButton*  bInterrupt  = new DbgButton(BarIcon("media-playback-pause"),  i18n("Interrupt"),  this);
    TQPushButton*  bNext       = new DbgButton(BarIcon("dbgnext"),       i18n("Step Over"),  this);
    TQPushButton*  bStep       = new DbgButton(BarIcon("dbgstep"),       i18n("Step Into"),    this);
    TQPushButton*  bFinish     = new DbgButton(BarIcon("dbgstepout"),    i18n("Step Out"),   this);
    TQPushButton*  bRunTo      = new DbgButton(BarIcon("dbgrunto"),      i18n("Run to Cursor"),   this);
    bPrevFocus_ = new DbgButton(BarIcon("dbgmemview"),    TQString(),      this);
    bKDevFocus_ = new DbgButton(BarIcon("tdevelop"),      TQString(),      this);

  connect(bRun,        TQ_SIGNAL(clicked()), part_,  TQ_SLOT(slotRun()));
  connect(bInterrupt,  TQ_SIGNAL(clicked()), part_,  TQ_SLOT(slotPause()));
  connect(bNext,       TQ_SIGNAL(clicked()), part_,  TQ_SLOT(slotStepOver()));
  connect(bStep,       TQ_SIGNAL(clicked()), part_,  TQ_SLOT(slotStepInto()));
  connect(bFinish,     TQ_SIGNAL(clicked()), part_,  TQ_SLOT(slotStepOut()));
  connect(bRunTo,      TQ_SIGNAL(clicked()), part_,  TQ_SLOT(slotRunToCursor()));
  connect(bKDevFocus_, TQ_SIGNAL(clicked()), this,   TQ_SLOT(slotKdevFocus()));
  connect(bPrevFocus_, TQ_SIGNAL(clicked()), this,   TQ_SLOT(slotPrevFocus()));

    TQToolTip::add( bRun,        i18n("Continue with application execution, may start the application") );
    TQToolTip::add( bInterrupt,  i18n("Interrupt the application execution") );
    TQToolTip::add( bNext,       i18n("Execute one line of code, but run through methods") );
    TQToolTip::add( bStep,       i18n("Execute one line of code, stepping into methods if appropriate") );
    TQToolTip::add( bFinish,     i18n("Execute to end of current stack frame") );
    TQToolTip::add( bRunTo,      i18n("Continues execution until the cursor position is reached.") );
    TQToolTip::add( bKDevFocus_, i18n("Set focus on TDevelop") );
    TQToolTip::add( bPrevFocus_, i18n("Set focus on window that had focus when TDevelop got focus") );

    TQWhatsThis::add( bRun,        i18n("Continue with application execution. May start the application.") );
    TQWhatsThis::add( bInterrupt,  i18n("Interrupt the application execution.") );
    TQWhatsThis::add( bNext,       i18n("Execute one line of code, but run through methods.") );

    TQWhatsThis::add( bStep,       i18n("Execute one line of code, stepping into methods if appropriate.") );
    
    TQWhatsThis::add( bFinish,     i18n("Execute to end of current stack frame.") );
    TQWhatsThis::add( bRunTo,      i18n("Continues execution until the cursor position is reached.") );
    TQWhatsThis::add( bKDevFocus_, i18n("Set focus on TDevelop.") );
    TQWhatsThis::add( bPrevFocus_, i18n("Set focus on window that had focus when TDevelop got focus.") );

    topLayout->addWidget(moveHandle);
    topLayout->addWidget(bRun);
    topLayout->addLayout(nextLayout);
    topLayout->addLayout(stepLayout);
    topLayout->addWidget(bFinish);
    topLayout->addWidget(bRunTo);
    topLayout->addWidget(bInterrupt);
    topLayout->addLayout(focusLayout);

    focusLayout->addWidget(bKDevFocus_);
    focusLayout->addWidget(bPrevFocus_);

    stepLayout->addWidget(bStep);

    nextLayout->addWidget(bNext);

//     int w = TQMAX(bRun->sizeHint().width(), bFinish->sizeHint().width());
//     w = TQMAX(w, bInterrupt->sizeHint().width());
//     w = TQMAX(w, bView->sizeHint().width());

    // they should have the same height, so don't be too fussy
//     int h = bFinish->sizeHint().height();
//
//     bNext->setMinimumHeight(h);
//     bNexti->setMinimumHeight(h);
//     bStep->setMinimumHeight(h);
//     bStepi->setMinimumHeight(h);
//     bKDevFocus_->setMinimumHeight(h);
//     bPrevFocus_->setMinimumHeight(h);

//    setMinimumSize(w+10, h*7);
//    setMaximumSize(w+10, h*7);

    setAppIndicator(appIsActive_);
    topLayout->activate();
}

// **************************************************************************

DbgToolBar::~DbgToolBar()
{
    slotUndock();
}

// **************************************************************************

void DbgToolBar::slotKdevFocus()
{
    // I really want to be able to set the focus on the _application_ being debugged
    // but this is the best compromise I can come up with. All we do is save the
    // window that had focus when they switch to the tdevelop window. To do this
    // the toolbar _cannot_ accept focus.
    // If anyone has a way of determining what window the app is _actually_ running on
    // then please fix and send a patch.

    if (winModule_->activeWindow() != topLevelWidget()->winId())
        activeWindow_ = winModule_->activeWindow();

    KWin::activateWindow(topLevelWidget()->winId());
}

// **************************************************************************

void DbgToolBar::slotPrevFocus()
{
    KWin::activateWindow(activeWindow_);
}

// **************************************************************************

// If the app is active then the app button is highlighted, otherwise
// kdev button is highlighted.
void DbgToolBar::slotDbgStatus(const TQString&, int state)
{
    bool appIndicator = state & s_appBusy;
    if (appIndicator != appIsActive_) {
        setAppIndicator(appIndicator);
        appIsActive_ = appIndicator;
    }
}

// **************************************************************************

void DbgToolBar::setAppIndicator(bool appIndicator)
{
    if (appIndicator) {
        bPrevFocus_->setPalette(TQPalette(colorGroup().mid()));
        bKDevFocus_->setPalette(TQPalette(colorGroup().background()));
    } else {
        bPrevFocus_->setPalette(TQPalette(colorGroup().background()));
        bKDevFocus_->setPalette(TQPalette(colorGroup().mid()));
    }
}

// **************************************************************************

void DbgToolBar::slotDock()
{
    if (docked_)
        return;

    //  Q_ASSERT(!docker_);
    hide();

    docker_->show();
    docked_ = true;
}

// **************************************************************************

void DbgToolBar::slotIconifyAndDock()
{
    if (docked_)
        return;

    //  KWin::iconifyWindow(ckDevelop_->winId(), true);
    slotDock();
}

// **************************************************************************

void DbgToolBar::slotUndock()
{
    if (!docked_)
        return;

    show();
    docker_->hide();
    docked_ = false;
}

// **************************************************************************

void DbgToolBar::slotActivateAndUndock()
{
    if (!docked_)
        return;

    KWin::activateWindow(topLevelWidget()->winId());
    slotUndock();
}

}

// **************************************************************************
#include "dbgtoolbar.moc"
