Blob Blame Raw


#include "toonzqt/treemodel.h"

#include <QStringList>
#include <QTreeView>
#include <QHeaderView>
#include <QMouseEvent>
#include <qvariant.h>
#include <qicon.h>
#include <qtextedit.h>

#include "tfx.h"
#include <assert.h>

//====================================================================================================
// Item
//----------------------------------------------------------------------------------------------------

TreeModel::Item::Item()
	: m_model(0), m_parent(0), m_depth(0), m_row(0), m_opened(false)
{
}

//------------------------------------------------------------------------------------------------------------------

TreeModel::Item::~Item()
{
	qDeleteAll(m_childItems);
	m_childItems.clear();
	m_model = 0;
	m_row = 0;
	m_depth = 0;
	m_parent = 0;
}

//------------------------------------------------------------------------------------------------------------------

void TreeModel::Item::updateChild(Item *child, int row)
{
	assert(m_model);
	child->m_model = m_model;
	child->m_depth = m_depth + 1;
	child->m_parent = this;
	child->m_row = row;
}

//------------------------------------------------------------------------------------------------------------------

void TreeModel::Item::updateChildren()
{
	int i;
	for (i = 0; i < m_childItems.size(); i++)
		updateChild(m_childItems[i], i);
}

//------------------------------------------------------------------------------------------------------------------

TreeModel::Item *TreeModel::Item::appendChild(TreeModel::Item *child)
{
	assert(child);
	assert(!m_childItems.contains(child));
	updateChild(child, m_childItems.size());
	m_childItems.append(child);
	return child;
}

//------------------------------------------------------------------------------------------------------------------
/*
void TreeModel::Item::deleteChild(Item *child)
{
  int index = m_childItems.indexOf(child);
  if(index != -1)
  {
    m_childItems.takeAt(index);
    assert(!m_childItems.contains(child)); 
    // m_childItems is not supposed to contain duplicated entries
    delete child;
    updateChildren();
  }
}
*/

//------------------------------------------------------------------------------------------------------------------
/*
Item* matchItem(Item*item, QList<Item*> &items)
{
  void *itemData = item->getInternalPointer();
  if(!itemData) return 0;
  int i;
  for(i=0;i<items.size();i++)
    if(items.at(i)->getInternalPointer()==itemData)
      return items.at(i);
  return 0;
}
*/
//------------------------------------------------------------------------------------------------------------------

void TreeModel::Item::setChildren(QList<Item *> &newChildren)
{
	assert(m_model);
	QModelIndex itemIndex = createIndex();

	// save old children and clear 'm_childItems'
	QList<Item *> oldChildren(m_childItems);
	m_childItems.clear();
	int i;

	// for each child to add
	for (i = 0; i < newChildren.size(); i++) {
		Item *newChild = newChildren.at(i);
		void *newInternalPointer = newChild->getInternalPointer();
		if (newInternalPointer != 0) {
			// search among old children
			int j;
			for (j = 0; j < oldChildren.size(); j++)
				if (oldChildren.at(j)->getInternalPointer() == newInternalPointer)
					break;
			if (j < oldChildren.size()) {
				Item *oldChild = oldChildren.takeAt(j);
				if (oldChild != newChild) {
					// Found! Delete newChild, remove it from 'newChildren' and
					// update consequently the index
					delete newChild;
					newChildren.takeAt(i);
					i--;
					// use the found child instead of the new one.
					newChild = oldChild;
					oldChild->refresh();
				} else {
					// should never happen; (if it happens this is not a big problem)
					assert(0);
				}
			}
		}
		// add the new child to 'm_childItems'
		m_childItems.push_back(newChild);
	}
	// update children model, row, parent, etc.
	updateChildren();

	// postpone item deletion to avoid the (possible) reuse of the
	// same pointer for the newly created items. (Pointers match is
	// used updating persistent indices. see: TreeModel::endRefresh())
	for (i = 0; i < oldChildren.size(); i++) {
		Item *itemToDelete = oldChildren[i];
		if (!m_model->m_itemsToDelete.contains(itemToDelete))
			m_model->m_itemsToDelete.push_back(itemToDelete);
	}
}

//------------------------------------------------------------------------------------------------------------------

QVariant TreeModel::Item::data(int role) const
{
	if (role == Qt::DecorationRole)
		return QIcon(isOpen()
						 ? ":Resources/folder_open.png"
						 : ":Resources/folder_close.png");
	else
		return QVariant();
}

//------------------------------------------------------------------------------------------------------------------

QModelIndex TreeModel::Item::createIndex()
{
	return m_parent ? m_model->createIndex(m_row, 0, this) : QModelIndex();
}

//====================================================================================================
// TreeModel
//----------------------------------------------------------------------------------------------------

TreeModel::TreeModel(TreeView *parent)
	: QAbstractItemModel(parent), m_rootItem(0), m_view(parent)
{
}

//------------------------------------------------------------------------------------------------------------------

TreeModel::~TreeModel()
{
	delete m_rootItem;
}

//------------------------------------------------------------------------------------------------------------------

void TreeModel::setExpandedItem(const QModelIndex &index, bool expanded)
{
	m_view->setExpanded(index, expanded);
}

//------------------------------------------------------------------------------------------------------------------

void TreeModel::beginRefresh()
{
	emit layoutAboutToBeChanged();
}

//------------------------------------------------------------------------------------------------------------------

void TreeModel::endRefresh()
{
	QList<QModelIndex> oldIndices, newIndices;
	int i;
	QList<Item *>::iterator it;

	for (i = m_itemsToDelete.size() - 1; i >= 0; i--) {
		int row = m_itemsToDelete[i]->getRow();
		Item *parentItem = m_itemsToDelete[i]->getParent();
		QModelIndex parentIndex = parentItem ? parentItem->createIndex() : QModelIndex();

		beginRemoveRows(parentIndex, row, row);
		removeRow(row, parentIndex); //NOTE: This is currently doing NOTHING? (see Qt's manual)
		endRemoveRows();
	}

	qDeleteAll(m_itemsToDelete);
	m_itemsToDelete.clear();

	if (!persistentIndexList().empty()) {
		for (i = 0; i < persistentIndexList().size(); i++) {
			QModelIndex oldIndex = persistentIndexList()[i];
			Item *item = static_cast<Item *>(oldIndex.internalPointer());
			if (item) {
				QModelIndex newIndex = item->createIndex();
				if (oldIndex != newIndex) {
					oldIndices.push_back(oldIndex);
					newIndices.push_back(newIndex);
				}
			}
		}
		changePersistentIndexList(oldIndices, newIndices);
	}

	emit layoutChanged();
}

//------------------------------------------------------------------------------------------------------------------

int TreeModel::columnCount(const QModelIndex &parent) const
{
	return 1;
}

//------------------------------------------------------------------------------------------------------------------

Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
	if (!index.isValid())
		return 0;
	return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

//------------------------------------------------------------------------------------------------------------------

QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
	// column=!0 are not supported
	if (column != 0)
		return QModelIndex();

	Item *parentItem = parent.isValid() ? (Item *)(parent.internalPointer()) : m_rootItem;
	// if m_rootItem has not been defined yet. (It should not happen, but just in case)
	if (!parentItem)
		return QModelIndex();

	int childCount = parentItem->getChildCount();
	if (row < 0 || row >= childCount)
		return QModelIndex();

	Item *item = parentItem->getChild(row);
	if (!item)
		return QModelIndex(); // it should never happen
	return item->createIndex();
}

//------------------------------------------------------------------------------------------------------------------

QModelIndex TreeModel::parent(const QModelIndex &index) const
{
	if (!index.isValid())
		return QModelIndex();

	Item *item = (Item *)index.internalPointer();

	TreeModel::Item *parentItem = item->getParent();

	if (!parentItem || parentItem == m_rootItem)
		return QModelIndex();

	return parentItem->createIndex();
}

//------------------------------------------------------------------------------------------------------------------

int TreeModel::rowCount(const QModelIndex &parent) const
{
	if (parent.column() > 0)
		return 0;

	if (!parent.isValid())
		return m_rootItem ? m_rootItem->getChildCount() : 0;
	else
		return ((Item *)(parent.internalPointer()))->getChildCount();
}

//------------------------------------------------------------------------------------------------------------------

void TreeModel::onExpanded(const QModelIndex &index)
{
	if (!index.isValid())
		return;

	Item *item = (Item *)(index.internalPointer());
	item->setIsOpen(true);
}

//---------------------------------------------------------------------------------------------------------------

void TreeModel::onCollapsed(const QModelIndex &index)
{
	if (!index.isValid())
		return;

	Item *item = (Item *)(index.internalPointer());
	item->setIsOpen(false);
}

//---------------------------------------------------------------------------------------------------------------

QVariant TreeModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();
	Item *item = static_cast<Item *>(index.internalPointer());
	return item->data(role);
}

//---------------------------------------------------------------------------------------------------------------

void TreeModel::setRootItem(Item *rootItem)
{
	if (rootItem == m_rootItem)
		return;
	delete m_rootItem;
	m_rootItem = rootItem;
	if (m_rootItem)
		m_rootItem->setModel(this);
}

//---------------------------------------------------------------------------------------------------------------

void TreeModel::setRowHidden(int row, const QModelIndex &parent, bool hide)
{
	m_view->setRowHidden(row, parent, hide);
}

//====================================================================================================
// TreeView
//----------------------------------------------------------------------------------------------------

TreeView::TreeView(QWidget *parent)
	: QTreeView(parent), m_dragging(false)
{
	header()->hide();
	setUniformRowHeights(true);
	setIconSize(QSize(32, 32));
}

//-----------------------------------------------------------------------------

//!Resizes viewport to contents
void TreeView::resizeToConts(void)
{
	resizeColumnToContents(0);
}

//-----------------------------------------------------------------------------

void TreeView::resizeEvent(QResizeEvent *event)
{
	resizeColumnToContents(0);
	QTreeView::resizeEvent(event);
}

//----------------------------------------------------------------------------------------------------------------

void TreeView::setModel(TreeModel *model)
{
	QTreeView::setModel(model);
	disconnect();

	connect(this, SIGNAL(expanded(const QModelIndex &)), model, SLOT(onExpanded(const QModelIndex &)));
	connect(this, SIGNAL(collapsed(const QModelIndex &)), model, SLOT(onCollapsed(const QModelIndex &)));
	// setItemDelegate(new Delegate(this));

	//Connect all possible changes that can alter the
	//bottom horizontal scrollbar to resize contents...
	connect(
		this, SIGNAL(expanded(const QModelIndex &)),
		this, SLOT(resizeToConts()));

	connect(
		this, SIGNAL(collapsed(const QModelIndex &)),
		this, SLOT(resizeToConts()));

	connect(
		this->model(), SIGNAL(layoutChanged()),
		this, SLOT(resizeToConts()));
}

//----------------------------------------------------------------------------------------------------------------

void TreeView::mouseDoubleClickEvent(QMouseEvent *)
{
	//ignore double click!
}

void TreeView::mousePressEvent(QMouseEvent *e)
{
	if (e->button() != Qt::RightButton)
		QTreeView::mousePressEvent(e);
	QModelIndex index = indexAt(e->pos());
	if (index.isValid()) {
		TreeModel::Item *item = static_cast<TreeModel::Item *>(index.internalPointer());
		QRect itemRect = visualRect(index);
		QPoint itemPos = e->pos() - itemRect.topLeft();
		if (e->button() == Qt::RightButton) {
			if (selectionMode() != QAbstractItemView::ExtendedSelection)
				setCurrentIndex(item->createIndex());
			onClick(item, itemPos, e);
			openContextMenu(item, e->globalPos());
		} else if (e->button() == Qt::LeftButton) {
			m_dragging = true;
			setMouseTracking(true);
			onClick(item, itemPos, e);
		}
		// for drag & drop
		else if (e->button() == Qt::MidButton) {
			m_dragging = true;
			setMouseTracking(true);
			onMidClick(item, itemPos, e);
		}
	}
}

//----------------------------------------------------------------------------------------------------------------

void TreeView::mouseMoveEvent(QMouseEvent *e)
{
	QTreeView::mouseMoveEvent(e);
	if (m_dragging) {
		QModelIndex index = indexAt(e->pos());
		if (index.isValid()) {
			TreeModel::Item *item = static_cast<TreeModel::Item *>(index.internalPointer());
			QRect itemRect = visualRect(index);
			QPoint itemPos = e->pos() - itemRect.topLeft();
			onDrag(item, itemPos, e);
		}
	}
}

//----------------------------------------------------------------------------------------------------------------

void TreeView::mouseReleaseEvent(QMouseEvent *e)
{
	QTreeView::mouseReleaseEvent(e);
	if (m_dragging) {
		m_dragging = false;
		setMouseTracking(false);
		onRelease();
	}
}

//----------------------------------------------------------------------------------------------------------------

/*
bool TreeView::Delegate::editorEvent(QEvent *e, QAbstractItemModel *abstractModel, const QStyleOptionViewItem &option, const QModelIndex &index)  
{
  if(e->type() != QEvent::MouseButtonPress) return false;
  TreeModel *model = dynamic_cast<TreeModel *>(abstractModel);
  if(!model || !index.isValid()) return false;

  TreeModel::Item *item = static_cast<TreeModel::Item *>(index.internalPointer());
  QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(e);
  QPoint pos = mouseEvent->pos();

  m_treeView->onClick(item, pos, option); 
  return true;
}
*/