/*
 * Copyright Johannes Sixt
 * This file is licensed under the GNU General Public License Version 2.
 * See the file COPYING in the toplevel directory of the source directory.
 */

#include "winstack.h"
#include "sourcewnd.h"
#include <tqbrush.h>
#include <tqfileinfo.h>
#include <tqpopupmenu.h>
#include <tdeapplication.h>
#include <tdemainwindow.h>
#include <tdelocale.h>			/* i18n */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "mydebug.h"



WinStack::WinStack(TQWidget* parent, const char* name) :
	KTabWidget(parent, name),
	m_pcLine(-1),
	m_valueTip(this),
	m_tipLocation(1,1,10,10),
	m_tabWidth(0)
{
    connect(&m_findDlg.m_buttonForward,
	    TQ_SIGNAL(clicked()), TQ_SLOT(slotFindForward()));
    connect(&m_findDlg.m_buttonBackward,
	    TQ_SIGNAL(clicked()), TQ_SLOT(slotFindBackward()));

    connect(this, TQ_SIGNAL(setTabWidth(int)), this, TQ_SLOT(slotSetTabWidth(int)));
}

WinStack::~WinStack()
{
}

void WinStack::contextMenuEvent(TQContextMenuEvent* e)
{
    // get the context menu from the GUI factory
    TQWidget* top = this;
    do
	top = top->parentWidget();
    while (!top->isTopLevel());
    TDEMainWindow* mw = static_cast<TDEMainWindow*>(top);
    TQPopupMenu* m =
	static_cast<TQPopupMenu*>(mw->factory()->container("popup_files_empty", mw));
    m->exec(e->globalPos());
}


void WinStack::reloadAllFiles()
{
    for (int i = count()-1; i >= 0; i--) {
	windowAt(i)->reloadFile();
    }
}

TQSize WinStack::sizeHint() const
{
    return TQSize(640, 480);
}

void WinStack::activate(const TQString& fileName, int lineNo, const DbgAddr& address)
{
    TQFileInfo fi(fileName);

    if (!fi.isFile()) {
	/*
	 * We didn't find that file. Now check if it is a relative path and
	 * try m_lastOpenDir as prefix.
	 */
	TRACE(fi.filePath() + (" not found, looking in " + m_lastOpenDir));
	if (!fi.isRelative() || m_lastOpenDir.isEmpty()) {
	    return;
	}
	fi.setFile(m_lastOpenDir + "/" + fi.filePath());
	if (!fi.isFile()) {
	    return;
	}
    }
    // if this is not an absolute path name, make it one
    activatePath(fi.absFilePath(), lineNo, address);
}

void WinStack::activateFile(const TQString& fileName)
{
    activatePath(fileName, 0, DbgAddr());
}

bool WinStack::activatePath(TQString pathName, int lineNo, const DbgAddr& address)
{
    // check whether the file is already open
    SourceWindow* fw = 0;
    for (int i = count()-1; i >= 0; i--) {
	if (windowAt(i)->fileName() == pathName) {
	    fw = windowAt(i);
	    break;
	}
    }
    if (fw == 0) {
	// not found, load it
	fw = new SourceWindow(pathName, this, "fileWindow");

	// slurp the file in
	if (!fw->loadFile()) {
	    // read failed
	    delete fw;
	    return false;
	}

	addTab(fw, TQFileInfo(pathName).fileName());
	setTabToolTip(fw, pathName);

	connect(fw, TQ_SIGNAL(clickedLeft(const TQString&,int,const DbgAddr&,bool)),
		TQ_SIGNAL(toggleBreak(const TQString&,int,const DbgAddr&,bool)));
	connect(fw, TQ_SIGNAL(clickedMid(const TQString&,int,const DbgAddr&)),
		TQ_SIGNAL(enadisBreak(const TQString&,int,const DbgAddr&)));

	// disassemble code
	connect(fw, TQ_SIGNAL(disassemble(const TQString&, int)),
		TQ_SIGNAL(disassemble(const TQString&, int)));
	connect(fw, TQ_SIGNAL(expanded(int)), TQ_SLOT(slotExpandCollapse(int)));
	connect(fw, TQ_SIGNAL(collapsed(int)), TQ_SLOT(slotExpandCollapse(int)));

	// tab width
	connect(this, TQ_SIGNAL(setTabWidth(int)), fw, TQ_SLOT(setTabWidth(int)));
	fw->setTabWidth(m_tabWidth);
	fw->setFocusPolicy(TQWidget::WheelFocus);

	// set PC if there is one
	emit newFileLoaded();
	if (m_pcLine >= 0) {
	    setPC(true, m_pcFile, m_pcLine, DbgAddr(m_pcAddress), m_pcFrame);
	}
    }
    return activateWindow(fw, lineNo, address);
}

bool WinStack::activateWindow(SourceWindow* fw, int lineNo, const DbgAddr& address)
{
    // make the line visible
    if (lineNo >= 0) {
	fw->scrollTo(lineNo, address);
    }

    showPage(fw);
    fw->setFocus();

    return true;
}

bool WinStack::activeLine(TQString& fileName, int& lineNo)
{
    DbgAddr dummy;
    return activeLine(fileName, lineNo, dummy);
}

bool WinStack::activeLine(TQString& fileName, int& lineNo, DbgAddr& address)
{
    if (activeWindow() == 0) {
	return false;
    }
    
    fileName = activeFileName();
    activeWindow()->activeLine(lineNo, address);
    return true;
}

void WinStack::updateLineItems(const KDebugger* dbg)
{
    for (int i = count()-1; i >= 0; i--) {
	windowAt(i)->updateLineItems(dbg);
    }
}

void WinStack::updatePC(const TQString& fileName, int lineNo, const DbgAddr& address, int frameNo)
{
    if (m_pcLine >= 0) {
	setPC(false, m_pcFile, m_pcLine, DbgAddr(m_pcAddress), m_pcFrame);
    }
    m_pcFile = fileName;
    m_pcLine = lineNo;
    m_pcAddress = address.asString();
    m_pcFrame = frameNo;
    if (lineNo >= 0) {
	setPC(true, fileName, lineNo, address, frameNo);
    }
}

SourceWindow* WinStack::findByFileName(const TQString& fileName) const
{
    for (int i = count()-1; i >= 0; i--) {
	if (windowAt(i)->fileNameMatches(fileName)) {
	    return windowAt(i);
	}
    }
    return 0;
}

void WinStack::setPC(bool set, const TQString& fileName, int lineNo,
		     const DbgAddr& address, int frameNo)
{
    TRACE((set ? "set PC: " : "clear PC: ") + fileName +
	  TQString().sprintf(":%d#%d ", lineNo, frameNo) + address.asString());
    SourceWindow* fw = findByFileName(fileName);
    if (fw)
	fw->setPC(set, lineNo, address, frameNo);
}

SourceWindow* WinStack::windowAt(int i) const
{
    return static_cast<SourceWindow*>(page(i));
}

SourceWindow* WinStack::activeWindow() const
{
    return static_cast<SourceWindow*>(currentPage());
}

TQString WinStack::activeFileName() const
{
    TQString f;
    if (activeWindow() != 0)
	f = activeWindow()->fileName();
    return f;
}

void WinStack::slotFindForward()
{
    if (activeWindow() != 0)
	activeWindow()->find(m_findDlg.searchText(), m_findDlg.caseSensitive(),
			     SourceWindow::findForward);
}

void WinStack::slotFindBackward()
{
    if (activeWindow() != 0)
	activeWindow()->find(m_findDlg.searchText(), m_findDlg.caseSensitive(),
			     SourceWindow::findBackward);
}

void WinStack::maybeTip(const TQPoint& p)
{
    SourceWindow* w = activeWindow();
    if (w == 0)
	return;

    // get the word at the point
    TQString word;
    TQRect r;
    if (!w->wordAtPoint(w->mapFrom(this, p), word, r))
	return;

    // must be valid
    assert(!word.isEmpty());
    assert(r.isValid());

    // remember the location
    m_tipLocation = TQRect(w->mapTo(this, r.topLeft()), r.size());

    emit initiateValuePopup(word);
}

void WinStack::slotShowValueTip(const TQString& tipText)
{
    m_valueTip.tip(m_tipLocation, tipText);
}

void WinStack::slotDisassembled(const TQString& fileName, int lineNo,
				const std::list<DisassembledCode>& disass)
{
    SourceWindow* fw = findByFileName(fileName);
    if (fw == 0) {
	// not found: ignore
	return;
    }

    fw->disassembled(lineNo, disass);
}

void WinStack::slotExpandCollapse(int)
{
    // update line items after expanding or collapsing disassembled code

    // HACK: we know that this will result in updateLineItems
    // should be done more cleanly with a separate signal
    emit newFileLoaded();

    if (m_pcLine >= 0) {
	setPC(true, m_pcFile, m_pcLine, DbgAddr(m_pcAddress), m_pcFrame);
    }
}


void WinStack::slotSetTabWidth(int numChars)
{
    m_tabWidth = numChars;
}

void WinStack::slotFileReload()
{
    if (activeWindow() != 0) {
	TRACE("reloading one file");
	activeWindow()->reloadFile();
    }
}

void WinStack::slotViewFind()
{
    if (m_findDlg.isVisible()) {
	m_findDlg.done(0);
    } else {
	m_findDlg.show();
    }
}

void WinStack::slotBrkptSet()
{
    TQString file;
    int lineNo;
    DbgAddr address;
    if (activeLine(file, lineNo, address))
	emit toggleBreak(file, lineNo, address, false);
}

void WinStack::slotBrkptSetTemp()
{
    TQString file;
    int lineNo;
    DbgAddr address;
    if (activeLine(file, lineNo, address))
	emit toggleBreak(file, lineNo, address, true);
}

void WinStack::slotBrkptEnable()
{
    TQString file;
    int lineNo;
    DbgAddr address;
    if (activeLine(file, lineNo, address))
	emit enadisBreak(file, lineNo, address);
}

void WinStack::slotMoveProgramCounter()
{
    TQString file;
    int lineNo;
    DbgAddr address;
    if (activeLine(file, lineNo, address))
	emit moveProgramCounter(file, lineNo, address);
}

void WinStack::slotClose()
{
    TQWidget* w = activeWindow();
    if (!w)
	return;

    removePage(w);
    delete w;
}


ValueTip::ValueTip(WinStack* parent) :
	TQToolTip(parent)
{
}

void ValueTip::maybeTip(const TQPoint& p)
{
    WinStack* w = static_cast<WinStack*>(parentWidget());
    w->maybeTip(p);
}


FindDialog::FindDialog() :
	TQDialog(0, "find", false),
	m_searchText(this, "text"),
	m_caseCheck(this, "case"),
	m_buttonForward(this, "forward"),
	m_buttonBackward(this, "backward"),
	m_buttonClose(this, "close"),
	m_layout(this, 8),
	m_buttons(4)
{
    setCaption(TQString(kapp->caption()) + i18n(": Search"));

    m_searchText.setMinimumSize(330, 24);
    m_searchText.setMaxLength(10000);
    m_searchText.setFrame(true);

    m_caseCheck.setText(i18n("&Case sensitive"));
    m_caseCheck.setChecked(true);
    m_buttonForward.setText(i18n("&Forward"));
    m_buttonForward.setDefault(true);
    m_buttonBackward.setText(i18n("&Backward"));
    m_buttonClose.setText(i18n("Close"));

    m_caseCheck.setMinimumSize(330, 24);

    // get maximum size of buttons
    TQSize maxSize(80,30);
    maxSize.expandedTo(m_buttonForward.sizeHint());
    maxSize.expandedTo(m_buttonBackward.sizeHint());
    maxSize.expandedTo(m_buttonClose.sizeHint());

    m_buttonForward.setMinimumSize(maxSize);
    m_buttonBackward.setMinimumSize(maxSize);
    m_buttonClose.setMinimumSize(maxSize);

    connect(&m_buttonClose, TQ_SIGNAL(clicked()), TQ_SLOT(reject()));

    m_layout.addWidget(&m_searchText);
    m_layout.addWidget(&m_caseCheck);
    m_layout.addLayout(&m_buttons);
    m_layout.addStretch(10);
    m_buttons.addWidget(&m_buttonForward);
    m_buttons.addStretch(10);
    m_buttons.addWidget(&m_buttonBackward);
    m_buttons.addStretch(10);
    m_buttons.addWidget(&m_buttonClose);

    m_layout.activate();

    m_searchText.setFocus();
    resize( 350, 120 );
}

FindDialog::~FindDialog()
{
}

void FindDialog::closeEvent(TQCloseEvent* ev)
{
    TQDialog::closeEvent(ev);
    emit closed();
}

void FindDialog::done(int result)
{
    TQDialog::done(result);
    emit closed();
}

#include "winstack.moc"
