/*
 * Analog and Digital Trace Viewing Widget
 *
 * 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * (c) 2012-2019 Timothy Pearson
 * Raptor Engineering
 * http://www.raptorengineeringinc.com
 */

#include "tracewidget.h"

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

#include <tqstyle.h>
#include <tqpixmap.h>
#include <tqpainter.h>
#include <tqpushbutton.h>
#include <tqtoolbutton.h>
#include <tqapplication.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 DIGITAL_SHADING_DARKNESS_FACTOR 400

#define CURSOR_MOVE_CAPTURE_DISTANCE 0

TQRectF::TQRectF() {
	m_x = 0;
	m_y = 0;
	m_w = 0;
	m_h = 0;
	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);
}

TraceWidgetPushButton::TraceWidgetPushButton(TQWidget *parent, const char* name) : TQPushButton(parent, name) {
	//
}

TraceWidgetPushButton::TraceWidgetPushButton(const TQString &text, TQWidget *parent, const char* name) : TQPushButton(text, parent, name) {
	//
}

TraceWidgetPushButton::TraceWidgetPushButton(const TQIconSet& icon, const TQString &text, TQWidget *parent, const char* name) : TQPushButton(icon, text, parent, name) {
	//
}

TraceWidgetPushButton::~TraceWidgetPushButton() {
	//
}

// Largely taken from TQPushButton::sizeHint()
TQSize TraceWidgetPushButton::sizeHint() const {
	constPolish();

	int w = 0, h = 0;

	// calculate contents size...
	if (iconSet() && !iconSet()->isNull()) {
		int iw = iconSet()->pixmap( TQIconSet::Small, TQIconSet::Normal ).width() + 4;
		int ih = iconSet()->pixmap( TQIconSet::Small, TQIconSet::Normal ).height();
		w += iw;
		h = TQMAX(h, ih);
	}

	if (isMenuButton()) {
		w += style().pixelMetric(TQStyle::PM_MenuButtonIndicator, this);
	}

	if (pixmap()) {
		TQPixmap *pm = (TQPixmap *)pixmap();
		w += pm->width();
		h += pm->height();
	}
	else {
		TQString s(text());
		bool empty = s.isEmpty();
		if (empty) {
			s = TQString::fromLatin1("XXXX");
		}
		TQFontMetrics fm = fontMetrics();
		TQSize sz = fm.size(ShowPrefix, s);
		if (!empty || !w) {
			w += sz.width();
		}
		if (!empty || !h) {
			h = TQMAX(h, sz.height());
		}
	}

	return (TQSize(w, h).expandedTo(TQApplication::globalStrut()).expandedTo(TQSize(20, 20)));
}

class TraceLabelLayout : public TQLayout
{
	public:
		TraceLabelLayout(TraceWidget *traceWidget, TQWidget *parent, int spacing=-1) : TQLayout(parent, 0, spacing), m_traceWidget(traceWidget) {
			//
		}
		TraceLabelLayout(TraceWidget *traceWidget, TQLayout* parent, int spacing=-1) : TQLayout(parent, spacing), m_traceWidget(traceWidget) {
			//
		}
		TraceLabelLayout(TraceWidget *traceWidget, int spacing=-1) : TQLayout(spacing), m_traceWidget(traceWidget) {
			//
		}
		~TraceLabelLayout();

		void addItem(TQLayoutItem *item);
		void addWidget(TQWidget *w, int alignment);
		TQSize sizeHint() const;
		TQSize minimumSize() const;
		TQLayoutIterator iterator();
		void setGeometry(const TQRect &rect);
		virtual void invalidate();

	private:
		TQPtrList<TQLayoutItem> list;
		TraceWidget* m_traceWidget;
};

class TraceLabelLayoutIterator : public TQGLayoutIterator
{
	public:
		TraceLabelLayoutIterator( TQPtrList<TQLayoutItem> *l ) : idx(0), list(l) {
			//
		}

		TQLayoutItem *current() {
			return idx < int(list->count()) ? list->at(idx) : 0;
		}

		TQLayoutItem *next() {
			idx++;
			return current();
		}

		TQLayoutItem *takeCurrent() {
			return list->take(idx);
		}

	private:
		int idx;
		TQPtrList<TQLayoutItem> *list;
};

TQLayoutIterator TraceLabelLayout::iterator() {
	return TQLayoutIterator(new TraceLabelLayoutIterator(&list));
}

void TraceLabelLayout::addItem(TQLayoutItem *item) {
	list.append( item );
}

void TraceLabelLayout::addWidget(TQWidget *w, int alignment) {
	if (!w) {
		return;
	}

	TQWidgetItem *b = new TQWidgetItem(w);
	b->setAlignment(alignment);
	addItem( b );
}

TraceLabelLayout::~TraceLabelLayout() {
	deleteAllItems();
}

void TraceLabelLayout::setGeometry(const TQRect &rect) {
	TQLayout::setGeometry(rect);

	TQPtrListIterator<TQLayoutItem> it(list);
	if (it.count() == 0) {
		return;
	}

	TQLayoutItem *item;
	while ((item = it.current()) != 0) {
		++it;

		TQWidgetItem *widgetItem = dynamic_cast<TQWidgetItem*>(item);
		if (!widgetItem) {
			continue;
		}

		TQWidget* widget = widgetItem->widget();
		if (!widget) {
			continue;
		}

		// Find the trace number
		const TraceData* currentTrace = NULL;
		for (uint trace=0;trace<m_traceWidget->m_traceArray.count();trace++) {
			if (m_traceWidget->m_traceArray[trace]->leftLabel == widget) {
				currentTrace = m_traceWidget->m_traceArray[trace];
				break;
			}
		}

		TQFontMetrics fm(currentTrace->leftLabel->font());
		int font_height = fm.boundingRect(currentTrace->leftLabel->text()).height();
		int font_vertical_offset = font_height/2;

		int graticule_height = m_traceWidget->m_graticuleWidget->height();
		int y = ((((currentTrace->offset+currentTrace->textOffset)-currentTrace->topEdge)/(currentTrace->bottomEdge-currentTrace->topEdge))*(graticule_height))-font_vertical_offset;
		if (m_traceWidget->m_showLeftTraceInfoArea) {
			if ((y < 0) || ((y+font_height) > graticule_height)) {
				currentTrace->leftLabel->hide();
				item->setGeometry(TQRect(0, 0, rect.width(), currentTrace->leftLabel->sizeHint().height()));
			}
			else {
				item->setGeometry(TQRect(0, y, rect.width(), currentTrace->leftLabel->sizeHint().height()));
				currentTrace->leftLabel->show();
			}
		}
		else {
			currentTrace->leftLabel->hide();
		}
	}
}

void TraceLabelLayout::invalidate() {
	setGeometry(geometry());
}

TQSize TraceLabelLayout::sizeHint() const
{
	TQSize size;
	if (!m_traceWidget->m_showLeftTraceInfoArea) {
		return TQSize(0, 0);
	}

	TQSize s(0, 0);
	TQPtrListIterator<TQLayoutItem> it( list );
	TQLayoutItem *item;
	while ((item = it.current()) != 0) {
		++it;
		s = s.expandedTo(item->sizeHint());
	}
	size = s + TQSize(spacing(), spacing());

	if (m_traceWidget->m_leftTraceInfoLabelsFit && list.getFirst()) {
		return TQSize(size.width(), ((list.getFirst()->sizeHint().height() + m_traceWidget->m_leftTraceInfoAreaFitSpacing) * list.count()));
	}
	else {
		return size;
	}
}

TQSize TraceLabelLayout::minimumSize() const
{
	TQSize minSize;
	if (!m_traceWidget->m_showLeftTraceInfoArea) {
		return TQSize(0, 0);
	}

	TQSize s(0, 0);
	TQPtrListIterator<TQLayoutItem> it(list);
	TQLayoutItem *item;
	while ((item = it.current()) != 0) {
		++it;
		s = s.expandedTo(item->minimumSize());
	}
	minSize = s + TQSize(spacing(), spacing());

	if (m_traceWidget->m_leftTraceInfoLabelsFit && list.getFirst()) {
		return TQSize(minSize.width(), ((list.getFirst()->minimumSize().height()+ m_traceWidget->m_leftTraceInfoAreaFitSpacing) * list.count()));
	}
	else {
		return minSize;
	}
}

class TraceCursorLabelLayout : public TQLayout
{
	public:
		TraceCursorLabelLayout(TraceWidget *traceWidget, TQWidget *parent, int spacing=-1) : TQLayout(parent, 0, spacing), m_traceWidget(traceWidget) {
			//
		}
		TraceCursorLabelLayout(TraceWidget *traceWidget, TQLayout* parent, int spacing=-1) : TQLayout(parent, spacing), m_traceWidget(traceWidget) {
			//
		}
		TraceCursorLabelLayout(TraceWidget *traceWidget, int spacing=-1) : TQLayout(spacing), m_traceWidget(traceWidget) {
			//
		}
		~TraceCursorLabelLayout();

		void addItem(TQLayoutItem *item);
		void addWidget(TQWidget *w, int alignment);
		TQSize sizeHint() const;
		TQSize minimumSize() const;
		TQLayoutIterator iterator();
		void setGeometry(const TQRect &rect);
		virtual void invalidate();

	private:
		TQPtrList<TQLayoutItem> list;
		TraceWidget* m_traceWidget;
};

class TraceCursorLabelLayoutIterator : public TQGLayoutIterator
{
	public:
		TraceCursorLabelLayoutIterator( TQPtrList<TQLayoutItem> *l ) : idx(0), list(l) {
			//
		}

		TQLayoutItem *current() {
			return idx < int(list->count()) ? list->at(idx) : 0;
		}

		TQLayoutItem *next() {
			idx++;
			return current();
		}

		TQLayoutItem *takeCurrent() {
			return list->take(idx);
		}

	private:
		int idx;
		TQPtrList<TQLayoutItem> *list;
};

TQLayoutIterator TraceCursorLabelLayout::iterator() {
	return TQLayoutIterator(new TraceCursorLabelLayoutIterator(&list));
}

void TraceCursorLabelLayout::addItem(TQLayoutItem *item) {
	list.append( item );
}

void TraceCursorLabelLayout::addWidget(TQWidget *w, int alignment) {
	if (!w) {
		return;
	}

	TQWidgetItem *b = new TQWidgetItem(w);
	b->setAlignment(alignment);
	addItem( b );
}

TraceCursorLabelLayout::~TraceCursorLabelLayout() {
	deleteAllItems();
}

void TraceCursorLabelLayout::setGeometry(const TQRect &rect) {
	TQLayout::setGeometry(rect);

	TQPtrListIterator<TQLayoutItem> it(list);
	if (it.count() == 0) {
		return;
	}

	TQLayoutItem *item;
	while ((item = it.current()) != 0) {
		++it;

		TQWidgetItem *widgetItem = dynamic_cast<TQWidgetItem*>(item);
		if (!widgetItem) {
			continue;
		}

		TQWidget* widget = widgetItem->widget();
		if (!widget) {
			continue;
		}

		// Find the trace number
		const TraceData* currentTrace = NULL;
		for (uint trace=0;trace<m_traceWidget->m_traceArray.count();trace++) {
			if (m_traceWidget->m_traceArray[trace]->leftCursorLabel == widget) {
				currentTrace = m_traceWidget->m_traceArray[trace];
				break;
			}
		}

		TQFontMetrics fm(currentTrace->leftCursorLabel->font());
		int font_height = fm.boundingRect(currentTrace->leftCursorLabel->text()).height();
		int font_vertical_offset = font_height/2;

		int graticule_height = m_traceWidget->m_graticuleWidget->height();
		int y = ((((currentTrace->offset+currentTrace->textOffset)-currentTrace->topEdge)/(currentTrace->bottomEdge-currentTrace->topEdge))*(graticule_height))-font_vertical_offset;
		if (m_traceWidget->m_showLeftTraceInfoArea) {
			if ((y < 0) || ((y+font_height) > graticule_height)) {
				currentTrace->leftCursorLabel->hide();
				item->setGeometry(TQRect(rect.x(), 0, rect.width(), currentTrace->leftCursorLabel->sizeHint().height()));
			}
			else {
				item->setGeometry(TQRect(rect.x(), y, rect.width(), currentTrace->leftCursorLabel->sizeHint().height()));
				currentTrace->leftCursorLabel->show();
			}
		}
		else {
			currentTrace->leftCursorLabel->hide();
		}
	}
}

void TraceCursorLabelLayout::invalidate() {
	setGeometry(geometry());
}

TQSize TraceCursorLabelLayout::sizeHint() const
{
	TQSize size;
	if (!m_traceWidget->m_showLeftTraceInfoArea) {
		return TQSize(0, 0);
	}

	TQSize s(0, 0);
	TQPtrListIterator<TQLayoutItem> it( list );
	TQLayoutItem *item;
	while ((item = it.current()) != 0) {
		++it;
		s = s.expandedTo(item->sizeHint());
	}
	size = s + TQSize(spacing(), spacing());

	if (m_traceWidget->m_leftTraceInfoLabelsFit && list.getFirst()) {
		return TQSize(size.width(), ((list.getFirst()->sizeHint().height() + m_traceWidget->m_leftTraceInfoAreaFitSpacing) * list.count()));
	}
	else {
		return size;
	}
}

TQSize TraceCursorLabelLayout::minimumSize() const
{
	TQSize minSize;
	if (!m_traceWidget->m_showLeftTraceInfoArea) {
		return TQSize(0, 0);
	}

	TQSize s(0, 0);
	TQPtrListIterator<TQLayoutItem> it(list);
	TQLayoutItem *item;
	while ((item = it.current()) != 0) {
		++it;
		s = s.expandedTo(item->minimumSize());
	}
	minSize = s + TQSize(spacing(), spacing());

	if (m_traceWidget->m_leftTraceInfoLabelsFit && list.getFirst()) {
		return TQSize(minSize.width(), ((list.getFirst()->minimumSize().height()+ m_traceWidget->m_leftTraceInfoAreaFitSpacing) * list.count()));
	}
	else {
		return minSize;
	}
}

TraceData::TraceData(TraceWidget* parent, TQWidget* labelParent) : TQObject(), parentWidget(parent) {
	color = TQColor(0, 255, 0);
	numberOfSamples = 0;
	sampleMax = 0;
	sampleMin = 0;
	sampleAverage = 0;
	leftEdgeIndex = -1;
	rightEdgeIndex = -1;
	offset = 0.0;
	textOffset = 0.0;
	leftEdge = 0;
	rightEdge = 0;
	topEdge = 0;
	bottomEdge = 0;
	traceName = i18n("Unknown");
	horizontalUnits = i18n("Units");
	verticalUnits = i18n("Units");
	m_digitalTraceDrawing = false;
	m_digitalShaderDarkness = DIGITAL_SHADING_DARKNESS_FACTOR;
	m_suppressNameInCursorText = false;
	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();
		leftLabel = new TQLabel(labelParent);
		leftLabel->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		leftLabel->setPaletteForegroundColor(color);
		leftLabel->setAlignment(TQt::AlignLeft|TQt::AlignVCenter|TQt::SingleLine);
		font = leftLabel->font();
		font.setPointSize(font.pointSize()-1);
		leftLabel->setFont(font);
		leftLabel->setText("<qt></qt>");
		leftLabel->hide();
		leftCursorLabel = new TQLabel(labelParent);
		leftCursorLabel->setPaletteBackgroundColor(labelParent->paletteBackgroundColor());
		leftCursorLabel->setPaletteForegroundColor(color);
		leftCursorLabel->setAlignment(TQt::AlignLeft|TQt::AlignVCenter|TQt::SingleLine);
		font = leftCursorLabel->font();
		font.setPointSize(font.pointSize()-1);
		leftCursorLabel->setFont(font);
		leftCursorLabel->setText("<qt></qt>");
		leftCursorLabel->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;
		leftLabel = NULL;
		leftCursorLabel = 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, int virtual_width, int virtual_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,baseline,horizoffset;

		// Obtain horizontal scrolling offset
		horizoffset = parentWidget->horizScrollOffset();

		// 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))*(virtual_width));
				if (leftEdgeIndex < 0) {
					if (x >= 0) {
						leftEdgeIndex = n;
					}
				}
				else {
					if (x >= virtual_width) {
						rightEdgeIndex = n;
						break;
					}
				}
			}
			if (rightEdgeIndex < 0) {
				rightEdgeIndex = numberOfSamples-1;
			}
		}
		activeSamples = std::abs(rightEdgeIndex-leftEdgeIndex);
		incr = (activeSamples/virtual_width)+1;
		for (n=leftEdgeIndex; n<numberOfSamples-incr; n=n+incr) {
			// Detect invalid samples and skip drawing of those samples
			// This avoids ugly drawing artifacts when not all sample data is available
			if (       isnan(positionArray[n])      || isinf(positionArray[n])
				|| isnan(positionArray[n+incr]) || isinf(positionArray[n+incr])
				|| isnan(sampleArray[n])        || isinf(sampleArray[n])
				|| isnan(sampleArray[n+incr])   || isinf(sampleArray[n+incr])) {
				continue;
			}

			x = (((positionArray[n]-leftEdge)/(rightEdge-leftEdge))*(virtual_width))-horizoffset;
			y = ((((sampleArray[n]+offset)-topEdge)/(bottomEdge-topEdge))*(virtual_height));
			x2 = (((positionArray[n+incr]-leftEdge)/(rightEdge-leftEdge))*(virtual_width))-horizoffset;
			y2 = ((((sampleArray[n+incr]+offset)-topEdge)/(bottomEdge-topEdge))*(virtual_height));
			baseline = ((((offset)-topEdge)/(bottomEdge-topEdge))*(virtual_height));

			// Do not draw lines that are placed fully off the screen
			if ((x < 0) && (x2 < 0)) continue;
			if ((y < 0) && (y2 < 0)) continue;
			if ((x > graticule_width) && (x2 > graticule_width)) continue;
			if ((y > graticule_height) && (y2 > graticule_height)) continue;

			// Clip lines to screen borders
			if (x < (0-horizoffset)) x = (0-horizoffset);
			if (x > (virtual_width-horizoffset)) x = (virtual_width-horizoffset);
			if (y < 0) y = 0;
			if (y > virtual_height) y = virtual_height;
			if (x2 < (0-horizoffset)) x2 = (0-horizoffset);
			if (x2 > (virtual_width-horizoffset)) x2 = (virtual_width-horizoffset);
			if (y2 < 0) y2 = 0;
			if (y2 > virtual_height) y2 = virtual_height;
			if (baseline < 0) baseline = 0;
			if (baseline > virtual_height) baseline = virtual_height;

			// Draw line(s)!
			if (m_digitalTraceDrawing) {
				p->drawLine(x+1, y, x2-1, y);
				p->drawLine(x2-1, y, x2+1, y2);

				// Draw fill areas
				if ((sampleArray[n] != 0) && (sampleArray[n+1] != 0)) {
					p->save();
					p->fillRect(x+1, y+1, x2-x, baseline-y, TQBrush(color.dark(m_digitalShaderDarkness)));
					p->restore();
				}
				else if ((sampleArray[n] != 0) && (sampleArray[n+1] == 0)) {
					p->save();
					// Fill sloping edges
					p->setPen(color.dark(m_digitalShaderDarkness));
					p->drawLine(x2-3, y+1, x2-1, y2-1);
					p->drawLine(x2-2, y+1, x2, y2-1);
					// Fill rectangle under trace
					p->fillRect(x+2, y+1, (x2-1)-(x+1)-1, baseline-y, TQBrush(color.dark(m_digitalShaderDarkness)));
					p->restore();
				}
				else if ((sampleArray[n] == 0) && (sampleArray[n+1] != 0)) {
					p->save();
					// Fill sloping edges
					p->setPen(color.dark(m_digitalShaderDarkness));
					p->drawLine(x2+1, y+1, x2+3, y2-1);
					p->drawLine(x2, y+1, x2+2, y2-1);
					p->restore();
				}
			}
			else {
				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+textOffset)-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(INT_MIN), double(INT_MAX), 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 TraceWidgetPushButton(labelParent);
		singleDecrBtn = new TraceWidgetPushButton(labelParent);
		multiIncrBtn = new TraceWidgetPushButton(labelParent);
		multiDecrBtn = new TraceWidgetPushButton(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, int virtual_width, int virtual_height, int offset) {
	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 = (std::abs(((position)/(100.0))*(virtual_width))-offset);
			if ((x >= 0) && (x < graticule_width)) {
				p->drawLine(x, 0, x, graticule_height);
			}
		}
		else {
			int y = std::abs(((position)/(100.0))*(virtual_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->virtualWidth();
	}
	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->virtualWidth();
	}
	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->virtualWidth();
	}
	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->virtualWidth();
	}
	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_prevWidgetWidth(-1),
	m_virtualWidth(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);
}

int GraticuleWidget::virtualWidth() {
	int widgetWidth = width();
	if (widgetWidth != m_prevWidgetWidth) {
		m_virtualWidth = widgetWidth;
		int minimumHorizWidth = m_base->m_horizDivs * m_base->m_minimumPixelsPerHorizDiv;
		if (m_virtualWidth < minimumHorizWidth) {
			m_virtualWidth = minimumHorizWidth;
		}

		if (m_base->m_horizScrollBar) {
			int offScreenPixels = m_virtualWidth - widgetWidth;
			if (offScreenPixels > 0) {
				m_base->m_horizScrollBar->setMinValue(0);
				m_base->m_horizScrollBar->setMaxValue(offScreenPixels);
				m_base->m_horizScrollBar->show();
			}
			else {
				m_base->m_horizScrollBar->hide();
				m_base->m_horizScrollBar->setMinValue(0);
				m_base->m_horizScrollBar->setMaxValue(0);
			}
		}
	}

	return m_virtualWidth;
}

void GraticuleWidget::horizScrollValueChanged(int) {
	updateGraticule();
	repaint(false);
}

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

	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 = virtualWidth() / m_base->m_horizDivs;
		x = 0;
		for (d=0; d<m_base->m_horizDivs; d++) {
			rx = x - m_base->horizScrollOffset();
			if ((rx >= 0) && (rx < m_graticulePixmap->width())) {
				p.drawLine(rx, 0, rx, 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(std::abs(((zoomBox.x())/(100.0))*(virtualWidth()))-m_base->horizScrollOffset(), std::abs(((zoomBox.y())/(100.0))*(height())), std::abs(((zoomBox.width())/(100.0))*(virtualWidth())), std::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(), virtualWidth(), 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(), virtualWidth(), m_graticulePixmap->height(), ((m_base->m_cursorArray[cursor]->orientation == TQt::Vertical)?m_base->horizScrollOffset():0));
		}
	}
	else {
		p.fillRect(x(), y(), virtualWidth(), 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_base->m_horizCursorDirectClickEnabled) {
				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/virtualWidth());
				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 (m_base->m_zoomBoxEnabled) {
					if ((x1 < virtualWidth()) && (y1 < height()) && (x2 < virtualWidth()) && (y2 < height()) && (x1 > 0) && (y1 > 0) && (x2 > 0) && (y2 > 0) && (pixelDiffX>0) && (pixelDiffY>0)) {
						x1 = ((x1/virtualWidth())*100.0);
						y1 = ((y1/height())*100.0);
						x2 = ((x2/virtualWidth())*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_base->m_horizCursorDirectClickEnabled) {
					// Allow the first two cursors to be set via click+drag
					x1 = ((x1/virtualWidth())*100.0);
					x2 = ((x2/virtualWidth())*100.0);
					m_base->setCursorPosition(0, x1);
					m_base->setCursorPosition(1, x2);
				}
			}
		}
	}
	else if (m_middleMouseDown) {
		if (e->button() == TQt::MidButton) {
			m_middleMouseDown = false;

			double x1 = m_prevDownPos.x()+m_base->horizScrollOffset();
			double y1 = m_prevDownPos.y();
			double x2 = e->x()+m_base->horizScrollOffset();
			double y2 = e->y();
			if ((x1 < virtualWidth()) && (y1 < height()) && (x2 < virtualWidth()) && (y2 < height()) && (x1 > 0) && (y1 > 0) && (x2 > 0) && (y2 > 0)) {
				TQPoint diff = e->pos() - m_prevDownPos;
				double dx = diff.x()*(100.0/virtualWidth());
				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++) {
			if ((int)cursor == m_base->m_hoverCursor) {
				continue;
			}

			double scaledYPos = (e->y()*100.0)/height();
			double scaledXPos = ((e->x()+m_base->horizScrollOffset())*100.0)/virtualWidth();
			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)*virtualWidth())/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() < virtualWidth()) && (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)/virtualWidth();
			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
			if (m_base->m_useAbsoluteHorizontalRange) {
				m_base->m_traceArray[trace]->graphStatusLabel->setText(TQString("<qt><nobr>@%2,%3</qt>").arg(TraceWidget::prettyFormat(realCursorXPosition, m_base->m_traceArray[trace]->rightEdge, 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+%4],%5</qt>").arg(TraceWidget::prettyFormat(realCursorXPosition, m_base->m_traceArray[trace]->rightEdge, m_base->m_traceArray[trace]->horizontalUnits)).arg(TraceWidget::prettyFormat(m_base->m_traceArray[trace]->leftEdge, m_base->m_traceArray[trace]->rightEdge, m_base->m_traceArray[trace]->horizontalUnits)).arg(TraceWidget::prettyFormat(realCursorXPosition - m_base->m_traceArray[trace]->leftEdge, 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 < virtualWidth()) && (y1 < height()) && (x2 < virtualWidth()) && (y2 < height()) && (x1 > 0) && (y1 > 0) && (x2 > 0) && (y2 > 0)) {
			if (m_base->m_zoomBoxEnabled) {
				x1 = ((x1/virtualWidth())*100.0);
				y1 = ((y1/height())*100.0);
				x2 = ((x2/virtualWidth())*100.0);
				y2 = ((y2/height())*100.0);
				m_base->setZoomCursorBox(TQRectF(x1, y1, x2, y2));
			}
			else if (m_base->m_horizCursorDirectClickEnabled) {
				// Allow the first two cursors to be set via click+drag
				x1 = ((x1/virtualWidth())*100.0);
				x2 = ((x2/virtualWidth())*100.0);
				m_base->setCursorPosition(0, x1);
				m_base->setCursorPosition(1, x2);
			}
		}
	}
	else if ((m_leftMouseDown) && (m_movingCursor >= 0)) {
		TQPoint diff = e->pos() - m_prevDownPos;
		double dx = diff.x()*(100.0/virtualWidth());
		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/virtualWidth());
		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));
	}

	if (m_base->m_hoverCursor >= 0) {
		if (m_base->m_cursorArray[m_base->m_hoverCursor]->enabled) {
			double scaledXPos = ((e->x()+m_base->horizScrollOffset())*100.0)/virtualWidth();
			if (scaledXPos < 0.0) {
				scaledXPos = 0.0;
			}
			if (scaledXPos > 100.0) {
				scaledXPos = 100.0;
			}
			m_base->m_cursorArray[m_base->m_hoverCursor]->position = scaledXPos;
			emit(cursorPositionChanged(m_base->m_hoverCursor, m_base->m_cursorArray[m_base->m_hoverCursor]->position));
			updateGraticule();
			repaint(false);
		}
	}

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

bool GraticuleWidget::userIsInteractingWithCursor() {
	return (m_movingCursor >= 0);
}

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_horizCursorDirectClickEnabled(false),
	m_horizScrollBar(0),
	m_useAbsoluteHorizontalRange(true),
	m_showLeftTraceInfoArea(false),
	m_showLeftCursorInfoArea(false),
	m_traceInfoCursor(0),
	m_hoverCursor(-1),
	m_leftTraceInfoLabelsFit(false),
	m_leftTraceInfoAreaFitSpacing(0),
	m_minimumPixelsPerHorizDiv(0) {
	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, 253, 1, 254);
	m_primaryLayout->setAlignment(TQt::AlignTop);
	m_rightPaneLayout = new TQGridLayout;
	m_leftPaneLayout = new TQGridLayout;
	m_traceLabelLayout = new TQGridLayout;
	m_infoLabelLayout = new TQGridLayout;
	m_cursorLabelLayout = new TQGridLayout;
	m_traceLeftLabelLayout = new TraceLabelLayout(this);
	m_traceLeftCursorLabelLayout = new TraceCursorLabelLayout(this);
	m_statusLabelLayout = new TQVBoxLayout;
	m_statusLabelLayoutInner = new TQVBoxLayout;
	m_primaryLayout->addLayout(m_traceLabelLayout, 255, 1);
	m_primaryLayout->addLayout(m_rightPaneLayout, 0, 255);
	m_primaryLayout->addLayout(m_leftPaneLayout, 0, 0);
	m_primaryLayout->addLayout(m_statusLabelLayout, 255, 255);
	m_primaryLayout->addLayout(m_statusLabelLayoutInner, 1, 253);
	m_rightPaneLayout->addLayout(m_cursorLabelLayout, 0, 1);
	m_rightPaneLayout->addLayout(m_infoLabelLayout, 1, 1);
	m_leftPaneLayout->addLayout(m_traceLeftLabelLayout, 0, 1);
	m_leftPaneLayout->addLayout(m_traceLeftCursorLabelLayout, 0, 2);
	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_leftPaneLayout->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);
	m_leftPaneLayout->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];
		m_traceArray[i] = NULL;
	}
}

int TraceWidget::horizScrollOffset() {
	if (m_horizScrollBar) {
		return m_horizScrollBar->value();
	}
	else {
		return 0;
	}
}

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, bool deferUpdate) {
	VERIFY_TRACE_ARRAY_SIZE

	unsigned int i;
	int prev_samples = m_traceArray[traceNumber]->sampleArray.count();

	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;

	// Zero the uninitialized portion of the data arrays to avoid ugly drawing artifacts on resize
	for (i=prev_samples; i<samples; i++) {
		m_traceArray[traceNumber]->sampleArray[i] = 0;
		m_traceArray[traceNumber]->positionArray[i] = 0;
	}

	if (!deferUpdate) {
		m_graticuleWidget->updateGraticule();
		updateTraceText();
	}
}

void TraceWidget::setDigitalTraceMode(uint traceNumber, bool enabled, bool deferUpdate) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->m_digitalTraceDrawing = enabled;
	if (!deferUpdate) {
		m_graticuleWidget->updateGraticule();
		updateTraceText();
	}
}

void TraceWidget::suppressNameInCursorText(uint traceNumber, bool suppress, bool deferUpdate) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->m_suppressNameInCursorText = suppress;

	if (!deferUpdate) {
		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, bool deferUpdate) {
	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;

	if (!deferUpdate) {
		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]->leftLabel->setPaletteBackgroundColor(paletteBackgroundColor());
		m_traceArray[trace]->leftLabel->setPaletteForegroundColor(m_traceArray[trace]->color);
		m_traceArray[trace]->leftCursorLabel->setPaletteBackgroundColor(paletteBackgroundColor());
		m_traceArray[trace]->leftCursorLabel->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));
			}
		}
		TQString traceDataString = TQString("<br>%1,%2,%3").arg(prettyFormat(m_traceArray[trace]->sampleMin, vertical_range, m_traceArray[trace]->verticalUnits)).arg(prettyFormat(m_traceArray[trace]->sampleMax, vertical_range, m_traceArray[trace]->verticalUnits)).arg(prettyFormat(m_traceArray[trace]->sampleAverage, 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%9</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)).arg(traceDataString));
		m_traceArray[trace]->leftLabel->setText(TQString("<qt><nobr>%1</qt>").arg(m_traceArray[trace]->traceName));
	}
}

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) {
				if (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) && ((int)cursor != m_hoverCursor)) {
							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;
									}
								}
							}
						}
						if (m_traceArray[trace]->m_suppressNameInCursorText) {
							cursorText.append(TQString("<br>%2%3").arg(prettyFormat(realCursorPosition, (m_useAbsoluteHorizontalRange)?m_traceArray[trace]->rightEdge:horizontal_range, m_traceArray[trace]->horizontalUnits)).arg(deltaText));
						}
						else {
							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));
						}
					}
				}

				if (cursor == m_traceInfoCursor) {
					double horizontal_range = (m_traceArray[trace]->rightEdge-m_traceArray[trace]->leftEdge);
					double realCursorPosition = (m_traceArray[trace]->leftEdge+((m_cursorArray[cursor]->position/100.0)*horizontal_range));

					if (m_traceArray[trace]->m_digitalTraceDrawing) {
						// Find closest data point
						unsigned int n;
						unsigned int closest = 0;
						for (n=0; n<m_traceArray[trace]->numberOfSamples-1; n++) {
							if ((realCursorPosition >= m_traceArray[trace]->positionArray[n]) && (realCursorPosition < m_traceArray[trace]->positionArray[n+1])) {
								closest = n;
								break;
							}
						}

						if (m_traceArray[trace]->sampleArray.count() > 0) {
							m_traceArray[trace]->leftCursorLabel->setText(TQString("<qt><nobr>%2</qt>").arg((m_traceArray[trace]->sampleArray[closest]==0)?"0":"1"));
						}
						else {
							m_traceArray[trace]->leftCursorLabel->setText(TQString("<qt><nobr></qt>"));
						}
					}
					else {
						// Find closest data point
						unsigned int n;
						unsigned int closest = 0;
						double diff;
						double distance = DBL_MAX;
						if (m_traceArray[trace]->positionArray.count() >= m_traceArray[trace]->numberOfSamples) {
							for (n=0; n<m_traceArray[trace]->numberOfSamples; n++) {
								diff = fabs(m_traceArray[trace]->positionArray[n] - realCursorPosition);
								if (diff < distance) {
									distance = diff;
									closest = n;
								}
							}
						}
						if (m_traceArray[trace]->sampleArray.count() > 0) {
							m_traceArray[trace]->leftCursorLabel->setText(TQString("<qt><nobr>%2</qt>").arg(TraceWidget::prettyFormat(m_traceArray[trace]->sampleArray[closest], m_traceArray[trace]->sampleArray[closest], m_traceArray[trace]->verticalUnits)));
						}
						else {
							m_traceArray[trace]->leftCursorLabel->setText(TQString("<qt><nobr></qt>"));
						}
					}
				}
			}
		}

		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, bool deferUpdate) {
	VERIFY_TRACE_ARRAY_SIZE

	unsigned int i;
	double curValue;

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

	// Calculate trace minimum, maximum, and average
	m_traceArray[traceNumber]->sampleMin = DBL_MAX;
	m_traceArray[traceNumber]->sampleMax = DBL_MIN;
	m_traceArray[traceNumber]->sampleAverage = 0;
	m_traceArray[traceNumber]->minIndex = 0;
	m_traceArray[traceNumber]->maxIndex = 0;
	for (i=0; i<m_traceArray[traceNumber]->sampleArray.size(); i++) {
		curValue = m_traceArray[traceNumber]->sampleArray[i];
		if (curValue < m_traceArray[traceNumber]->sampleMin) {
			m_traceArray[traceNumber]->sampleMin = curValue;
			m_traceArray[traceNumber]->minIndex = i;
		}
		if (curValue > m_traceArray[traceNumber]->sampleMax) {
			m_traceArray[traceNumber]->sampleMax = curValue;
			m_traceArray[traceNumber]->maxIndex = i;
		}
		m_traceArray[traceNumber]->sampleAverage = m_traceArray[traceNumber]->sampleAverage + curValue;
	}
	m_traceArray[traceNumber]->sampleAverage = m_traceArray[traceNumber]->sampleAverage / m_traceArray[traceNumber]->numberOfSamples;

	if (!deferUpdate) {
		updateTraceText();
		updateCursorText();
		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, bool deferUpdate) {
	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;

	if (!deferUpdate) {
		updateCursorText();
		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, TextDisplayType showText, bool deferUpdate) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->enabled = enabled;
	if (enabled) {
		if (showText == FullText) {
			m_traceArray[traceNumber]->paramLabel->show();
			m_traceArray[traceNumber]->leftLabel->show();
			m_traceArray[traceNumber]->leftCursorLabel->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]->leftLabel->hide();
			m_traceArray[traceNumber]->leftCursorLabel->hide();
			m_traceArray[traceNumber]->graphStatusLabel->hide();
			if (showText == SummaryText) {
				m_traceArray[traceNumber]->graphStatusLabelInner->show();
			}
			else {
				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();
		}
	}
	else {
		m_traceArray[traceNumber]->paramLabel->hide();
		m_traceArray[traceNumber]->leftLabel->hide();
		m_traceArray[traceNumber]->leftCursorLabel->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();
	}

	if (!deferUpdate) {
		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, bool deferUpdate) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->traceName = name;
	if (!deferUpdate) {
		updateTraceText();
	}
}

TQString TraceWidget::traceHorizontalUnits(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->horizontalUnits;
}

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

	m_traceArray[traceNumber]->horizontalUnits = units;
	if (!deferUpdate) {
		updateTraceText();
	}
}

TQString TraceWidget::traceVerticalUnits(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->verticalUnits;
}

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

	m_traceArray[traceNumber]->verticalUnits = units;
	if (!deferUpdate) {
		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();
		if ((int)cursorNumber != m_hoverCursor) {
			m_cursorArray[cursorNumber]->singleIncrBtn->show();
			m_cursorArray[cursorNumber]->singleDecrBtn->show();
			m_cursorArray[cursorNumber]->multiIncrBtn->show();
			m_cursorArray[cursorNumber]->multiDecrBtn->show();
		}
		else {
			m_cursorArray[cursorNumber]->singleIncrBtn->hide();
			m_cursorArray[cursorNumber]->singleDecrBtn->hide();
			m_cursorArray[cursorNumber]->multiIncrBtn->hide();
			m_cursorArray[cursorNumber]->multiDecrBtn->hide();
		}
	}
	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::setTraceInfoCursor(uint cursorNumber) {
	VERIFY_CURSOR_ARRAY_SIZE

	m_traceInfoCursor = cursorNumber;
	updateCursorText();
}

void TraceWidget::setHoverCursor(uint cursorNumber) {
	VERIFY_CURSOR_ARRAY_SIZE

	m_hoverCursor = cursorNumber;
	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));
		}
		if ((!m_zoomBoxEnabled) && (m_horizCursorDirectClickEnabled) && (m_cursorArray.count() > 1)) {
			double x;
			if (m_cursorArray[0]->position > m_cursorArray[1]->position) {
				x = m_cursorArray[1]->position;
			}
			else {
				x = m_cursorArray[0]->position;
			}
			return TQRectF(x, 0, fabs(m_cursorArray[0]->position - m_cursorArray[1]->position), 100);
		}
		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[k] = 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();
}

void TraceWidget::setHorizCursorDirectClickEnabled(bool enabled) {
	m_horizCursorDirectClickEnabled = enabled;
	m_graticuleWidget->updateGraticule();
}

void TraceWidget::showLeftTraceInfoArea(bool show) {
	m_showLeftTraceInfoArea = show;
	for (uint i=0;i<m_traceArray.count();i++) {
		if (m_showLeftTraceInfoArea) {
			m_traceArray[i]->leftLabel->show();
		}
		else {
			m_traceArray[i]->leftLabel->hide();
		}
	}
}

void TraceWidget::showLeftCursorTraceInfoArea(bool show) {
	m_showLeftCursorInfoArea = show;
	for (uint i=0;i<m_traceArray.count();i++) {
		if (m_showLeftCursorInfoArea) {
			m_traceArray[i]->leftCursorLabel->show();
		}
		else {
			m_traceArray[i]->leftCursorLabel->hide();
		}
	}
}

void TraceWidget::fitLeftTraceInfoArea(bool fit) {
	m_leftTraceInfoLabelsFit = fit;
	m_traceLeftLabelLayout->invalidate();
	m_traceLeftCursorLabelLayout->invalidate();
}

void TraceWidget::setLeftTraceInfoAreaFitSpacing(int spacing) {
	m_leftTraceInfoAreaFitSpacing = spacing;
	m_traceLeftLabelLayout->invalidate();
	m_traceLeftCursorLabelLayout->invalidate();
}

void TraceWidget::setMinimumPixelsPerHorizDiv(unsigned int pixels) {
	m_minimumPixelsPerHorizDiv = pixels;
}

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) {
	setTraceOffset(traceNumber, offset, false);
}

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

	m_traceArray[traceNumber]->offset = offset;

	if (!deferUpdate) {
		m_graticuleWidget->repaint(false);
		updateTraceText();
	}
}

double TraceWidget::traceTextOffset(uint traceNumber) {
	VERIFY_TRACE_ARRAY_SIZE

	return m_traceArray[traceNumber]->textOffset;
}

void TraceWidget::setTraceTextOffset(uint traceNumber, double offset) {
	setTraceOffset(traceNumber, offset, false);
}

bool TraceWidget::horizontalRangeModeAbsolute() {
	return m_useAbsoluteHorizontalRange;
}

void TraceWidget::setHorizontalRangeModeAbsolute(bool absolute) {
	m_useAbsoluteHorizontalRange = absolute;
}

void TraceWidget::setTraceTextOffset(uint traceNumber, double offset, bool deferUpdate) {
	VERIFY_TRACE_ARRAY_SIZE

	m_traceArray[traceNumber]->textOffset = offset;

	if (!deferUpdate) {
		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) {
			m_traceLeftLabelLayout->invalidate();
			m_traceLeftCursorLabelLayout->invalidate();
			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_traceLeftLabelLayout->addWidget(m_traceArray[i]->leftLabel, TQt::AlignTop);
				m_traceLeftCursorLabelLayout->addWidget(m_traceArray[i]->leftCursorLabel, TQt::AlignTop);
				m_statusLabelLayout->insertWidget(i, m_traceArray[i]->graphStatusLabel, TQt::AlignTop);
				m_statusLabelLayoutInner->insertWidget(i, m_traceArray[i]->graphStatusLabelInner);
			}
		}
	}
	else if (newsize < oldcount) {
		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_traceLeftLabelLayout->remove(m_traceArray[i]->leftLabel);
				m_traceLeftCursorLabelLayout->remove(m_traceArray[i]->leftCursorLabel);
				m_statusLabelLayout->remove(m_traceArray[i]->graphStatusLabel);
				m_statusLabelLayoutInner->remove(m_traceArray[i]->graphStatusLabelInner);
			}
			delete m_traceArray[i];
			m_traceArray[i] = NULL;
		}
		m_traceArray.resize(newsize);
	}
}

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];
		}
	}
}

bool TraceWidget::userIsInteractingWithCursor() {
	return m_graticuleWidget->userIsInteractingWithCursor();
}

TQSize TraceWidget::sizeHint() const {
	return TQWidget::sizeHint();
}

TQSize TraceWidget::minimumSizeHint() const {
	return TQWidget::minimumSizeHint();
}

TraceScrollView::TraceScrollView(TQWidget* parent, const char* name) : TQScrollView(parent, name) {
	m_traceWidget = new TraceWidget(viewport());
	addChild(m_traceWidget);
}

TraceScrollView::~TraceScrollView() {
	delete m_traceWidget;
	m_traceWidget = NULL;
}

TQSize TraceScrollView::sizeHint() const {
	return TQScrollView::sizeHint();
}

TQSize TraceScrollView::minimumSizeHint() const {
	return m_traceWidget->minimumSizeHint();
}

TraceWidget* TraceScrollView::traceWidget() {
	return m_traceWidget;
}

TraceScrollWidget::TraceScrollWidget(TQWidget* parent, const char* name) : TQVBox(parent, name) {
	m_traceScrollView = new TraceScrollView(this);
	m_horizScrollBar = new TQScrollBar(this);
	m_traceScrollView->m_traceWidget->m_horizScrollBar = m_horizScrollBar;
	connect(m_horizScrollBar, TQT_SIGNAL(valueChanged(int)), m_traceScrollView->m_traceWidget->m_graticuleWidget, TQT_SLOT(horizScrollValueChanged(int)));
	m_traceScrollView->setHScrollBarMode(TQScrollView::AlwaysOff);
	m_horizScrollBar->setOrientation(TQt::Horizontal);
	m_horizScrollBarMode = TQScrollView::AlwaysOff;
}

TraceScrollWidget::~TraceScrollWidget() {
	delete m_traceScrollView;
	m_traceScrollView = NULL;
	delete m_horizScrollBar;
	m_horizScrollBar = NULL;
}

void TraceScrollWidget::setResizePolicy(TQScrollView::ResizePolicy rp) {
	m_traceScrollView->setResizePolicy(rp);
}

TQScrollView::ResizePolicy TraceScrollWidget::resizePolicy() const {
	return m_traceScrollView->resizePolicy();
}

void TraceScrollWidget::setHScrollBarMode(TQScrollView::ScrollBarMode sm) {
	m_horizScrollBarMode = sm;
}

void TraceScrollWidget::setVScrollBarMode(TQScrollView::ScrollBarMode sm) {
	m_traceScrollView->setVScrollBarMode(sm);
}

TraceWidget* TraceScrollWidget::traceWidget() {
	return m_traceScrollView->m_traceWidget;
}

#include "tracewidget.moc"
