/*	Author:		Magdin Stoica
	Supervisor: Dr. Ryan Leduc
	
	Project created in conformity with the requirements for the Degree of Master of Engineering in Software Engineering, 
	Computing and Software Department, 
	McMaster University
	2003 - 2007
*/

#include <qevent.h>
#include <iostream>
#include <QMenu>
#include <QContextMenuEvent>
#include <QMainWindow>
#include <QMenuBar>
#include <QHeaderView>

#include "DespotTreeWidget.h"
#include "CommonDefinitions.h"

namespace DESpot
{

DespotTreeWidget::DespotTreeWidget(QWidget * parent) : 
		QTreeWidget(parent), m_pBoxWidget(null), m_pCrtItem(null), 
                m_pContextMenu(null), m_bCircularCursor(false), m_sharedFocus(false)
{
	//this is needed because if the sorting indicator is not shown the sorting doesn't work
	m_sortIndicators.resize(header()->count(), true); //true = ascending
	connect(header(), SIGNAL(sectionClicked(int)), this, SLOT(onSortByColumn(int)));
	connect(header(), SIGNAL(sectionCountChanged(int, int)), this, SLOT(updateSortIndicators(int, int)));
}

//_________________________________________________________________________________________________

DespotTreeWidget::~DespotTreeWidget(void)
{
	m_pBoxWidget = null;
}

//_________________________________________________________________________________________________

void DespotTreeWidget::setBoxWidget(QWidget* pBoxWidget)
{
	m_pBoxWidget = pBoxWidget;
	m_pBoxWidget->installEventFilter(this);
}

//_________________________________________________________________________________________________

QWidget* DespotTreeWidget::getBoxWidget()
{
	return m_pBoxWidget;
}

//_________________________________________________________________________________________________

void DespotTreeWidget::setCircularCursor(bool bCircularCursor)
{
	m_bCircularCursor = bCircularCursor;
}

//_________________________________________________________________________________________________

void DespotTreeWidget::setShareFocus(bool shareFocus /*= true*/)
{
	m_sharedFocus = shareFocus;
}

//_________________________________________________________________________________________________

void DespotTreeWidget::setContextMenu(QMenu& contextMenu)
{
	m_pContextMenu = &contextMenu;
}

//_________________________________________________________________________________________________

QTreeWidgetItem* DespotTreeWidget::getNextSibling(QTreeWidgetItem* item)
{
	QModelIndex index = indexFromItem(item);
	
	//try the next index
	QModelIndex nextIndex = index.sibling(index.row() + 1, 0);
	if (nextIndex.isValid())
	{
		return itemFromIndex(nextIndex);
	}

	//try the previous index if there was no next one
	QModelIndex prevIndex = index.sibling(index.row() - 1, 0);
	if (prevIndex.isValid())
	{
		return itemFromIndex(prevIndex);
	}

	//there is no sibling left
	return null;
}

//_________________________________________________________________________________________________

QModelIndex DespotTreeWidget::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
	QModelIndex defaultMovedIndex = QTreeWidget::moveCursor(cursorAction, modifiers);

	if (m_bCircularCursor == false)
	{
		return defaultMovedIndex;
	}

	QModelIndex crtIndex = currentIndex();
	
	//if there is no current item return the default
	if (crtIndex.isValid() == false)
		return defaultMovedIndex;

	switch(cursorAction)
	{
		case MoveRight:
		case MoveNext:
			if (crtIndex.column() == columnCount() - 1)
			{
				if (crtIndex.row() == topLevelItemCount() - 1)
				{
					//we are the end of the last row so move back to first row and first column
					return crtIndex.sibling(0, 0);
				}
				else
				{
					//we are already to the end so move back to first column of the next row
					return crtIndex.sibling(crtIndex.row() + 1, 0);
				}
			}
			else
			{
				//move to the next column in the same row
				return crtIndex.sibling(crtIndex.row(), crtIndex.column() + 1);
			}
			break;

		case MoveLeft:
		case MovePrevious:
			if (crtIndex.column() == 0)
			{
				if (crtIndex.row()  == 0)
				{
					//we are at first row and first column so move to last row, last column
					return crtIndex.sibling(topLevelItemCount() - 1, columnCount() - 1);
				}
				else
				{
					//we are already at the beginning so move to the last column of the previous row
					return crtIndex.sibling(crtIndex.row() - 1, columnCount() - 1);
				}
			}
			else
			{
				//move to the previous column in the row
				return crtIndex.sibling(crtIndex.row(), crtIndex.column() - 1);
			}
			break;

		case MoveDown:
			if (crtIndex.row() == topLevelItemCount() - 1)
			{
				//we are at the last row so move to the first row and same column as current  index
				return defaultMovedIndex.sibling(0, crtIndex.column());
			}
			else 
			{
				return defaultMovedIndex.sibling(defaultMovedIndex.row(), crtIndex.column());
			}
			break;

		case MoveUp:
			if (crtIndex.row() == 0)
			{
				return defaultMovedIndex.sibling(topLevelItemCount() - 1, crtIndex.column());
			}
			else
			{
				return defaultMovedIndex.sibling(defaultMovedIndex.row(), crtIndex.column());
			}

                 default:
                        break;
	}

	//if we got here it means we didn't handle it differently than the base class implementation so return the base result
	return defaultMovedIndex;
}

//_________________________________________________________________________________________________

void DespotTreeWidget::keyPressEvent(QKeyEvent * keyEvent)
{
	QTreeWidget::keyPressEvent(keyEvent);
	
	switch(keyEvent->key())
	{
		case Qt::Key_Delete:
			emit onCurrentItemDeleted();
			break;
	}
}

//_________________________________________________________________________________________________

void DespotTreeWidget::resizeEvent(QResizeEvent* event)
{
	emit onWidthChanged(event->size().width());
}

//_________________________________________________________________________________________________
//When the focus is received restore the current item in the tree widget to what it was
//when the focus was lost
void DespotTreeWidget::focusInEvent(QFocusEvent* event)
{
	QTreeWidget::focusInEvent(event);

	if (m_sharedFocus)
	{
		if (currentItem() && (currentItem()->isSelected() == false)/*&& (m_ctxMenuDisplayed == false)*/)
		{
			currentItem()->setSelected(true);
			emit onActiveItemChanged(currentItem(), null);
		}
	}
}

//_________________________________________________________________________________________________
//Whyen focus is lost remembers which item was the current item and resets the current item to null
//such that when the control has no focus it doesn't have a current item either. When the focus is
//restored then the current item will be restored. This is needed because certain UI deletes or changes
//the current item and this enforces the need for focus when those operations are performed
void DespotTreeWidget::focusOutEvent(QFocusEvent* event)
{
	QTreeWidget::focusOutEvent(event);

	if (m_sharedFocus)
	{
		bool menuHasFocus = ((QMainWindow*)topLevelWidget())->menuWidget()->hasFocus() || (event->reason() == Qt::PopupFocusReason) || (event->reason() == Qt::MenuBarFocusReason);

		//while the context menu is displayed the widget looses focus and the active item is NULL.
		//However this is not desired as actions in the context menu work WITH the active item. Thus
		//this temporary loss of focus must be ignored
		if (currentItem() && (menuHasFocus == false) && (state() != QAbstractItemView::EditingState))
		{
			currentItem()->setSelected(false);
			emit onActiveItemChanged(null, currentItem());
		}
	}
}   

//_________________________________________________________________________________________________

void DespotTreeWidget::contextMenuEvent(QContextMenuEvent* event)
{
	if (m_pContextMenu)
	{
		m_pContextMenu->exec(event->globalPos());
	}
}

//_________________________________________________________________________________________________

void DespotTreeWidget::updateSortIndicators(int /*oldCount*/, int newCount)
{
	if (m_sortIndicators.size() < (unsigned int)newCount)
	{
		m_sortIndicators.resize(newCount, true);
		sortItems(sortColumn(), Qt::AscendingOrder);
	}
}

//_________________________________________________________________________________________________

void DespotTreeWidget::onSortByColumn(int column)
{
	if (isSortingEnabled() && (header()->isSortIndicatorShown() == false))
	{
		//this is needed because if the sorting indicator is not shown the sorting doesn't work		
		sortItems(column, m_sortIndicators[column] ? Qt::AscendingOrder : Qt::DescendingOrder);
		m_sortIndicators[column] = ~m_sortIndicators[column];
	}
}

//_________________________________________________________________________________________________

bool DespotTreeWidget::eventFilter(QObject* target, QEvent* event)
{
	if (target == m_pBoxWidget && event->type() == QEvent::Resize)
	{
		//The box widget was resized so resize the tree widget itself as well to match
		QResizeEvent* resizeEvent = dynamic_cast<QResizeEvent*>(event);
		resize(resizeEvent->size() - QSize(18,28));
	}

	return QTreeWidget::eventFilter(target, event);
}

} //end of namespace DESpot

