// **************************************************************************
//    begin                : Sun Aug 8 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 "variablewidget.h"
#include "rdbparser.h"
#include "rdbcommand.h"

#include <kdebug.h>
#include <kpopupmenu.h>
#include <klineedit.h>
#include <kdeversion.h>

#include <qheader.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qhbox.h>
#include <qpainter.h>
#include <qpushbutton.h>
#include <qregexp.h>
#include <qcursor.h>
#include <klocale.h>

#include <qpoint.h>
#include <qclipboard.h>
#include <kapplication.h>

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

namespace RDBDebugger
{

VariableWidget::VariableWidget(QWidget *parent, const char *name)
    : QWidget(parent, name)
{
    varTree_ = new VariableTree(this);
    QLabel *label = new QLabel(i18n("E&xpression to watch:"), this);

    QHBox *watchEntry = new QHBox( this );
    watchVarEditor_ = new KHistoryCombo( watchEntry, "var-to-watch editor");
    label->setBuddy(watchVarEditor_);

    QPushButton *addButton = new QPushButton(i18n("&Add"), watchEntry );
    addButton->adjustSize();
    addButton->setFixedWidth(addButton->width());

    QBoxLayout * vbox = new QVBoxLayout();
    vbox->addWidget( label );
    vbox->addWidget( watchEntry );

    QVBoxLayout *topLayout = new QVBoxLayout(this, 2);
    topLayout->addWidget(varTree_, 10);
    topLayout->addLayout( vbox );

    connect( addButton, SIGNAL(clicked()), SLOT(slotAddWatchExpression()) );
    connect( watchVarEditor_, SIGNAL(returnPressed()), SLOT(slotAddWatchExpression()) );
}


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

void VariableWidget::setEnabled(bool bEnabled)
{
    QWidget::setEnabled(bEnabled);
    if (bEnabled && parentWidget() != 0) {
        varTree_->setColumnWidth(0, parentWidget()->width()/2);
	}
}
// **************************************************************************

void VariableWidget::slotAddWatchExpression()
{
    QString watchVar(watchVarEditor_->currentText());
    if (!watchVar.isEmpty()) {
        slotAddWatchExpression(watchVar);
    }
}

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

void VariableWidget::slotAddWatchExpression(const QString &ident)
{
    if (!ident.isEmpty()) {
        watchVarEditor_->addToHistory(ident);
        varTree_->slotAddWatchExpression(ident);
        watchVarEditor_->clearEdit();
    }
}

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

void VariableWidget::focusInEvent(QFocusEvent */*e*/)
{
    varTree_->setFocus();
}

void VariableWidget::restorePartialProjectSession(const QDomElement* el)
{
	varTree_->watchRoot()->restorePartialProjectSession(el);
}

void VariableWidget::savePartialProjectSession(QDomElement* el)
{
	varTree_->watchRoot()->savePartialProjectSession(el);
}

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

VariableTree::VariableTree(VariableWidget *parent, const char *name)
    : KListView(parent, name),
      QToolTip( viewport() ),
      activationId_(0),
      currentThread_(-1),
	  selectedFrame_(0),
	  watchRoot_(0),
	  globalRoot_(0)
{
    setRootIsDecorated(true);
    setAllColumnsShowFocus(true);
    setColumnWidthMode(0, Manual);
    setSorting(VAR_NAME_COLUMN);
    QListView::setSelectionMode(QListView::Single);

    addColumn(i18n("Variable"), 100 );
    addColumn(i18n("Value"), 100 );

    connect( this, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
             SLOT(slotContextMenu(KListView*, QListViewItem*)) );
    
	connect( this, SIGNAL(pressed(QListViewItem*)),
             this, SLOT(slotPressed(QListViewItem*)) );

	watchRoot_ = new WatchRoot(this);
}

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

VariableTree::~VariableTree()
{
}

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

void VariableTree::clear()
{
    QListViewItem *sibling = firstChild();
    while (sibling != 0) {
		QListViewItem * current = sibling;
		sibling = sibling->nextSibling();
		if (current->rtti() != RTTI_WATCH_ROOT) {
			delete current;
		}
    }
	
	globalRoot_ = 0;
	selectedFrame_ = 0;
	return;
}

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

void VariableTree::slotContextMenu(KListView *, QListViewItem *item)
{
    if (item == 0)
        return;

    setSelected(item, true);    // Need to select this item.

    if (item->parent() != 0) {
        KPopupMenu popup(this);
        popup.insertTitle(item->text(VAR_NAME_COLUMN));
        int idRemoveWatch = -2;
        if (item->rtti() == RTTI_WATCH_VAR_ITEM) {
            idRemoveWatch = popup.insertItem( i18n("Remove Watch Expression") );
		}

        int	idCopyToClipboard = popup.insertItem( i18n("Copy to Clipboard") );
        int res = popup.exec(QCursor::pos());

        if (res == idRemoveWatch) {
			emit removeWatchExpression(((WatchVarItem*)item)->displayId());
            delete item;
        } else if (res == idCopyToClipboard) {
            QClipboard *qb = KApplication::clipboard();
            QString text = "{ \"" + item->text( VAR_NAME_COLUMN ) + "\", " +
                            "\"" + item->text( VALUE_COLUMN ) + "\" }";

#if KDE_VERSION > 305
            qb->setText( text, QClipboard::Clipboard );
#else
            qb->setText( text );
#endif
        }
    }
}

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

void VariableTree::setSelected(QListViewItem * item, bool selected)
{
	// Save the last selected VarFrameRoot for slotPressed() to restore
	if (item->rtti() == RTTI_VAR_FRAME_ROOT && selected) {
		selectedFrame_ = (VarFrameRoot *) item;
	}
	
	QListView::setSelected(item, selected);
}

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

// Makes sure that only VarFrameRoot items can be selected
void VariableTree::slotPressed(QListViewItem * item)
{
	if (item == 0) {
		return;
	}
	
	while (item->rtti() == RTTI_VAR_ITEM) {
		item = item->parent();
	}
	
	if (	item->rtti() == RTTI_GLOBAL_ROOT
			|| item->rtti() == RTTI_WATCH_ROOT
			|| item->rtti() == RTTI_WATCH_VAR_ITEM )
	{
		if (selectedFrame_ != 0) {
			setSelected(selectedFrame_, true);
		}
		return;
	}
	
	if (item->rtti() == RTTI_VAR_FRAME_ROOT) {
		VarFrameRoot * frame = (VarFrameRoot*) item;
        emit selectFrame(frame->frameNo(), frame->threadNo());
	}
	
	return;
}

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

void VariableTree::prune()
{
	QListViewItem *child = firstChild();

    while (child != 0) {
        QListViewItem *nextChild = child->nextSibling();

        // Only prune var frames, not the watch or global root
        if (child->rtti() == RTTI_VAR_FRAME_ROOT) {
			if (((VarFrameRoot*) child)->isActive()) {
				if (child->isOpen()) {
					((VarFrameRoot*) child)->prune();
				}
			} else {
				delete child;
			}
		}
		
        child = nextChild;
    }
}

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

// The debugger has moved onto the next program pause, so invalidate 
// everything in the Variable Tree
void VariableTree::nextActivationId() 
{ 
	activationId_++; 
	globalRoot()->setActivationId();
	watchRoot()->setActivationId();
	// ..but that's only the Watch and Global roots
}
    
// **************************************************************************

// VarFrameRoot frames in the Variable Tree from the previous program pause, 
// are set active here. Notified by the Frame Stack widget when it parses the 
// backtrace from the 'where' command after a pause.
//
// After that, any frames which aren't marked as active must have gone
// out of scope and will end up pruned.
void VariableTree::slotFrameActive(int frameNo, int threadNo, const QString& frameName)
{
	VarFrameRoot * frame = findFrame(frameNo, threadNo);
	if (frameNo == 1) {
		// If the current frame 1 doesn't exist, create it
		if (frame == 0) {
			frame = new VarFrameRoot(this, frameNo, threadNo);
		}
		
		frame->setFrameName(frameName);
	}
	
	if (frame != 0 && frame->text(VAR_NAME_COLUMN) == frameName) {
		frame->setActivationId();
	}
}

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

bool VariableTree::schedule()
{
    QListViewItem *	child = firstChild();
	VarFrameRoot * frame = 0;

    while (child != 0) {
        if (child->rtti() == RTTI_VAR_FRAME_ROOT) {
			frame = (VarFrameRoot *) child;
			Q_ASSERT( !frame->isWaitingForData() );
			
			if (frame->needsVariables()) {
				if (QApplication::overrideCursor() == 0) {
					QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
				}
				
				// Tell the controller to fetch the variable values
				emit selectFrame(frame->frameNo(), frame->threadNo());
				return true;
			}
		}
		
        child = child->nextSibling();
    }
	
	frame = findFrame(1, currentThread_);
	Q_ASSERT( frame != 0 );
	Q_ASSERT( !frame->needsVariables() );
		
	// All over, nothing left to fetch. 
	// Return to frame 1, and prune the inactive items
	// from the variable tree..
	QApplication::restoreOverrideCursor();
	emit selectFrame(1, currentThread_);
	prune();
	
	return false;
}

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

void VariableTree::slotAddWatchExpression(const QString &watchVar)
{
    new WatchVarItem(watchRoot(), watchVar, UNKNOWN_TYPE);
	emit addWatchExpression(watchVar, true);
}


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

void VariableTree::setFetchGlobals(bool fetch)
{
	emit fetchGlobals(fetch);
}

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

VarFrameRoot *VariableTree::findFrame(int frameNo, int threadNo) const
{
    // frames only exist on the top level so we only need to
    // check the siblings
    QListViewItem *sibling = firstChild();
    while (sibling != 0) {
		if (	sibling->rtti() == RTTI_VAR_FRAME_ROOT
				&& ((VarFrameRoot*) sibling)->frameNo() == frameNo 
				&& ((VarFrameRoot*) sibling)->threadNo() == threadNo ) 
		{
			return (VarFrameRoot*) sibling;
		}
		
        sibling = sibling->nextSibling();
    }

    return 0;
}

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

WatchRoot *VariableTree::watchRoot()
{
    return watchRoot_;
}

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

GlobalRoot *VariableTree::globalRoot()
{
	if (globalRoot_ == 0) {
		globalRoot_ = new GlobalRoot(this);
	}

    return globalRoot_;
}

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

// Watch variables can be added before the start of a debugging session,
// so tell the controller about any already in the tree at start.
void VariableTree::resetWatchVars()
{
	for (QListViewItem *child = watchRoot()->firstChild(); child != 0; child = child->nextSibling()) {
		((WatchVarItem*) child)->setDisplayId(-1);
        emit addWatchExpression(child->text(VAR_NAME_COLUMN), false);
	}
}

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

void VariableTree::maybeTip(const QPoint &p)
{
    VarItem * item = dynamic_cast<VarItem*>( itemAt(p) );
    if (item != 0) {
        QRect r = itemRect(item);
        if (r.isValid()) {
            tip(r, item->tipText());
		}
    }
}


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

LazyFetchItem::LazyFetchItem(VariableTree *parent)
    : KListViewItem(parent),
      activationId_(0),
	  waitingForData_(false)
{
    setActivationId();
}

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

LazyFetchItem::LazyFetchItem(LazyFetchItem *parent)
    : KListViewItem(parent),
      activationId_(0),
      waitingForData_(false)
{
    setActivationId();
}

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

LazyFetchItem::~LazyFetchItem()
{
}

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

void LazyFetchItem::paintCell(QPainter *p, const QColorGroup &cg,
                              int column, int width, int align)
{
    if (p == 0) {
        return;
	}
	
    // make toplevel item (watch and frame items) names bold
    if (column == VAR_NAME_COLUMN && parent() == 0) {
        QFont f = p->font();
        f.setBold(true);
        p->setFont(f);
    }
	
    QListViewItem::paintCell( p, cg, column, width, align );
}

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

VarItem *LazyFetchItem::findItem(const QString &name) const
{
	QListViewItem *child = firstChild();

    // Check the siblings on this branch
    while (child != 0) {
		if (child->text(VAR_NAME_COLUMN) == name) {
			return (VarItem*) child;
		}

        child = child->nextSibling();
    }

    return 0;
}

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

void LazyFetchItem::prune()
{
    QListViewItem *child = firstChild();

    while (child != 0) {
        LazyFetchItem *item = (LazyFetchItem*) child;
        child = child->nextSibling();
		// Never prune a branch if we are waiting on data to arrive.
		if (!waitingForData_) {
			if (item->isActive()) {
 				item->prune();
			} else {
				delete item;
			}
        }
    }
}


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

VarItem::VarItem(LazyFetchItem *parent, const QString &varName, DataType dataType)
    : LazyFetchItem (parent),
      cache_(QCString()),
      dataType_(dataType),
      highlight_(false)
{
    setText(VAR_NAME_COLUMN, varName);
	setSelectable(false);
	
	// Order the VarItems so that globals are first, then
	// constants, class variables, instance variables and 
	// finally local variables
	
	// Matches either an array element or a string slice,
	// Order on the array index or the first number in the
	// range specifying the slice.
	QRegExp arrayelement_re("\\[(\\d+)(\\.\\.\\d+)?\\]");
	key_ = varName;
	
	if (arrayelement_re.search(varName) != -1) {
		key_.sprintf("%.6d", arrayelement_re.cap(1).toInt());
	} else if (key_.startsWith("$")) {
		key_.prepend("1001");		// Global variable
	} else if (QRegExp("^[A-Z]").search(varName) != -1) {
		key_.prepend("1002");		// Constant
	} else if (key_.startsWith("@@")) {
		key_.prepend("1003");		// Class variable
	} else if (key_.startsWith("@")) {
		key_.prepend("1004");		// Instance variable
	} else {
		key_.prepend("1005");		// Local variable or parameter
	}

//    kdDebug(9012) << " ### VarItem::VarItem *CONSTR* " << varName << endl;	
}

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

VarItem::~VarItem()
{
}

QString VarItem::key(int /*column*/, bool /*ascending*/) const 
{
	return key_;
}

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

// Returns the path of a ruby item. If it is an instance variable, assume
// that there is an attr_accessor method for it.
// For example, @foobar within instance obj is accessed as obj.foobar.
// But don't strip off the @ for an instance variable with no path,
// and leave a plain '@foobar' as it is.
QString VarItem::fullName() const
{
	QString itemName = text(VAR_NAME_COLUMN);
    QString vPath("");
    const VarItem *item = this;

	if (item->parent()->rtti() != RTTI_VAR_ITEM) {
		return itemName;
	}

    // This stops at the root item (FrameRoot or GlobalRoot)
	while (item->rtti() == RTTI_VAR_ITEM) {
		QString itemName = item->text(VAR_NAME_COLUMN);
		
		if (vPath.startsWith("[")) {
			// If it's a Hash or an Array, then just insert the value. As
			// in adding '[0]' to foo.bar to give foo.bar[0]
 			vPath.prepend(itemName);
		} else {
			if (vPath.isEmpty()) {
				vPath = itemName;
			} else {
 				vPath.prepend(itemName + ".");
			}
        }
		item = (VarItem*) item->parent();
    }

	// Change 'self.@foobar' to '@foobar'
	vPath.replace(QRegExp("^self\\.@"), "@");
	
	// Use instance_variable_get() to access any '@var's in the middle of a path	
	QRegExp re_instance_var("\\.(@[^\\[.]+)");
	int pos = re_instance_var.search(vPath);	
	while (pos != -1) {	
		vPath.replace(	pos, 
						re_instance_var.matchedLength(), 
						QString(".instance_variable_get(:") + re_instance_var.cap(1) + ")" );
		pos = re_instance_var.search(vPath, pos);	
	}		

    return vPath;
}

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

void VarItem::setText(int column, const QString &data)
{
	setActivationId();
	
    if (column == VALUE_COLUMN) {
		highlight_ = (!text(VALUE_COLUMN).isEmpty() && text(VALUE_COLUMN) != data);
    }

    QListViewItem::setText(column, data);
    repaint();
}

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

void VarItem::expandValue(char *buf)
{
    LazyFetchItem::stopWaitingForData();
    RDBParser::parseExpandedVariable(this, buf);
}

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

void VarItem::setOpen(bool open)
{
    QListViewItem::setOpen(open);
	
	Q_ASSERT(	dataType_ == REFERENCE_TYPE 
				|| dataType_ == ARRAY_TYPE 
				|| dataType_ == HASH_TYPE
				|| dataType_ == STRING_TYPE
				|| dataType_ == STRUCT_TYPE );
					
	update();
	return;
}

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

void VarItem::update()
{
	if (isOpen()) {
		startWaitingForData();
//		emit ((VariableTree*)listView())->expandItem(this, fullName().latin1());
		((VariableTree*)listView())->expandItem(this, fullName().latin1());
	}

	return;
}

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

DataType VarItem::dataType() const
{
    return dataType_;
}

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

void VarItem::setDataType(DataType dataType)
{
    dataType_ = dataType;
}

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

// Overridden to highlight the changed items
void VarItem::paintCell(QPainter *p, const QColorGroup &cg,
                        int column, int width, int align)
{
    if (p == 0) {
        return;
	}

    if (column == VALUE_COLUMN) {
		// Show color values as colors, and make the text color the same
		// as the base color
		if (dataType_ == COLOR_TYPE) {
			QRegExp color_re("\\s(#.*)>");
			
			if (color_re.search(text(column)) != -1) {
        		QColorGroup color_cg(	cg.foreground(), cg.background(), 
										cg.light(), cg.dark(), cg.mid(), 
										QColor(color_re.cap(1)), QColor(color_re.cap(1)) );
        		QListViewItem::paintCell(p, color_cg, column, width, align);
				return;
			}
		}
		
		// Highlight recently changed items in red
		if (highlight_) {
        	QColorGroup hl_cg(	cg.foreground(), cg.background(), 
								cg.light(), cg.dark(), cg.mid(), 
								red, cg.base() );
        	QListViewItem::paintCell(p, hl_cg, column, width, align);
			return;
		}
	}
	
	QListViewItem::paintCell(p, cg, column, width, align);
	return;
}

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

QString VarItem::tipText() const
{
    const unsigned int MAX_TOOLTIP_SIZE = 70;
    QString tip = text(VALUE_COLUMN);

    if (tip.length() < MAX_TOOLTIP_SIZE) {
	    return tip;
    } else {
	    return tip.mid(0, MAX_TOOLTIP_SIZE - 1) + " [...]";
	}
}

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

VarFrameRoot::VarFrameRoot(VariableTree *parent, int frameNo, int threadNo)
    : LazyFetchItem(parent),
      needsVariables_(true),
      frameNo_(frameNo),
      threadNo_(threadNo),
      cache_("")
{
    setExpandable(true);
}

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

VarFrameRoot::~VarFrameRoot()
{
}

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

void VarFrameRoot::addLocals(char *variables)
{
    cache_.append(variables);
}

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

void VarFrameRoot::setLocals()
{
    RDBParser::parseVariables(this, cache_.data());
	cache_ = "";
    needsVariables_ = false;
	stopWaitingForData();
	prune();
	
	return;
}

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

// Override setOpen so that we can decide what to do when we do change
// state.
void VarFrameRoot::setOpen(bool open)
{
	bool localsViewChanged = (isOpen() != open);
    QListViewItem::setOpen(open);
	
    if (localsViewChanged) {
		((VariableTree*)listView())->selectFrame(frameNo_, threadNo_);
	}

	return;
}

void VarFrameRoot::setFrameName(const QString &frameName)
{
	setText(VAR_NAME_COLUMN, frameName); 
	setText(VALUE_COLUMN, "");
	
	return; 
}

void VarFrameRoot::setActivationId() 
{ 
	LazyFetchItem::setActivationId(); 
	stopWaitingForData();
	needsVariables_ = true;
	cache_ = "";
} 
	
bool VarFrameRoot::needsVariables() const 
{ 
	return (	text(VAR_NAME_COLUMN).contains("try_initialize") == 0 
				&& isOpen() 
				&& !isWaitingForData() 
				&& needsVariables_ ); 
}

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


GlobalRoot::GlobalRoot(VariableTree *parent)
    : LazyFetchItem(parent)
{
    setText(0, i18n("Global"));
    setExpandable(true);
    setOpen(false);
	setSelectable(false);
}

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

GlobalRoot::~GlobalRoot()
{
}

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

void GlobalRoot::setGlobals(char * globals)
{
    setActivationId();
    RDBParser::parseVariables(this, globals);
	
	return;
}

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

void GlobalRoot::setOpen(bool open)
{
	bool globalsViewChanged = (isOpen() != open);
	QListViewItem::setOpen(open);
	
	if (globalsViewChanged) {
    	((VariableTree*)listView())->setFetchGlobals(isOpen());
	}
	
	return;
}

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

WatchVarItem::WatchVarItem( LazyFetchItem *parent, const QString &varName, DataType dataType, int displayId )
    : VarItem(parent, varName, dataType),
	displayId_(displayId)
{
}

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

WatchVarItem::~WatchVarItem()
{
}

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

void WatchVarItem::setDisplayId(int id)
{
	displayId_ = id;
}

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

int WatchVarItem::displayId()
{
	return displayId_;
}

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

WatchRoot::WatchRoot(VariableTree *parent)
    : LazyFetchItem(parent)
{
    setText(VAR_NAME_COLUMN, i18n("Watch"));
    setOpen(true);
	setSelectable(false);
}

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

WatchRoot::~WatchRoot()
{
}

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

// Sets the initial value of a new Watch item, along with the
// display id
void WatchRoot::setWatchExpression(char * buf, char * expression)
{
	QString expr(expression);
	QRegExp display_re("^(\\d+):\\s([^\n]+)\n");
	
    for (	QListViewItem *child = firstChild(); 
			child != 0; 
			child = child->nextSibling() ) 
	{
        WatchVarItem *varItem = (WatchVarItem*) child;
		if (	varItem->text(VAR_NAME_COLUMN) == expr 
				&& varItem->displayId() == -1
				&& display_re.search(buf) >= 0 ) 
		{
			varItem->setDisplayId(display_re.cap(1).toInt());
			// Skip over the 'thing = ' part of expr to get the value
			varItem->setText(	VALUE_COLUMN, 
								display_re.cap(2).mid(varItem->text(VAR_NAME_COLUMN).length() + strlen(" = ")) );
			return;
		}
	}
}

// After a program pause, this updates the new value of a Watch item
// expr is the thing = value part of "1: a = 1", id is the display number
void WatchRoot::updateWatchExpression(int id, const QString& expr)
{
    for (	QListViewItem *child = firstChild(); 
			child != 0; 
			child = child->nextSibling() ) 
	{
        WatchVarItem *varItem = (WatchVarItem*) child;
		if (varItem->displayId() == id) {
			Q_ASSERT( expr.startsWith(varItem->text(VAR_NAME_COLUMN)) );
			// Skip over the 'thing = ' part of expr to get the value
			varItem->setText(	VALUE_COLUMN, 
								expr.mid(varItem->text(VAR_NAME_COLUMN).length() + strlen(" = ")) );
			return;
		}
	}
}

void WatchRoot::savePartialProjectSession(QDomElement* el)
{
    QDomDocument domDoc = el->ownerDocument();
    if (domDoc.isNull()) {
        return;
	}
	
    QDomElement watchEl = domDoc.createElement("watchExpressions");
    
	for (	QListViewItem *child = firstChild(); 
			child != 0; 
			child = child->nextSibling() )
	{
        QDomElement subEl = domDoc.createElement("el");
        subEl.appendChild(domDoc.createTextNode(child->text(VAR_NAME_COLUMN)));
        watchEl.appendChild(subEl);
	}
	
    if (!watchEl.isNull()) {
        el->appendChild(watchEl);
	}
	
	return;
}

void WatchRoot::restorePartialProjectSession(const QDomElement* el)
{
    QDomDocument domDoc = el->ownerDocument();
    if (domDoc.isNull()) {
        return;
	}
	
    QDomElement watchEl = el->namedItem("watchExpressions").toElement();
    QDomElement subEl = watchEl.firstChild().toElement();
	
    while (!subEl.isNull()) {
		new WatchVarItem(this, subEl.firstChild().toText().data(), UNKNOWN_TYPE);
		
        subEl = subEl.nextSibling().toElement();
    }
	
	return;
}

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

}


#include "variablewidget.moc"

