/*-*-c++-*-
 * $Id: cmainwin.cpp,v 1.2 2002/03/05 14:47:14 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 "cmainwin.h"
#include "cconfigmanager.h"
#include "cgraphmanager.h"
#include "cgraphobject.h"
#include "structviewitem.h"
#include "clistview.h"
#include "clistviewitem.h"
#include "qlcrashdoc.h"
#include "cnumberofelementsdialog.h"
#include "cdumpview.h"
#include "qlcrash.h"
#include "csearchlistdialog.h"
#include "cprintexprdialog.h"
#include "cwalkexpressiondialog.h"
#include "ctrace.h"
#include "ctracelowcore.h"

#include "cs390dbf.h"

#include <qapplication.h>
#include <qclipboard.h>
#include <qsplitter.h>
#include <qworkspace.h>
#include <qvaluelist.h>
#include <qpopupmenu.h>
#include <qwidgetlist.h>
#include <qmessagebox.h>
#include <qheader.h>

#include <assert.h>
#include <ctype.h>
#include <stdlib.h>

// ID's for struct view popup
#define SV_POPUP_TYPE		1
#define SV_POPUP_WHATIS		2
#define SV_POPUP_PRINT		3
#define SV_POPUP_WALK		4
#define SV_POPUP_PRINTARRAY	5
#define SV_POPUP_DUMP		6

// ID's for task list popup
#define TL_POPUP_TRACE		1
#define TL_POPUP_DUMP		2
#define TL_POPUP_PRINT		3

// ID's for symbol list popup
#define SL_POPUP_PRINT		1
#define SL_POPUP_WALK		2

// Column containing the process state in task list
#define TL_COL_WITH_CPU		6

// Column containing the process name
#define TL_COL_NAME			7

// Default sorting column in task list
#define TL_COL_SORTING		2

// Symbol list
#define SL_COL_ADDRESS		2

CMainWin::CMainWin(QLcrashApp* app, const char *name )
	: QWidget(app, name)
	, oTrace(0)
	, oTraceLowcore(0)
	, oSymtabList(0)
{
	oDoc = app->document();
	oConsoleList.setAutoDelete(true);
	oSplitter = new QSplitter(Qt::Vertical, this);
	oSplitter->setFrameStyle(QFrame::Box + QFrame::Plain);
	oWorkspace = new QWorkspace(oSplitter);
	
	oConsole = new CNConsole(oSplitter);
	oConsole->setCaption(tr("Main console"));
	oConsole->resize(100, 200);
	oConsoleList.append(oConsole);
	
	// do not use the widget's prefered size, use user specified values
	oSplitter->setResizeMode(oConsole, QSplitter::KeepSize);
	
	oWorkspace->setBackgroundColor(Qt::white);
	oTaskList = 0;
	oActiveWindow = 0;
	
	oPrintExprDialog = new CPrintExprDialog(tr("Expression"), app);
	oWalkExprDialog = new CWalkExpressionDialog(app);
	
	connect(oConsole, SIGNAL(sigLine(CNConsole*, QString)), oDoc, SLOT(slotFromConsole(CNConsole*, QString)));
	connect(oConsole, SIGNAL(sigClosed(CNConsole*)), SLOT(slotConsoleClosed(CNConsole*)));
	connect(oDoc, SIGNAL(sigToConsole(CNConsole*, QString)), SLOT(slotToConsole(CNConsole*, QString)));
	connect(oDoc, SIGNAL(sigErrToConsole(CNConsole*, QString)), SLOT(slotErrToConsole(CNConsole*, QString)));
	connect(oWorkspace, SIGNAL(windowActivated(QWidget*)), SLOT(slotWindowActivated(QWidget*)));
	
	initPopups();
	
	QTimer* timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), SLOT(slotTimeout()));
	timer->start(100, true);
	
	setFocusPolicy(StrongFocus);
	oConsole->setFocus();
}

CMainWin::~CMainWin()
{
}

void
CMainWin::resizeEvent(QResizeEvent*)
{
	oSplitter->setGeometry(0, 0, width(), height());
}

bool
CMainWin::eventFilter(QObject* obj, QEvent* e)
{
	if (obj == oTrace) {
		if (e->type() == QEvent::Close) {
			oTrace = 0;
		}
	}
	else if (obj == oTraceLowcore) {
		if (e->type() == QEvent::Close) {
			oTraceLowcore = 0;
		}
	}
	
	return false;
}

void
CMainWin::focusInEvent(QFocusEvent* e)
{
	if (e->reason() != QFocusEvent::Tab) {
		QWidget::focusInEvent(e);
	}
}

QFont
CMainWin::consoleFont()
{
	return oConsole->font();
}

void
CMainWin::setConsoleFont(QFont f)
{
	oConsole->setFont(f);
}

void
CMainWin::slotAddTree(CListView* root)
{
	CGraphManager* man = new CGraphManager(oWorkspace);
	CGraphObject* obj;

	QSize size = calculateViewSize(root);
	root->resize(size);

	man->resize(600, 500);
	if (root->caption().isEmpty()) {
		obj = man->addWidget(root, root->rootName() + "@" + root->rootValue());
		man->setCaption(root->rootName() + "@" + root->rootValue());
	}
	else {
		obj = man->addWidget(root, root->caption());
		man->setCaption(root->caption());
	}
	obj->show();

	restoreChildren();
	man->show();
	setFrame(man);
	man->parentWidget()->resize(500, 400);

	oList2Manager.insert(root, man);
	assert(oList2Manager[root] == man);
	oList2Object.insert(root, obj);
	assert(oList2Object[root] == obj);

	connect(root,
		SIGNAL(mouseButtonPressed(int, QListViewItem*, const QPoint&, int)),
		SLOT(slotSVClicked(int, QListViewItem*, const QPoint&, int))
	);
	connect(root,
		SIGNAL(rightButtonClicked(QListViewItem*, const QPoint&, int)),
		SLOT(slotSVRightClicked(QListViewItem*, const QPoint&, int))
	);
	connect(root,
		SIGNAL(doubleClicked(QListViewItem*)),
		SLOT(slotSVDoubleClicked(QListViewItem*))
	);
	connect(man, SIGNAL(sigClosed(CGraphManager*)), SLOT(slotGMClosed(CGraphManager*)));
	connect(man, SIGNAL(sigChildClosed(QWidget*)), SLOT(slotSVClosed(QWidget*)));

	emit sigNewChildWin(man);
}

void
CMainWin::initPopups()
{
	oSVPopup = new QPopupMenu(this);
	oSVPopup->insertItem(tr("Type info"), SV_POPUP_TYPE);
	oSVPopup->insertItem(tr("whatis"), SV_POPUP_WHATIS);
	oSVPopup->insertItem(tr("Print..."), SV_POPUP_PRINT);
	oSVPopup->insertItem(tr("walk"), SV_POPUP_WALK);
	oSVPopup->insertItem(tr("Print array") + "...", SV_POPUP_PRINTARRAY);
	oSVPopup->insertItem(tr("dump"), SV_POPUP_DUMP);
	
	oTLPopup = new QPopupMenu(this);
	oTLPopup->insertItem(tr("graph print"), TL_POPUP_PRINT);
	oTLPopup->insertItem(tr("trace"), TL_POPUP_TRACE);
//	oTLPopup->insertItem(tr("dump"), TL_POPUP_DUMP);
	
	oSLPopup = new QPopupMenu(this);
	oSLPopup->insertItem(tr("Print") + "...", SL_POPUP_PRINT);
	oSLPopup->insertItem(tr("Walk") + "...", SL_POPUP_WALK);
}

void
CMainWin::slotSLClicked(QListViewItem* item)
{
	if (item == 0) {
		return;
	}
	
	// copy address to clipboard
	if (!item->text(0).isEmpty()) {
		QString addr = item->text(SL_COL_ADDRESS);
		if (addr.at(1) != 'x') {
			addr.prepend("0x");
		}
		
		QApplication::clipboard()->setText(addr);
	}	
}

void
CMainWin::slotSLDoubleClicked(QListViewItem* item)
{
	if (item == 0 || oDoc->waitForInit()) {
		return;
	}
	oPrintExprDialog->setText(item->text(2), true);
        int ret = oPrintExprDialog->exec();
	if (ret == QDialog::Accepted && oPrintExprDialog->text().length() > 0U) {
                       graphPrint(oPrintExprDialog->text(), oPrintExprDialog->count());
        }
}

void
CMainWin::slotSLRightClicked(QListViewItem* item, const QPoint& pos, int)
{
	if (item == 0) {
		return;
	}
	
	// show context menu
	int id = oSLPopup->exec(pos);
	
	switch (id) {
		case SL_POPUP_PRINT:
			slotSLDoubleClicked(item);
			break;
		case SL_POPUP_WALK:
			graphWalkDialog(QString::null, QString::null, item->text(SL_COL_ADDRESS));
			break;
	}
}

void
CMainWin::slotSVRightClicked(QListViewItem* item, const QPoint& pos, int)
{
	if (item == 0) {
		return;
	}
	
	if (item->listView() != 0) {
		// check if the 'walk' command is available
		if (item->text(2) == item->listView()->firstChild()->text(0) + "*") {
			oSVPopup->setItemEnabled(SV_POPUP_WALK, true);
		}
		else if (item->text(2) == "list_head") {
			oSVPopup->setItemEnabled(SV_POPUP_WALK, true);
		}
		else {
			oSVPopup->setItemEnabled(SV_POPUP_WALK, false);
		}
		
		// check if item is a pointer
		bool isPtr = item->text(2).right(1) == "*";
		
		// check if 'print array' and 'dump' commands are available
		if (isPtr) {
			oSVPopup->setItemEnabled(SV_POPUP_PRINT, true);
			oSVPopup->setItemEnabled(SV_POPUP_PRINTARRAY, true);
			oSVPopup->setItemEnabled(SV_POPUP_DUMP, true);
		}
		else {
			oSVPopup->setItemEnabled(SV_POPUP_PRINTARRAY, false);
			if (isdigit(item->text(1).at(0).latin1())) { // allow dump view on integers
				oSVPopup->setItemEnabled(SV_POPUP_PRINT, true);
				oSVPopup->setItemEnabled(SV_POPUP_DUMP, true);
			}
			else {
				oSVPopup->setItemEnabled(SV_POPUP_PRINT, false);
				oSVPopup->setItemEnabled(SV_POPUP_DUMP, false);
			}
		}
	}
	else {
		oSVPopup->setItemEnabled(SV_POPUP_TYPE, false);
		oSVPopup->setItemEnabled(SV_POPUP_WHATIS, false);
		oSVPopup->setItemEnabled(SV_POPUP_WALK, false);
		oSVPopup->setItemEnabled(SV_POPUP_PRINTARRAY, false);
		oSVPopup->setItemEnabled(SV_POPUP_DUMP, false);
	}
	
	int id = oSVPopup->exec(pos);
	QString type = dynamic_cast<StructViewItem*>(item)->type();
	while (type.findRev("*") != -1) {
		type.truncate(type.length() - 1);
	}
	
	// provide the full attribute name if we can't get informations using the attribute type
	QString name = "";
	if (item->parent() != 0) {
		name = item->parent()->text(0) + "." + item->text(0);
	}
	
	switch (id) {
		case SV_POPUP_TYPE:
			oDoc->printTypeInfo(type, name);
			break;
		case SV_POPUP_WHATIS:
			oDoc->printWhatis(type, name);
			break;
		case SV_POPUP_PRINT:
			graphPrintDialog(item->text(1), true);
			break;
		case SV_POPUP_WALK:
			displayWalk(item);
			break;
		case SV_POPUP_PRINTARRAY:
			displayArray(item);
			break;
		case SV_POPUP_DUMP:
			displayDump(item);
			break;
	}
}

void
CMainWin::slotSVClicked(int, QListViewItem* item, const QPoint&, int col)
{
	if (item != 0) {
		QApplication::clipboard()->setText(item->text(col));
		dynamic_cast<StructViewItem*>(item)->setMarkedColumn(col);
	}
}

void
CMainWin::slotSVDoubleClicked(QListViewItem* item)
{
	if (item != 0) {
		QString type = dynamic_cast<StructViewItem*>(item)->type();
				
		int len = type.length();
		if (len > 0 && type[len - 1] == '*') { // it is a pointer			
			// check if it is already displayed
			
			QListView* view = item->listView();
			assert(view != 0);
			CGraphManager* man = oList2Manager[view];
			assert(man != 0);

			QString caption = type + "@" + item->text(1);
			QMap<QListView*, CGraphObject*>::Iterator it = oList2Object.begin();
			while (it != oList2Object.end()) {
				if (caption == it.data()->caption()) {
					man->adjustNewObject(it.data());
					return;
				}
				++it;
			}
			
			QListView* tree = oDoc->getTree(item->text(0), item->text(1), type);
			type.truncate(type.length() - 1);
			CGraphObject* parent = oList2Object[item->listView()];
			QString wireLabel = item->text(0);
			if (isdigit(wireLabel[0].latin1()) && item->parent() != 0) { // array member
				wireLabel.prepend(item->parent()->text(0) + "[");
				wireLabel.append("]");
			}
			
			displayTree(tree, parent, man, caption, wireLabel);
		}
	}
}

void
CMainWin::slotLowcore(CTrace* view, const QString& addr)
{
	const char* caption = "Lowcore";
	
	if (oTraceLowcore == 0) {
	    	restoreChildren();
		oTraceLowcore = new CTraceLowcore(oWorkspace);
		QString font = oDoc->configManager()->item(CFG_GRP_CONSOLE, CFG_ATT_CONSOLE_FONTFAMILY, "Courier");
		int fsize = oDoc->configManager()->item(CFG_GRP_CONSOLE, CFG_ATT_CONSOLE_FONTSIZE, 12);
		oTraceLowcore->setFont(QFont(font, fsize));
		oTraceLowcore->installEventFilter(this);
		oTraceLowcore->show();
		
		view->installEventFilter(oTraceLowcore);
		setFrame(oTraceLowcore);
		oTraceLowcore->setContent(oDoc->getTraceLowcore(addr));
		oTraceLowcore->resize(
			oTraceLowcore->contentsWidth() + 11, 
			oTraceLowcore->contentsHeight() + 11
		);
		oTraceLowcore->setCaption(caption);
		emit sigNewChildWin(oTraceLowcore);
	}
	else {
		oTraceLowcore->setContent(oDoc->getTraceLowcore(addr));
	}

	oTraceLowcore->raise();
}

void
CMainWin::displayTree(QListView* tree, CGraphObject* parent, CGraphManager* man, const QString& caption, const QString& wireLabel)
{
	if (tree != 0) {
		QSize size = calculateViewSize(tree);
		tree->resize(size);
		
		if (!parent->isViewMany() || parent->oneChild() == 0) {
			CGraphObject* obj;
			obj = man->addWidget(tree, caption);
			obj->show();
			
			oList2Manager.insert(tree, man);
			oList2Object.insert(tree, obj);
			
			// attach child to parent
			man->attach(parent, obj, wireLabel);
			man->layoutObject(obj); //! always automatic layout?
			
			if (parent->isViewMany()) {
				parent->setOneChild(obj);
			}
		}
		else { // there is a single child to be replaced
			oList2Manager.remove(static_cast<QListView*>(parent->oneChild()->widget()));
			oList2Object.remove(static_cast<QListView*>(parent->oneChild()->widget()));
			oList2Manager.insert(tree, man);
			oList2Object.insert(tree, parent->oneChild());
			parent->oneChild()->replaceWidget(tree);
			parent->oneChild()->setCaption(caption);
			parent->oneChild()->setWireLabel(wireLabel);
			man->adjustNewObject(parent->oneChild());
			man->calculateEdges(parent);
			// redraw instead of update - this is neccessary to get the arrow right
			man->redraw();
		}
		
		connect(tree,
		SIGNAL(mouseButtonPressed(int, QListViewItem*, const QPoint&, int)),
			SLOT(slotSVClicked(int, QListViewItem*, const QPoint&, int))
		);
		connect(tree,
			SIGNAL(rightButtonClicked(QListViewItem*, const QPoint&, int)),
			SLOT(slotSVRightClicked(QListViewItem*, const QPoint&, int))
		);
		connect(tree,
			SIGNAL(doubleClicked(QListViewItem*)),
			SLOT(slotSVDoubleClicked(QListViewItem*))
		);


		PRINT(cerr << "Tree built" << endl);
	}
	else {
		PRINT(cerr << "tree view creation failed");
	}
}

QSize
CMainWin::calculateViewSize(QListView* list)
{
	// calculate list height
	int h = 70; // minumum height
	h += list->firstChild()->childCount() * 20;
	if (h > 400) {
		h = 400;
	}
	
	return QSize(250, h);
}

void
CMainWin::slotTaskList(TaskListType* tl)
{
	assert(tl != 0);
	QVector<QString>* vec = tl->first();
	const int col = vec->count();
	
	// needed for initial size
	bool created = false;
	
	if (oTaskList == 0) {
		oTaskList = new CListView(oWorkspace);
		oTaskList->setCaption(tr("Task list"));
		oTaskList->setAllColumnsShowFocus(true);
		oTaskList->setShowSortIndicator(true);
		oTaskList->setFindColumn(TL_COL_NAME);
		oTaskList->show();
		restoreChildren();
		setFrame(oTaskList);
		created = true;
		
		connect(oTaskList, SIGNAL(sigClosed(CListView*)), SLOT(slotTLClosed(CListView*)));
		connect(oTaskList, SIGNAL(rightButtonPressed(QListViewItem*, const QPoint&, int)),
			SLOT(slotTLRightClicked(QListViewItem*, const QPoint&, int))
		);
		connect(oTaskList, SIGNAL(doubleClicked(QListViewItem*)), SLOT(slotTLDoubleClicked(QListViewItem*)));
		connect(oTaskList, SIGNAL(pressed(QListViewItem*)), SLOT(slotTLClicked(QListViewItem*)));

		// create header
		for (int i = 0; i < col; i++) {
			oTaskList->addColumn(*vec->at(i));
		}
		oTaskList->setSorting(TL_COL_SORTING);
		
		emit sigNewChildWin(oTaskList);
	}
	else {
		oTaskList->clear();
	}
	
	// fill list
	vec = tl->next();
	while (vec != 0) {
		CListViewItem* lvi = new CListViewItem(oTaskList);
		for (int i = 0; i < col; i++) {
			lvi->setText(i, *vec->at(i));
			
			if (i == TL_COL_WITH_CPU && *vec->at(i) != "-") {
				lvi->setColor(red);
			}
		}
		vec = tl->next();
	}
	
	// resize window if it was just created
	if (created) {
		QHeader* hdr = oTaskList->header();
		int size = 0;
		const int len = hdr->count();
	
		for (int i = 0; i < len; i++) {
			size = size + hdr->sectionSize(i);
		}
		
		oTaskList->resize(size + 20, 300);
	}
	
	oTaskList->setFocus();
	oTaskList->raise();
}

void
CMainWin::slotGMClosed(CGraphManager* gm)
{
	QMap<QListView*, CGraphManager*>::Iterator it = oList2Manager.begin();
	while (it != oList2Manager.end()) {
		if (*it == gm) {
			QListView* tmp = it.key();
			++it; // increase before removal
			oList2Object.remove(tmp);
			oList2Manager.remove(tmp);
		}
		else {
			++it;
		}
	}
}

void
CMainWin::slotTLClosed(CListView*)
{
	oTaskList = 0;
}

void
CMainWin::slotTLClicked(QListViewItem* lvi)
{
	if (lvi != 0 && !lvi->text(0).isEmpty()) {
		QString addr = lvi->text(0);
		if (addr.at(1) != 'x') {
			addr.prepend("0x");
		}
		QApplication::clipboard()->setText(addr);
	}
}

void
CMainWin::slotTLRightClicked(QListViewItem* lvi, const QPoint& pos, int)
{
	if (lvi != 0) {
		QString addr = lvi->text(0);
		if (addr.at(1) != 'x') {
			addr.prepend("0x");
		}
		int id = oTLPopup->exec(pos);
		switch (id) {
			case TL_POPUP_TRACE:
				graphTrace(addr);
				break;
			case TL_POPUP_PRINT:
				slotTLDoubleClicked(lvi);
				break;
			case TL_POPUP_DUMP:
				break;
		}
	}
}

void
CMainWin::slotTLDoubleClicked(QListViewItem* lvi)
{
	QString addr = lvi->text(0);
	if (addr.at(1) != 'x') {
		addr.prepend("0x");
	}
	CListView* lv = oDoc->getTree("task_struct", addr, "task_struct*");
	if (lv != 0) {
		slotAddTree(lv);
	}
}

void
CMainWin::slotToConsole(CNConsole* con, QString str)
{
	if (con == 0) {
		// output to the first console
		con = oConsoleList.first();
		if (con == 0) {
			return;
		}
	}
	
	con->slotLine(str);
}

void
CMainWin::slotErrToConsole(CNConsole* con, QString str)
{
	if (con == 0) {
		// output to the first console
		con = oConsoleList.first();
		if (con == 0) {
			return;
		}
	}
	
	con->setColor(Qt::red);
	con->slotLine(str);
	con->setColor(Qt::black);
}

void
CMainWin::slotConsoleClosed(CNConsole* con)
{
	CNConsole* walker = oConsoleList.first();
	while (walker != 0) {
		if (walker == con) {
			oConsoleList.remove();
			break;
		}
		walker = oConsoleList.next();
	}
}

void
CMainWin::slotSVClosed(QWidget* w)
{
	QListView* lv = dynamic_cast<QListView*>(w);
	oList2Manager.remove(lv);
	oList2Object.remove(lv);
}

void
CMainWin::slotNewConsole()
{
	// do not create a new console if lcrash is not ready yet
	if (oDoc->waitForInit()) {
		return;
	}
	
	// get console font
	QString family = oDoc->configManager()->item(CFG_GRP_CONSOLE, CFG_ATT_CONSOLE_FONTFAMILY, "Courier");
	int size = oDoc->configManager()->item(CFG_GRP_CONSOLE, CFG_ATT_CONSOLE_FONTSIZE, 10);
	
	CNConsole* con = new CNConsole(oWorkspace);
	con->show();
	con->setCaption(tr("Console"));
	con->setFont(QFont(family, size, QFont::Normal));
	oConsoleList.append(con);
	connect(con, SIGNAL(sigLine(CNConsole*, QString)), oDoc, SLOT(slotFromConsole(CNConsole*, QString)));
	connect(con, SIGNAL(sigClosed(CNConsole*)), SLOT(slotConsoleClosed(CNConsole*)));
	
	// display all mdi-windows in their original size
	restoreChildren();
	
	oDoc->slotFromConsole(con, "\n");
	setFrame(con);
	con->parentWidget()->resize(600, 300);
	initCommandCompletion(con);
	
	emit sigNewChildWin(con);
}

void
CMainWin::slotTraceList()
{
	graphTrace(QString::null);
}

void
CMainWin::slotSymtabList()
{
	if (oSymtabList == 0) {
		oSymtabList = new CListView(oWorkspace);
		oSymtabList->addColumn(tr("Name"));
		oSymtabList->addColumn(tr("Type"));
		oSymtabList->addColumn(tr("Address"));
		oSymtabList->setSorting(0);
		oSymtabList->setShowSortIndicator(true);
		oSymtabList->setAllColumnsShowFocus(true);
		oSymtabList->setCaption(tr("Global symbols"));
		oSymtabList->setFindColumn(0);
		oSymtabList->show();
		restoreChildren();
		setFrame(oSymtabList);
		oSymtabList->parentWidget()->resize(400, 300);
		
		connect(oSymtabList, SIGNAL(clicked(QListViewItem*)), SLOT(slotSLClicked(QListViewItem*)));
		connect(oSymtabList, SIGNAL(doubleClicked(QListViewItem*)), SLOT(slotSLDoubleClicked(QListViewItem*)));
		connect(oSymtabList, SIGNAL(rightButtonPressed(QListViewItem*, const QPoint&, int)), SLOT(slotSLRightClicked(QListViewItem*, const QPoint&, int)));
	
		slotSymtabListUpdate();
		
		emit sigNewChildWin(oSymtabList);
	}
	else {
		if (oSymtabList->isHidden()) {
			oSymtabList->show();
			emit sigNewChildWin(oSymtabList);
		}
		
		oSymtabList->setFocus();
	}
}

void
CMainWin::slotSymtabListUpdate()
{
	if (oSymtabList == 0) {
		return;
	}
	
	oSymtabList->clear();
	oSymtabList->setSorting(-1);
	QValueList<CSymtabItem> list = oDoc->getGlobalSymbols();
	QValueList<CSymtabItem>::Iterator it = list.begin();
	
	CListViewItem* after = 0;
	while (it != list.end()) {
		CListViewItem* lvi = (after != 0) ? new CListViewItem(oSymtabList, after) : new CListViewItem(oSymtabList);
		after = lvi;
	
		lvi->setText(0, (*it).name());
		lvi->setText(1, (*it).type());
		lvi->setText(2, (*it).address());
	
		++it;
	}
	
	oSymtabList->setSorting(0);
}

void
CMainWin::slotNewS390dbf()
{
	if (oDoc->waitForInit()) { // wait for lcrash
		return;
	}
	
	restoreChildren();
	CS390dbf* dbf = new CS390dbf(oDoc, oWorkspace, 0, WDestructiveClose);
	if (!dbf->isEmpty()) {
		dbf->setCaption(tr("s390dbf"));
		dbf->show();
		setFrame(dbf);
		dbf->parentWidget()->resize(600, 300);
		
		emit sigNewChildWin(dbf);
	}
	else {
		delete dbf;
	}
}

void
CMainWin::layoutActiveWindow()
{
	CGraphManager* man = dynamic_cast<CGraphManager*>(oWorkspace->activeWindow());
	if (man != 0) {
		man->layout();
	}
}

void
CMainWin::findItem()
{
	QWidget* w = oWorkspace->activeWindow();
	
	if (w == 0) {
		return;
	}
	
	if (w->isA("CListView")) {
		static_cast<CListView*>(w)->findDialog();
	}
	else if (w->isA("CGraphManager")) {
		CGraphManager* man = dynamic_cast<CGraphManager*>(w);
		if (man != 0 && man->selectedObject() != 0) {
			CListView* view = dynamic_cast<CListView*>(man->selectedObject()->widget());
			if (view != 0) {
				view->findDialog();
			}
		}
	}
}

void
CMainWin::slotTimeout()
{
	// initial widget size
	QValueList<int> size;
	size.append(height() / 6 * 5);
	oSplitter->setGeometry(0, 0, width(), height());
	oSplitter->setSizes(size);
	
	// console font
	QString family = oDoc->configManager()->item(CFG_GRP_CONSOLE, CFG_ATT_CONSOLE_FONTFAMILY, "Courier");
	int height = oDoc->configManager()->item(CFG_GRP_CONSOLE, CFG_ATT_CONSOLE_FONTSIZE, 10);
	oConsole->setFont(QFont(family, height, QFont::Normal));
}

void
CMainWin::displayArray(QListViewItem* item)
{
	CNumberOfElementsDialog* d = new CNumberOfElementsDialog(this);
	d->resize(200, 10);
	int result = d->exec();
	int num = d->value();
	delete d;
	
	#define MINVAL 1
	#define MAXVAL 500
	if (result == QDialog::Accepted && (num < MINVAL || num > MAXVAL)) {
		QMessageBox::critical(
			this,
			stdCaption(tr("Invalid numer")),
			tr("The input must not exceed a value of %1").arg(QString::number(MAXVAL))
		);
		num = 0;
	}
	#undef MINVAL
	#undef MAXVAL
	
	if (result == QDialog::Accepted && num > 0) {
		QString type = dynamic_cast<StructViewItem*>(item)->type();
		if (type.right(1) == "*") {
			type.truncate(type.length() - 1);
		}
		
		CGraphManager* man = oList2Manager[item->listView()];
		assert(man != 0);
		CGraphObject* parent = oList2Object[item->listView()];
		assert(parent != 0);
		QString caption = tr("Array") + ": " + type + "@" + item->text(1);
		QString wireLabel = item->text(0) + "#" + QString::number(num);
		QListView* tree = oDoc->getArray(item->text(1), type, num);
		
		displayTree(tree, parent, man, caption, wireLabel);
		
		if (tree == 0) {
			oDoc->printErr(tr("Could not create array view") + CMD_PROMPT + "\n");
		}
	}
}

void
CMainWin::displayWalk(QListViewItem* item)
{
	if (item == 0) {
		return;
	}
	
	QString type = dynamic_cast<StructViewItem*>(item)->type();
	if (type.right(1) == "*") {
		type.truncate(type.length() - 1);
	}
	else if (type != "list_head") {
		oDoc->printErr(tr("Invalid item for a walk"));
		return;
	}
	
	CGraphManager* man = oList2Manager[item->listView()];
	assert(man != 0);
	CGraphObject* parent = oList2Object[item->listView()];
	QString caption = tr("Walk") + ": " + type + "@" + item->text(1);
	QString wireLabel = tr("walk") + ":" + item->text(0);
	
	if (item->text(2) == "list_head") {
		// get value of `next' member
		QListViewItem* child = item->firstChild();
		if (child != 0) {
			oWalkExprDialog->setDefault(
				QString::null,
				item->text(0),
				(isdigit(child->text(1).at(0).latin1()))
				? child->text(1)
				: QString::null
			);
		}
		int ret = oWalkExprDialog->fExec();
		
		if (ret == QDialog::Accepted) {
			CListView* tree = oDoc->getWalk(
				oWalkExprDialog->member(),
				oWalkExprDialog->address(),
				oWalkExprDialog->type()
			);
			if (tree != 0) {
				if (!tree->caption().isEmpty()) {
					caption = tree->caption();
				}
				displayTree(tree, parent, man, caption, wireLabel);
			}
		}
	}
	else {
		QListView* tree = oDoc->getWalk(item->text(0), item->parent()->text(1), type);
		
		if (tree != 0) {
			if (!tree->caption().isEmpty()) {
				caption = tree->caption();
			}
			displayTree(tree, parent, man, caption, wireLabel);
		}
	}
}

void
CMainWin::displayDump(QListViewItem* item)
{
	const unsigned int len = item->text(1).length();
	if (item == 0 || len == 0) {
		return;
	}
	
	restoreChildren();
	
	QLcrashApp* app = dynamic_cast<QLcrashApp*>(parentWidget());
	const char* cptr = item->text(1).latin1();
	CDumpView* dv = new CDumpView(strtoull(cptr, 0, 0), app, 0);
	dv->show();
	
	emit sigNewChildWin(dv);
}

void
CMainWin::slotWindowActivated(QWidget* w)
{
	if (w != oActiveWindow) {
		if (oActiveWindow && oActiveWindow->isA("CDumpView")) {
			CDumpView* v = static_cast<CDumpView*>(oActiveWindow);
			
			v->enableToolbar(false);
		}
		if (w && w->isA("CDumpView")) {
			CDumpView* v = static_cast<CDumpView*>(w);
			v->enableToolbar(true);
			update();
		}
	
		oActiveWindow = w;
	}
}

void
CMainWin::restoreChildren()
{
	QWidgetList list = oWorkspace->windowList();
	QWidget* walker = list.first();
	while (walker != 0 && walker->isVisible()) {
		walker->showNormal();
		walker = list.next();
	}
}

void
CMainWin::setFrame(QWidget* w)
{
#ifdef WIN32
	QFrame* frame = (QFrame*)w->parentWidget();
#else
	QFrame* frame = dynamic_cast<QFrame*>(w->parentWidget());
#endif
	if (frame != 0) {
		frame->setFrameStyle(QFrame::Box | QFrame::Raised);
		frame->setLineWidth(2);
		frame->setMidLineWidth(1);
	}
	
	// testing
	frame->setFocusPolicy(ClickFocus);
}

void
CMainWin::setCursor(const QCursor& c)
{
	oWorkspace->setCursor(c);
	QWidgetList list = oWorkspace->windowList();
	QWidget* walker = list.first();
	while (walker != 0) {
		walker->setCursor(c);
		walker = list.next();
	}
}

void
CMainWin::unsetCursor()
{
	oWorkspace->unsetCursor();
	QWidgetList list = oWorkspace->windowList();
	QWidget* walker = list.first();
	while (walker != 0) {
		walker->unsetCursor();
		walker = list.next();
	}
}

void
CMainWin::scrollLeft()
{
	CGraphManager* man = dynamic_cast<CGraphManager*>(oWorkspace->activeWindow());
	if (man != 0 && !oConsole->hasFocus()) {
		man->scrollBy(-100, 0);
	}
}

void
CMainWin::scrollRight()
{
	CGraphManager* man = dynamic_cast<CGraphManager*>(oWorkspace->activeWindow());
	if (man != 0 && !oConsole->hasFocus()) {
		man->scrollBy(100, 0);
	}
}

void
CMainWin::scrollUp()
{
	CGraphManager* man = dynamic_cast<CGraphManager*>(oWorkspace->activeWindow());
	if (man != 0 && !oConsole->hasFocus()) {
		man->scrollBy(0, -100);
	}
}

void
CMainWin::scrollDown()
{
	CGraphManager* man = dynamic_cast<CGraphManager*>(oWorkspace->activeWindow());
	if (man != 0 && !oConsole->hasFocus()) {
		man->scrollBy(0, 100);
	}
}

void
CMainWin::graphPrint(const QString& expr, int count)
{
	if (count == 1) {
		oDoc->slotFromConsole(0, "graph print " + expr);
	}
	else { // print array
		if (expr.find('*') == -1) { // no pointer
			QMessageBox::critical(
				this,
				stdCaption(tr("Warning")),
				tr("The expression doesn't seem to contain a pointer"),
				1, 0, 0
			);
				return;
		}
		// assume that an address starts with `0x'
		int addrPos = expr.findRev("0x", -1, false);
		if (addrPos == -1) { // no address found
			QMessageBox::critical(
				this,
				stdCaption(tr("Warning")),
				tr("The expression doesn't seem to contain an address"),
				1, 0, 0
			);
				return;
		}
		// get type from string
		QString type = oDoc->getTypeFromExpression(expr);
		if (!type.isEmpty()) {
			CListView* tree = oDoc->getArray(
				expr.mid(addrPos),
				type,
				count
			);
			
			if (tree != 0) {
				slotAddTree(tree);
			}
		}
		else { // could not get type
			oDoc->printErr(tr("Invalid type") + ": " + expr);
		}
	}
}

void
CMainWin::graphPrintDialog(const QString& def, bool rewindCursor)
{
	if (!oDoc->waitForInit()) {
		oPrintExprDialog->setText(def, rewindCursor);
		int ret = oPrintExprDialog->exec();
	
		if (ret == QDialog::Accepted && oPrintExprDialog->text().length() > 0U) {
			graphPrint(oPrintExprDialog->text(), oPrintExprDialog->count());
		}
	}
}

void
CMainWin::graphWalkDialog(const QString& type, const QString& member, const QString& addr)
{
	if (!oDoc->waitForInit()) {
		oWalkExprDialog->setDefault(type, member, addr);
		int ret = oWalkExprDialog->fExec();
		
		if (ret == QDialog::Accepted) {
			CListView* wv = oDoc->getWalk(
				oWalkExprDialog->member(),
				oWalkExprDialog->address(),
				oWalkExprDialog->type()
			);
			
			if (wv != 0) {
				wv->setCaption(
					"walk:"
					+ oWalkExprDialog->type()
					+ "."
					+ oWalkExprDialog->member()
					+ "@"
					+ oWalkExprDialog->address()
				);
				slotAddTree(wv);
			}
		}
	}
}

void
CMainWin::graphTrace(const QString& addr)
{
	if (oTrace == 0) {
		oTrace = new CTrace(oDoc, oWorkspace);
		oTrace->setCaption(tr("Trace"));
		oTrace->show();
		
		setFrame(oTrace);
		oTrace->update();
		oTrace->installEventFilter(this);
		connect(
			oTrace, 
			SIGNAL(sigLowcore(CTrace*, const QString&)),
			SLOT(slotLowcore(CTrace*, const QString&))
		);
		emit sigNewChildWin(oTrace);

		// display all mdi-windows in their original size
		restoreChildren();
		oTrace->parentWidget()->resize(600, 300);
	}
	else {
		oTrace->setTask();
	}
	
	if (!addr.isEmpty()) {
		oTrace->setMarkedProcess(addr);
	}

	oTrace->setFocus();
}

void
CMainWin::initCommandCompletion(CNConsole* con)
{
	QString cmds = oDoc->getLcrashHelp();
	if (con == 0) {
		con = oConsole;
	}
	con->updateCommandSet(cmds);
}
