/*-*-c++-*-
 * $Id: cdumpview.cpp,v 1.3 2002/04/24 10:21:26 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 "osdep.h"

#include "cdumpview.h"
#include "cdumpviewdisplay.h"
#include "qlcrash.h"
#include "qlcrashdoc.h"
#include "cmainwin.h"
#include "ctoolbutton.h"
#include "cconfigmanager.h"

#include <qtoolbar.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qtoolbutton.h>
#include <qstatusbar.h>
#include <qpushbutton.h>
#include <qcombobox.h>
#include <qpopupmenu.h>
#include <qworkspace.h>

#include <limits.h>
#include <stdio.h>
#include <stdlib.h> // strtoull()
#include <math.h> // pow()

// vertical navbar
#include "fastuparrow.xpm"
#include "slowuparrow.xpm"
#include "slowdownarrow.xpm"
#include "fastdownarrow.xpm"

// horizontal navbar
#include "fastleftarrow.xpm"
#include "slowleftarrow.xpm"
#include "slowrightarrow.xpm"
#include "fastrightarrow.xpm"
#include "prev.xpm"
#include "next.xpm"

CDumpView::CDumpView(UINT64 addr, QLcrashApp* app, QWidget *parent, const char *name, WFlags f)
	: QMainWindow(parent, name, f)
	, oApp(app)
{
	oPrevList.setAutoDelete(true);
	oNextList.setAutoDelete(true);
	oMainbar = 0;
	oNavbar = 0;
	
	oView = new CDumpViewDisplay(app, this);
	
	QPoint p(0, 0);
	CMainWin* mw = dynamic_cast<CMainWin*>(app->centralWidget());
	mw->restoreChildren();
	reparent(mw->workspace(), p, true);
#ifdef WIN32
	QFrame* cf = (QFrame*)parentWidget();
#else
	QFrame* cf = dynamic_cast<QFrame*>(parentWidget());
#endif
	cf->setFrameStyle(QFrame::Box | QFrame::Raised);
	cf->setLineWidth(2);
	cf->setMidLineWidth(1);
	cf->resize(500, 400);
	
	initToolbar();
	setCentralWidget(oView);
		
	connect(oView, SIGNAL(sigMessage(QString, int)), SLOT(slotStatusText(QString, int)));
	connect(oView, SIGNAL(sigAddrJump()), SLOT(slotAddrJump()));
	connect(oPrevMenu, SIGNAL(activated(int)), SLOT(slotPrevMenu(int)));
	connect(oNextMenu, SIGNAL(activated(int)), SLOT(slotNextMenu(int)));
	
	char buf[30];
#ifdef WIN32
	sprintf(buf, "%I64x", addr);
#else
	sprintf(buf, "%qx", addr);
#endif
	oCurAddr = addr;
	oAddress->setText(buf);
	oView->displayAddress(addr);
	setCaption(tr("Dump view"));
	updateCaption();
}

CDumpView::~CDumpView()
{
	delete oMainbar;
	delete oNavbar;
}

void
CDumpView::resizeEvent(QResizeEvent*)
{
/*
	int w = width();
	int h = height();
	oView->setGeometry(0, 0, w, h);
*/
}

QSize
CDumpView::sizeHint() const
{
	return QSize();
}

bool
CDumpView::eventFilter(QObject* obj, QEvent* e)
{
	if (obj == this && hasFocus() && e->type() == QEvent::KeyPress) {
		QKeyEvent* ke = static_cast<QKeyEvent*>(e);
		switch (ke->key()) {
			case Key_Up:
				oSlowUp->animateClick();
				break;
			case Key_Down:
				oSlowDown->animateClick();
				break;
			case Key_PageUp:
				oFastUp->animateClick();
				break;
			case Key_PageDown:
				oFastDown->animateClick();
				break;
		}
	}	
	
	return false;
}

void
CDumpView::initToolbar()
{
	if (oMainbar != 0) {
		return;
	}
	
	QLcrashDoc* doc = oApp->document();
	
	// determine toolbar position
	CConfigManager* cm = doc->configManager();
	bool dockMain = (bool) cm->item(CFG_GRP_DUMP, CFG_ATT_DUMP_TBDOCKMAIN, 0);
	QMainWindow* toolbarParent = (
		dockMain
		? static_cast<QMainWindow*>(oApp)
		: static_cast<QMainWindow*>(this)
	);
	
	oMainbar = new QToolBar(toolbarParent);
	oMainbar->show();
	oNavbar = new QToolBar(toolbarParent);
	toolbarParent->moveToolBar(oNavbar, QMainWindow::Top);
	oNavbar->show();
	
	oPrevMenu = new QPopupMenu(this);
	oNextMenu = new QPopupMenu(this);
	
	// create widgets for main tool bar
	
	QLabel* lbl = new QLabel(tr("Address") + ":", oMainbar);
	lbl->setAutoResize(true);
	lbl->update();
	oAddress = new QLineEdit(oMainbar);	
	oAddress->setFixedWidth(oAddress->width());
	oAddress->setMaxLength(2 * oApp->document()->targetWordSize());
	lbl->setBuddy(oAddress);
	
	oAddrPrev = new CToolButton(
		QPixmap(prev_xpm),
		tr("Previous"),
		QString::null,
		this,
		SLOT(slotAddrPrev()),
		oMainbar
	);
	oAddrPrev->setAutoRepeat(false);
	oAddrPrev->setEnabled(false);
	oAddrPrev->setPopup(oPrevMenu);
	
	oAddrNext = new CToolButton(
		QPixmap(next_xpm),
		tr("Next"),
		QString::null,
		this,
		SLOT(slotAddrNext()),
		oMainbar
	);
	oAddrNext->setAutoRepeat(false);
	oAddrNext->setEnabled(false);
	oAddrNext->setPopup(oNextMenu);
	
	oMainbar->addSeparator();
	
	lbl = new QLabel(tr("Format") + ":", oMainbar);
	lbl->setAutoResize(true);
	lbl->update();
	oDumpFormat = new QComboBox(oMainbar);
	oDumpFormat->insertItem(tr("Word"));
	oDumpFormat->insertItem(tr("Double word"));
	oDumpFormat->insertItem(tr("Half word"));
	oDumpFormat->insertItem(tr("Byte"));
	oDumpFormat->setFixedWidth(oDumpFormat->width());
	lbl->setBuddy(oDumpFormat);
	
	oMainbar->addSeparator();
	
	lbl = new QLabel(tr("Type") + ":", oMainbar);
	lbl->setAutoResize(true);
	lbl->update();
	oDumpType = new QComboBox(oMainbar);
	oDumpType->insertItem(tr("Hex"));
	oDumpType->insertItem(tr("Decimal"));
	oDumpType->insertItem(tr("Octal"));
	oDumpType->setFixedWidth(oDumpType->width());
	lbl->setBuddy(oDumpType);
	
	oMainbar->addSeparator();
	
	lbl = new QLabel(tr("Byteorder") + ":", oMainbar);
	lbl->setAutoResize(true);
	lbl->update();
	oDumpEndian = new QComboBox(oMainbar);
	oDumpEndian->insertItem("LE");
	oDumpEndian->insertItem("BE");
	lbl->setBuddy(oDumpEndian);
	
	// create widgets for navigator bar
	
	oFastUp = new QToolButton(
		QPixmap(fastleftarrow_xpm),
		tr("Page up"),
		QString::null,
		this,
		SLOT(slotFastDown()),
		oNavbar
	);
	oSlowUp = new QToolButton(
		QPixmap(slowleftarrow_xpm),
		tr("Line up"),
		QString::null,
		this,
		SLOT(slotSlowDown()),
		oNavbar
	);
	oSlowDown = new QToolButton(
		QPixmap(slowrightarrow_xpm),
		tr("Line down"),
		QString::null,
		this,
		SLOT(slotSlowUp()),
		oNavbar
	);
	oFastDown = new QToolButton(
		QPixmap(fastrightarrow_xpm),
		tr("Page down"),
		QString::null,
		this,
		SLOT(slotFastUp()),
		oNavbar
	);
	
	oFastUp->setAutoRepeat(true);
	oSlowUp->setAutoRepeat(true);
	oSlowDown->setAutoRepeat(true);
	oFastDown->setAutoRepeat(true);
	
	connect(oNavbar, SIGNAL(orientationChanged(Orientation)), SLOT(slotNavOrientationChanged(Orientation)));
	connect(oAddress, SIGNAL(returnPressed()), SLOT(slotAddressChanged()));
	connect(oAddrPrev, SIGNAL(sigActivated(int)), SLOT(slotPrevMenu(int)));
	connect(oAddrNext, SIGNAL(sigActivated(int)), SLOT(slotNextMenu(int)));
	connect(oDumpFormat, SIGNAL(activated(int)), oView, SLOT(slotDisplayFormat(int)));
	connect(oDumpType, SIGNAL(activated(int)), oView, SLOT(slotDisplayType(int)));
	connect(oDumpEndian, SIGNAL(activated(int)), oView, SLOT(slotDisplayByteOrder(int)));
	
	oDumpEndian->setCurrentItem(doc->targetLittleEndian() ? 0 : 1);
	oView->slotDisplayByteOrder(doc->targetLittleEndian() ? 0 : 1);
	
	if (dockMain) {
		oApp->showToolbar(oMainbar, true);
		oApp->showToolbar(oNavbar, false);
	}
}

void
CDumpView::slotFastUp()
{
	UINT64 addr = oView->address();
	if (addr < ULONG_LONG_MAX) {
		char buf[30];
		addr += 10 * 16L;
		if (addr > ULONG_LONG_MAX) {
			addr = ULONG_LONG_MAX;
		}
		
		oView->displayAddress(addr);
#ifdef WIN32
		sprintf(buf, "%I64x", addr);
#else
		sprintf(buf, "%qx", addr);
#endif
		oAddress->setText(buf);
		updateCaption();
	}
}

void
CDumpView::slotSlowUp()
{
	UINT64 addr = oView->address();
	if (addr < ULONG_LONG_MAX) {
		addr += 1 * 16L;
		if (addr > ULONG_LONG_MAX) {
			addr = ULONG_LONG_MAX;
		}
		
		char buf[30];
#ifdef WIN32
		sprintf(buf, "%I64x", addr);
#else
		sprintf(buf, "%qx", addr);
#endif
		oView->displayAddress(addr);
		oAddress->setText(buf);
		updateCaption();
	}
}

void
CDumpView::slotSlowDown()
{
	UINT64 addr = oView->address();
	UINT64 nAddr;
	if (addr > 0L) {
		nAddr = addr - 1 * 16L;
		if (nAddr > addr) {
			nAddr = 0L;
		}
		
		char buf[30];
#ifdef WIN32
		sprintf(buf, "%I64x", nAddr);
#else
		sprintf(buf, "%qx", nAddr);
#endif
		oView->displayAddress(nAddr);
		oAddress->setText(buf);
		updateCaption();
	}
}

void
CDumpView::slotFastDown()
{
	UINT64 addr = oView->address();
	UINT64 nAddr;
	if (addr > 0L) {
		nAddr = addr - 10 * 16L;
		if (nAddr > addr) {
			nAddr = 0L;
		}
		
		char buf[30];
#ifdef WIN32
		sprintf(buf, "%I64x", nAddr);
#else
		sprintf(buf, "%qx", nAddr);
#endif
		oView->displayAddress(nAddr);
		oAddress->setText(buf);
		updateCaption();
	}
}

void
CDumpView::slotNavOrientationChanged(Qt::Orientation orient)
{
	QPixmap pm;
	
	if (orient == Vertical) {
		pm = QPixmap(fastuparrow_xpm);
		oFastUp->setPixmap(pm);
		pm = QPixmap(slowuparrow_xpm);
		oSlowUp->setPixmap(pm);
		pm = QPixmap(slowdownarrow_xpm);
		oSlowDown->setPixmap(pm);
		pm = QPixmap(fastdownarrow_xpm);
		oFastDown->setPixmap(pm);
	}
	else {
		pm = QPixmap(fastleftarrow_xpm);
		oFastUp->setPixmap(pm);
		pm = QPixmap(slowleftarrow_xpm);
		oSlowUp->setPixmap(pm);
		pm = QPixmap(slowrightarrow_xpm);
		oSlowDown->setPixmap(pm);
		pm = QPixmap(fastrightarrow_xpm);
		oFastDown->setPixmap(pm);
	}
}

void
CDumpView::slotClose()
{
	delete this;
}

void
CDumpView::slotAddressChanged()
{
	char* eptr;
	QString str = oAddress->text();
	const char* cstr = str.latin1();
	UINT64 addr = strtoull(cstr, &eptr, 16);
	
	if (str.length() > 0 && *eptr == '\0' && addr < ((unsigned long)-1)) {
		addAddress(addr);
		oView->displayAddress(addr);
		updateCaption();
		// get rid off the focus
		oView->setFocus();
	}
	else {
		oApp->statusBar()->message(tr("Invalid address"), 3000);
	}
}

void
CDumpView::slotStatusText(QString str, int time)
{
	if (time > 0) {
		oApp->statusBar()->message(str, time);
	}
	else {
		oApp->statusBar()->message(str);
	}
}

void
CDumpView::slotAddrPrev()
{
	if (oPrevList.count() > 0) {
		char buf[30];
#ifdef WIN32
		sprintf(buf, "%I64x", oCurAddr);
#else
		sprintf(buf, "%qx", oCurAddr);
#endif
		oNextList.prepend(new QString(buf));
		oCurAddr = strtoull(oPrevList.first()->latin1(), 0, 16);
		oPrevList.removeFirst();
#ifdef WIN32
		sprintf(buf, "%I64x", oCurAddr);
#else
		sprintf(buf, "%qx", oCurAddr);
#endif
		oAddress->setText(buf);
		oView->displayAddress(oCurAddr);
		
		createHistoryPopup();
		oAddress->setText(buf);
		updateCaption();
	}
}

void
CDumpView::slotAddrNext()
{
	if (oNextList.count() > 0) {
		char buf[30];
#ifdef WIN32
		sprintf(buf, "%I64x", oCurAddr);
#else
		sprintf(buf, "%qx", oCurAddr);
#endif
		oPrevList.prepend(new QString(buf));
		oCurAddr = strtoull(oNextList.first()->latin1(), 0, 16);
		oNextList.removeFirst();
#ifdef WIN32
		sprintf(buf, "%I64x", oCurAddr);
#else
		sprintf(buf, "%qx", oCurAddr);
#endif
		oAddress->setText(buf);
		oView->displayAddress(oCurAddr);
		
		createHistoryPopup();
		oAddress->setText(buf);
		updateCaption();
	}
}

void
CDumpView::addAddress(UINT64 addr)
{
	char buf[30];
#ifdef WIN32
	sprintf(buf, "%I64x", oCurAddr);
#else
	sprintf(buf, "%qx", oCurAddr);
#endif
	QString str(buf);
	
	oAddrPrev->setEnabled(true);
	oPrevList.prepend(new QString(str));
	oCurAddr = addr;
	
	createHistoryPopup();
}

void
CDumpView::slotAddrJump()
{
	char buf[30];
	addAddress(oView->address());
#ifdef WIN32
	sprintf(buf, "%I64x", oView->address());
#else
	sprintf(buf, "%qx", oView->address());
#endif
	oAddress->setText(buf);
	updateCaption();
}

void
CDumpView::slotPrevMenu(int id)
{
	QString str = oPrevMenu->text(id);
	UINT64 addr = strtoull(str.latin1(), 0, 16);
	char buf[30];
#ifdef WIN32
	sprintf(buf, "%I64x", oCurAddr);
#else
	sprintf(buf, "%qx", oCurAddr);
#endif
	oNextList.prepend(new QString(buf));
	oCurAddr = addr;
	
	QString* walker = oPrevList.first();
	while (walker != 0) {
		if (*walker == str) {
			oPrevList.remove();
			break;
		}
		walker = oPrevList.next();
	}
	
	createHistoryPopup();
	oView->displayAddress(addr);
#ifdef WIN32
	sprintf(buf, "%I64x", oCurAddr);
#else
	sprintf(buf, "%qx", oCurAddr);
#endif
	oAddress->setText(buf);
	updateCaption();
}

void
CDumpView::slotNextMenu(int id)
{
	QString str = oNextMenu->text(id);
	UINT64 addr = strtoull(str.latin1(), 0, 16);
	char buf[30];
#ifdef WIN32
	sprintf(buf, "%I64x", oCurAddr);
#else
	sprintf(buf, "%qx", oCurAddr);
#endif
	oPrevList.prepend(new QString(buf));
	oCurAddr = addr;
	
	QString* walker = oNextList.first();
	while (walker != 0) {
		if (*walker == str) {
			oNextList.remove();
			break;
		}
		walker = oNextList.next();
	}
	
	createHistoryPopup();
	oView->displayAddress(addr);
#ifdef WIN32
	sprintf(buf, "%I64x", oCurAddr);
#else
	sprintf(buf, "%qx", oCurAddr);
#endif
	oAddress->setText(buf);
	updateCaption();
}

void
CDumpView::createHistoryPopup()
{
	QPopupMenu* menu = new QPopupMenu(this);
	QString* str = oPrevList.first();
	int i = 1;
	while (str != 0) {
		menu->insertItem(*str, i++);
		str = oPrevList.next();
	}
	delete oPrevMenu;
	oPrevMenu = menu;
	oAddrPrev->setPopup(menu);
	
	menu = new QPopupMenu(this);
	str = oNextList.first();
	i = 1;
	while (str != 0) {
		menu->insertItem(*str, i++);
		str = oNextList.next();
	}
	delete oNextMenu;
	oNextMenu = menu;
	oAddrNext->setPopup(menu);
	
	oAddrPrev->setEnabled(oPrevList.count() ? true : false);
	oAddrNext->setEnabled(oNextList.count() ? true : false);
}

void
CDumpView::enableToolbar(bool w)
{
	// ignore invocation if our toolbars are docked in this window
	if (oMainbar && oMainbar->parentWidget() == oApp) {
		if (w) {
			oApp->showToolbar(oMainbar, true);
			oApp->showToolbar(oNavbar, false);
		}
		else {
			oApp->hideToolbar(oMainbar, true);
			oApp->hideToolbar(oNavbar, false);
		}
	}
}

void
CDumpView::updateCaption()
{
	return;
	setCaption(tr("Dump view") + " : " + oView->caption());
}
