//Author:    Timothy Pearson <kb9vqf@pearsoncomputing.net>, (C) 2012
//Copyright: See COPYING file that comes with this distribution

#include "tracewidget.h"

#include <stdlib.h>
#include <cmath>

#include <tqpixmap.h>
#include <tqpainter.h>
#include <tqpushbutton.h>
#include <tqtoolbutton.h>

#include <tqlabel.h>
#include <tqlayout.h>

#include <tdelocale.h>
#include <kinputdialog.h>

#define VERIFY_TRACE_ARRAY_SIZE if (traceNumber >= m_traceArray.count()) resizeTraceArray(traceNumber+1);
#define VERIFY_CURSOR_ARRAY_SIZE if (cursorNumber >= m_cursorArray.count()) resizeCursorArray(cursorNumber+1);

#define CURSOR_DARKNESS_FACTOR 200
#define ZOOM_SHADING_DARKNESS_FACTOR 200

#define CURSOR_MOVE_CAPTURE_DISTANCE 0

TQRectF::TQRectF() {
	m_valid = false;
}

TQRectF::TQRectF(double x, double y, double w, double h) {
	m_x = x;
	m_y = y;
	m_w = w;
	m_h = h;
	m_valid = true;
}

double TQRectF::x() const {
	return m_x;
}

double TQRectF::y() const {
	return m_y;
}

double TQRectF::width() const {
	return m_w;
}

double TQRectF::height() const {
	return m_h;
}

void TQRectF::setX(double val) {
	m_x = val;
	m_valid = true;
}

void TQRectF::setY(double val) {
	m_y = val;
	m_valid = true;
}

void TQRectF::setWidth(double val) {
	m_w = val;
	m_valid = true;
}

void TQRectF::setHeight(double val) {
	m_h = val;
	m_valid = true;
}

bool TQRectF::isNull() const {
	return !m_valid;
}

bool TQRectF::operator!() const {
	return isNull();
}

bool TQRectF::operator==(const TQRectF &r1) {
	bool match = true;
	if (r1.m_valid != m_valid) match = false;
	if (r1.m_x != m_x) match = false;
	if (r1.m_y != m_y) match = false;
	if (r1.m_w != m_w) match = false;
	if (r1.m_h != m_h) match = false;
	return match;
}

bool TQRectF::operator!=(const TQRectF &r1) {
	return !operator==(r1);
}

TraceData::TraceData(TraceWidget* parent, TQWidget* labelParent) : TQObject(), parentWidget(parent) {
	color = TQColor(0, 255, 0);
	numberOfSamples = 0;
	leftEdgeIndex = -1;
	rightEdgeIndex = -1;
	offset = 0.0;
	leftEdge = 0;
	rightEdge = 0;
	topEdge = 0;
	bottomEdge = 0;
	traceName = i18n("Unknown");
	horizontalUnits = i18n("Units");
	verticalUnits = i18n("Units");
	enabled = false;

	if (labelParent) {
		paramLabel = new TQLabel(labelParent);
		paramLabel->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		paramLabel->setPaletteForegroundColor(color);
		paramLabel->setAlignment(TQt::AlignHCenter|TQt::AlignVCenter|TQt::SingleLine);
		TQFont font;
		font = paramLabel->font();
		font.setPointSize(font.pointSize()-1);
		paramLabel->setFont(font);
		paramLabel->hide();
		graphStatusLabel = new TQLabel(labelParent);
		graphStatusLabel->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		graphStatusLabel->setPaletteForegroundColor(color);
		graphStatusLabel->setAlignment(TQt::AlignHCenter|TQt::AlignVCenter|TQt::SingleLine);
		font = graphStatusLabel->font();
		font.setPointSize(font.pointSize()-1);
		graphStatusLabel->setFont(font);
		graphStatusLabel->setText("<qt></qt>");
		graphStatusLabel->hide();
		graphStatusLabelInner = new TQLabel(labelParent);
		graphStatusLabelInner->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		graphStatusLabelInner->setPaletteForegroundColor(color);
		graphStatusLabelInner->setAlignment(TQt::AlignHCenter|TQt::AlignVCenter|TQt::SingleLine);
		font = graphStatusLabelInner->font();
		font.setPointSize(font.pointSize()-1);
		graphStatusLabelInner->setFont(font);
		graphStatusLabelInner->setText("<qt></qt>");
		graphStatusLabelInner->hide();
		singleIncrBtn = new TQToolButton(TQt::UpArrow, labelParent);
		singleDecrBtn = new TQToolButton(TQt::DownArrow, labelParent);
		posResetBtn = new TQToolButton(labelParent);
		posResetBtn->setText("0");
		posSetBtn = new TQToolButton(labelParent);
		posSetBtn->setText("M");
		singleIncrBtn->setFixedSize(16,16);
		singleDecrBtn->setFixedSize(16,16);
		posResetBtn->setFixedSize(16,16);
		posSetBtn->setFixedSize(16,16);
		singleIncrBtn->setAutoRepeat(true);
		singleDecrBtn->setAutoRepeat(true);
		posResetBtn->setAutoRepeat(false);
		posSetBtn->setAutoRepeat(false);
		singleIncrBtn->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed));
		singleDecrBtn->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed));
		posResetBtn->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed));
		posSetBtn->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed));
		singleIncrBtn->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		singleIncrBtn->setPaletteForegroundColor(color);
		singleDecrBtn->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		singleDecrBtn->setPaletteForegroundColor(color);
		posResetBtn->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		posResetBtn->setPaletteForegroundColor(color);
		posSetBtn->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		posSetBtn->setPaletteForegroundColor(color);
		singleIncrBtn->hide();
		singleDecrBtn->hide();
		posResetBtn->hide();
		posSetBtn->hide();
		connect(singleIncrBtn, SIGNAL(clicked()), this, SLOT(movePosOneTick()));
		connect(singleDecrBtn, SIGNAL(clicked()), this, SLOT(moveNegOneTick()));
		connect(posResetBtn, SIGNAL(clicked()), this, SLOT(resetVPosition()));
		connect(posSetBtn, SIGNAL(clicked()), this, SLOT(setVPosition()));
	}
	else {
		paramLabel = NULL;
		graphStatusLabel = NULL;
		graphStatusLabelInner = NULL;
		singleIncrBtn = NULL;
		singleDecrBtn = NULL;
		posResetBtn = NULL;
		posSetBtn = NULL;
	}
}

TraceData::~TraceData() {
	//
}

void TraceData::drawTrace(TQPainter* p, int graticule_width, int graticule_height) {
	p->save();
	p->setPen(color);

	if ((bottomEdge != topEdge) && (enabled) && (positionArray.count() >= numberOfSamples) && (sampleArray.count() >= numberOfSamples) && (numberOfSamples > 0)) {
		// Draw the points
		unsigned int n;
		unsigned int incr;
		unsigned int activeSamples;
		int x,y,x2,y2;
		// Determine how many samples are actually being displayed
		if ((leftEdgeIndex < 0) || (rightEdgeIndex < 0)) {
			for (n=0; n<numberOfSamples; n++) {
				x = (((positionArray[n]-leftEdge)/(rightEdge-leftEdge))*(graticule_width));
				if (leftEdgeIndex < 0) {
					if (x >= 0) {
						leftEdgeIndex = n;
					}
				}
				else {
					if (x >= graticule_width) {
						rightEdgeIndex = n;
						break;
					}
				}
			}
			if (rightEdgeIndex < 0) {
				rightEdgeIndex = numberOfSamples-1;
			}
		}
		activeSamples = abs(rightEdgeIndex-leftEdgeIndex);
		incr = (activeSamples/graticule_width)+1;
		for (n=leftEdgeIndex; n<numberOfSamples-incr; n=n+incr) {
			x = (((positionArray[n]-leftEdge)/(rightEdge-leftEdge))*(graticule_width));
			y = ((((sampleArray[n]+offset)-topEdge)/(bottomEdge-topEdge))*(graticule_height));
			x2 = (((positionArray[n+incr]-leftEdge)/(rightEdge-leftEdge))*(graticule_width));
			y2 = ((((sampleArray[n+incr]+offset)-topEdge)/(bottomEdge-topEdge))*(graticule_height));

			if (x < 0) x = 0;
			if (x > graticule_width) x = graticule_width;
			if (y < 0) y = 0;
			if (y > graticule_height) y = graticule_height;
			if (x2 < 0) x2 = 0;
			if (x2 > graticule_width) x2 = graticule_width;
			if (y2 < 0) y2 = 0;
			if (y2 > graticule_height) y2 = graticule_height;

			p->drawLine(x, y, x2, y2);
		}

		// Draw the zero level indicator
		int pixel_size = 20;
		TQFont painterFont = p->font();
		painterFont.setFamily("Monospace");
		painterFont.setPixelSize(pixel_size);
		p->setFont(painterFont);
		int font_vertical_offset = pixel_size/5;
		int font_height = p->fontMetrics().boundingRect("→").height();

		x = 0;
		y = (((offset-topEdge)/(bottomEdge-topEdge))*(graticule_height))+(font_height/2)-(font_vertical_offset/2);
		if (y > graticule_height) {
			font_height = p->fontMetrics().boundingRect("↓").height();
			y = graticule_height-font_vertical_offset;
			p->drawText(x, y, trUtf8("↓"));
		}
		else if (y < 0) {
			font_height = p->fontMetrics().boundingRect("↑").height();
			y = font_height-font_vertical_offset;
			p->drawText(x, y, trUtf8("↑"));
		}
		else {
			p->drawText(x, y, trUtf8("→"));
		}
	}

	p->restore();
}

void TraceData::movePosOneTick() {
	double increment;
	increment = (bottomEdge-topEdge)/parentWidget->m_graticuleWidget->height();
	offset -= increment;
	emit(offsetChanged(offset));

	parentWidget->updateTraceText();
	parentWidget->updateCursorText();
	parentWidget->m_graticuleWidget->repaint(false);
}

void TraceData::moveNegOneTick() {
	double increment;
	increment = (bottomEdge-topEdge)/parentWidget->m_graticuleWidget->height();
	offset += increment;
	emit(offsetChanged(offset));

	parentWidget->updateTraceText();
	parentWidget->updateCursorText();
	parentWidget->m_graticuleWidget->repaint(false);
}

void TraceData::resetVPosition() {
	offset  = 0.0;
	emit(offsetChanged(offset));

	parentWidget->updateTraceText();
	parentWidget->updateCursorText();
	parentWidget->m_graticuleWidget->repaint(false);
}

void TraceData::setVPosition() {
	bool ok;
	double newoffset;

	ok = false;
	newoffset = KInputDialog::getDouble(i18n("Set Trace Offset"), i18n("New offset for %1 (%2):").arg(traceName).arg(verticalUnits), offset, (double)-2147483647, (double)2147483647, 0.1, 1, &ok, parentWidget);
	if (ok) {
		offset = newoffset;
		emit(offsetChanged(offset));

		parentWidget->updateTraceText();
		parentWidget->updateCursorText();
		parentWidget->m_graticuleWidget->repaint(false);
	}
}

CursorData::CursorData(TraceWidget* parent, TQWidget* labelParent) : TQObject(), parentWidget(parent) {
	color = TQColor(0, 255, 0);
	highlightColor = TQColor(192, 255, 192);
	highlighted = false;
	enabled = false;
	orientation = TQt::Vertical;
	position = 50;
	cursorName = i18n("Cursor <?>");

	if (labelParent) {
		paramLabel = new TQLabel(labelParent);
		paramLabel->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		paramLabel->setPaletteForegroundColor(color);
		paramLabel->setAlignment(TQt::AlignHCenter|TQt::AlignVCenter|TQt::SingleLine);
		TQFont font;
		font = paramLabel->font();
		font.setPointSize(font.pointSize()-1);
		paramLabel->setFont(font);
		paramLabel->hide();
		singleIncrBtn = new TQPushButton(labelParent);
		singleDecrBtn = new TQPushButton(labelParent);
		multiIncrBtn = new TQPushButton(labelParent);
		multiDecrBtn = new TQPushButton(labelParent);
		font = singleIncrBtn->font();
		font.setPointSize(font.pointSize()-1);
		singleIncrBtn->setFont(font);
		singleDecrBtn->setFont(font);
		multiIncrBtn->setFont(font);
		multiDecrBtn->setFont(font);
		singleIncrBtn->setText("+");
		singleDecrBtn->setText("-");
		multiIncrBtn->setText("++");
		multiDecrBtn->setText("--");
		singleIncrBtn->setAutoRepeat(true);
		singleDecrBtn->setAutoRepeat(true);
		multiIncrBtn->setAutoRepeat(true);
		multiDecrBtn->setAutoRepeat(true);
		singleIncrBtn->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed));
		singleDecrBtn->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed));
		multiIncrBtn->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed));
		multiDecrBtn->setSizePolicy(TQSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed));
		singleIncrBtn->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		singleIncrBtn->setPaletteForegroundColor(color);
		singleDecrBtn->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		singleDecrBtn->setPaletteForegroundColor(color);
		multiIncrBtn->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		multiIncrBtn->setPaletteForegroundColor(color);
		multiDecrBtn->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		multiDecrBtn->setPaletteForegroundColor(color);
		singleIncrBtn->hide();
		singleDecrBtn->hide();
		multiIncrBtn->hide();
		multiDecrBtn->hide();
		paramLabel->installEventFilter(this);
		singleIncrBtn->installEventFilter(this);
		singleDecrBtn->installEventFilter(this);
		multiIncrBtn->installEventFilter(this);
		multiDecrBtn->installEventFilter(this);
		connect(singleIncrBtn, SIGNAL(clicked()), this, SLOT(movePosOneTick()));
		connect(singleDecrBtn, SIGNAL(clicked()), this, SLOT(moveNegOneTick()));
		connect(multiIncrBtn, SIGNAL(clicked()), this, SLOT(movePosMultiTicks()));
		connect(multiDecrBtn, SIGNAL(clicked()), this, SLOT(moveNegMultiTicks()));
	}
	else {
		paramLabel = NULL;
		singleIncrBtn = NULL;
		singleDecrBtn = NULL;
		multiIncrBtn = NULL;
		multiDecrBtn = NULL;
	}
}

CursorData::~CursorData() {
	//
}

void CursorData::drawCursor(TQPainter* p, int graticule_width, int graticule_height) {
	if (enabled) {
		if (highlighted) {
			p->setPen(highlightColor.dark(parentWidget->m_cursorDarkness));
		}
		else {
			p->setPen(color.dark(parentWidget->m_cursorDarkness));
		}

		if (orientation == TQt::Vertical) {
			int x = abs(((position)/(100.0))*(graticule_width));
			p->drawLine(x, 0, x, graticule_height);
		}
		else {
			int y = abs(((position)/(100.0))*(graticule_height));
			p->drawLine(0, y, graticule_width, y);
		}
	}
}

void CursorData::movePosOneTick() {
	double increment;
	if (orientation == TQt::Horizontal) {
		increment = 100.0/parentWidget->m_graticuleWidget->height();
	}
	else {
		increment = 100.0/parentWidget->m_graticuleWidget->width();
	}
	if (orientation == TQt::Horizontal) {
		position -= increment;
	}
	else {
		position += increment;
	}
	if (position < 0.0) position = 0.0;
	if (position > 100.0) position = 100.0;

	emit(positionChanged(position));
	parentWidget->updateCursorText();
	parentWidget->m_graticuleWidget->updateGraticule();
	parentWidget->m_graticuleWidget->repaint(false);
}

void CursorData::moveNegOneTick() {
	double increment;
	if (orientation == TQt::Horizontal) {
		increment = 100.0/parentWidget->m_graticuleWidget->height();
	}
	else {
		increment = 100.0/parentWidget->m_graticuleWidget->width();
	}
	if (orientation == TQt::Horizontal) {
		position += increment;
	}
	else {
		position -= increment;
	}
	if (position < 0.0) position = 0.0;
	if (position > 100.0) position = 100.0;

	emit(positionChanged(position));
	parentWidget->updateCursorText();
	parentWidget->m_graticuleWidget->updateGraticule();
	parentWidget->m_graticuleWidget->repaint(false);
}

void CursorData::movePosMultiTicks() {
	double increment;
	if (orientation == TQt::Horizontal) {
		increment = 100.0/parentWidget->m_graticuleWidget->height();
	}
	else {
		increment = 100.0/parentWidget->m_graticuleWidget->width();
	}
	if (orientation == TQt::Horizontal) {
		position -= (increment*10.0);
	}
	else {
		position += (increment*10.0);
	}
	if (position < 0.0) position = 0.0;
	if (position > 100.0) position = 100.0;

	emit(positionChanged(position));
	parentWidget->updateCursorText();
	parentWidget->m_graticuleWidget->updateGraticule();
	parentWidget->m_graticuleWidget->repaint(false);
}

void CursorData::moveNegMultiTicks() {
	double increment;
	if (orientation == TQt::Horizontal) {
		increment = 100.0/parentWidget->m_graticuleWidget->height();
	}
	else {
		increment = 100.0/parentWidget->m_graticuleWidget->width();
	}
	if (orientation == TQt::Horizontal) {
		position += (increment*10.0);
	}
	else {
		position -= (increment*10.0);
	}
	if (position < 0.0) position = 0.0;
	if (position > 100.0) position = 100.0;

	emit(positionChanged(position));
	parentWidget->updateCursorText();
	parentWidget->m_graticuleWidget->updateGraticule();
	parentWidget->m_graticuleWidget->repaint(false);
}

bool CursorData::eventFilter(TQObject *o, TQEvent *e) {
	// Intercept mouse entry/leave events for cursor labels and movement buttons
	if ((o == paramLabel) || (o == singleIncrBtn) || (o == singleDecrBtn) || (o == multiIncrBtn) || (o == multiDecrBtn)) {
		if (e->type() == TQEvent::Enter) {
			highlighted = true;
			parentWidget->updateCursorText();
			parentWidget->m_graticuleWidget->updateGraticule();
			parentWidget->m_graticuleWidget->repaint(false);
		}
		if (e->type() == TQEvent::Leave) {
			highlighted = false;
			parentWidget->updateCursorText();
			parentWidget->m_graticuleWidget->updateGraticule();
			parentWidget->m_graticuleWidget->repaint(false);
		}
	}
	// Allow event processing by other routines
	return FALSE;
}

GraticuleWidget::GraticuleWidget(TraceWidget* parent, const char* name) : TQWidget(parent, name),
	m_base(parent),
	m_graticulePixmap(0),
	m_leftMouseDown(false),
	m_middleMouseDown(false),
	m_closestCursor(-1),
	m_closestCursorDistance(-1),
	m_movingCursor(-1) {
	setBackgroundMode(NoBackground);
	setSizePolicy(TQSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding));

	setPaletteBackgroundColor(TQt::black);
	setPaletteForegroundColor(TQColor(0,128,0));
	setMouseTracking(true);
	setCursor(tqcrossCursor);
}

GraticuleWidget::~GraticuleWidget() {
	//
}

TQSizePolicy GraticuleWidget::sizePolicy() const {
	return TQSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding, true, true);
}

void GraticuleWidget::updateGraticule() {
	unsigned int d,s,x,y;

	if (m_graticulePixmap) {
		delete m_graticulePixmap;
	}
	m_graticulePixmap = new TQPixmap(width(), height());

	// Draw the graticule into the pixmap
	TQPainter p(m_graticulePixmap);
	p.setPen(TQPen(foregroundColor(), 1, TQt::SolidLine));
	p.fillRect(0, 0, m_graticulePixmap->width(), m_graticulePixmap->height(), backgroundColor());
	p.setPen(TQPen(foregroundColor(), 1, TQt::DotLine));
	if (m_base->m_horizDivs > 0) {
		s = m_graticulePixmap->width() / m_base->m_horizDivs;
		x = 0;
		for (d=0; d<m_base->m_horizDivs; d++) {
			p.drawLine(x, 0, x, m_graticulePixmap->height());
			x += s;
		}
	}
	if (m_base->m_vertDivs > 0) {
		s = m_graticulePixmap->height() / m_base->m_vertDivs;
		y = 0;
		for (d=0; d<m_base->m_vertDivs; d++) {
			p.drawLine(0, y, m_graticulePixmap->width(), y);
			y += s;
		}
	}
	p.setPen(TQPen(foregroundColor(), 1, TQt::SolidLine));
	p.drawRect(0, 0, m_graticulePixmap->width(), m_graticulePixmap->height());

	// If there are 4 or more cursors, lightly shade the area returned by ZoomBox
	TQRectF zoomBox = m_base->zoomBox();
	if (!zoomBox.isNull()) {
		// Translate zoombox coordinates to local drawing coordinates
		TQRect drawingRect(abs(((zoomBox.x())/(100.0))*(width())), abs(((zoomBox.y())/(100.0))*(height())), abs(((zoomBox.width())/(100.0))*(width())), abs(((zoomBox.height())/(100.0))*(height())));
		p.fillRect(drawingRect, TQBrush(foregroundColor().dark(m_base->m_zoomBoxDarkness), TQt::BDiagPattern));
	}

	// Repaint the widget
	repaint(false);
}

void GraticuleWidget::paintEvent(TQPaintEvent*) {
	// Paint onto background pixmap before painting to window to reduce flickering
	TQPixmap updatePixmap(width(), height());
	TQPainter p(&updatePixmap);
	if (m_graticulePixmap) {
		// Draw the graticule pixmap to erase the widget
		p.drawPixmap(0, 0, *m_graticulePixmap);

		// Draw the traces
		for (uint trace=0;trace<m_base->m_traceArray.count();trace++) {
			m_base->m_traceArray[trace]->drawTrace(&p, m_graticulePixmap->width(), m_graticulePixmap->height());
		}

		// Draw the cursors
		for (uint cursor=0;cursor<m_base->m_cursorArray.count();cursor++) {
			m_base->m_cursorArray[cursor]->drawCursor(&p, m_graticulePixmap->width(), m_graticulePixmap->height());
		}
	}
	else {
		p.fillRect(x(), y(), width(), height(), backgroundColor());
	}
	p.end();
	// Paint to window
	bitBlt(this, 0, 0, &updatePixmap, 0, 0, updatePixmap.width(), updatePixmap.height(), TQt::CopyROP);
}

void GraticuleWidget::resizeEvent(TQResizeEvent *) {
	updateGraticule();
}

void GraticuleWidget::mousePressEvent(TQMouseEvent *e) {
	if ((e->button() == TQt::LeftButton) && (!m_leftMouseDown) && (!m_middleMouseDown)) {
		if (m_closestCursorDistance <= CURSOR_MOVE_CAPTURE_DISTANCE) {
			m_prevDownPos = e->pos();
			m_movingCursor = m_closestCursor;
			m_prevCursorPos = m_base->m_cursorArray[m_movingCursor]->position;
			m_leftMouseDown = true;
		}
		else {
			m_prevCursorRect = m_base->zoomCursorBox();
			if (m_base->m_zoomBoxEnabled) {
				m_leftMouseDown = true;
				m_prevDownPos = e->pos();
			}
		}
	}
	else if ((e->button() == TQt::MidButton) && (!m_leftMouseDown) && (!m_middleMouseDown)) {
		m_prevCursorRect = m_base->zoomCursorBox();
		if (m_base->m_zoomBoxEnabled) {
			m_middleMouseDown = true;
			m_prevDownPos = e->pos();
			setCursor(tqsizeAllCursor);
		}
	}
}

void GraticuleWidget::mouseReleaseEvent(TQMouseEvent *e) {
	if (m_leftMouseDown) {
		if (e->button() == TQt::LeftButton) {
			m_leftMouseDown = false;

			if (m_movingCursor >= 0) {
				TQPoint diff = e->pos() - m_prevDownPos;
				double dx = diff.x()*(100.0/width());
				double dy = diff.y()*(100.0/height());
				if (m_base->m_cursorArray[m_movingCursor]->orientation == TQt::Horizontal) {
					m_base->m_cursorArray[m_movingCursor]->position = m_prevCursorPos+dy;
				}
				else {
					m_base->m_cursorArray[m_movingCursor]->position = m_prevCursorPos+dx;
				}
				if (m_base->m_cursorArray[m_movingCursor]->position < 0.0) {
					m_base->m_cursorArray[m_movingCursor]->position = 0.0;
				}
				if (m_base->m_cursorArray[m_movingCursor]->position > 100.0) {
					m_base->m_cursorArray[m_movingCursor]->position = 100.0;
				}
				emit(cursorPositionChanged(m_movingCursor, m_base->m_cursorArray[m_movingCursor]->position));
				m_movingCursor = -1;
				updateGraticule();
				repaint(false);
			}
			else {
				double x1 = m_prevDownPos.x();
				double y1 = m_prevDownPos.y();
				double x2 = e->x();
				double y2 = e->y();
				double pixelDiffX = fabs(x1-x2);
				double pixelDiffY = fabs(y1-y2);
				if ((x1 < width()) && (y1 < height()) && (x2 < width()) && (y2 < height()) && (x1 > 0) && (y1 > 0) && (x2 > 0) && (y2 > 0) && (pixelDiffX>0) && (pixelDiffY>0)) {
					x1 = ((x1/width())*100.0);
					y1 = ((y1/height())*100.0);
					x2 = ((x2/width())*100.0);
					y2 = ((y2/height())*100.0);
					m_base->setZoomCursorBox(TQRectF(x1, y1, x2, y2));
				}
				else {
					// Reset original zoom box
					m_base->setZoomCursorBox(m_prevCursorRect);
				}
			}
		}
	}
	else if (m_middleMouseDown) {
		if (e->button() == TQt::MidButton) {
			m_middleMouseDown = false;

			double x1 = m_prevDownPos.x();
			double y1 = m_prevDownPos.y();
			double x2 = e->x();
			double y2 = e->y();
			if ((x1 < width()) && (y1 < height()) && (x2 < width()) && (y2 < height()) && (x1 > 0) && (y1 > 0) && (x2 > 0) && (y2 > 0)) {
				TQPoint diff = e->pos() - m_prevDownPos;
				double dx = diff.x()*(100.0/width());
				double dy = diff.y()*(100.0/height());
				m_base->setZoomCursorBox(TQRectF(m_prevCursorRect.x()+dx, m_prevCursorRect.y()+dy, m_prevCursorRect.width()+dx, m_prevCursorRect.height()+dy));
			}
			else {
				// Reset original zoom box
				m_base->setZoomCursorBox(m_prevCursorRect);
			}
			setCursor(tqcrossCursor);
		}
	}

	updateGraticule();
	repaint(false);
}

void GraticuleWidget::mouseDoubleClickEvent(TQMouseEvent *) {
	//
}

void GraticuleWidget::mouseMoveEvent(TQMouseEvent *e) {
	// If we are within X pixels of a cursor, change icon to appopriate drag arrows and set up to drag the cursor instead of replacing the zoom box
	// Replacing the zoom box might need to be assigned to the right mouse button...
	bool cursorHighlightChanged = false;
	m_closestCursor = -1;
	m_closestCursorDistance = -1;
	if ((!m_leftMouseDown) && (!m_middleMouseDown) && (m_movingCursor < 0)) {
		for (uint cursor=0;cursor<m_base->m_cursorArray.count();cursor++) {
			double scaledYPos = (e->y()*100.0)/height();
			double scaledXPos = (e->x()*100.0)/width();
			unsigned int pixelDistance;
			if (m_base->m_cursorArray[cursor]->orientation == TQt::Horizontal) {
				pixelDistance = (fabs(m_base->m_cursorArray[cursor]->position - scaledYPos)*height())/100.0;
				if (pixelDistance < m_closestCursorDistance) {
					m_closestCursorDistance = pixelDistance;
					m_closestCursor = cursor;
				}
			}
			else {
				pixelDistance = (fabs(m_base->m_cursorArray[cursor]->position - scaledXPos)*width())/100.0;
				if (pixelDistance < m_closestCursorDistance) {
					m_closestCursorDistance = pixelDistance;
					m_closestCursor = cursor;
				}
			}
			// Ensure previous highlighting is cleared
			// This is needed when two cursors are placed right next to one another
			if (m_base->m_cursorArray[m_closestCursor]->highlighted) {
				cursorHighlightChanged = true;
			}
			m_base->m_cursorArray[cursor]->highlighted = false;
		}
		if (m_closestCursor >= 0) {
			if (m_closestCursorDistance <= CURSOR_MOVE_CAPTURE_DISTANCE) {
				if (m_base->m_cursorArray[m_closestCursor]->orientation == TQt::Horizontal) {
					setCursor(tqsizeVerCursor);
				}
				else {
					setCursor(tqsizeHorCursor);
				}
				if (!m_base->m_cursorArray[m_closestCursor]->highlighted) {
					cursorHighlightChanged = true;
				}
				m_base->m_cursorArray[m_closestCursor]->highlighted = true;
			}
			else {
				setCursor(tqcrossCursor);
				if (m_base->m_cursorArray[m_closestCursor]->highlighted) {
					cursorHighlightChanged = true;
				}
				m_base->m_cursorArray[m_closestCursor]->highlighted = false;
			}
		}
		else {
			setCursor(tqcrossCursor);
		}
	}

	// Print current cursor location for all traces
	if ((e->x() < width()) && (e->y() < height())) {
		for (uint trace=0;trace<m_base->m_traceArray.count();trace++) {
			// Calculate location
			double scaledYPos = (e->y()*100.0)/height();
			double scaledXPos = (e->x()*100.0)/width();
			double horizontal_range = (m_base->m_traceArray[trace]->rightEdge-m_base->m_traceArray[trace]->leftEdge);
			double vertical_range = (m_base->m_traceArray[trace]->bottomEdge-m_base->m_traceArray[trace]->topEdge);
			double realCursorYPosition = (m_base->m_traceArray[trace]->topEdge+((scaledYPos/100.0)*vertical_range)-m_base->m_traceArray[trace]->offset);
			double realCursorXPosition = (m_base->m_traceArray[trace]->leftEdge+((scaledXPos/100.0)*horizontal_range));
#if 0
			m_base->m_traceArray[trace]->graphStatusLabel->setText(TQString("<qt><nobr>%1<br>@%2,%3</qt>").arg(m_base->m_traceArray[trace]->traceName).arg(TraceWidget::prettyFormat(realCursorXPosition, horizontal_range, m_base->m_traceArray[trace]->horizontalUnits)).arg(TraceWidget::prettyFormat(realCursorYPosition, vertical_range, m_base->m_traceArray[trace]->verticalUnits)));
#else
			m_base->m_traceArray[trace]->graphStatusLabel->setText(TQString("<qt><nobr>@%2,%3</qt>").arg(TraceWidget::prettyFormat(realCursorXPosition, (m_base->m_useAbsoluteHorizontalRange)?m_base->m_traceArray[trace]->rightEdge:horizontal_range, m_base->m_traceArray[trace]->horizontalUnits)).arg(TraceWidget::prettyFormat(realCursorYPosition, vertical_range, m_base->m_traceArray[trace]->verticalUnits)));
#endif
			m_base->m_traceArray[trace]->graphStatusLabelInner->setText(m_base->m_traceArray[trace]->graphStatusLabel->text());
		}
	}
	else {
		for (uint trace=0;trace<m_base->m_traceArray.count();trace++) {
			m_base->m_traceArray[trace]->graphStatusLabel->setText("<qt></qt>");
			m_base->m_traceArray[trace]->graphStatusLabelInner->setText(m_base->m_traceArray[trace]->graphStatusLabel->text());
		}
	}

	if ((m_leftMouseDown) && (m_movingCursor < 0)) {
		double x1 = m_prevDownPos.x();
		double y1 = m_prevDownPos.y();
		double x2 = e->x();
		double y2 = e->y();
		if ((x1 < width()) && (y1 < height()) && (x2 < width()) && (y2 < height()) && (x1 > 0) && (y1 > 0) && (x2 > 0) && (y2 > 0)) {
			x1 = ((x1/width())*100.0);
			y1 = ((y1/height())*100.0);
			x2 = ((x2/width())*100.0);
			y2 = ((y2/height())*100.0);
			m_base->setZoomCursorBox(TQRectF(x1, y1, x2, y2));
		}
	}
	else if ((m_leftMouseDown) && (m_movingCursor >= 0)) {
		TQPoint diff = e->pos() - m_prevDownPos;
		double dx = diff.x()*(100.0/width());
		double dy = diff.y()*(100.0/height());
		if (m_base->m_cursorArray[m_movingCursor]->orientation == TQt::Horizontal) {
			m_base->m_cursorArray[m_movingCursor]->position = m_prevCursorPos+dy;
		}
		else {
			m_base->m_cursorArray[m_movingCursor]->position = m_prevCursorPos+dx;
		}
		if (m_base->m_cursorArray[m_movingCursor]->position < 0.0) {
			m_base->m_cursorArray[m_movingCursor]->position = 0.0;
		}
		if (m_base->m_cursorArray[m_movingCursor]->position > 100.0) {
			m_base->m_cursorArray[m_movingCursor]->position = 100.0;
		}
		emit(cursorPositionChanged(m_movingCursor, m_base->m_cursorArray[m_movingCursor]->position));
		updateGraticule();
		repaint(false);
		cursorHighlightChanged = false;
	}
	else if (m_middleMouseDown) {
		TQPoint diff = e->pos() - m_prevDownPos;
		double dx = diff.x()*(100.0/width());
		double dy = diff.y()*(100.0/height());
		m_base->setZoomCursorBox(TQRectF(m_prevCursorRect.x()+dx, m_prevCursorRect.y()+dy, m_prevCursorRect.width()+dx, m_prevCursorRect.height()+dy));
	}

	m_base->updateCursorText();
	if (cursorHighlightChanged) {
		updateGraticule();
		repaint(false);
	}
}

void GraticuleWidget::enterEvent(TQEvent *) {
	//
}

void GraticuleWidget::leaveEvent(TQEvent *) {
	for (uint trace=0;trace<m_base->m_traceArray.count();trace++) {
		m_base->m_traceArray[trace]->graphStatusLabel->setText("<qt></qt>");
		m_base->m_traceArray[trace]->graphStatusLabelInner->setText(m_base->m_traceArray[trace]->graphStatusLabel->text());
	}
}

TraceWidget::TraceWidget(TQWidget* parent, const char* name) : TQWidget(parent, name),
	m_horizDivs(0),
	m_vertDivs(0),
	m_cursorDarkness(CURSOR_DARKNESS_FACTOR),
	m_zoomBoxDarkness(ZOOM_SHADING_DARKNESS_FACTOR),
	m_zoomCursorStartIndex(0),
	m_zoomBoxEnabled(false),
	m_useAbsoluteHorizontalRange(true) {
	setBackgroundMode(NoBackground);
	setSizePolicy(TQSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding));

	m_primaryLayout = new TQGridLayout(this);
	m_graticuleWidget = new GraticuleWidget(this);
	connect(m_graticuleWidget, SIGNAL(cursorPositionChanged(uint, double)), this, SLOT(processChangedCursor(uint, double)));
	m_primaryLayout->addMultiCellWidget(m_graticuleWidget, 0, 254, 0, 254);
	m_primaryLayout->setAlignment(TQt::AlignTop);
	m_rightPaneLayout = new TQGridLayout;
	m_traceLabelLayout = new TQGridLayout;
	m_infoLabelLayout = new TQGridLayout;
	m_cursorLabelLayout = new TQGridLayout;
	m_statusLabelLayout = new TQVBoxLayout;
	m_statusLabelLayoutInner = new TQVBoxLayout;
	m_primaryLayout->addLayout(m_traceLabelLayout, 255, 0);
	m_primaryLayout->addLayout(m_rightPaneLayout, 0, 255);
	m_primaryLayout->addLayout(m_statusLabelLayout, 255, 255);
	m_primaryLayout->addLayout(m_statusLabelLayoutInner, 1, 253);
	m_rightPaneLayout->addLayout(m_cursorLabelLayout, 0, 0);
	m_rightPaneLayout->addLayout(m_infoLabelLayout, 1, 0);
	m_traceLabelLayout->addItem(new TQSpacerItem(0, 0, TQSizePolicy::Expanding, TQSizePolicy::Minimum), 0, 255);
	m_rightPaneLayout->addItem(new TQSpacerItem(0, 0, TQSizePolicy::Minimum, TQSizePolicy::Expanding), 255, 0);
	m_primaryLayout->addItem(new TQSpacerItem(0, 0, TQSizePolicy::Expanding, TQSizePolicy::Minimum), 1, 128);
	m_statusLabelLayout->setSpacing(0);

	setPaletteBackgroundColor(TQt::black);
	setPaletteForegroundColor(TQColor(0,128,0));
}

TraceWidget::~TraceWidget() {
	for (uint i=0;i<m_traceArray.count();i++) {
		delete m_traceArray[i];
	}
}

void TraceWidget::setForegroundColor(const TQColor color) {
	setPaletteForegroundColor(color);
	m_graticuleWidget->setPaletteForegroundColor(color);
}

void TraceWidget::setBackgroundColor(const TQColor color) {
	setPaletteBackgroundColor(color);
	m_graticuleWidget->setPaletteBackgroundColor(color);
	for (uint trace=0;trace<m_traceArray.count();trace++) {
		m_traceArray[trace]->paramLabel->setPaletteBackgroundColor(color);
		m_traceArray[trace]->graphStatusLabel->setPaletteBackgroundColor(color);
		m_traceArray[trace]->graphStatusLabelInner->setPaletteBackgroundColor(color);
		m_traceArray[trace]->singleIncrBtn->setPaletteBackgroundColor(color);
		m_traceArray[trace]->singleDecrBtn->setPaletteBackgroundColor(color);
		m_traceArray[trace]->posResetBtn->setPaletteBackgroundColor(color);
		m_traceArray[trace]->posSetBtn->setPaletteBackgroundColor(color);
	}
	for (uint cursor=0;cursor<m_cursorArray.count();cursor++) {
		m_cursorArray[cursor]->paramLabel->setPaletteBackgroundColor(color);
		m_cursorArray[cursor]->singleIncrBtn->setPaletteBackgroundColor(color);
		m_cursorArray[cursor]->singleDecrBtn->setPaletteBackgroundColor(color);
		m_cursorArray[cursor]->multiIncrBtn->setPaletteBackgroundColor(color);
		m_cursorArray[cursor]->multiDecrBtn->setPaletteBackgroundColor(color);
	}
}

void TraceWidget::setNumberOfSamples(uint traceNumber, unsigned int samples) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->numberOfSamples = samples;
	m_traceArray[traceNumber]->sampleArray.resize(samples);
	m_traceArray[traceNumber]->positionArray.resize(samples);
	m_traceArray[traceNumber]->leftEdgeIndex = -1;
	m_traceArray[traceNumber]->rightEdgeIndex = -1;

	m_graticuleWidget->updateGraticule();
	updateTraceText();
}

void TraceWidget::setNumberOfHorizontalDivisions(unsigned int divisions) {
	m_horizDivs = divisions;
	m_graticuleWidget->updateGraticule();
	updateTraceText();
	updateCursorText();
}

void TraceWidget::setNumberOfVerticalDivisions(unsigned int divisions) {
	m_vertDivs = divisions;
	m_graticuleWidget->updateGraticule();
	updateTraceText();
	updateCursorText();
}

void TraceWidget::setDisplayLimits(uint traceNumber, TQRectF limits) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->leftEdge = limits.x();
	m_traceArray[traceNumber]->rightEdge = limits.width();
	m_traceArray[traceNumber]->topEdge = limits.y();
	m_traceArray[traceNumber]->bottomEdge = limits.height();
	m_traceArray[traceNumber]->leftEdgeIndex = -1;
	m_traceArray[traceNumber]->rightEdgeIndex = -1;

	m_graticuleWidget->updateGraticule();
	m_graticuleWidget->repaint(false);
	updateTraceText();
	updateCursorText();
}

TQRectF TraceWidget::displayLimits(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return TQRectF(m_traceArray[traceNumber]->leftEdge, m_traceArray[traceNumber]->topEdge, m_traceArray[traceNumber]->rightEdge, m_traceArray[traceNumber]->bottomEdge);
}

void TraceWidget::updateTraceText() {
	for (uint trace=0;trace<m_traceArray.count();trace++) {
		double horizontal_units_per_division = fabs(m_traceArray[trace]->rightEdge-m_traceArray[trace]->leftEdge)/m_horizDivs;
		double vertical_units_per_division = fabs(m_traceArray[trace]->bottomEdge-m_traceArray[trace]->topEdge)/m_vertDivs;
		double horizontal_range = (m_traceArray[trace]->rightEdge-m_traceArray[trace]->leftEdge);
		double vertical_range = (m_traceArray[trace]->bottomEdge-m_traceArray[trace]->topEdge);
		m_traceArray[trace]->paramLabel->setPaletteBackgroundColor(paletteBackgroundColor());
		m_traceArray[trace]->paramLabel->setPaletteForegroundColor(m_traceArray[trace]->color);
		m_traceArray[trace]->graphStatusLabel->setPaletteBackgroundColor(paletteBackgroundColor());
		m_traceArray[trace]->graphStatusLabel->setPaletteForegroundColor(m_traceArray[trace]->color);
		m_traceArray[trace]->graphStatusLabelInner->setPaletteBackgroundColor(paletteBackgroundColor());
		m_traceArray[trace]->graphStatusLabelInner->setPaletteForegroundColor(m_traceArray[trace]->color);
		TQString offsetText;
		double offset = m_traceArray[trace]->offset;
		if (offset != 0) {
			if (offset < 0) {
				offsetText = TQString(" -%1").arg(prettyFormat(fabs(offset), vertical_range, m_traceArray[trace]->verticalUnits));
			}
			else {
				offsetText = TQString(" +%1").arg(prettyFormat(fabs(offset), vertical_range, m_traceArray[trace]->verticalUnits));
			}
		}
		m_traceArray[trace]->paramLabel->setText(TQString("<qt><nobr>%1%2<br>%3/div,%4/div<br>%5,%6<br>%7,%8</qt>").arg(m_traceArray[trace]->traceName).arg(offsetText).arg(prettyFormat(horizontal_units_per_division, horizontal_range, m_traceArray[trace]->horizontalUnits)).arg(prettyFormat(vertical_units_per_division, vertical_range, m_traceArray[trace]->verticalUnits)).arg(prettyFormat(m_traceArray[trace]->leftEdge, (m_useAbsoluteHorizontalRange)?m_traceArray[trace]->rightEdge:horizontal_range, m_traceArray[trace]->horizontalUnits)).arg(prettyFormat(m_traceArray[trace]->topEdge, vertical_range, m_traceArray[trace]->verticalUnits)).arg(prettyFormat(m_traceArray[trace]->rightEdge, (m_useAbsoluteHorizontalRange)?m_traceArray[trace]->rightEdge:horizontal_range, m_traceArray[trace]->horizontalUnits)).arg(prettyFormat(m_traceArray[trace]->bottomEdge, vertical_range, m_traceArray[trace]->verticalUnits)));
	}
}

void TraceWidget::updateCursorText() {
	for (uint cursor=0;cursor<m_cursorArray.count();cursor++) {
		m_cursorArray[cursor]->paramLabel->setPaletteBackgroundColor(paletteBackgroundColor());
		if (m_cursorArray[cursor]->highlighted) {
			m_cursorArray[cursor]->paramLabel->setPaletteForegroundColor(m_cursorArray[cursor]->highlightColor);
			m_cursorArray[cursor]->singleIncrBtn->setPaletteForegroundColor(m_cursorArray[cursor]->highlightColor);
			m_cursorArray[cursor]->singleDecrBtn->setPaletteForegroundColor(m_cursorArray[cursor]->highlightColor);
			m_cursorArray[cursor]->multiIncrBtn->setPaletteForegroundColor(m_cursorArray[cursor]->highlightColor);
			m_cursorArray[cursor]->multiDecrBtn->setPaletteForegroundColor(m_cursorArray[cursor]->highlightColor);
		}
		else {
			m_cursorArray[cursor]->paramLabel->setPaletteForegroundColor(m_cursorArray[cursor]->color);
			m_cursorArray[cursor]->singleIncrBtn->setPaletteForegroundColor(m_cursorArray[cursor]->color);
			m_cursorArray[cursor]->singleDecrBtn->setPaletteForegroundColor(m_cursorArray[cursor]->color);
			m_cursorArray[cursor]->multiIncrBtn->setPaletteForegroundColor(m_cursorArray[cursor]->color);
			m_cursorArray[cursor]->multiDecrBtn->setPaletteForegroundColor(m_cursorArray[cursor]->color);
		}
		TQString cursorText;
		cursorText = TQString("<qt><nobr>%1").arg(m_cursorArray[cursor]->cursorName);
		// If this is a horizontal cursor, list all vertical positions for all channels
		// If this is a vertical cursor, list the horizontal positions for all channels

		for (uint trace=0;trace<m_traceArray.count();trace++) {
			if ((m_traceArray[trace]->enabled) && (m_cursorArray[cursor]->activeTraceLabelList.contains(trace) > 0)) {
				double horizontal_range = (m_traceArray[trace]->rightEdge-m_traceArray[trace]->leftEdge);
				double vertical_range = (m_traceArray[trace]->bottomEdge-m_traceArray[trace]->topEdge);

				if (m_cursorArray[cursor]->orientation == TQt::Horizontal) {
					double realCursorPosition = (m_traceArray[trace]->topEdge+((m_cursorArray[cursor]->position/100.0)*vertical_range)-m_traceArray[trace]->offset);
					TQString deltaText;
					if (cursor >= m_zoomCursorStartIndex) {
						for (uint cursor2=m_zoomCursorStartIndex;cursor2<m_cursorArray.count();cursor2++) {
							if (cursor2 != cursor) {
								if (m_cursorArray[cursor2]->orientation == m_cursorArray[cursor]->orientation) {
									double realSecondaryCursorPosition = (m_traceArray[trace]->topEdge+((m_cursorArray[cursor2]->position/100.0)*vertical_range)-m_traceArray[trace]->offset);
									deltaText = trUtf8("Δ") + prettyFormat(fabs(realCursorPosition-realSecondaryCursorPosition), vertical_range, m_traceArray[trace]->verticalUnits);
									break;
								}
							}
						}
					}
					cursorText.append(TQString("<br>%1: %2%3").arg(m_traceArray[trace]->traceName).arg(prettyFormat(realCursorPosition, vertical_range, m_traceArray[trace]->verticalUnits)).arg(deltaText));
				}
				else {
					double realCursorPosition = (m_traceArray[trace]->leftEdge+((m_cursorArray[cursor]->position/100.0)*horizontal_range));
					TQString deltaText;
					if (cursor >= m_zoomCursorStartIndex) {
						for (uint cursor2=m_zoomCursorStartIndex;cursor2<m_cursorArray.count();cursor2++) {
							if (cursor2 != cursor) {
								if (m_cursorArray[cursor2]->orientation == m_cursorArray[cursor]->orientation) {
									double realSecondaryCursorPosition = (m_traceArray[trace]->leftEdge+((m_cursorArray[cursor2]->position/100.0)*horizontal_range));
									deltaText = trUtf8("Δ") + prettyFormat(fabs(realCursorPosition-realSecondaryCursorPosition), horizontal_range, m_traceArray[trace]->horizontalUnits);
									break;
								}
							}
						}
					}
					cursorText.append(TQString("<br>%1: %2%3").arg(m_traceArray[trace]->traceName).arg(prettyFormat(realCursorPosition, (m_useAbsoluteHorizontalRange)?m_traceArray[trace]->rightEdge:horizontal_range, m_traceArray[trace]->horizontalUnits)).arg(deltaText));
				}
			}
		}

		cursorText.append("</qt>");
		m_cursorArray[cursor]->paramLabel->setText(cursorText);
	}
}

TQDoubleArray& TraceWidget::samples(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->sampleArray;
}

void TraceWidget::setSamples(uint traceNumber, TQDoubleArray& tqda) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->sampleArray = tqda;
	m_traceArray[traceNumber]->numberOfSamples = tqda.size();

	m_graticuleWidget->repaint(false);
}

TQDoubleArray& TraceWidget::positions(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->positionArray;
}

void TraceWidget::setPositions(uint traceNumber, TQDoubleArray& tqda) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->positionArray = tqda;
	m_traceArray[traceNumber]->numberOfSamples = tqda.size();
	m_traceArray[traceNumber]->leftEdgeIndex = -1;
	m_traceArray[traceNumber]->rightEdgeIndex = -1;

	m_graticuleWidget->repaint(false);
}

TQColor TraceWidget::traceColor(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->color;
}

void TraceWidget::setTraceColor(uint traceNumber, TQColor color) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->color = color;

	m_graticuleWidget->updateGraticule();
	m_graticuleWidget->repaint(false);
	updateTraceText();
}

bool TraceWidget::traceEnabled(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->enabled;
}

void TraceWidget::setTraceEnabled(uint traceNumber, bool enabled, bool showText) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->enabled = enabled;
	if (enabled) {
		if (showText) {
			m_traceArray[traceNumber]->paramLabel->show();
			m_traceArray[traceNumber]->graphStatusLabel->show();
			m_traceArray[traceNumber]->graphStatusLabelInner->hide();
			m_traceArray[traceNumber]->singleIncrBtn->show();
			m_traceArray[traceNumber]->singleDecrBtn->show();
			m_traceArray[traceNumber]->posResetBtn->show();
			m_traceArray[traceNumber]->posSetBtn->show();
		}
		else {
			m_traceArray[traceNumber]->paramLabel->hide();
			m_traceArray[traceNumber]->graphStatusLabel->hide();
			m_traceArray[traceNumber]->graphStatusLabelInner->show();
			m_traceArray[traceNumber]->singleIncrBtn->hide();
			m_traceArray[traceNumber]->singleDecrBtn->hide();
			m_traceArray[traceNumber]->posResetBtn->hide();
			m_traceArray[traceNumber]->posSetBtn->hide();
		}
	}
	else {
		m_traceArray[traceNumber]->paramLabel->hide();
		m_traceArray[traceNumber]->graphStatusLabel->hide();
		m_traceArray[traceNumber]->graphStatusLabelInner->hide();
		m_traceArray[traceNumber]->singleIncrBtn->hide();
		m_traceArray[traceNumber]->singleDecrBtn->hide();
		m_traceArray[traceNumber]->posResetBtn->hide();
		m_traceArray[traceNumber]->posSetBtn->hide();
	}

	m_graticuleWidget->updateGraticule();
	m_graticuleWidget->repaint(false);
	updateTraceText();
}

TQString TraceWidget::traceName(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->traceName;
}

void TraceWidget::setTraceName(uint traceNumber, TQString name) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->traceName = name;
	updateTraceText();
}

TQString TraceWidget::traceHorizontalUnits(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->horizontalUnits;
}

void TraceWidget::setTraceHorizontalUnits(uint traceNumber, TQString units) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->horizontalUnits = units;
	updateTraceText();
}

TQString TraceWidget::traceVerticalUnits(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->verticalUnits;
}

void TraceWidget::setTraceVerticalUnits(uint traceNumber, TQString units) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->verticalUnits = units;
	updateTraceText();
}

double TraceWidget::cursorPosition(uint cursorNumber) {
	VERIFY_CURSOR_ARRAY_SIZE

	return m_cursorArray[cursorNumber]->position;
}

void TraceWidget::setCursorPosition(uint cursorNumber, double position) {
	VERIFY_CURSOR_ARRAY_SIZE

	if (position < 0.0) position = 0.0;
	if (position > 100.0) position = 100.0;

	m_cursorArray[cursorNumber]->position = position;
	emit(cursorPositionChanged(cursorNumber, m_cursorArray[cursorNumber]->position));
	updateCursorText();
	m_graticuleWidget->updateGraticule();
	m_graticuleWidget->repaint(false);
}

TQColor TraceWidget::cursorColor(uint cursorNumber) {
	VERIFY_CURSOR_ARRAY_SIZE

	return m_cursorArray[cursorNumber]->color;
}

void TraceWidget::setCursorColor(uint cursorNumber, TQColor color) {
	VERIFY_CURSOR_ARRAY_SIZE

	m_cursorArray[cursorNumber]->color = color;
	updateCursorText();
	m_graticuleWidget->updateGraticule();
	m_graticuleWidget->repaint(false);
}

TQColor TraceWidget::cursorHighlightColor(uint cursorNumber) {
	VERIFY_CURSOR_ARRAY_SIZE

	return m_cursorArray[cursorNumber]->highlightColor;
}

void TraceWidget::setCursorHighlightColor(uint cursorNumber, TQColor color) {
	VERIFY_CURSOR_ARRAY_SIZE

	m_cursorArray[cursorNumber]->highlightColor = color;
	updateCursorText();
	m_graticuleWidget->updateGraticule();
	m_graticuleWidget->repaint(false);
}

void TraceWidget::setCursorActiveTraceList(uint cursorNumber, TraceNumberList list) {
	VERIFY_CURSOR_ARRAY_SIZE

	m_cursorArray[cursorNumber]->activeTraceLabelList = list;
	updateCursorText();
}

bool TraceWidget::cursorEnabled(uint cursorNumber) {
	VERIFY_CURSOR_ARRAY_SIZE

	return m_cursorArray[cursorNumber]->enabled;
}

void TraceWidget::setCursorEnabled(uint cursorNumber, bool enabled) {
	VERIFY_CURSOR_ARRAY_SIZE

	m_cursorArray[cursorNumber]->enabled = enabled;
	if (enabled) {
		m_cursorArray[cursorNumber]->paramLabel->show();
		m_cursorArray[cursorNumber]->singleIncrBtn->show();
		m_cursorArray[cursorNumber]->singleDecrBtn->show();
		m_cursorArray[cursorNumber]->multiIncrBtn->show();
		m_cursorArray[cursorNumber]->multiDecrBtn->show();
	}
	else {
		m_cursorArray[cursorNumber]->paramLabel->hide();
		m_cursorArray[cursorNumber]->singleIncrBtn->hide();
		m_cursorArray[cursorNumber]->singleDecrBtn->hide();
		m_cursorArray[cursorNumber]->multiIncrBtn->hide();
		m_cursorArray[cursorNumber]->multiDecrBtn->hide();
	}

	m_graticuleWidget->updateGraticule();
	updateCursorText();
}

TQString TraceWidget::cursorName(uint cursorNumber) {
	VERIFY_CURSOR_ARRAY_SIZE

	return m_cursorArray[cursorNumber]->cursorName;
}

void TraceWidget::setCursorName(uint cursorNumber, TQString name) {
	VERIFY_CURSOR_ARRAY_SIZE

	m_cursorArray[cursorNumber]->cursorName = name;
	updateCursorText();
}

TQt::Orientation TraceWidget::cursorOrientation(uint cursorNumber) {
	VERIFY_CURSOR_ARRAY_SIZE

	return m_cursorArray[cursorNumber]->orientation;
}

void TraceWidget::setCursorOrientation(uint cursorNumber, TQt::Orientation orient) {
	VERIFY_CURSOR_ARRAY_SIZE

	m_cursorArray[cursorNumber]->orientation = orient;
	updateCursorText();
}

void TraceWidget::setNumberOfTraces(uint traceNumber) {
	resizeTraceArray(traceNumber);
}

void TraceWidget::setNumberOfCursors(uint cursorNumber) {
	resizeCursorArray(cursorNumber);
}

TQRectF TraceWidget::zoomBox() {
	uint i;

	if ((m_cursorArray.count() < (4+m_zoomCursorStartIndex)) || (!m_zoomBoxEnabled)) {
		if (!m_zoomBoxPrev.isNull()) {
			m_zoomBoxPrev = TQRectF();
			emit(zoomBoxChanged(m_zoomBoxPrev));
		}
		return m_zoomBoxPrev;
	}
	else {
		// Find the first two horizontal and first two vertical cursors
		// If two of each cannot be found, return TQRectF()
		double horiz[2];
		double vert[2];
		int j = 0;
		int k = 0;
		for (i=m_zoomCursorStartIndex;i<m_cursorArray.count(); i++) {
			if (m_cursorArray[i]->orientation == TQt::Horizontal) {
				if (j<2) {
					vert[j] = m_cursorArray[i]->position;
					j++;
				}
			}
			else {
				if (k<2) {
					horiz[k] = m_cursorArray[i]->position;
					k++;
				}
			}
			if ((j>1) && (k>1)) {
				break;
			}
		}
		if ((j>1) && (k>1)) {
			// Calculate zoom box
			double leftBound = (horiz[0] < horiz[1])?horiz[0]:horiz[1];
			double topBound = (vert[0] < vert[1])?vert[0]:vert[1];
			TQRectF newRect(leftBound, topBound, fabs(horiz[0]-horiz[1]), fabs(vert[0]-vert[1]));
			if (m_zoomBoxPrev != newRect) {
				m_zoomBoxPrev = newRect;
				emit(zoomBoxChanged(m_zoomBoxPrev));
			}
			return m_zoomBoxPrev;
		}
		else {
			if (!m_zoomBoxPrev.isNull()) {
				m_zoomBoxPrev = TQRectF();
				emit(zoomBoxChanged(m_zoomBoxPrev));
			}
			return m_zoomBoxPrev;
		}
	}
}

TQRectF TraceWidget::zoomCursorBox() {
	uint i;

	if ((m_cursorArray.count() < (4+m_zoomCursorStartIndex)) || (!m_zoomBoxEnabled)) {
		return TQRectF();
	}
	else {
		// Find the first two horizontal and first two vertical cursors
		// If two of each cannot be found, return TQRectF()
		double horiz[2];
		double vert[2];
		int j = 0;
		int k = 0;
		for (i=m_zoomCursorStartIndex;i<m_cursorArray.count(); i++) {
			if (m_cursorArray[i]->orientation == TQt::Horizontal) {
				if (j<2) {
					vert[j] = m_cursorArray[i]->position;
					j++;
				}
			}
			else {
				if (k<2) {
					horiz[k] = m_cursorArray[i]->position;
					k++;
				}
			}
			if ((j>1) && (k>1)) {
				break;
			}
		}
		if ((j>1) && (k>1)) {
			return TQRectF(horiz[0], vert[0], horiz[1], vert[1]);
		}
		else {
			return TQRectF();
		}
	}
}

void TraceWidget::setZoomCursorBox(const TQRectF rect) {
	uint i;

	TQRectF boundedRect = rect;

	if (boundedRect.x() < 0.0) {
		boundedRect.setX(0.0);
	}
	if (boundedRect.x() > 100.0) {
		boundedRect.setX(100.0);
	}
	if (boundedRect.y() < 0.0) {
		boundedRect.setY(0.0);
	}
	if (boundedRect.y() > 100.0) {
		boundedRect.setY(100.0);
	}
	if (boundedRect.width() < 0.0) {
		boundedRect.setWidth(0.0);
	}
	if (boundedRect.width() > 100.0) {
		boundedRect.setWidth(100.0);
	}
	if (boundedRect.height() < 0.0) {
		boundedRect.setHeight(0.0);
	}
	if (boundedRect.height() > 100.0) {
		boundedRect.setHeight(100.0);
	}

	if ((m_cursorArray.count() < (4+m_zoomCursorStartIndex)) || (!m_zoomBoxEnabled)) {
		return;
	}
	else {
		// Find the first two horizontal and first two vertical cursors
		// If two of each cannot be found, return TQRectF()
		CursorData* horiz[2];
		CursorData* vert[2];
		uint horizIndex[2];
		uint vertIndex[2];
		int j = 0;
		int k = 0;
		for (i=m_zoomCursorStartIndex;i<m_cursorArray.count(); i++) {
			if (m_cursorArray[i]->orientation == TQt::Horizontal) {
				if (j<2) {
					vert[j] = m_cursorArray[i];
					vertIndex[j] = i;
					j++;
				}
			}
			else {
				if (k<2) {
					horiz[k] = m_cursorArray[i];
					horizIndex[j] = i;
					k++;
				}
			}
			if ((j>1) && (k>1)) {
				break;
			}
		}
		if ((j>1) && (k>1)) {
			// Set cursors...
			vert[0]->position = boundedRect.y();
			emit(cursorPositionChanged(vertIndex[0], m_cursorArray[vertIndex[0]]->position));
			vert[1]->position = boundedRect.height();
			emit(cursorPositionChanged(vertIndex[1], m_cursorArray[vertIndex[1]]->position));
			horiz[0]->position = boundedRect.x();
			emit(cursorPositionChanged(horizIndex[0], m_cursorArray[horizIndex[0]]->position));
			horiz[1]->position = boundedRect.width();
			emit(cursorPositionChanged(horizIndex[1], m_cursorArray[horizIndex[1]]->position));

			updateCursorText();
			m_graticuleWidget->updateGraticule();
			m_graticuleWidget->repaint(false);

			return;
		}
	}
}

unsigned int TraceWidget::zoomCursorStartIndex() {
	return m_zoomCursorStartIndex;
}

void TraceWidget::setZoomCursorStartIndex(unsigned int index) {
	m_zoomCursorStartIndex = index;
}

void TraceWidget::setZoomBoxEnabled(bool enabled) {
	m_zoomBoxEnabled = enabled;
	m_graticuleWidget->updateGraticule();
}

TQString TraceWidget::prettyFormat(double value, double rangeDetectValue, TQString baseUnits, unsigned int precision) {
	TQString result;
	TQString unitMultiplier;
	double valueMultiplier;

	if (fabs(rangeDetectValue) < 1e-9) {
		unitMultiplier = "p";
		valueMultiplier = 1e+12;
	}
	else if (fabs(rangeDetectValue) < 1e-6) {
		unitMultiplier = "n";
		valueMultiplier = 1e+9;
	}
	else if (fabs(rangeDetectValue) < 1e-3) {
		unitMultiplier = "u";
		valueMultiplier = 1e+6;
	}
	else if (fabs(rangeDetectValue) < 1e-0) {
		unitMultiplier = "m";
		valueMultiplier = 1e+3;
	}
	else if (fabs(rangeDetectValue) < 1e+3) {
		unitMultiplier = "";
		valueMultiplier = 1e+0;
	}
	else if (fabs(rangeDetectValue) < 1e+6) {
		unitMultiplier = "k";
		valueMultiplier = 1e-3;
	}
	else if (fabs(rangeDetectValue) < 1e+9) {
		unitMultiplier = "M";
		valueMultiplier = 1e-6;
	}
	else if (fabs(rangeDetectValue) < 1e+12) {
		unitMultiplier = "G";
		valueMultiplier = 1e-9;
	}
	else if (fabs(rangeDetectValue) < 1e+15) {
		unitMultiplier = "T";
		valueMultiplier = 1e-12;
	}
	else {
		unitMultiplier = "";
		valueMultiplier = 1.0;
	}

	double scaledValue = value * valueMultiplier;
	TQString valueString = TQString("%1").arg(scaledValue, 0, 'f', precision);
	if (valueString.contains("-") && valueString.contains(".")) {
		valueString.truncate(precision+2);
	}
	else if (valueString.contains("-")) {
		valueString.truncate(precision+1);
	}
	else if (valueString.contains(".")) {
		valueString.truncate(precision+1);
	}
	else {
		valueString.truncate(precision);
	}
	if (valueString.endsWith(".")) {
		valueString.truncate(valueString.length()-1);
	}
	result = TQString("%1%2%3").arg(valueString).arg(unitMultiplier).arg(baseUnits);

	return result;
}

double TraceWidget::traceOffset(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->offset;
}

void TraceWidget::setTraceOffset(uint traceNumber, double offset) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->offset = offset;

	m_graticuleWidget->repaint(false);
	updateTraceText();
}

void TraceWidget::processChangedOffset(double offset) {
	// Find the sending trace number
	const TraceData* sendingTrace = dynamic_cast<const TraceData*>(sender());
	if (sendingTrace) {
		int tracenumber = -1;
		for (uint trace=0;trace<m_traceArray.count();trace++) {
			if (sendingTrace == m_traceArray[trace]) {
				tracenumber = trace;
			}
		}
		if (tracenumber >= 0) {
			emit(offsetChanged(tracenumber, offset));
		}
	}
}

void TraceWidget::processChangedCusorPosition(double position) {
	// Find the sending cursor number
	const CursorData* sendingCursor = dynamic_cast<const CursorData*>(sender());
	if (sendingCursor) {
		int cursornumber = -1;
		for (uint cursor=0;cursor<m_cursorArray.count();cursor++) {
			if (sendingCursor == m_cursorArray[cursor]) {
				cursornumber = cursor;
			}
		}
		if (cursornumber >= 0) {
			emit(cursorDragged(cursornumber, position));
		}
	}
}

void TraceWidget::processChangedCursor(uint cursorNumber, double newPosition) {
	emit(cursorDragged(cursorNumber, newPosition));
}

void TraceWidget::resizeTraceArray(uint newsize) {
	uint oldcount = m_traceArray.count();

	if (newsize > oldcount) {
		m_traceArray.resize(newsize);
		for (uint i=oldcount;i<newsize;i++) {
			m_traceArray[i] = new TraceData(this, this);
			connect(m_traceArray[i], SIGNAL(offsetChanged(double)), this, SLOT(processChangedOffset(double)));
			if (m_traceArray[i]->paramLabel) {
				m_traceLabelLayout->addMultiCellWidget(m_traceArray[i]->paramLabel, 0, 2, i*2, i*2);
				m_traceLabelLayout->addWidget(m_traceArray[i]->singleIncrBtn, 0, (i*2)+1);
				m_traceLabelLayout->addWidget(m_traceArray[i]->posResetBtn, 1, (i*2)+1);
				m_traceLabelLayout->addWidget(m_traceArray[i]->posSetBtn, 2, (i*2)+1);
				m_traceLabelLayout->addWidget(m_traceArray[i]->singleDecrBtn, 3, (i*2)+1);
				m_statusLabelLayout->insertWidget(i, m_traceArray[i]->graphStatusLabel, TQt::AlignTop);
				m_statusLabelLayoutInner->insertWidget(i, m_traceArray[i]->graphStatusLabelInner);
			}
		}
	}
	else {
		m_traceArray.resize(newsize);
		for (uint i=newsize;i<oldcount;i++) {
			if (m_traceArray[i]->paramLabel) {
				m_traceLabelLayout->remove(m_traceArray[i]->paramLabel);
				m_traceLabelLayout->remove(m_traceArray[i]->singleIncrBtn);
				m_traceLabelLayout->remove(m_traceArray[i]->posResetBtn);
				m_traceLabelLayout->remove(m_traceArray[i]->posSetBtn);
				m_traceLabelLayout->remove(m_traceArray[i]->singleDecrBtn);
				m_statusLabelLayout->remove(m_traceArray[i]->graphStatusLabel);
				m_statusLabelLayoutInner->remove(m_traceArray[i]->graphStatusLabelInner);
			}
			delete m_traceArray[i];
		}
	}
}

void TraceWidget::resizeCursorArray(uint newsize) {
	uint oldcount = m_cursorArray.count();

	if (newsize > oldcount) {
		m_cursorArray.resize(newsize);
		for (uint i=oldcount;i<newsize;i++) {
			m_cursorArray[i] = new CursorData(this, this);
			connect(m_cursorArray[i], SIGNAL(positionChanged(double)), this, SLOT(processChangedCusorPosition(double)));
			if (m_cursorArray[i]->paramLabel) {
				m_cursorLabelLayout->addMultiCellWidget(m_cursorArray[i]->paramLabel, i*2, i*2, 0, 3);
				m_cursorLabelLayout->addMultiCellWidget(m_cursorArray[i]->multiIncrBtn, (i*2)+1, (i*2)+1, 0, 0, TQt::AlignHCenter);
				m_cursorLabelLayout->addMultiCellWidget(m_cursorArray[i]->singleIncrBtn, (i*2)+1, (i*2)+1, 1, 1, TQt::AlignHCenter);
				m_cursorLabelLayout->addMultiCellWidget(m_cursorArray[i]->singleDecrBtn, (i*2)+1, (i*2)+1, 2, 2, TQt::AlignHCenter);
				m_cursorLabelLayout->addMultiCellWidget(m_cursorArray[i]->multiDecrBtn, (i*2)+1, (i*2)+1, 3, 3, TQt::AlignHCenter);
			}
		}
	}
	else {
		m_cursorArray.resize(newsize);
		for (uint i=newsize;i<oldcount;i++) {
			if (m_cursorArray[i]->paramLabel) {
				m_cursorLabelLayout->remove(m_cursorArray[i]->paramLabel);
				m_cursorLabelLayout->remove(m_cursorArray[i]->multiIncrBtn);
				m_cursorLabelLayout->remove(m_cursorArray[i]->singleIncrBtn);
				m_cursorLabelLayout->remove(m_cursorArray[i]->singleDecrBtn);
				m_cursorLabelLayout->remove(m_cursorArray[i]->multiDecrBtn);
			}
			delete m_cursorArray[i];
		}
	}
}