//##########################################################################
//#                                                                        #
//#                              CLOUDCOMPARE                              #
//#                                                                        #
//#  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; version 2 or later of the License.      #
//#                                                                        #
//#  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.                          #
//#                                                                        #
//#          COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI)             #
//#                                                                        #
//##########################################################################

#include "ccPropertiesTreeDelegate.h"

//Local
#include "ccColorScaleEditorDlg.h"
#include "ccColorScaleSelector.h"
#include "mainwindow.h"
#include "matrixDisplayDlg.h"
#include "sfEditDlg.h"

//qCC_glWindow
#include <ccGLWindowInterface.h>
#include <ccGuiParameters.h>

//qCC_db
#include <cc2DLabel.h>
#include <cc2DViewportLabel.h>
#include <cc2DViewportObject.h>
#include <ccAdvancedTypes.h>
#include <ccCameraSensor.h>
#include <ccColorScalesManager.h>
#include <ccCone.h>
#include <ccFacet.h>
#include <ccGBLSensor.h>
#include <ccGenericPrimitive.h>
#include <ccHObject.h>
#include <ccHObjectCaster.h>
#include <ccImage.h>
#include <ccIndexedTransformationBuffer.h>
#include <ccKdTree.h>
#include <ccMaterialSet.h>
#include <ccMesh.h>
#include <ccOctree.h>
#include <ccPlane.h>
#include <ccPointCloud.h>
#include <ccPolyline.h>
#include <ccScalarField.h>
#include <ccSensor.h>
#include <ccSphere.h>
#include <ccSubMesh.h>
#include <ccCoordinateSystem.h>

//Qt
#include <QAbstractItemView>
#include <QCheckBox>
#include <QComboBox>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QLabel>
#include <QLocale>
#include <QPushButton>
#include <QScrollBar>
#include <QSlider>
#include <QSpinBox>
#include <QStandardItemModel>
#include <QToolButton>
#include <QEvent>

//System
#include <cassert>

class FilterMouseWheelNoFocusEvent : public QObject
{
public:
	explicit FilterMouseWheelNoFocusEvent(QObject *parent)
	{
	}
protected:
	bool eventFilter(QObject* o, QEvent* e) override
	{
		const QWidget* widget = dynamic_cast<QWidget*>(o);
		if (e->type() == QEvent::Wheel && widget && !widget->hasFocus())
		{
			e->ignore();
			return true;
		}
		return QObject::eventFilter(o, e);
	}
};


// Default 'None' string
const char* ccPropertiesTreeDelegate::s_noneString = QT_TR_NOOP( "None" );

// Default color sources string
const char* ccPropertiesTreeDelegate::s_rgbColor = "RGB";
const char* ccPropertiesTreeDelegate::s_sfColor = QT_TR_NOOP( "Scalar field" );

// Other strings
const char* ccPropertiesTreeDelegate::s_defaultPointSizeString = QT_TR_NOOP( "Default" );
const char* ccPropertiesTreeDelegate::s_defaultPolyWidthSizeString = QT_TR_NOOP( "Default Width" );

// Default separator colors
constexpr const char* SEPARATOR_STYLESHEET = "QLabel { background-color : darkGray; color : white; }";

//Shortcut to create a delegate item
static QStandardItem* ITEM(const QString& name,
	Qt::ItemFlag additionalFlags = Qt::NoItemFlags,
	ccPropertiesTreeDelegate::CC_PROPERTY_ROLE role = ccPropertiesTreeDelegate::OBJECT_NO_PROPERTY)
{
	QStandardItem* item = new QStandardItem(name);
	//flags
	item->setFlags(Qt::ItemIsEnabled | additionalFlags);
	//role (if any)
	if (role != ccPropertiesTreeDelegate::OBJECT_NO_PROPERTY)
	{
		item->setData(role);
	}

	return item;
}

//Shortcut to create a checkable delegate item
static QStandardItem* CHECKABLE_ITEM(bool checkState, ccPropertiesTreeDelegate::CC_PROPERTY_ROLE role)
{
	QStandardItem* item = ITEM("", Qt::ItemIsUserCheckable, role);
	//check state
	item->setCheckState(checkState ? Qt::Checked : Qt::Unchecked);

	return item;
}

//Shortcut to create a persistent editor item
static QStandardItem* PERSISTENT_EDITOR(ccPropertiesTreeDelegate::CC_PROPERTY_ROLE role)
{
	return ITEM(QString(), Qt::ItemIsEditable, role);
}

ccPropertiesTreeDelegate::ccPropertiesTreeDelegate(QStandardItemModel* model,
	QAbstractItemView* view,
	QObject *parent)
	: QStyledItemDelegate(parent)
	, m_currentObject(nullptr)
	, m_model(model)
	, m_view(view)
	, m_lastFocusItemRole(OBJECT_NO_PROPERTY)
{
	assert(m_model && m_view);
}

ccPropertiesTreeDelegate::~ccPropertiesTreeDelegate()
{
	unbind();
}

QSize ccPropertiesTreeDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
	assert(m_model);

	QStandardItem* item = m_model->itemFromIndex(index);

	if (item && item->data().isValid())
	{
		switch (item->data().toInt())
		{
		case OBJECT_CURRENT_DISPLAY:
		case OBJECT_CURRENT_SCALAR_FIELD:
		case OBJECT_OCTREE_TYPE:
		case OBJECT_COLOR_RAMP_STEPS:
		case OBJECT_CLOUD_POINT_SIZE:
			return QSize(50, 24);
		case OBJECT_COLOR_SOURCE:
		case OBJECT_POLYLINE_WIDTH:
		case OBJECT_CURRENT_COLOR_RAMP:
			return QSize(70, 24);
		case OBJECT_CLOUD_SF_EDITOR:
			return QSize(250, 200);
		case OBJECT_SENSOR_MATRIX_EDITOR:
		case OBJECT_HISTORY_MATRIX_EDITOR:
		case OBJECT_GLTRANS_MATRIX_EDITOR:
			return QSize(250, 140);
		}
	}

	return QStyledItemDelegate::sizeHint(option, index);
}

void ccPropertiesTreeDelegate::unbind()
{
	if (m_model)
	{
		m_model->disconnect(this);
	}
}

ccHObject* ccPropertiesTreeDelegate::getCurrentObject()
{
	return m_currentObject;
}

void ccPropertiesTreeDelegate::fillModel(ccHObject* hObject)
{
	if (!hObject)
	{
		return;
	}

	unbind();

	m_currentObject = hObject;

	//save current scroll position
	int scrollPos = (m_view && m_view->verticalScrollBar() ? m_view->verticalScrollBar()->value() : 0);

	if (m_model)
	{
		m_model->removeRows(0, m_model->rowCount());
		m_model->setColumnCount(2);
		m_model->setHeaderData(0, Qt::Horizontal, tr( "Property" ));
		m_model->setHeaderData(1, Qt::Horizontal, tr( "State/Value" ));
	}

	if (m_currentObject->isHierarchy())
	{
		if (!m_currentObject->isA(CC_TYPES::VIEWPORT_2D_LABEL)) //don't need to display this kind of info for viewport labels!
		{
			fillWithHObject(m_currentObject);
		}
	}

	if (m_currentObject->isA(CC_TYPES::COORDINATESYSTEM))
	{
		fillWithCoordinateSystem(ccHObjectCaster::ToCoordinateSystem(m_currentObject));
	}
	else if (m_currentObject->isKindOf(CC_TYPES::POINT_CLOUD))
	{
		fillWithPointCloud(ccHObjectCaster::ToGenericPointCloud(m_currentObject));
	}
	else if (m_currentObject->isKindOf(CC_TYPES::MESH))
	{
		fillWithMesh(ccHObjectCaster::ToGenericMesh(m_currentObject));

		if (m_currentObject->isKindOf(CC_TYPES::PRIMITIVE))
		{
			fillWithPrimitive(ccHObjectCaster::ToPrimitive(m_currentObject));
		}
	}
	else if (m_currentObject->isA(CC_TYPES::FACET))
	{
		fillWithFacet(ccHObjectCaster::ToFacet(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::POLY_LINE))
	{
		fillWithPolyline(ccHObjectCaster::ToPolyline(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::POINT_OCTREE))
	{
		fillWithPointOctree(ccHObjectCaster::ToOctree(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::POINT_KDTREE))
	{
		fillWithPointKdTree(ccHObjectCaster::ToKdTree(m_currentObject));
	}
	else if (m_currentObject->isKindOf(CC_TYPES::IMAGE))
	{
		fillWithImage(ccHObjectCaster::ToImage(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::LABEL_2D))
	{
		fillWithLabel(ccHObjectCaster::To2DLabel(m_currentObject));
	}
	else if (m_currentObject->isKindOf(CC_TYPES::VIEWPORT_2D_OBJECT))
	{
		fillWithViewportObject(ccHObjectCaster::To2DViewportObject(m_currentObject));
	}
	else if (m_currentObject->isKindOf(CC_TYPES::GBL_SENSOR))
	{
		fillWithGBLSensor(ccHObjectCaster::ToGBLSensor(m_currentObject));
	}
	else if (m_currentObject->isKindOf(CC_TYPES::CAMERA_SENSOR))
	{
		fillWithCameraSensor(ccHObjectCaster::ToCameraSensor(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::MATERIAL_SET))
	{
		fillWithMaterialSet(static_cast<ccMaterialSet*>(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::NORMAL_INDEXES_ARRAY))
	{
		fillWithCCArray(static_cast<NormsIndexesTableType*>(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::TEX_COORDS_ARRAY))
	{
		fillWithCCArray(static_cast<TextureCoordsContainer*>(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::NORMALS_ARRAY))
	{
		fillWithCCArray(static_cast<NormsTableType*>(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::RGB_COLOR_ARRAY))
	{
		fillWithCCArray(static_cast<ColorsTableType*>(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::RGBA_COLOR_ARRAY))
	{
		fillWithCCArray(static_cast<RGBAColorsTableType*>(m_currentObject));
	}
	else if (m_currentObject->isA(CC_TYPES::TRANS_BUFFER))
	{
		fillWithTransBuffer(static_cast<ccIndexedTransformationBuffer*>(m_currentObject));
	}

	//transformation history
	if (m_currentObject->isKindOf(CC_TYPES::POINT_CLOUD)
		|| m_currentObject->isKindOf(CC_TYPES::MESH)
		|| m_currentObject->isKindOf(CC_TYPES::FACET)
		|| m_currentObject->isKindOf(CC_TYPES::POLY_LINE)
		|| m_currentObject->isKindOf(CC_TYPES::SENSOR))
	{
		addSeparator( tr( "Transformation history" ) );
		appendWideRow(PERSISTENT_EDITOR(OBJECT_HISTORY_MATRIX_EDITOR));

		if (m_currentObject->isGLTransEnabled())
		{
			addSeparator( tr( "Display transformation" ) );
			appendWideRow(PERSISTENT_EDITOR(OBJECT_GLTRANS_MATRIX_EDITOR));
		}
	}

	//meta-data
	fillWithMetaData(m_currentObject);

	//go back to original position
	if (scrollPos > 0)
	{
		m_view->verticalScrollBar()->setSliderPosition(scrollPos);
	}

	if (m_model)
	{
		connect(m_model, &QStandardItemModel::itemChanged, this, &ccPropertiesTreeDelegate::updateItem);
	}
}

void ccPropertiesTreeDelegate::appendRow(QStandardItem* leftItem, QStandardItem* rightItem, bool openPersistentEditor/*=false*/)
{
	assert(leftItem && rightItem);
	assert(m_model);

	if (m_model)
	{
		//append row
		QList<QStandardItem*> rowItems{ leftItem, rightItem };

		m_model->appendRow(rowItems);

		//the persistent editor (if any) is always the right one!
		if (openPersistentEditor && (m_view != nullptr))
		{
			m_view->openPersistentEditor(m_model->index(m_model->rowCount() - 1, 1));
		}
	}
}

void ccPropertiesTreeDelegate::appendWideRow(QStandardItem* item, bool openPersistentEditor/*=true*/)
{
	assert(item);
	assert(m_model);

	if (m_model && item)
	{
		m_model->appendRow(item);
		
		if (openPersistentEditor && (m_view != nullptr))
		{
			m_view->openPersistentEditor(m_model->index(m_model->rowCount() - 1, 0));
		}
	}
}


void ccPropertiesTreeDelegate::addSeparator(const QString& title)
{
	if (m_model)
	{
		//DGM: we can't use the 'text' of the item as it will be displayed under the associated editor (label)!
		//So we simply use the 'accessible description' field
		QStandardItem* leftItem = new QStandardItem(/*title*/);
		leftItem->setData(TREE_VIEW_HEADER);
		leftItem->setAccessibleDescription(title);
		m_model->appendRow(leftItem);
		
		if ( m_view != nullptr )
		{
			m_view->openPersistentEditor(m_model->index(m_model->rowCount() - 1, 0));
		}
	}
}

void ccPropertiesTreeDelegate::fillWithMetaData(const ccObject* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}
	const QVariantMap& metaData = _obj->metaData();
	if (metaData.empty())
	{
		return;
	}

	addSeparator( tr( "Meta data" ) );

	for (QVariantMap::ConstIterator it = metaData.constBegin(); it != metaData.constEnd(); ++it)
	{
		QVariant var = it.value();
		QString value;
		
		if (var.canConvert(QVariant::String))
		{
			var.convert(QVariant::String);
			value = var.toString();
		}
		else
		{
			value = QString(QVariant::typeToName(var.type()));
		}

		appendRow(ITEM(it.key()), ITEM(value));
	}
}

void ccPropertiesTreeDelegate::fillWithHObject(ccHObject* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}
	addSeparator( tr( "CC Object" ) );

	//name
	appendRow(ITEM( tr( "Name" ) ), ITEM(_obj->getName(), Qt::ItemIsEditable, OBJECT_NAME));

	//visibility
	if (!_obj->isVisibilityLocked())
	{
		appendRow(ITEM(tr("Visible")), CHECKABLE_ITEM(_obj->isVisible(), OBJECT_VISIBILITY));
	}

	//normals
	if (_obj->hasNormals())
	{
		appendRow(ITEM(tr("Normals")), CHECKABLE_ITEM(_obj->normalsShown(), OBJECT_NORMALS_SHOWN));
	}

	//color source
	if (_obj->hasColors() || _obj->hasScalarFields())
	{
		appendRow(ITEM(tr("Colors")), PERSISTENT_EDITOR(OBJECT_COLOR_SOURCE), true);
	}

	//Bounding-box
	{
		ccBBox box;
		bool fitBBox = false;
		if (_obj->getSelectionBehavior() == ccHObject::SELECTION_FIT_BBOX)
		{
			ccGLMatrix trans;
			box = _obj->getOwnFitBB(trans);
			box += trans.getTranslationAsVec3D();
			fitBBox = true;
		}
		else
		{
			box = _obj->getBB_recursive();
		}

		if (box.isValid())
		{
			//name in 3D (we can't display a 3D name if the bounding-box is not valid!
			appendRow(ITEM(tr("Show name (in 3D)")), CHECKABLE_ITEM(_obj->nameShownIn3D(), OBJECT_NAME_IN_3D));

			//Box dimensions
			CCVector3 bboxDiag = box.getDiagVec();
			appendRow(ITEM(fitBBox ? tr( "Local box dimensions" ) : tr( "Box dimensions" )),
				ITEM(QStringLiteral("X: %1 (%2 : %3)\nY: %4 (%5 : %6)\nZ: %7 (%8 : %9)")
					.arg(bboxDiag.x).arg(box.minCorner().x).arg(box.maxCorner().x)
					.arg(bboxDiag.y).arg(box.minCorner().y).arg(box.maxCorner().y)
					.arg(bboxDiag.z).arg(box.minCorner().z).arg(box.maxCorner().z)
				));


			//Box center
			CCVector3 bboxCenter = box.getCenter();

			ccShiftedObject* shiftedObj = ccHObjectCaster::ToShifted(_obj);

			//local bounding box center
			appendRow(ITEM(shiftedObj? tr("Shifted box center") : tr("Box center")),
				ITEM(QStringLiteral("X: %0\nY: %1\nZ: %2").arg(bboxCenter.x).arg(bboxCenter.y).arg(bboxCenter.z)));

			if (shiftedObj)
			{
				CCVector3d globalBBoxCenter = shiftedObj->toGlobal3d(bboxCenter);

				//global bounding box center
				appendRow(ITEM(tr("Global box center")),
					ITEM(QStringLiteral("X: %0\nY: %1\nZ: %2").arg(globalBBoxCenter.x, 0, 'f').arg(globalBBoxCenter.y, 0, 'f').arg(globalBBoxCenter.z, 0, 'f')));
			}
		}
	}

	//infos (unique ID, children) //DGM: on the same line so as to gain space
	appendRow(ITEM( tr( "Info" ) ), ITEM( tr("Object ID: %1 - Children: %2").arg(_obj->getUniqueID()).arg(_obj->getChildrenNumber()) ));

	//display window
	if (!_obj->isLocked())
	{
		appendRow(ITEM(tr("Current Display")), PERSISTENT_EDITOR(OBJECT_CURRENT_DISPLAY), true);
	}
}

void ccPropertiesTreeDelegate::fillWithShifted(const ccShiftedObject* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	//global shift & scale
	const CCVector3d& shift = _obj->getGlobalShift();
	appendRow(ITEM( tr( "Global shift" )), ITEM(QStringLiteral("(%1;%2;%3)").arg(shift.x, 0, 'f', 2).arg(shift.y, 0, 'f', 2).arg(shift.z, 0, 'f', 2)));

	double scale = _obj->getGlobalScale();
	appendRow(ITEM( tr( "Global scale" ) ), ITEM(QStringLiteral("%1").arg(scale, 0, 'f', 6)));
}

void ccPropertiesTreeDelegate::fillWithCoordinateSystem(const ccCoordinateSystem* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	CCVector3 origin = _obj->getOrigin();
	addSeparator(tr("Coordinate System"));
	appendRow(ITEM(tr("Origin")),
		ITEM(QStringLiteral("X: %0\nY: %1\nZ: %2").arg(origin.x).arg(origin.y).arg(origin.z)));
	appendRow(ITEM(tr("Planes Visible")), CHECKABLE_ITEM(_obj->axisPlanesAreShown(), OBJECT_COORDINATE_SYSTEM_DISP_PLANES));
	appendRow(ITEM(tr("Planes Stippled")), CHECKABLE_ITEM(static_cast<const ccMesh*>(_obj)->stipplingEnabled(), OBJECT_MESH_STIPPLING));
	appendRow(ITEM(tr("Axis Lines Visible")), CHECKABLE_ITEM(_obj->axisLinesAreShown(), OBJECT_COORDINATE_SYSTEM_DISP_AXES));
	appendRow(ITEM(tr("Axis width")), PERSISTENT_EDITOR(OBJECT_COORDINATE_SYSTEM_AXES_WIDTH), true);
	appendRow(ITEM(tr("Display scale")), PERSISTENT_EDITOR(OBJECT_COORDINATE_SYSTEM_DISP_SCALE), true);

}

void ccPropertiesTreeDelegate::fillWithPointCloud(ccGenericPointCloud* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}
	addSeparator( tr( "Cloud" ) );

	//number of points
	appendRow(ITEM( tr( "Points" ) ), ITEM(QLocale(QLocale::English).toString(_obj->size())));

	//global shift & scale
	fillWithShifted(_obj);

	//custom point size
	appendRow(ITEM( tr( "Point size" ) ), PERSISTENT_EDITOR(OBJECT_CLOUD_POINT_SIZE), true);

	//scalar field
	fillSFWithPointCloud(_obj);

	//scan grid structure(s), waveform, etc.
	if (_obj->isA(CC_TYPES::POINT_CLOUD))
	{
		const ccPointCloud* cloud = static_cast<const ccPointCloud*>(_obj);

		//scan grid(s)
		size_t gridCount = cloud->gridCount();
		if (gridCount != 0)
		{
			if (gridCount != 1)
			{
				addSeparator(tr("Scan grids"));
			}
			else
			{
				addSeparator(tr("Scan grid"));
			}

			for (size_t i = 0; i < gridCount; ++i)
			{
				//grid size + valid point count
				ccPointCloud::Grid::Shared grid = cloud->grid(i);
				appendRow(ITEM(tr("Scan #%1").arg(i + 1)), ITEM(tr("%1 x %2 (%3 points)").arg(grid->w).arg(grid->h).arg(QLocale(QLocale::English).toString(grid->validCount))));
			}
		}

		//waveform
		if (cloud->hasFWF())
		{
			addSeparator( tr( "Waveform" ));
			appendRow(ITEM( tr( "Waves" ) ), ITEM(QString::number(cloud->waveforms().size()))); //DGM: in fact some of them might be null/invalid!
			appendRow(ITEM( tr("Descriptors" ) ), ITEM(QString::number(cloud->fwfDescriptors().size())));

			double dataSize_mb = (cloud->fwfData() ? cloud->fwfData()->size() : 0) / static_cast<double>(1 << 20);
			appendRow(ITEM( tr( "Data size" ) ), ITEM(QStringLiteral("%1 Mb").arg(dataSize_mb, 0, 'f', 2)));
		}

		//normals
		if (cloud->hasNormals())
		{
			fillWithDrawNormals(_obj);
		}
	}
}

void ccPropertiesTreeDelegate::fillWithDrawNormals(ccGenericPointCloud* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}
	assert(_obj->isA(CC_TYPES::POINT_CLOUD));
	if (!_obj->isA(CC_TYPES::POINT_CLOUD))
	{
		return;
	}

	addSeparator( tr( "Draw normals as lines" ) );

	//visibility
	const ccPointCloud* cloud = static_cast<const ccPointCloud*>(_obj);
	appendRow(ITEM(tr("Draw")), CHECKABLE_ITEM(cloud->normalsAreDrawn(), OBJECT_CLOUD_DRAW_NORMALS));

	//normals length
	appendRow(ITEM(tr("Length")), PERSISTENT_EDITOR(OBJECT_CLOUD_NORMAL_LENGTH), true);

	//normals color
	appendRow(ITEM(tr("Color")), PERSISTENT_EDITOR(OBJECT_CLOUD_NORMAL_COLOR), true);
}

void ccPropertiesTreeDelegate::fillSFWithPointCloud(ccGenericPointCloud* _obj)
{
	assert(m_model);
	if (!_obj || !m_model)
	{
		return;
	}
	//for "real" point clouds only
	ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(_obj);
	if (!cloud)
	{
		return;
	}

	//Scalar fields
	unsigned sfCount = cloud->getNumberOfScalarFields();
	if (sfCount != 0)
	{
		addSeparator(sfCount > 1 ?  tr( "Scalar Fields" ) : tr( "Scalar Field" ));

		//fields number
		appendRow(ITEM( tr( "Count" ) ), ITEM(QString::number(sfCount)));

		//fields list combo
		appendRow(ITEM( tr( "Active" ) ), PERSISTENT_EDITOR(OBJECT_CURRENT_SCALAR_FIELD), true);

		//no need to go any further if no SF is currently active
		CCCoreLib::ScalarField* sf = cloud->getCurrentDisplayedScalarField();
		if (sf)
		{
			//field shift
			ccScalarField* ccSF = dynamic_cast<ccScalarField*>(sf);
			if (ccSF)
			{
				appendRow(ITEM( tr( "Shift" ) ), ITEM(QString::number(ccSF->getGlobalShift(), 'f', 2)));
			}

			addSeparator("Color Scale");

			//color scale selection combo box
			appendRow(ITEM( tr( "Current" ) ), PERSISTENT_EDITOR(OBJECT_CURRENT_COLOR_RAMP), true);

			//color scale steps
			appendRow(ITEM( tr( "Steps" ) ), PERSISTENT_EDITOR(OBJECT_COLOR_RAMP_STEPS), true);

			//scale visible?
			appendRow(ITEM( tr( "Visible" ) ), CHECKABLE_ITEM(cloud->sfColorScaleShown(), OBJECT_SF_SHOW_SCALE));

			addSeparator( tr( "SF display params" ) );

			//SF edit dialog (warning: 2 columns)
			appendWideRow(PERSISTENT_EDITOR(OBJECT_CLOUD_SF_EDITOR));
		}
	}
}

void ccPropertiesTreeDelegate::fillWithPrimitive(const ccGenericPrimitive* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Primitive" ) );

	//type
	appendRow(ITEM( tr( "Type" ) ), ITEM(_obj->getTypeName()));

	//drawing steps
	if (_obj->hasDrawingPrecision())
	{
		appendRow(ITEM( tr( "Drawing precision" ) ), PERSISTENT_EDITOR(OBJECT_PRIMITIVE_PRECISION), true);
	}

	if (_obj->isA(CC_TYPES::SPHERE))
	{
		appendRow(ITEM( tr( "Radius" ) ), PERSISTENT_EDITOR(OBJECT_SPHERE_RADIUS), true);
	}
	else if (_obj->isKindOf(CC_TYPES::CONE)) //cylinders are also cones!
	{
		appendRow(ITEM( tr( "Height" ) ), PERSISTENT_EDITOR(OBJECT_CONE_HEIGHT), true);
		if (_obj->isA(CC_TYPES::CYLINDER))
		{
			appendRow(ITEM( tr( "Radius" ) ), PERSISTENT_EDITOR(OBJECT_CONE_BOTTOM_RADIUS), true);
		}
		else
		{
			appendRow(ITEM( tr( "Bottom radius" ) ), PERSISTENT_EDITOR(OBJECT_CONE_BOTTOM_RADIUS), true);
			appendRow(ITEM( tr( "Top radius" ) ), PERSISTENT_EDITOR(OBJECT_CONE_TOP_RADIUS), true);
		}

		const ccCone* cone = static_cast<const ccCone*>(_obj);
		CCVector3 apex = cone->computeApex();
		appendRow(ITEM(tr("Apex")), ITEM(QStringLiteral("X: %0\nY: %1\nZ: %2").arg(apex.x).arg(apex.y).arg(apex.z)));

		double angle_deg = cone->computeHalfAngle_deg();
		appendRow(ITEM(tr("Half angle")), ITEM(QStringLiteral("%1 deg.").arg(angle_deg)));
	}
	else if (_obj->isKindOf(CC_TYPES::PLANE))
	{
		//planar entity commons
		fillWithPlanarEntity(static_cast<const ccPlane*>(_obj));
	}
}

void ccPropertiesTreeDelegate::fillWithFacet(const ccFacet* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator(tr("Facet"));

	//planar entity commons
	fillWithPlanarEntity(_obj);

	//surface
	appendRow(ITEM(tr("Surface")), ITEM(QLocale(QLocale::English).toString(_obj->getSurface())));

	//RMS
	appendRow(ITEM(tr("RMS")), ITEM(QLocale(QLocale::English).toString(_obj->getRMS())));

	//center
	appendRow(ITEM(tr("Center")), ITEM(QStringLiteral("(%1 ; %2 ; %3)").arg(_obj->getCenter().x).arg(_obj->getCenter().y).arg(_obj->getCenter().z)));

	//contour visibility
	if (_obj->getContour())
	{
		appendRow(ITEM(tr("Show contour")), CHECKABLE_ITEM(_obj->getContour()->isVisible(), OBJECT_FACET_CONTOUR));
	}

	//polygon visibility
	if (_obj->getPolygon())
	{
		appendRow(ITEM(tr("Show polygon")), CHECKABLE_ITEM(_obj->getPolygon()->isVisible(), OBJECT_FACET_MESH));
	}
}

void ccPropertiesTreeDelegate::fillWithPlanarEntity(const ccPlanarEntityInterface* _obj)
{
	if (!_obj || !m_model)
	{
		return;
	}
	//normal
	CCVector3 N = _obj->getNormal();
	appendRow(ITEM( tr( "Normal" ) ), ITEM(QStringLiteral("(%1 ; %2 ; %3)").arg(N.x).arg(N.y).arg(N.z)));

	//Dip & Dip direction (in degrees)
	PointCoordinateType dip_deg;
	PointCoordinateType dipDir_deg;
	ccNormalVectors::ConvertNormalToDipAndDipDir(N, dip_deg, dipDir_deg);
	appendRow(ITEM( tr( "Dip / Dip dir. (integer)" ) ), ITEM(QStringLiteral("(%1 ; %2) deg.").arg(static_cast<int>(std::round(dip_deg))).arg(static_cast<int>(std::round(dipDir_deg)))));
	appendRow(ITEM( tr( "Dip / Dip dir." ) ), ITEM(QStringLiteral("(%1; %2) deg.").arg(dip_deg, 0, 'f', 2).arg(dipDir_deg, 0, 'f', 2)));

	//normal vector visibility
	appendRow(ITEM( tr( "Show normal vector" ) ), CHECKABLE_ITEM(_obj->normalVectorIsShown(), OBJECT_PLANE_NORMAL_VECTOR));
}

void ccPropertiesTreeDelegate::fillWithMesh(const ccGenericMesh* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	bool isSubMesh = _obj->isA(CC_TYPES::SUB_MESH);

	addSeparator(isSubMesh ?  tr( "Sub-mesh" ) : tr( "Mesh" ) );

	//number of facets
	appendRow(ITEM( tr( "Faces" ) ), ITEM(QLocale(QLocale::English).toString(_obj->size())));

	//material/texture
	if (_obj->hasMaterials())
		appendRow(ITEM( tr( "Materials/textures" ) ), CHECKABLE_ITEM(_obj->materialsShown(), OBJECT_MATERIALS));

	//wireframe
	appendRow(ITEM( tr( "Wireframe" ) ), CHECKABLE_ITEM(_obj->isShownAsWire(), OBJECT_MESH_WIRE));

	//stippling (ccMesh only)
	//if (_obj->isA(CC_TYPES::MESH)) //DGM: can't remember why?
	appendRow(ITEM( tr( "Stippling" ) ), CHECKABLE_ITEM(static_cast<const ccMesh*>(_obj)->stipplingEnabled(), OBJECT_MESH_STIPPLING));

	//we also integrate vertices SF into mesh properties
	ccGenericPointCloud* vertices = _obj->getAssociatedCloud();
	if (vertices && (!vertices->isLocked() || _obj->isAncestorOf(vertices)))
	{
		fillSFWithPointCloud(vertices);
	}

	//global shift & scale
	fillWithShifted(_obj);
}

void ccPropertiesTreeDelegate::fillWithPolyline(const ccPolyline* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Polyline" ) );

	//number of vertices
	appendRow(ITEM( tr( "Vertices" ) ), ITEM(QLocale(QLocale::English).toString(_obj->size())));

	//polyline length
	appendRow(ITEM( tr( "Length" ) ), ITEM(QLocale(QLocale::English).toString(_obj->computeLength())));

	//custom line width
	appendRow(ITEM( tr( "Line width" ) ), PERSISTENT_EDITOR(OBJECT_POLYLINE_WIDTH), true);

	//global shift & scale
	fillWithShifted(_obj);
}

void ccPropertiesTreeDelegate::fillWithPointOctree(const ccOctree* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Octree" ) );

	//display mode
	appendRow(ITEM( tr( "Display mode" ) ), PERSISTENT_EDITOR(OBJECT_OCTREE_TYPE), true);

	//level
	appendRow(ITEM( tr( "Display level" ) ), PERSISTENT_EDITOR(OBJECT_OCTREE_LEVEL), true);

	addSeparator( tr( "Current level" ) );

	//current display level
	int level = _obj->getDisplayedLevel();
	assert(level > 0 && level <= ccOctree::MAX_OCTREE_LEVEL);

	//cell size
	const double cellSize = static_cast<double>(_obj->getCellSize(static_cast<unsigned char>(level)));
	appendRow(ITEM( tr( "Cell size" ) ), ITEM(QString::number(cellSize)));

	//cell count
	unsigned cellCount = _obj->getCellNumber(static_cast<unsigned char>(level));
	appendRow(ITEM( tr( "Cell count" ) ), ITEM(QLocale(QLocale::English).toString(cellCount)));

	//total volume of filled cells
	appendRow(ITEM( tr( "Filled volume" ) ), ITEM(QString::number(cellCount*pow(cellSize, 3.0))));
}

void ccPropertiesTreeDelegate::fillWithPointKdTree(const ccKdTree* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Kd-tree" ) );

	//max error
	appendRow(ITEM( tr( "Max Error" ) ), ITEM(QString::number(_obj->getMaxError())));
	//max error measure
	{
		QString errorMeasure;
		switch (_obj->getMaxErrorType())
		{
		case CCCoreLib::DistanceComputationTools::RMS:
			errorMeasure = tr( "RMS" );
			break;
		case CCCoreLib::DistanceComputationTools::MAX_DIST_68_PERCENT:
			errorMeasure = tr( "Max dist @ 68%" );
			break;
		case CCCoreLib::DistanceComputationTools::MAX_DIST_95_PERCENT:
			errorMeasure = tr( "Max dist @ 95%" );
			break;
		case CCCoreLib::DistanceComputationTools::MAX_DIST_99_PERCENT:
			errorMeasure = tr( "Max dist @ 99%" );
			break;
		case CCCoreLib::DistanceComputationTools::MAX_DIST:
			errorMeasure = tr( "Max distance" );
			break;
		default:
			assert(false);
			errorMeasure =  tr( "unknown" );
			break;
		}
		appendRow(ITEM( tr( "Error measure" ) ), ITEM(errorMeasure));
	}
}

void ccPropertiesTreeDelegate::fillWithImage(const ccImage* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Image" ));

	//image width
	appendRow(ITEM( tr( "Width" ) ), ITEM(QString::number(_obj->getW())));

	//image height
	appendRow(ITEM( tr( "Height" ) ), ITEM(QString::number(_obj->getH())));

	//transparency
	appendRow(ITEM( tr( "Alpha" ) ), PERSISTENT_EDITOR(OBJECT_IMAGE_ALPHA), true);

	if (_obj->getAssociatedSensor())
	{
		addSeparator( tr( "Sensor" ) );
		//"Set Viewport" button (shortcut to associated sensor)
		appendRow(ITEM( tr( "Apply Viewport" ) ), PERSISTENT_EDITOR(OBJECT_APPLY_IMAGE_VIEWPORT), true);
	}
}

void ccPropertiesTreeDelegate::fillWithLabel(const cc2DLabel* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Label" ) );

	//Body
	QStringList body = _obj->getLabelContent(ccGui::Parameters().displayedNumPrecision);
	appendRow(ITEM( tr( "Body" ) ), ITEM(body.join("\n")));

	//Show label in 2D
	appendRow(ITEM( tr( "Show 2D label" ) ), CHECKABLE_ITEM(_obj->isDisplayedIn2D(), OBJECT_LABEL_DISP_2D));

	//Show label in 3D
	appendRow(ITEM( tr( "Show legend(s)" ) ), CHECKABLE_ITEM(_obj->isPointLegendDisplayed(), OBJECT_LABEL_POINT_LEGEND));
}

void ccPropertiesTreeDelegate::fillWithViewportObject(const cc2DViewportObject* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Viewport" ) );

	//Name
	appendRow(ITEM( tr( "Name" ) ), ITEM(_obj->getName().isEmpty() ? tr( "undefined" ) : _obj->getName()));

	//"Apply Viewport" button
	appendRow(ITEM( tr( "Apply viewport" ) ), PERSISTENT_EDITOR(OBJECT_APPLY_LABEL_VIEWPORT), true);

	//"Update Viewport" button
	appendRow(ITEM( tr( "Update viewport" ) ), PERSISTENT_EDITOR(OBJECT_UPDATE_LABEL_VIEWPORT), true);
	
}

void ccPropertiesTreeDelegate::fillWithTransBuffer(const ccIndexedTransformationBuffer* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Trans. buffer" ) );

	//Associated positions
	appendRow(ITEM( tr( "Count" ) ), ITEM(QString::number(_obj->size())));

	//Show path as polyline
	appendRow(ITEM( tr( "Show path" ) ), CHECKABLE_ITEM(_obj->isPathShownAsPolyline(), OBJECT_SHOW_TRANS_BUFFER_PATH));

	//Show trihedrons
	appendRow(ITEM( tr( "Show trihedrons" ) ), CHECKABLE_ITEM(_obj->trihedronsShown(), OBJECT_SHOW_TRANS_BUFFER_TRIHDERONS));

	//Trihedrons scale
	appendRow(ITEM( tr( "Scale" ) ), PERSISTENT_EDITOR(OBJECT_TRANS_BUFFER_TRIHDERONS_SCALE), true);
}

void ccPropertiesTreeDelegate::fillWithSensor(const ccSensor* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	//Sensor drawing scale
	appendRow(ITEM( tr( "Drawing scale" ) ), PERSISTENT_EDITOR(OBJECT_SENSOR_DISPLAY_SCALE), true);

	//"Apply Viewport" button
	appendRow(ITEM( tr( "Apply Viewport" ) ), PERSISTENT_EDITOR(OBJECT_APPLY_SENSOR_VIEWPORT), true);

	//sensor aboslute orientation
	addSeparator( tr( "Position/Orientation" ) );
	appendWideRow(PERSISTENT_EDITOR(OBJECT_SENSOR_MATRIX_EDITOR));

	//Associated positions
	addSeparator( tr( "Associated positions" ) );

	//number of positions
	appendRow(ITEM( tr( "Count" ) ), ITEM(QString::number(_obj->getPositions() ? _obj->getPositions()->size() : 0)));

	double minIndex = 0.0;
	double maxIndex = 0.0;
	_obj->getIndexBounds(minIndex, maxIndex);
	if (minIndex != maxIndex)
	{
		//Index span
		appendRow(ITEM( tr( "Indices" ) ), ITEM(QStringLiteral("%1 - %2").arg(minIndex).arg(maxIndex)));

		//Current index
		appendRow(ITEM( tr( "Active index" ) ), PERSISTENT_EDITOR(OBJECT_SENSOR_INDEX), true);
	}
}

void ccPropertiesTreeDelegate::fillWithGBLSensor(const ccGBLSensor* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "TLS/GBL Sensor" ) );

	//Uncertainty
	appendRow(ITEM( tr( "Uncertainty" ) ), PERSISTENT_EDITOR(OBJECT_SENSOR_UNCERTAINTY), true);

	//angles
	addSeparator( tr( "Angular viewport (degrees)" ) );
	{
		//Angular range (yaw)
		PointCoordinateType yawMin = _obj->getMinYaw();
		PointCoordinateType yawMax = _obj->getMaxYaw();
		appendRow(ITEM( tr( "Yaw span" ) ),
				  ITEM( QStringLiteral("[%1 ; %2]")
						.arg( CCCoreLib::RadiansToDegrees( yawMin ), 0, 'f', 2)
						.arg( CCCoreLib::RadiansToDegrees( yawMax ), 0, 'f', 2)));
		
		//Angular steps (yaw)
		PointCoordinateType yawStep = _obj->getYawStep();
		appendRow(ITEM( tr( "Yaw step" ) ),
				  ITEM( QStringLiteral("%1")
						.arg( CCCoreLib::RadiansToDegrees( yawStep ), 0, 'f', 4)));

		//Angular range (pitch)
		PointCoordinateType pitchMin = _obj->getMinPitch();
		PointCoordinateType pitchMax = _obj->getMaxPitch();
		appendRow(ITEM( tr( "Pitch span" ) ),
				  ITEM( QStringLiteral("[%1 ; %2]")
						.arg( CCCoreLib::RadiansToDegrees( pitchMin ), 0, 'f', 2)
						.arg( CCCoreLib::RadiansToDegrees( pitchMax ), 0, 'f', 2)));

		//Angular steps (pitch)
		PointCoordinateType pitchStep = _obj->getPitchStep();
		appendRow(ITEM( tr( "Pitch step" ) ),
				  ITEM( QStringLiteral("%1")
						.arg( CCCoreLib::RadiansToDegrees( pitchStep ), 0, 'f', 4)));
	}

	//Positions
	fillWithSensor(_obj);
}

void ccPropertiesTreeDelegate::fillWithCameraSensor(const ccCameraSensor* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Camera Sensor" ) );

	const ccCameraSensor::IntrinsicParameters& params = _obj->getIntrinsicParameters();

	//Focal
	appendRow(ITEM( tr( "Vert. focal" ) ), ITEM(QString::number(params.vertFocal_pix) + " pix."));

	//Array size
	appendRow(ITEM( tr( "Array size" ) ), ITEM(QStringLiteral("%1 x %2").arg(params.arrayWidth).arg(params.arrayHeight)));

	//Principal point
	appendRow(ITEM( tr( "Principal point" ) ), ITEM(QStringLiteral("(%1 ; %2)").arg(params.principal_point[0]).arg(params.principal_point[1])));

	//Pixel size
	if (params.pixelSize_mm[0] != 0 || params.pixelSize_mm[1] != 0)
	{
		appendRow(ITEM( tr( "Pixel size" ) ), ITEM(QStringLiteral("%1 x %2").arg(params.pixelSize_mm[0]).arg(params.pixelSize_mm[1])));
	}

	//Field of view
	appendRow(ITEM( tr( "Field of view" ) ), ITEM(QString::number( CCCoreLib::RadiansToDegrees( params.vFOV_rad ) ) + " deg."));

	//Skewness
	appendRow(ITEM( tr( "Skew" ) ), ITEM(QString::number(params.skew)));

	addSeparator( tr( "Frustum display" ) );

	//Draw frustum
	appendRow(ITEM( tr( "Show lines" ) ), CHECKABLE_ITEM(_obj->frustumIsDrawn(), OBJECT_SENSOR_DRAW_FRUSTUM));
	appendRow(ITEM( tr( "Show side planes" ) ), CHECKABLE_ITEM(_obj->frustumPlanesAreDrawn(), OBJECT_SENSOR_DRAW_FRUSTUM_PLANES));

	//Positions
	fillWithSensor(_obj);
}

void ccPropertiesTreeDelegate::fillWithMaterialSet(const ccMaterialSet* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}
	addSeparator( tr( "Material set" ) );

	//Count
	appendRow(ITEM( tr( "Count" ) ), ITEM(QString::number(_obj->size())));

	//ccMaterialSet objects are 'shareable'
	fillWithShareable(_obj);
}

void ccPropertiesTreeDelegate::fillWithShareable(const CCShareable* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Array" ) );

	//Link count
	unsigned linkCount = _obj->getLinkCount(); //if we display it, it means it is a member of the DB --> i.e. link is already >1
	appendRow(ITEM( tr( "Shared" ) ), ITEM(linkCount < 3 ? tr("No") : tr("Yes (%1)").arg(linkCount - 1)));
}

template<class Type, int N, class ComponentType>
void ccPropertiesTreeDelegate::fillWithCCArray(const ccArray<Type, N, ComponentType>* _obj)
{
	assert(_obj && m_model);
	if (!_obj || !m_model)
	{
		return;
	}

	addSeparator( tr( "Array" ) );

	//Name
	appendRow(ITEM( tr( "Name" ) ), ITEM(_obj->getName().isEmpty() ? tr( "undefined" ) : _obj->getName()));

	//Count
	appendRow(ITEM( tr( "Elements" ) ), ITEM(QLocale(QLocale::English).toString(static_cast<qulonglong>(_obj->size()))));

	//Capacity
	appendRow(ITEM( tr( "Capacity" ) ), ITEM(QLocale(QLocale::English).toString(static_cast<qulonglong>(_obj->capacity()))));

	//Memory
	appendRow(ITEM( tr( "Memory" ) ), ITEM(QStringLiteral("%1 Mb").arg((_obj->capacity() * sizeof(Type)) / 1048576.0, 0, 'f', 2)));

	//ccArray objects are 'Shareable'
	fillWithShareable(_obj);
}

bool ccPropertiesTreeDelegate::isWideEditor(int itemData) const
{
	switch (itemData)
	{
	case OBJECT_CLOUD_SF_EDITOR:
	case OBJECT_SENSOR_MATRIX_EDITOR:
	case OBJECT_HISTORY_MATRIX_EDITOR:
	case OBJECT_GLTRANS_MATRIX_EDITOR:
	case TREE_VIEW_HEADER:
		return true;
	default:
		break;
	}

	return false;
}

QWidget* ccPropertiesTreeDelegate::createEditor(QWidget *parent,
	const QStyleOptionViewItem &option,
	const QModelIndex &index) const
{
	if (!m_model || !m_currentObject)
		return nullptr;

	QStandardItem* item = m_model->itemFromIndex(index);

	if (!item || !item->data().isValid())
		return nullptr;

	int itemData = item->data().toInt();
	if (item->column() == 0 && !isWideEditor(itemData))
	{
		//on the first column, only editors spanning on 2 columns are allowed
		return nullptr;
	}

	QWidget* outputWidget = nullptr;

	switch (itemData)
	{
	case OBJECT_CURRENT_DISPLAY:
	{
		QComboBox *comboBox = new QComboBox(parent);

		std::vector<ccGLWindowInterface*> glWindows;
		MainWindow::GetGLWindows(glWindows);

		comboBox->addItem( tr( s_noneString ) );

		for (auto &glWindow : glWindows)
		{
			comboBox->addItem(glWindow->getWindowTitle());
		}

		connect(comboBox, qOverload<const QString&>(&QComboBox::currentIndexChanged),
				this, &ccPropertiesTreeDelegate::objectDisplayChanged);

		outputWidget = comboBox;
	}
	break;
	case OBJECT_CURRENT_SCALAR_FIELD:
	{
		ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
		assert(cloud);		

		QComboBox *comboBox = new QComboBox(parent);

		comboBox->addItem( tr( s_noneString ) );
		int nsf = cloud ? cloud->getNumberOfScalarFields() : 0;
		for (int i = 0; i < nsf; ++i)
		{
			comboBox->addItem(QString(cloud->getScalarFieldName(i)));
		}

		connect(comboBox, qOverload<int>(&QComboBox::activated),
				this, &ccPropertiesTreeDelegate::scalarFieldChanged);

		outputWidget = comboBox;
	}
	break;
	case OBJECT_CURRENT_COLOR_RAMP:
	{
		ccColorScaleSelector* selector = new ccColorScaleSelector(ccColorScalesManager::GetUniqueInstance(), parent, QString::fromUtf8(":/CC/images/ccGear.png"));
		//fill combobox box with Color Scales Manager
		selector->init();
		connect(selector, &ccColorScaleSelector::colorScaleSelected, this, &ccPropertiesTreeDelegate::colorScaleChanged);
		connect(selector, &ccColorScaleSelector::colorScaleEditorSummoned, this, &ccPropertiesTreeDelegate::spawnColorRampEditor);

		outputWidget = selector;
	}
	break;
	case OBJECT_COLOR_RAMP_STEPS:
	{
		QSpinBox *spinBox = new QSpinBox(parent);
		spinBox->setRange(ccColorScale::MIN_STEPS, ccColorScale::MAX_STEPS);
		spinBox->setSingleStep(4);

		connect(spinBox, qOverload<int>(&QSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::colorRampStepsChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_CLOUD_SF_EDITOR:
	{
		sfEditDlg* sfd = new sfEditDlg(parent);

		//DGM: why does this widget can't follow its 'policy' ?!
		//QSizePolicy pol = sfd->sizePolicy();
		//QSizePolicy::Policy hpol = pol.horizontalPolicy();
		//sfd->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Maximum);
		//parent->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Maximum);

		connect(sfd, &sfEditDlg::entitySFHasChanged, this, &ccPropertiesTreeDelegate::updateDisplay);

		outputWidget = sfd;
	}
	break;
	case OBJECT_HISTORY_MATRIX_EDITOR:
	case OBJECT_GLTRANS_MATRIX_EDITOR:
	case OBJECT_SENSOR_MATRIX_EDITOR:
	{
		MatrixDisplayDlg* mdd = new MatrixDisplayDlg(parent);

		//no signal connection, it's a display-only widget

		outputWidget = mdd;
	}
	break;
	case TREE_VIEW_HEADER:
	{
		QLabel* headerLabel = new QLabel(parent);
		headerLabel->setStyleSheet(SEPARATOR_STYLESHEET);

		//no signal connection, it's a display-only widget

		outputWidget = headerLabel;
	}
	break;
	case OBJECT_OCTREE_TYPE:
	{
		QComboBox* comboBox = new QComboBox(parent);

		comboBox->addItem( tr( "Wire" ), QVariant(ccOctree::WIRE) );
		comboBox->addItem( tr( "Points" ), QVariant(ccOctree::MEAN_POINTS) );
		comboBox->addItem( tr( "Plain cubes" ), QVariant(ccOctree::MEAN_CUBES) );

		connect(comboBox, qOverload<int>(&QComboBox::activated),
				this, &ccPropertiesTreeDelegate::octreeDisplayModeChanged);

		outputWidget = comboBox;
	}
	break;
	case OBJECT_OCTREE_LEVEL:
	{
		QSpinBox* spinBox = new QSpinBox(parent);
		spinBox->setRange(1, CCCoreLib::DgmOctree::MAX_OCTREE_LEVEL);

		connect(spinBox, qOverload<int>(&QSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::octreeDisplayedLevelChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_PRIMITIVE_PRECISION:
	{
		QSpinBox* spinBox = new QSpinBox(parent);
		spinBox->setRange(4, 1024);
		spinBox->setSingleStep(4);

		connect(spinBox, qOverload<int>(&QSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::primitivePrecisionChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_SPHERE_RADIUS:
	{
		QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
		spinBox->setDecimals(6);
		spinBox->setRange(0, 1.0e6);
		spinBox->setSingleStep(1.0);

		connect(spinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::sphereRadiusChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_CONE_HEIGHT:
	{
		QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
		spinBox->setDecimals(6);
		spinBox->setRange(0, 1.0e6);
		spinBox->setSingleStep(1.0);

		connect(spinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::coneHeightChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_CONE_BOTTOM_RADIUS:
	{
		QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
		spinBox->setDecimals(6);
		spinBox->setRange(0, 1.0e6);
		spinBox->setSingleStep(1.0);

		connect(spinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::coneBottomRadiusChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_CONE_TOP_RADIUS:
	{
		QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
		spinBox->setDecimals(6);
		spinBox->setRange(0, 1.0e6);
		spinBox->setSingleStep(1.0);

		connect(spinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::coneTopRadiusChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_IMAGE_ALPHA:
	{
		QSlider* slider = new QSlider(Qt::Horizontal, parent);
		slider->setRange(0, 255);
		slider->setSingleStep(1);
		slider->setPageStep(16);
		slider->setTickPosition(QSlider::NoTicks);
		connect(slider, &QAbstractSlider::valueChanged, this, &ccPropertiesTreeDelegate::imageAlphaChanged);

		outputWidget = slider;
	}
	break;
	case OBJECT_SENSOR_INDEX:
	{
		ccSensor* sensor = ccHObjectCaster::ToSensor(m_currentObject);
		assert(sensor);
		
		double minIndex = 0.0;
		double maxIndex = 0.0;
		if (sensor)
		{
			sensor->getIndexBounds(minIndex, maxIndex);
		}

		QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
		spinBox->setRange(minIndex, maxIndex);
		spinBox->setSingleStep((maxIndex - minIndex) / 1000.0);

		connect(spinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::sensorIndexChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_TRANS_BUFFER_TRIHDERONS_SCALE:
	{
		QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
		spinBox->setRange(1.0e-3, 1.0e6);
		spinBox->setDecimals(3);
		spinBox->setSingleStep(1.0);

		connect(spinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::trihedronsScaleChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_APPLY_IMAGE_VIEWPORT:
	{
		QPushButton* button = new QPushButton( tr( "Apply" ), parent );
		connect(button, &QAbstractButton::clicked, this, &ccPropertiesTreeDelegate::applyImageViewport);

		button->setMinimumHeight(30);

		outputWidget = button;
	}
	break;
	case OBJECT_APPLY_SENSOR_VIEWPORT:
	{
		QPushButton* button = new QPushButton( tr( "Apply" ), parent );
		connect(button, &QAbstractButton::clicked, this, &ccPropertiesTreeDelegate::applySensorViewport);

		button->setMinimumHeight(30);

		outputWidget = button;
	}
	break;
	case OBJECT_APPLY_LABEL_VIEWPORT:
	{
		QPushButton* button = new QPushButton( tr( "Apply" ), parent );
		connect(button, &QAbstractButton::clicked, this, &ccPropertiesTreeDelegate::applyLabelViewport);

		button->setMinimumHeight(30);
		outputWidget = button;
	}
	break;
	case OBJECT_UPDATE_LABEL_VIEWPORT:
	{
		QPushButton* button = new QPushButton( tr( "Update" ), parent );
		connect(button, &QAbstractButton::clicked, this, &ccPropertiesTreeDelegate::updateLabelViewport);

		button->setMinimumHeight(30);
		outputWidget = button;
	}
	break;
	case OBJECT_SENSOR_UNCERTAINTY:
	{
		QLineEdit* lineEdit = new QLineEdit(parent);
		lineEdit->setValidator(new QDoubleValidator(1.0e-8, 1.0, 8, lineEdit));
		connect(lineEdit, &QLineEdit::editingFinished, this, &ccPropertiesTreeDelegate::sensorUncertaintyChanged);

		outputWidget = lineEdit;
	}
	break;
	case OBJECT_SENSOR_DISPLAY_SCALE:
	{
		QDoubleSpinBox *spinBox = new QDoubleSpinBox(parent);
		spinBox->setRange(1.0e-3, 1.0e6);
		spinBox->setDecimals(3);
		spinBox->setSingleStep(1.0e-1);

		connect(spinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::sensorScaleChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_CLOUD_POINT_SIZE:
	{
		QComboBox *comboBox = new QComboBox(parent);

		comboBox->addItem( tr( s_defaultPointSizeString ) ); //size = 0
		
		for (int i = static_cast<int>(ccGLWindowInterface::MIN_POINT_SIZE_F); i <= static_cast<int>(ccGLWindowInterface::MAX_POINT_SIZE_F); ++i)
		{
			comboBox->addItem(QString::number(i));
		}

		connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged),
				this, &ccPropertiesTreeDelegate::cloudPointSizeChanged);

		outputWidget = comboBox;
	}
	break;
	case OBJECT_POLYLINE_WIDTH:
	{
		QComboBox *comboBox = new QComboBox(parent);

		comboBox->addItem( tr( s_defaultPolyWidthSizeString ) ); //size = 0
				
		for (int i = static_cast<int>(ccGLWindowInterface::MIN_LINE_WIDTH_F); i <= static_cast<int>(ccGLWindowInterface::MAX_LINE_WIDTH_F); ++i)
		{
			comboBox->addItem(QString::number(i));
		}

		connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged),
				this, &ccPropertiesTreeDelegate::polyineWidthChanged);

		outputWidget = comboBox;
	}
	break;
	case OBJECT_COLOR_SOURCE:
	{
		QComboBox *comboBox = new QComboBox(parent);

		comboBox->addItem( tr( s_noneString ) );
		
		if (m_currentObject)
		{
			if (m_currentObject->hasColors())
			{
				comboBox->addItem(s_rgbColor);
				comboBox->setItemIcon(comboBox->count() - 1, QIcon(QString::fromUtf8(":/CC/images/typeRgbCcolor.png")));
			}
			if (m_currentObject->hasScalarFields())
			{
				comboBox->addItem( tr( s_sfColor ) );
				comboBox->setItemIcon(comboBox->count() - 1, QIcon(QString::fromUtf8(":/CC/images/typeSF.png")));
			}
			connect(comboBox, qOverload<const QString&>(&QComboBox::currentIndexChanged),
					this, &ccPropertiesTreeDelegate::colorSourceChanged);
		}

		outputWidget = comboBox;
	}
	break;
	case OBJECT_COORDINATE_SYSTEM_AXES_WIDTH:
	{
		QComboBox* comboBox = new QComboBox(parent);

		comboBox->addItem(tr(s_defaultPolyWidthSizeString)); //size = 0

		for (int i = static_cast<int>(ccCoordinateSystem::MIN_AXIS_WIDTH_F); i <= static_cast<int>(ccCoordinateSystem::MAX_AXIS_WIDTH_F); ++i)
		{
			comboBox->addItem(QString::number(i));
		}
		ccCoordinateSystem* cs = ccHObjectCaster::ToCoordinateSystem(m_currentObject);
		if (cs)
		{
			comboBox->setCurrentIndex(static_cast<int>(cs->getAxisWidth()));
		}
		connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged),
			this, &ccPropertiesTreeDelegate::coordinateSystemAxisWidthChanged);

		outputWidget = comboBox;
	}
	break;
	case OBJECT_COORDINATE_SYSTEM_DISP_SCALE:
	{
		QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
		spinBox->setRange(1.0e-3, 1.0e6);
		spinBox->setDecimals(3);
		spinBox->setSingleStep(1.0e-1);
		ccCoordinateSystem* cs = ccHObjectCaster::ToCoordinateSystem(m_currentObject);
		if (cs)
		{
			spinBox->setValue(cs->getDisplayScale());
		}
		
		connect(spinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
			this, &ccPropertiesTreeDelegate::coordinateSystemDisplayScaleChanged);

		outputWidget = spinBox;
	}
	break;
	case OBJECT_CLOUD_NORMAL_COLOR:
	{
		QComboBox *comboBox = new QComboBox(parent);

		comboBox->addItem("Yellow");
		comboBox->addItem("Red");
		comboBox->addItem("Green");
		comboBox->addItem("Blue");
		comboBox->addItem("Black");

		connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged),
				this, &ccPropertiesTreeDelegate::normalColorChanged);

		outputWidget = comboBox;
	}
	break;
	case OBJECT_CLOUD_NORMAL_LENGTH:
	{
		QDoubleSpinBox *spinBox = new QDoubleSpinBox(parent);
		spinBox->setRange(ccColorScale::MIN_STEPS, ccColorScale::MAX_STEPS);
		spinBox->setSingleStep(0.1);
		spinBox->setMinimum(0);

		connect(spinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
				this, &ccPropertiesTreeDelegate::normalLengthChanged);

		outputWidget = spinBox;
	}
	break;
	default:
		return QStyledItemDelegate::createEditor(parent, option, index);
	}

	if (outputWidget)
	{
		outputWidget->setFocusPolicy(Qt::StrongFocus); //Qt doc: << The returned editor widget should have Qt::StrongFocus >>
		outputWidget->installEventFilter(new FilterMouseWheelNoFocusEvent(outputWidget));
		if (m_lastFocusItemRole == itemData)
		{
			outputWidget->setFocus(Qt::MouseFocusReason);
		}
	}
	else
	{
		//shouldn't happen
		assert(false);
	}

	return outputWidget;
}

void ccPropertiesTreeDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
	QStyledItemDelegate::updateEditorGeometry(editor, option, index);

	if (!m_model || !editor)
	{
		return;
	}

	QStandardItem* item = m_model->itemFromIndex(index);

	if (item &&	item->data().isValid() && item->column() == 0)
	{
		if (isWideEditor(item->data().toInt()))
		{
			QWidget* widget = qobject_cast<QWidget*>(editor);
			if (!widget)
			{
				return;
			}
			//we must resize the SF edit widget so that it spans on both columns!
			QRect rect = m_view->visualRect(m_model->index(item->row(), 1)); //second column width
			widget->resize(option.rect.width() + rect.width(), widget->height());
		}
	}
}

void SetDoubleSpinBoxValue(QWidget *editor, double value, bool keyboardTracking = false)
{
	QDoubleSpinBox* spinBox = qobject_cast<QDoubleSpinBox*>(editor);
	if (!spinBox)
	{
		assert(false);
		return;
	}
	spinBox->setKeyboardTracking(keyboardTracking);
	spinBox->setValue(value);
}

void SetSpinBoxValue(QWidget *editor, int value, bool keyboardTracking = false)
{
	QSpinBox* spinBox = qobject_cast<QSpinBox*>(editor);
	if (!spinBox)
	{
		assert(false);
		return;
	}
	spinBox->setKeyboardTracking(keyboardTracking);
	spinBox->setValue(value);
}

void SetComboBoxIndex(QWidget *editor, int index)
{
	QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
	if (!comboBox)
	{
		assert(false);
		return;
	}
	assert(index < 0 || index < comboBox->maxCount());
	comboBox->setCurrentIndex(index);
}

void ccPropertiesTreeDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
	if (!m_model || !m_currentObject)
		return;

	QStandardItem* item = m_model->itemFromIndex(index);

	if (!item || !item->data().isValid() || (item->column() == 0 && !isWideEditor(item->data().toInt())))
		return;

	switch (item->data().toInt())
	{
	case OBJECT_CURRENT_DISPLAY:
	{
		QComboBox *comboBox = qobject_cast<QComboBox*>(editor);
		if (!comboBox)
		{
			assert(false);
			return;
		}

		ccGLWindowInterface* win = static_cast<ccGLWindowInterface*>(m_currentObject->getDisplay());
		int pos = (win ? comboBox->findText(win->getWindowTitle()) : 0);

		comboBox->setCurrentIndex(std::max(pos, 0)); //0 = "NONE"
		break;
	}
	case OBJECT_CURRENT_SCALAR_FIELD:
	{
		ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
		assert(cloud);
		if (!cloud)
		{
			return;
		}
		int pos = cloud->getCurrentDisplayedScalarFieldIndex();
		SetComboBoxIndex(editor, pos + 1);
		break;
	}
	case OBJECT_CURRENT_COLOR_RAMP:
	{
		QFrame *selectorFrame = qobject_cast<QFrame*>(editor);
		if (!selectorFrame)
		{
			return;
		}
		ccColorScaleSelector* selector = static_cast<ccColorScaleSelector*>(selectorFrame);

		ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
		assert(cloud);
		if (!cloud)
		{
			return;
		}
		ccScalarField* sf = cloud->getCurrentDisplayedScalarField();
		if (sf)
		{
			if (sf->getColorScale())
			{
				selector->setSelectedScale(sf->getColorScale()->getUuid());
			}
			else
			{
				selector->setSelectedScale(QString());
			}
		}
		break;
	}
	case OBJECT_COLOR_RAMP_STEPS:
	{
		ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
		assert(cloud);
		ccScalarField* sf = cloud ? cloud->getCurrentDisplayedScalarField() : nullptr;
		if (sf)
			SetSpinBoxValue(editor, sf->getColorRampSteps(), true);
		break;
	}
	case OBJECT_CLOUD_SF_EDITOR:
	{
		sfEditDlg *sfd = qobject_cast<sfEditDlg*>(editor);
		if (!sfd)
		{
			return;
		}

		ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
		assert(cloud);

		ccScalarField* sf = cloud->getCurrentDisplayedScalarField();
		if (sf)
		{
			sfd->fillDialogWith(sf);
		}
		break;
	}
	case OBJECT_HISTORY_MATRIX_EDITOR:
	{
		MatrixDisplayDlg *mdd = qobject_cast<MatrixDisplayDlg*>(editor);
		if (!mdd)
		{
			return;
		}

		mdd->fillDialogWith(m_currentObject->getGLTransformationHistory());
		break;
	}
	case OBJECT_GLTRANS_MATRIX_EDITOR:
	{
		MatrixDisplayDlg *mdd = qobject_cast<MatrixDisplayDlg*>(editor);
		if (!mdd)
		{
			return;
		}

		mdd->fillDialogWith(m_currentObject->getGLTransformation());
		break;
	}
	case OBJECT_SENSOR_MATRIX_EDITOR:
	{
		MatrixDisplayDlg* mdd = qobject_cast<MatrixDisplayDlg*>(editor);
		if (!mdd)
		{
			return;
		}

		ccSensor* sensor = ccHObjectCaster::ToSensor(m_currentObject);
		assert(sensor);
		if (!sensor)
		{
			return;
		}
		ccIndexedTransformation trans;
		if (sensor->getActiveAbsoluteTransformation(trans))
		{
			mdd->fillDialogWith(trans);
		}
		else
		{
			mdd->clear();
			mdd->setEnabled(false);
		}
		break;
	}
	case TREE_VIEW_HEADER:
	{
		QLabel* label = qobject_cast<QLabel*>(editor);
		if (label)
		{
			label->setText(item->accessibleDescription());
		}
		break;
	}
	case OBJECT_OCTREE_TYPE:
	{
		ccOctree* octree = ccHObjectCaster::ToOctree(m_currentObject);
		assert(octree);
		SetComboBoxIndex(editor, static_cast<int>(octree->getDisplayMode()));
		break;
	}
	case OBJECT_OCTREE_LEVEL:
	{
		ccOctree* octree = ccHObjectCaster::ToOctree(m_currentObject);
		assert(octree);
		SetSpinBoxValue(editor, octree ? octree->getDisplayedLevel() : 0);
		break;
	}
	case OBJECT_PRIMITIVE_PRECISION:
	{
		ccGenericPrimitive* primitive = ccHObjectCaster::ToPrimitive(m_currentObject);
		assert(primitive);
		SetSpinBoxValue(editor, primitive ? primitive->getDrawingPrecision() : 0);
		break;
	}
	case OBJECT_SPHERE_RADIUS:
	{
		ccSphere* sphere = ccHObjectCaster::ToSphere(m_currentObject);
		assert(sphere);
		SetDoubleSpinBoxValue(editor, sphere ? sphere->getRadius() : 0.0);
		break;
	}
	case OBJECT_CONE_HEIGHT:
	{
		ccCone* cone = ccHObjectCaster::ToCone(m_currentObject);
		assert(cone);
		SetDoubleSpinBoxValue(editor, cone ? cone->getHeight() : 0.0);
		break;
	}
	case OBJECT_CONE_BOTTOM_RADIUS:
	{
		ccCone* cone = ccHObjectCaster::ToCone(m_currentObject);
		assert(cone);
		SetDoubleSpinBoxValue(editor, cone ? cone->getBottomRadius() : 0.0);
		break;
	}
	case OBJECT_CONE_TOP_RADIUS:
	{
		ccCone* cone = ccHObjectCaster::ToCone(m_currentObject);
		assert(cone);
		SetDoubleSpinBoxValue(editor, cone ? cone->getTopRadius() : 0.0);
		break;
	}
	case OBJECT_IMAGE_ALPHA:
	{
		QSlider *slider = qobject_cast<QSlider*>(editor);
		if (!slider)
		{
			return;
		}

		ccImage* image = ccHObjectCaster::ToImage(m_currentObject);
		assert(image);
		if (!image)
		{
			return;
		}
		slider->setValue(static_cast<int>(image->getAlpha()*255.0f));
		//slider->setTickPosition(QSlider::NoTicks);
		break;
	}
	case OBJECT_SENSOR_INDEX:
	{
		ccSensor* sensor = ccHObjectCaster::ToSensor(m_currentObject);
		assert(sensor);
		SetDoubleSpinBoxValue(editor, sensor ? sensor->getActiveIndex() : 0.0);
		break;
	}
	case OBJECT_SENSOR_UNCERTAINTY:
	{
		QLineEdit *lineEdit = qobject_cast<QLineEdit*>(editor);
		if (!lineEdit)
		{
			return;
		}

		ccGBLSensor* sensor = ccHObjectCaster::ToGBLSensor(m_currentObject);
		assert(sensor);
		lineEdit->setText(QString::number(sensor ? sensor->getUncertainty() : 0, 'g', 8));
		break;
	}
	case OBJECT_SENSOR_DISPLAY_SCALE:
	{
		ccSensor* sensor = ccHObjectCaster::ToSensor(m_currentObject);
		assert(sensor);
		SetDoubleSpinBoxValue(editor, sensor ? sensor->getGraphicScale() : 0.0);
		break;
	}
	case OBJECT_TRANS_BUFFER_TRIHDERONS_SCALE:
	{
		ccIndexedTransformationBuffer* buffer = ccHObjectCaster::ToTransBuffer(m_currentObject);
		assert(buffer);
		SetDoubleSpinBoxValue(editor, buffer ? buffer->trihedronsDisplayScale() : 0.0);
		break;
	}
	case OBJECT_CLOUD_POINT_SIZE:
	{
		ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(m_currentObject);
		assert(cloud);
		if (!cloud)
		{
			return;
		}
		SetComboBoxIndex(editor, static_cast<int>(cloud->getPointSize()));
		break;
	}
	case OBJECT_POLYLINE_WIDTH:
	{
		ccPolyline* poly = ccHObjectCaster::ToPolyline(m_currentObject);
		assert(poly);
		if (!poly)
		{
			return;
		}
		SetComboBoxIndex(editor, static_cast<int>(poly->getWidth()));
		break;
	}
	case OBJECT_COLOR_SOURCE:
	{
		int currentIndex = 0; //no color
		int lastIndex = currentIndex;
		if (m_currentObject->hasColors())
		{
			++lastIndex;
			if (m_currentObject->colorsShown())
			{
				currentIndex = lastIndex;
			}
		}
		if (m_currentObject->hasScalarFields())
		{
			++lastIndex;
			if (m_currentObject->sfShown())
			{
				currentIndex = lastIndex;
			}
		}
		SetComboBoxIndex(editor, currentIndex);
		break;
	}
	case OBJECT_CLOUD_NORMAL_COLOR:
	{
		ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(m_currentObject);
		assert(cloud);
		if (!cloud)
		{
			return;
		}
		SetComboBoxIndex(editor, static_cast<ccPointCloud*>(cloud)->getNormalLineColor());
		break;
	}
	case OBJECT_CLOUD_NORMAL_LENGTH:
	{
		ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(m_currentObject);
		assert(cloud);
		if (!cloud)
		{
			return;
		}
		SetDoubleSpinBoxValue(editor, static_cast<ccPointCloud*>(cloud)->getNormalLength());
		break;
	}
	default:
		QStyledItemDelegate::setEditorData(editor, index);
		break;
	}
}

void ccPropertiesTreeDelegate::updateItem(QStandardItem * item)
{
	if (!m_currentObject || item->column() == 0 || !item->data().isValid())
		return;

	bool redraw = false;
	switch (item->data().toInt())
	{
	case OBJECT_NAME:
		m_currentObject->setName(item->text());
		Q_EMIT ccObjectPropertiesChanged(m_currentObject);
		break;
	case OBJECT_VISIBILITY:
	{
		bool objectWasDisplayed = m_currentObject->isDisplayed();
		m_currentObject->setVisible(item->checkState() == Qt::Checked);
		bool objectIsDisplayed = m_currentObject->isDisplayed();
		if (objectWasDisplayed != objectIsDisplayed)
		{
			if (m_currentObject->isGroup())
				Q_EMIT ccObjectAndChildrenAppearanceChanged(m_currentObject);
			else
				Q_EMIT ccObjectAppearanceChanged(m_currentObject);
		}
	}
	break;
	case OBJECT_NORMALS_SHOWN:
		m_currentObject->showNormals(item->checkState() == Qt::Checked);
		redraw = true;
		break;
	case OBJECT_MATERIALS:
	{
		ccGenericMesh* mesh = ccHObjectCaster::ToGenericMesh(m_currentObject);
		assert(mesh);
		mesh->showMaterials(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_SF_SHOW_SCALE:
	{
		ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
		assert(cloud);
		cloud->showSFColorsScale(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;	
	case OBJECT_COORDINATE_SYSTEM_DISP_AXES:
	{
		ccCoordinateSystem* cs = ccHObjectCaster::ToCoordinateSystem(m_currentObject);
		if (cs)
		{
			cs->showAxisLines(item->checkState() == Qt::Checked);
		}
	}
	redraw = true;
	break;
	case OBJECT_COORDINATE_SYSTEM_DISP_PLANES:
	{
		ccCoordinateSystem* cs = ccHObjectCaster::ToCoordinateSystem(m_currentObject);
		if (cs)
		{
			cs->showAxisPlanes(item->checkState() == Qt::Checked);
		}
	}
	redraw = true;
	break;
	case OBJECT_FACET_CONTOUR:
	{
		ccFacet* facet = ccHObjectCaster::ToFacet(m_currentObject);
		assert(facet);
		if (facet && facet->getContour())
			facet->getContour()->setVisible(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_FACET_MESH:
	{
		ccFacet* facet = ccHObjectCaster::ToFacet(m_currentObject);
		assert(facet);
		if (facet && facet->getPolygon())
			facet->getPolygon()->setVisible(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_PLANE_NORMAL_VECTOR:
	{
		ccPlanarEntityInterface* plane = ccHObjectCaster::ToPlanarEntity(m_currentObject);
		assert(plane);
		if (plane)
			plane->showNormalVector(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_MESH_WIRE:
	{
		ccGenericMesh* mesh = ccHObjectCaster::ToGenericMesh(m_currentObject);
		assert(mesh);
		mesh->showWired(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_MESH_STIPPLING:
	{
		ccGenericMesh* mesh = ccHObjectCaster::ToGenericMesh(m_currentObject);
		assert(mesh);
		mesh->enableStippling(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_LABEL_DISP_2D:
	{
		cc2DLabel* label = ccHObjectCaster::To2DLabel(m_currentObject);
		assert(label);
		label->setDisplayedIn2D(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_LABEL_POINT_LEGEND:
	{
		cc2DLabel* label = ccHObjectCaster::To2DLabel(m_currentObject);
		assert(label);
		label->displayPointLegend(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_NAME_IN_3D:
		m_currentObject->showNameIn3D(item->checkState() == Qt::Checked);
		redraw = true;
		break;
	case OBJECT_SHOW_TRANS_BUFFER_PATH:
	{
		ccIndexedTransformationBuffer* buffer = ccHObjectCaster::ToTransBuffer(m_currentObject);
		assert(buffer);
		buffer->showPathAsPolyline(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_SHOW_TRANS_BUFFER_TRIHDERONS:
	{
		ccIndexedTransformationBuffer* buffer = ccHObjectCaster::ToTransBuffer(m_currentObject);
		assert(buffer);
		buffer->showTrihedrons(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_SENSOR_DRAW_FRUSTUM:
	{
		ccCameraSensor* sensor = ccHObjectCaster::ToCameraSensor(m_currentObject);
		sensor->drawFrustum(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_SENSOR_DRAW_FRUSTUM_PLANES:
	{
		ccCameraSensor* sensor = ccHObjectCaster::ToCameraSensor(m_currentObject);
		sensor->drawFrustumPlanes(item->checkState() == Qt::Checked);
	}
	redraw = true;
	break;
	case OBJECT_CLOUD_DRAW_NORMALS:
	{
		ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(m_currentObject);
		bool isChecked = (item->checkState() == Qt::Checked);
		if (cloud)
		{
			static_cast<ccPointCloud*>(cloud)->showNormalsAsLines(isChecked);
		}
	}
	redraw = true;
	break;
	}

	if (redraw)
	{
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::updateDisplay()
{
	ccHObject* object = m_currentObject;
	if (!object)
	{
		return;
	}

	bool objectIsDisplayed = object->isDisplayed();
	if (!objectIsDisplayed)
	{
		//DGM: point clouds may be mesh vertices of meshes which may depend on several of their parameters
		if (object->isKindOf(CC_TYPES::POINT_CLOUD))
		{
			ccHObject* parent = object->getParent();
			if (parent && parent->isKindOf(CC_TYPES::MESH) && parent->isDisplayed()) //specific case: vertices
			{
				object = parent;
				objectIsDisplayed = true;
			}
		}
		// Allows show name toggle on normally non-visible objects to update the screen
		else if (object->isKindOf(CC_TYPES::HIERARCHY_OBJECT))
		{
			objectIsDisplayed = true;
		}
	}

	if (objectIsDisplayed)
	{
		if (object->isGroup())
		{
			Q_EMIT ccObjectAndChildrenAppearanceChanged(m_currentObject);
		}
		else
		{
			Q_EMIT ccObjectAppearanceChanged(m_currentObject);
		}
	}
}

void ccPropertiesTreeDelegate::updateModel()
{
	//simply re-fill model!
	fillModel(m_currentObject);
}

void ccPropertiesTreeDelegate::scalarFieldChanged(int pos)
{
	if (!m_currentObject)
	{
		return;
	}

	ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
	if (cloud && cloud->getCurrentDisplayedScalarFieldIndex() + 1 != pos)
	{
		cloud->setCurrentDisplayedScalarField(pos - 1);
		cloud->showSF(pos > 0);

		updateDisplay();
		//we must also reset the properties display!
		updateModel();
	}
}

void ccPropertiesTreeDelegate::spawnColorRampEditor()
{
	if (!m_currentObject)
	{
		return;
	}

	ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
	assert(cloud);
	ccScalarField* sf = (cloud ? static_cast<ccScalarField*>(cloud->getCurrentDisplayedScalarField()) : nullptr);
	if (sf)
	{
		ccGLWindowInterface* glWindow = static_cast<ccGLWindowInterface*>(cloud->getDisplay());
		ccColorScaleEditorDialog* editorDialog = new ccColorScaleEditorDialog(	ccColorScalesManager::GetUniqueInstance(),
																				MainWindow::TheInstance(),
																				sf->getColorScale(),
																				glWindow ? glWindow->asWidget() : nullptr);
		editorDialog->setAssociatedScalarField(sf);
		if (editorDialog->exec())
		{
			if (editorDialog->getActiveScale())
			{
				sf->setColorScale(editorDialog->getActiveScale());
				updateDisplay();
			}

			//save current scale manager state to persistent settings
			ccColorScalesManager::GetUniqueInstance()->toPersistentSettings();

			updateModel();
		}
	}
}

void ccPropertiesTreeDelegate::colorScaleChanged(int pos)
{
	if (!m_currentObject)
	{
		return;
	}

	if (pos < 0)
	{
		assert(false);
		return;
	}

	ccColorScaleSelector* selector = dynamic_cast<ccColorScaleSelector*>(QObject::sender());
	if (!selector)
	{
		return;
	}

	ccColorScale::Shared colorScale = selector->getScale(pos);
	if (!colorScale)
	{
		ccLog::Error("Internal error: color scale doesn't seem to exist anymore!");
		return;
	}

	//get current SF
	ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
	assert(cloud);
	ccScalarField* sf = cloud ? static_cast<ccScalarField*>(cloud->getCurrentDisplayedScalarField()) : nullptr;
	if (sf && sf->getColorScale() != colorScale)
	{
		sf->setColorScale(colorScale);
		updateDisplay();
		updateModel();
	}
}

void ccPropertiesTreeDelegate::colorRampStepsChanged(int pos)
{
	if (!m_currentObject)
	{
		return;
	}

	ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
	assert(cloud);
	if (!cloud)
	{
		return;
	}
	ccScalarField* sf = static_cast<ccScalarField*>(cloud->getCurrentDisplayedScalarField());
	if (sf && sf->getColorRampSteps() != pos)
	{
		sf->setColorRampSteps(pos);
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::octreeDisplayModeChanged(int pos)
{
	if (!m_currentObject)
	{
		return;
	}
	QComboBox* comboBox = dynamic_cast<QComboBox*>(QObject::sender());
	if (!comboBox)
	{
		return;
	}

	ccOctree* octree = ccHObjectCaster::ToOctree(m_currentObject);
	assert(octree);

	int mode = comboBox->itemData(pos, Qt::UserRole).toInt();
	if (octree && octree->getDisplayMode() != mode)
	{
		octree->setDisplayMode(static_cast<ccOctree::DisplayMode>(mode));
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::octreeDisplayedLevelChanged(int val)
{
	if (!m_currentObject)
	{
		return;
	}

	ccOctree* octree = ccHObjectCaster::ToOctree(m_currentObject);
	assert(octree);

	if (octree && octree->getDisplayedLevel() != val) //to avoid infinite loops!
	{
		octree->setDisplayedLevel(val);
		updateDisplay();

		//record item role to force the scroll focus (see 'createEditor').
		m_lastFocusItemRole = OBJECT_OCTREE_LEVEL;

		//we must also reset the properties display!
		updateModel();
	}
}

void ccPropertiesTreeDelegate::primitivePrecisionChanged(int val)
{
	if (!m_currentObject)
	{
		return;
	}

	ccGenericPrimitive* primitive = ccHObjectCaster::ToPrimitive(m_currentObject);
	assert(primitive);

	if (primitive->getDrawingPrecision() != static_cast<unsigned int>(val))
	{
		bool wasVisible = primitive->isVisible();
		primitive->setDrawingPrecision(val);
		primitive->setVisible(wasVisible);

		updateDisplay();

		//record item role to force the scroll focus (see 'createEditor').
		m_lastFocusItemRole = OBJECT_PRIMITIVE_PRECISION;

		//we must also reset the properties display!
		updateModel();
	}
}

void ccPropertiesTreeDelegate::sphereRadiusChanged(double val)
{
	if (!m_currentObject)
		return;

	ccSphere* sphere = ccHObjectCaster::ToSphere(m_currentObject);
	assert(sphere);
	if (!sphere)
		return;

	PointCoordinateType radius = static_cast<PointCoordinateType>(val);
	if (sphere->getRadius() != radius)
	{
		bool wasVisible = sphere->isVisible();
		sphere->setRadius(radius);
		sphere->setVisible(wasVisible);

		updateDisplay();

		//record item role to force the scroll focus (see 'createEditor').
		m_lastFocusItemRole = OBJECT_SPHERE_RADIUS;

		//we must also reset the properties display!
		updateModel();
	}
}

void ccPropertiesTreeDelegate::coneHeightChanged(double val)
{
	if (!m_currentObject)
		return;

	ccCone* cone = ccHObjectCaster::ToCone(m_currentObject);
	assert(cone);
	if (!cone)
		return;

	PointCoordinateType height = static_cast<PointCoordinateType>(val);
	if (cone->getHeight() != height)
	{
		bool wasVisible = cone->isVisible();
		cone->setHeight(height);
		cone->setVisible(wasVisible);

		updateDisplay();

		//record item role to force the scroll focus (see 'createEditor').
		m_lastFocusItemRole = OBJECT_CONE_HEIGHT;

		//we must also reset the properties display!
		updateModel();
	}
}

void ccPropertiesTreeDelegate::coneBottomRadiusChanged(double val)
{
	if (!m_currentObject)
		return;

	ccCone* cone = ccHObjectCaster::ToCone(m_currentObject);
	assert(cone);
	if (!cone)
		return;

	PointCoordinateType radius = static_cast<PointCoordinateType>(val);
	if (cone->getBottomRadius() != radius)
	{
		bool wasVisible = cone->isVisible();
		cone->setBottomRadius(radius); //works for both the bottom and top radii for cylinders!
		cone->setVisible(wasVisible);

		updateDisplay();

		//record item role to force the scroll focus (see 'createEditor').
		m_lastFocusItemRole = OBJECT_CONE_BOTTOM_RADIUS;

		//we must also reset the properties display!
		updateModel();
	}
}

void ccPropertiesTreeDelegate::coneTopRadiusChanged(double val)
{
	if (!m_currentObject)
		return;

	ccCone* cone = ccHObjectCaster::ToCone(m_currentObject);
	assert(cone);
	if (!cone)
		return;

	PointCoordinateType radius = static_cast<PointCoordinateType>(val);
	if (cone->getTopRadius() != radius)
	{
		bool wasVisible = cone->isVisible();
		cone->setTopRadius(radius); //works for both the bottom and top radii for cylinders!
		cone->setVisible(wasVisible);

		updateDisplay();

		//record item role to force the scroll focus (see 'createEditor').
		m_lastFocusItemRole = OBJECT_CONE_TOP_RADIUS;

		//we must also reset the properties display!
		updateModel();
	}
}

void ccPropertiesTreeDelegate::imageAlphaChanged(int val)
{
	ccImage* image = ccHObjectCaster::ToImage(m_currentObject);
	assert(image);
	if (!image)
		return;

	float alpha = val / 255.0f;
	if (image && image->getAlpha() != alpha)
	{
		image->setAlpha(alpha);
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::applyImageViewport()
{
	if (!m_currentObject)
		return;

	ccImage* image = ccHObjectCaster::ToImage(m_currentObject);
	assert(image);
	if (!image)
		return;

	if (image->getAssociatedSensor() && image->getAssociatedSensor()->applyImageViewport(image))
	{
		ccLog::Print("[ApplyImageViewport] Viewport applied");
	}
}

void ccPropertiesTreeDelegate::applySensorViewport()
{
	if (!m_currentObject)
		return;

	ccSensor* sensor = ccHObjectCaster::ToSensor(m_currentObject);
	assert(sensor);
	if (!sensor)
		return;

	if (sensor->applyViewport())
	{
		ccLog::Print("[ApplySensorViewport] Viewport applied");
	}
}

void ccPropertiesTreeDelegate::applyLabelViewport()
{
	if (!m_currentObject)
		return;

	cc2DViewportObject* viewport = ccHObjectCaster::To2DViewportObject(m_currentObject);
	assert(viewport);
	if (!viewport)
	{
		return;
	}

	ccGLWindowInterface* win = MainWindow::GetActiveGLWindow();
	if (!win)
		return;

	win->setViewportParameters(viewport->getParameters());
	win->redraw();
}

void ccPropertiesTreeDelegate::updateLabelViewport()
{
	if (!m_currentObject)
	{
		return;
	}

	cc2DViewportObject* viewport = ccHObjectCaster::To2DViewportObject(m_currentObject);
	assert(viewport);
	if (!viewport)
	{
		return;
	}

	ccGLWindowInterface* win = MainWindow::GetActiveGLWindow();
	if (!win)
	{
		return;
	}

	viewport->setParameters(win->getViewportParameters());

	// Update the custom light position as well
	{
		bool customLightEnabled = win->customLightEnabled();
		CCVector3f customLightPos = win->getCustomLightPosition();

		viewport->setMetaData("CustomLightEnabled", customLightEnabled);
		viewport->setMetaData("CustomLightPosX", customLightPos.x);
		viewport->setMetaData("CustomLightPosY", customLightPos.y);
		viewport->setMetaData("CustomLightPosZ", customLightPos.z);
	}

	ccLog::Print(QString("Viewport '%1' has been updated").arg(viewport->getName()));
}

void ccPropertiesTreeDelegate::sensorUncertaintyChanged()
{
	if (!m_currentObject)
	{
		return;
	}

	QLineEdit* lineEdit = qobject_cast<QLineEdit*>(QObject::sender());
	if (!lineEdit)
	{
		assert(false);
		return;
	}

	ccGBLSensor* sensor = ccHObjectCaster::ToGBLSensor(m_currentObject);
	assert(sensor);

	PointCoordinateType uncertainty = static_cast<PointCoordinateType>(lineEdit->text().toDouble());
	if (sensor && sensor->getUncertainty() != uncertainty)
	{
		sensor->setUncertainty(uncertainty);
	}
}

void ccPropertiesTreeDelegate::sensorScaleChanged(double val)
{
	if (!m_currentObject)
	{
		return;
	}

	ccSensor* sensor = ccHObjectCaster::ToSensor(m_currentObject);
	assert(sensor);

	if (sensor && sensor->getGraphicScale() != static_cast<PointCoordinateType>(val))
	{
		sensor->setGraphicScale(static_cast<PointCoordinateType>(val));
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::coordinateSystemDisplayScaleChanged(double val)
{
	if (!m_currentObject)
	{
		return;
	}

	ccCoordinateSystem* cs = ccHObjectCaster::ToCoordinateSystem(m_currentObject);
	assert(cs);

	if (cs && cs->getDisplayScale() != static_cast<PointCoordinateType>(val))
	{
		cs->setDisplayScale(static_cast<PointCoordinateType>(val));
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::sensorIndexChanged(double val)
{
	if (!m_currentObject)
	{
		return;
	}

	ccSensor* sensor = ccHObjectCaster::ToSensor(m_currentObject);
	assert(sensor);

	if (sensor && sensor->getActiveIndex() != val)
	{
		sensor->setActiveIndex(val);
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::trihedronsScaleChanged(double val)
{
	if (!m_currentObject)
	{
		return;
	}

	ccIndexedTransformationBuffer* buffer = ccHObjectCaster::ToTransBuffer(m_currentObject);
	assert(buffer);

	if (buffer && buffer->trihedronsDisplayScale() != static_cast<float>(val))
	{
		buffer->setTrihedronsDisplayScale(static_cast<float>(val));
		if (buffer->trihedronsShown())
		{
			updateDisplay();
		}
	}
}

void ccPropertiesTreeDelegate::normalColorChanged(int colorIdx)
{
	if (!m_currentObject)
	{
		return;
	}

	ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(m_currentObject);
	assert(cloud);

	if (cloud)
	{
		static_cast<ccPointCloud*>(cloud)->setNormalLineColor(colorIdx);
		cloud->redrawDisplay();
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::normalLengthChanged(double length)
{
	if (!m_currentObject)
	{
		return;
	}

	ccGenericPointCloud* cloud = ccHObjectCaster::ToPointCloud(m_currentObject);
	assert(cloud);

	if (cloud)
	{
		static_cast<ccPointCloud*>(cloud)->setNormalLength(length);
		cloud->redrawDisplay();
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::cloudPointSizeChanged(int size)
{
	if (!m_currentObject)
	{
		return;
	}

	ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(m_currentObject);
	assert(cloud);

	if (cloud && cloud->getPointSize() != size)
	{
		cloud->setPointSize(size);
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::polyineWidthChanged(int size)
{
	if (!m_currentObject)
	{
		return;
	}

	ccPolyline* polyline = ccHObjectCaster::ToPolyline(m_currentObject);
	assert(polyline);

	if (polyline && polyline->getWidth() != static_cast<PointCoordinateType>(size))
	{
		polyline->setWidth(static_cast<PointCoordinateType>(size));
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::coordinateSystemAxisWidthChanged(int size)
{
	if (!m_currentObject)
	{
		return;
	}

	ccCoordinateSystem* cs = ccHObjectCaster::ToCoordinateSystem(m_currentObject);
	assert(cs);

	if (cs && cs->getAxisWidth() != static_cast<PointCoordinateType>(size))
	{
		cs->setAxisWidth(static_cast<PointCoordinateType>(size));
		updateDisplay();
	}
}

void ccPropertiesTreeDelegate::objectDisplayChanged(const QString& newDisplayTitle)
{
	if (!m_currentObject)
	{
		return;
	}

	QString actualDisplayTitle;

	ccGLWindowInterface* win = static_cast<ccGLWindowInterface*>(m_currentObject->getDisplay());
	if (win)
	{
		actualDisplayTitle = win->getWindowTitle();
	}
	else
	{
		actualDisplayTitle = tr(s_noneString);
	}

	if (actualDisplayTitle != newDisplayTitle)
	{
		//we first mark the "old displays" before removal,
		//to be sure that they will also be redrawn!
		m_currentObject->prepareDisplayForRefresh_recursive();

		ccGLWindowInterface* win = MainWindow::GetGLWindow(newDisplayTitle);
		m_currentObject->setDisplay_recursive(win);
		if (win)
		{
			m_currentObject->prepareDisplayForRefresh_recursive();
			win->zoomGlobal();
		}

		MainWindow::RefreshAllGLWindow(false);
	}
}

void ccPropertiesTreeDelegate::colorSourceChanged(const QString & source)
{
	if (!m_currentObject)
	{
		return;
	}

	bool appearanceChanged = false;

	if (source == tr( s_noneString ))
	{
		appearanceChanged = m_currentObject->colorsShown() || m_currentObject->sfShown();
		m_currentObject->showColors(false);
		m_currentObject->showSF(false);
	}
	else if (source == s_rgbColor)
	{
		appearanceChanged = !m_currentObject->colorsShown() || m_currentObject->sfShown();
		m_currentObject->showColors(true);
		m_currentObject->showSF(false);
	}
	else if (source == tr( s_sfColor ))
	{
		appearanceChanged = m_currentObject->colorsShown() || !m_currentObject->sfShown();
		m_currentObject->showColors(false);
		m_currentObject->showSF(true);
	}
	else
	{
		assert(false);
	}

	if (appearanceChanged)
	{
		updateDisplay();
	}
}
