/*
Copyright (C) 2006 - 2009 Evan Teran
                   eteran@alum.rit.edu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

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.
*/

#include "QDisassemblyView.h"
#include "AnalyzerInterface.h"
#include "ArchProcessorInterface.h"
#include "Configuration.h"
#include "Debugger.h"
#include "DebuggerCoreInterface.h"
#include "Instruction.h"
#include "SymbolManager.h"
#include "SyntaxHighlighter.h"
#include "Util.h"
#include "format_address.h"

#include <QAbstractItemDelegate>
#include <QAbstractTextDocumentLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QPixmap>
#include <QScrollBar>
#include <QTextDocument>
#include <QTextDocument>
#include <QTextLayout>
#include <QToolTip>
#include <QtGlobal>
#include <climits>

#define DEFAULT_BYTE_WIDTH 8



void drawRichText(QPainter *painter, int x, int y, QTextDocument *text) {
	painter->save();
	painter->translate(x, y);
	QAbstractTextDocumentLayout::PaintContext context;
	context.palette.setColor(QPalette::Text, painter->pen().color());
	text->documentLayout()->draw(painter, context);
	painter->restore();
}

//------------------------------------------------------------------------------
// Name: QDisassemblyView(QWidget * parent)
// Desc: constructor
//------------------------------------------------------------------------------
QDisassemblyView::QDisassemblyView(QWidget * parent) : QAbstractScrollArea(parent),
		m_AddressOffset(0), m_CurrentAddress(0), 
		m_Breakpoint(":/debugger/images/edb14-breakpoint.png"),
		m_CurrentAddressIcon(":/debugger/images/edb14-arrow.png"),
		m_SelectedInstructionAddress(0), 
		m_SelectedInstructionSize(0), m_MovingLine1(false),
		m_MovingLine2(false), m_Line1(0), m_Line2(0),
		m_Highhighlighter(new SyntaxHighlighter(this)) {
		

	setShowAddressSeparator(true);
	
	setFont(QFont("Monospace", 8));
	setMouseTracking(true);
	setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QDisassemblyView::~QDisassemblyView() {
}

//------------------------------------------------------------------------------
// Name: setShowAddressSeparator(bool value)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::setShowAddressSeparator(bool value) {
	util::format_address<edb::address_t>::format_string(m_AddressFormatString, value);
	m_ShowAddressSeparator = value;
}

//------------------------------------------------------------------------------
// Name: formatAddress(edb::address_t address)
// Desc: 
//------------------------------------------------------------------------------
QString QDisassemblyView::formatAddress(edb::address_t address) const {
	return util::format_address<edb::address_t>::format(m_AddressFormatString, address);
}

//------------------------------------------------------------------------------
// Name: repaint()
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::repaint() {
	viewport()->repaint();
}

//------------------------------------------------------------------------------
// Name: addressShown(edb::address_t address) const
// Desc: returns true if a given address is in the visible range
//------------------------------------------------------------------------------
bool QDisassemblyView::addressShown(edb::address_t address) const {
	return m_ShowAddresses.contains(address);
}

//------------------------------------------------------------------------------
// Name: setCurrentAddress(edb::address_t address)
// Desc: sets the 'current address' (where EIP is usually)
//------------------------------------------------------------------------------
void QDisassemblyView::setCurrentAddress(edb::address_t address) {
	m_CurrentAddress = address;
}

//------------------------------------------------------------------------------
// Name: setRegion(const MemRegion &r)
// Desc: sets the memory region we are viewing
//------------------------------------------------------------------------------
void QDisassemblyView::setRegion(const MemRegion &r) {
	if(m_Region != r) {
		m_Region = r;
		updateScrollbars();
	}
	repaint();
}

//------------------------------------------------------------------------------
// Name: clear()
// Desc: clears the display
//------------------------------------------------------------------------------
void QDisassemblyView::clear() {
	setRegion(MemRegion());
}

//------------------------------------------------------------------------------
// Name: setAddressOffset(edb::address_t address)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::setAddressOffset(edb::address_t address) {
	m_AddressOffset = address;
}

//------------------------------------------------------------------------------
// Name: scrollTo(edb::address_t address)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::scrollTo(edb::address_t address) {
	verticalScrollBar()->setValue(address - m_AddressOffset);
}

//------------------------------------------------------------------------------
// Name: formatInstructionBytes(const edb::Instruction &insn, int maxStringPx, const QFontMetrics &metrics) const
// Desc: 
//------------------------------------------------------------------------------
QString QDisassemblyView::formatInstructionBytes(const edb::Instruction &insn, int maxStringPx, const QFontMetrics &metrics) const {
	const QString byteBuffer = edb::v1::formatBytes(QByteArray::fromRawData(reinterpret_cast<const char *>(insn.buffer()), insn.size()));
	return metrics.elidedText(byteBuffer, Qt::ElideRight, maxStringPx);
}

//------------------------------------------------------------------------------
// Name: formatInstructionBytes(const edb::Instruction &insn) const
// Desc: 
//------------------------------------------------------------------------------
QString QDisassemblyView::formatInstructionBytes(const edb::Instruction &insn) const {
	return edb::v1::formatBytes(QByteArray::fromRawData(reinterpret_cast<const char *>(insn.buffer()), insn.size()));
}

static const QColor filling_dis_colour	= Qt::gray;
static const QColor default_dis_colour	= Qt::blue;
static const QColor invalid_dis_colour	= Qt::blue;
static const QColor data_dis_colour		= Qt::blue;

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::drawInstruction(QPainter &painter, const edb::Instruction &insn, bool upper, int y, int line_height, int l2) const {

	const bool is_filling	= edb::v1::archProcessor().isFilling(insn);
	int x					= m_FontWidth + m_FontWidth + l2 + (m_FontWidth / 2);
	const int ret			= insn.size();
	

	if(insn.valid()) {
		const QString opcode = QString::fromStdString(insn.format(upper));
		
		if(is_filling) {
			painter.setPen(filling_dis_colour);
			painter.drawText(
				x,
				y, 
				opcode.length() * m_FontWidth, 
				line_height, 
				Qt::AlignTop, 
				opcode);
		} else {
			painter.setPen(default_dis_colour);
			QTextDocument doc;
			doc.setDefaultFont(font());
			doc.setDocumentMargin(0);
			doc.setPlainText(opcode);
			m_Highhighlighter->setDocument(&doc);
			drawRichText(&painter, x, y, &doc);
		}

	} else {
		QString asmBuffer;
		const quint8 *const buf = insn.buffer();

		switch(insn.size()) {
		case 1:
			painter.setPen(data_dis_colour);
			asmBuffer.sprintf("db 0x%02x", *buf & 0xff);
			break;
		case 2:
			painter.setPen(data_dis_colour);
			asmBuffer.sprintf("dw 0x%04x", *reinterpret_cast<const quint16 *>(buf) & 0xffff);
			break;
		case 4:
			painter.setPen(data_dis_colour);
			asmBuffer.sprintf("dd 0x%08x", *reinterpret_cast<const quint32 *>(buf) & 0xffffffff);
			break;
		case 8:
			painter.setPen(data_dis_colour);
			asmBuffer.sprintf("dq 0x%016llx", *reinterpret_cast<const quint64 *>(buf));
			break;
		default:
			// we tried...didn't we?
			painter.setPen(invalid_dis_colour);
			asmBuffer = tr("invalid");
		}
		
		painter.drawText(
			x,
			y, 
			asmBuffer.length() * m_FontWidth, 
			line_height, 
			Qt::AlignTop, 
			asmBuffer);
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::drawFunctionMarkers( QPainter &painter, edb::address_t address, int l2, int y, int insn_size, AnalyzerInterface *analyzer) {
	if(analyzer != 0) {
	
		painter.setPen(QPen(palette().shadow().color(), 2));
	
		const AnalyzerInterface::AddressCatagory cat = analyzer->catagory(address);
		const int line_height = lineHeight();
		const int x = l2 + m_FontWidth;
		
		switch(cat) {
		case AnalyzerInterface::ADDRESS_FUNC_START:

			// half of a horizontal
			painter.drawLine(
				x,
				y + line_height / 2,
				l2 + (m_FontWidth / 2) + m_FontWidth,
				y + line_height / 2
				);
				
			// half of a vertical
			painter.drawLine(
				x, 
				y + line_height / 2, 
				x, 
				y + line_height
				);

			break;
		case AnalyzerInterface::ADDRESS_FUNC_BODY:
			if(analyzer->catagory(address + insn_size - 1) == AnalyzerInterface::ADDRESS_FUNC_END) {
				goto do_end;
			} else {
				
				// vertical line
				painter.drawLine(
					x, 
					y, 
					x, 
					y + line_height);
			
			}
			break;
		case AnalyzerInterface::ADDRESS_FUNC_END:
		do_end:

			// half of a vertical
			painter.drawLine(
				x, 
				y, 
				x, 
				y + line_height / 2
				);

			// half of a horizontal
			painter.drawLine(
				x,
				y + line_height / 2,
				l2 + (m_FontWidth / 2) + m_FontWidth,
				y + line_height / 2
				);
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: paintEvent(QPaintEvent *)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::paintEvent(QPaintEvent *) {

	QPainter painter(viewport());
	
	const bool uppercase = edb::v1::config().uppercaseDisassembly;
		
	int viewableLines		= viewport()->height() / lineHeight();
	int currentLine			= verticalScrollBar()->value();
	int row_index			= 0;
	int y					= 0;
	const int l1			= line1();
	const int l2			= line2();
	const int line_height	= lineHeight();
	const int font_width	= painter.fontMetrics().maxWidth();
	
	// TODO: reimplement me 
	// const Configuration::Syntax syntax = edb::v1::config().syntax;
	const int regionSize = m_Region.size();
	
	if(regionSize == 0) {
		return;
	}
	
	m_ShowAddresses.clear();
	m_ShowAddresses.insert(m_AddressOffset + currentLine);
	
	const int bytesWidth = l2 - l1;
	
	const QBrush alternatedBaseColour	= palette().alternateBase();
	const QBrush bytesColour			= palette().text();
	const QBrush dividerColour			= palette().shadow();
	const QPen bytesPen					= bytesColour.color();
	const QPen dividerPen				= dividerColour.color();
	
	AnalyzerInterface *const analyzer = edb::v1::analyzer();
	
	edb::address_t last_address = 0;

	while(viewableLines >= 0 && currentLine < regionSize) {
		const edb::address_t address = m_AddressOffset + currentLine;

		quint8 buf[edb::Instruction::MAX_SIZE + 1];

		// do the longest read we can while still not passing the region end
		int buf_size = qMin<edb::address_t>((m_Region.end - address), sizeof(buf));

		// read in the bytes...
		if(!edb::v1::getInstructionBytes(address, buf, buf_size)) {
			// if the read failed, let's pretend that we were able to read a 
			// single 0xff byte so that we have _something_ to display.
			buf_size = 1;
			*buf = 0xff;
		}

		// disassemble the instruction
		const edb::Instruction insn1(buf, buf_size, address, std::nothrow);
		const edb::Instruction insn2(buf, 1, address, std::nothrow);
		
		// has the analyzer decided that the next instruction is a block start? if
		// so, then cut this one short so that it doesn't overlap with the block
		// anymore
		const edb::Instruction &insn(
			(analyzer != 0) && (analyzer->catagory(address + 1) == AnalyzerInterface::ADDRESS_FUNC_START) ?
			insn2 :
			insn1);

		const int insn_size = insn.size();
		
		//TODO: remove this hack here
		if(insn_size == 0) {
			return;
		}
				
		// draw alternating line backgrounds
		if(row_index & 1) {
			painter.fillRect(0, y, width(), line_height, alternatedBaseColour);
		}
		
		drawFunctionMarkers(painter, address, l2, y, insn_size, analyzer);

		// draw breakpoint icon or eip indicator
		if(address == m_CurrentAddress) {
			painter.drawPixmap(1, y + 1, m_CurrentAddressIcon);
		} else if(edb::v1::debuggerBase->findBreakpoint(address) != 0) {
			painter.drawPixmap(1, y + 1, m_Breakpoint);
		}
		
		// format the different components
		const QString byteBuffer	= formatInstructionBytes(insn, bytesWidth, painter.fontMetrics());
		const QString addressBuffer	= formatAddress(address);

		// draw the address
		painter.setPen(QPen(Qt::red));
		painter.drawText(
			m_Breakpoint.width() + 1, 
			y, 
			addressBuffer.length() * font_width, 
			line_height, 
			Qt::AlignTop, 
			addressBuffer);

		// draw the data bytes
		painter.setPen(bytesPen);
		painter.drawText(
			l1 + (font_width / 2),
			y, 
			byteBuffer.length() * font_width, 
			line_height, 
			Qt::AlignTop, 
			byteBuffer);


		//optionally draw the symbol name
		const SymbolManager::Symbol *sym = edb::v1::symbolManager().find(address);
		if(sym != 0) {

			const int maxStringPx = l1 - (m_Breakpoint.width() + 1 + ((addressBuffer.length() + 1) * font_width));

			if(maxStringPx >= font_width) {
				const QString symBuffer = painter.fontMetrics().elidedText(sym->name, Qt::ElideRight, maxStringPx);

				painter.drawText(
					m_Breakpoint.width() + 1 + ((addressBuffer.length() + 1) * font_width), 
					y, 
					symBuffer.length() * font_width, 
					line_height, 
					Qt::AlignTop, 
					symBuffer);
			}
		}

		// for relative jumps draw the jump direction indicators
		switch(insn.type()) {
		case edb::Instruction::OP_JCC:
		case edb::Instruction::OP_JMP:
		case edb::Instruction::OP_LOOP:
		case edb::Instruction::OP_LOOPE:
		case edb::Instruction::OP_LOOPNE:
			if(insn.operand(0).generalType() == edb::Operand::TYPE_REL) {
				const edb::Instruction::address_t target = insn.operand(0).relativeTarget();
			
				painter.drawText(
					l2 + font_width + (font_width / 2),
					y, 
					font_width, 
					line_height, 
					Qt::AlignTop,
					QString((target > address) ? QChar(0x02C7) : QChar(0x02C6))
					);
			}
			break;
		default:
			break;
		}
		
		// draw the disassembly
		currentLine += drawInstruction(painter, insn, uppercase, y, line_height, l2);
		m_ShowAddresses.insert(address);
		last_address = address;
			
		y += line_height;
		++row_index;
		--viewableLines;
	}
	
	m_ShowAddresses.remove(last_address);
	
	painter.setPen(dividerPen);
	painter.drawLine(l1, 0, l1, height());
	painter.drawLine(l2, 0, l2, height());
}

//------------------------------------------------------------------------------
// Name: setFont(const QFont &f)
// Desc: overloaded version of setFont, calculates font metrics for later
//------------------------------------------------------------------------------
void QDisassemblyView::setFont(const QFont &f) {

	// TODO: assert that we are using a fixed font & find out if we care?
	QAbstractScrollArea::setFont(f);

	// recalculate all of our metrics/offsets
	const QFontMetrics metrics(f);
	m_FontWidth		= metrics.maxWidth();
	m_FontHeight	= metrics.height();
	
	updateScrollbars();
}

//------------------------------------------------------------------------------
// Name: resizeEvent (QResizeEvent *)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::resizeEvent (QResizeEvent *) {
	updateScrollbars();
}

//------------------------------------------------------------------------------
// Name: lineHeight() const
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::lineHeight() const {
	return qMax(m_FontHeight, m_CurrentAddressIcon.height());
}

//------------------------------------------------------------------------------
// Name: updateScrollbars()
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::updateScrollbars() {
	const unsigned int totalLines		= m_Region.size();
	const unsigned int viewableLines	= viewport()->height() / lineHeight();
	const unsigned int scrollMax 		= (totalLines > viewableLines) ? totalLines - 1 : 0;
		
	verticalScrollBar()->setMaximum(scrollMax);
}

//------------------------------------------------------------------------------
// Name: autoLine1() const
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::autoLine1() const {
	const unsigned int elements = addressLen();
	return (elements * m_FontWidth) + (m_FontWidth / 2) + m_Breakpoint.width() + 1;
}

//------------------------------------------------------------------------------
// Name: line1() const
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::line1() const {
	if(m_Line1 == 0) {
		return autoLine1();
	} else {
		return m_Line1;
	}
}

//------------------------------------------------------------------------------
// Name: line2() const
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::line2() const {
	if(m_Line2 == 0) {
		return line1() + (DEFAULT_BYTE_WIDTH * 3) * m_FontWidth;
	} else {
		return m_Line2;
	}
}

//------------------------------------------------------------------------------
// Name: addressLen() const
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::addressLen() const {
	const unsigned int addressLength = (sizeof(edb::address_t) * CHAR_BIT) / 4;
	return addressLength + (m_ShowAddressSeparator ? 1 : 0);
}

//------------------------------------------------------------------------------
// Name: InstructionSize(quint8 *buffer, std::size_t size)
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::InstructionSize(quint8 *buffer, std::size_t size) {
	edb::Instruction insn(buffer, size, 0, std::nothrow);
	return insn.size();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
edb::address_t QDisassemblyView::addressFromPoint(const QPoint &pos) const {
	const edb::address_t address = addressFromCoord(pos.x(), pos.y()) + m_AddressOffset;
	if(address >= m_Region.end) {
		return 0;
	}
	return address;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::getInstructionSize(edb::address_t address, bool &ok, quint8 *buf, int &size) const {
	int ret = 0;
	
	if(size < 0) {
		ok = false;
	} else {	
		ok = edb::v1::getInstructionBytes(address, buf, size);
			
		if(ok) {
			ret = InstructionSize(buf, size);
		}
	}
	return ret;
}

//------------------------------------------------------------------------------
// Name: getInstructionSize(edb::address_t address, bool &ok) const
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::getInstructionSize(edb::address_t address, bool &ok) const {
	quint8 buf[edb::Instruction::MAX_SIZE];
	
	// do the longest read we can while still not crossing region end
	int buf_size = sizeof(buf);			
	if(m_Region.end != 0 && address + buf_size > m_Region.end) {
		
		if(address <= m_Region.end) {
			buf_size = m_Region.end - address;
		} else {
			buf_size = 0;
		}
	}
	
	return getInstructionSize(address, ok, buf, buf_size);
}

//------------------------------------------------------------------------------
// Name: addressFromCoord(int x, int y) const
// Desc: 
//------------------------------------------------------------------------------
edb::address_t QDisassemblyView::addressFromCoord(int x, int y) const {
	Q_UNUSED(x);
	
	const int line = y / lineHeight();
	const edb::address_t offset = verticalScrollBar()->value();
	edb::address_t address = offset;

	// add up all the instructions sizes up to the line we want
	for(int i = 0; i < line; ++i) {
		bool ok;
				
		int size = getInstructionSize(m_AddressOffset + address, ok);
		if(ok) {
			address += (size != 0) ? size : 1;
		} else {
			address += 1;
		}
	}
	
	return address;
}

//------------------------------------------------------------------------------
// Name: mouseDoubleClickEvent(QMouseEvent *event)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::mouseDoubleClickEvent(QMouseEvent *event) {
	if(event->button() == Qt::LeftButton) {
		if(event->x() < line1()) {
			const edb::address_t address = addressFromPoint(event->pos());
					
			if(m_Region.contains(address)) {
				emit breakPointToggled(address);
				repaint();
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: event(QEvent *event)
// Desc: 
//------------------------------------------------------------------------------
bool QDisassemblyView::event(QEvent *event) {

	if (event->type() == QEvent::ToolTip) {
		bool show = false;
	
		const QHelpEvent *const helpEvent = static_cast<QHelpEvent *>(event);

		if(helpEvent->x() >= line1() && helpEvent->x() < line2()) {
		
			const edb::address_t address = addressFromPoint(helpEvent->pos());
			
			quint8 buf[edb::Instruction::MAX_SIZE];
			
			// do the longest read we can while still not passing the region end
			int buf_size = qMin<edb::address_t>((m_Region.end - address), sizeof(buf));
			if(edb::v1::getInstructionBytes(address, buf, buf_size)) {
				const edb::Instruction insn(buf, buf_size, address, std::nothrow);

				if((line1() + (static_cast<int>(insn.size()) * 3) * m_FontWidth) > line2()) {
					const QString byteBuffer = formatInstructionBytes(insn);
					QToolTip::showText(helpEvent->globalPos(), byteBuffer);
					show = true;
				}
			}
		}
		
		if(!show) {
			QToolTip::showText(helpEvent->globalPos(), QString());
		}
	}
	
	return QAbstractScrollArea::event(event);
}

//------------------------------------------------------------------------------
// Name: mouseReleaseEvent(QMouseEvent *event)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::mouseReleaseEvent(QMouseEvent *event) {

	Q_UNUSED(event);
	
	m_MovingLine1 = false;
	m_MovingLine2 = false;

	setCursor(Qt::ArrowCursor);
	repaint();
}

//------------------------------------------------------------------------------
// Name: updateSelectedAddress(QMouseEvent *event)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::updateSelectedAddress(QMouseEvent *event) {
	bool ok;
	const edb::address_t address	= addressFromPoint(event->pos());
	const int size					= getInstructionSize(address, ok);

	if(ok) {
		m_SelectedInstructionAddress	= address;
		m_SelectedInstructionSize		= size;
	} else {
		m_SelectedInstructionAddress	= 0;
		m_SelectedInstructionSize		= 0;
	}
}

//------------------------------------------------------------------------------
// Name: nearLine(int x, int linex)
// Desc: determins if a point is close to a column divider or not
//------------------------------------------------------------------------------
bool QDisassemblyView::nearLine(int x, int linex) {
	return std::abs(x - linex) < 3;
}

//------------------------------------------------------------------------------
// Name: mousePressEvent(QMouseEvent *event)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::mousePressEvent(QMouseEvent *event) {
	
	if(event->button() == Qt::LeftButton) {
		if(nearLine(event->x(), line1())) {
			m_MovingLine1 = true;
		} else if(nearLine(event->x(), line2())) {
			m_MovingLine2 = true;
		} else {
			updateSelectedAddress(event);
		}
	} else {
		updateSelectedAddress(event);
	}
}

//------------------------------------------------------------------------------
// Name: mouseMoveEvent(QMouseEvent *event)
// Desc: 
//------------------------------------------------------------------------------
void QDisassemblyView::mouseMoveEvent(QMouseEvent *event) {

	if(m_MovingLine1) {
		if(event->x() >= autoLine1() && event->x() + m_FontWidth < line2()) {
			if(m_Line2 == 0) {
				m_Line2 = line2();
			}
			m_Line1 = event->x();
		}
		repaint();
	} else if(m_MovingLine2) {
		if(event->x() > line1() + m_FontWidth && event->x() + 1 < width() - (verticalScrollBar()->width() + 3)) {
			m_Line2 = event->x();
		}
		repaint();
	} else {
		if(nearLine(event->x(), line1()) || nearLine(event->x(), line2())) {
			setCursor(Qt::SplitHCursor);
		} else {
			setCursor(Qt::ArrowCursor);
		}
	}
}

//------------------------------------------------------------------------------
// Name: selectedAddress() const
// Desc: 
//------------------------------------------------------------------------------
edb::address_t QDisassemblyView::selectedAddress() const {
	return m_SelectedInstructionAddress;
}

//------------------------------------------------------------------------------
// Name: selectedSize() const
// Desc: 
//------------------------------------------------------------------------------
int QDisassemblyView::selectedSize() const {
	return m_SelectedInstructionSize;
}
