Blob Blame Raw


#include "tools/imagegrouping.h"

// TnzTools includes
#include "tools/strokeselection.h"
#include "tools/tool.h"
#include "tools/toolhandle.h"
#include "tools/toolutils.h"
#include "vectorselectiontool.h"

// TnzQt includes
#include "toonzqt/tselectionhandle.h"
#include "toonzqt/selectioncommandids.h"

// TnzLib includes
#include "toonz/tscenehandle.h"
#include "toonz/txshlevelhandle.h"

// TnzCore includes
#include "tvectorimage.h"
#include "tundo.h"
#include "tthreadmessage.h"

// Qt includes
#include <QMenu>

//=============================================================================
namespace
{
//-----------------------------------------------------------------------------

void groupWithoutUndo(TVectorImage *vimg, StrokeSelection *selection)
{
	int count = 0, fromStroke = -1, lastSelected = -1;

	for (int i = 0; i < (int)vimg->getStrokeCount(); i++)
		if (selection->isSelected(i)) {
			if (fromStroke == -1)
				fromStroke = i;
			else if (lastSelected != i - 1) //non sono contigui gli stroke selezionati: faccio affiorare quelli sotto
			{
				int j = 0;
				for (j = 0; j < count; j++)
					selection->select(fromStroke + j, false);
				vimg->moveStrokes(fromStroke, count, i);
				fromStroke = i - count;
				for (j = 0; j < count; j++)
					selection->select(fromStroke + j, true);
			}
			lastSelected = i;
			count++;
		}

	assert(count > 0);

	vimg->group(fromStroke, count);
	TTool::getApplication()->getCurrentTool()->getTool()->notifyImageChanged();
}

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

void ungroupWithoutUndo(TVectorImage *vimg, StrokeSelection *selection)
{
	for (int i = 0; i < (int)vimg->getStrokeCount();)
		if (selection->isSelected(i)) {
			if (!vimg->isStrokeGrouped(i)) {
				i++;
				continue;
			}
			i += vimg->ungroup(i);
		} else
			i++;
	TTool::getApplication()->getCurrentTool()->getTool()->notifyImageChanged();
}

//=============================================================================
// GroupUndo
//-----------------------------------------------------------------------------

class GroupUndo : public ToolUtils::TToolUndo
{

	std::auto_ptr<StrokeSelection> m_selection;

public:
	GroupUndo(TXshSimpleLevel *level, const TFrameId &frameId, StrokeSelection *selection)
		: ToolUtils::TToolUndo(level, frameId), m_selection(selection) {}

	void undo() const
	{
		TVectorImageP image = m_level->getFrame(m_frameId, true);
		if (image)
			ungroupWithoutUndo(image.getPointer(), m_selection.get());
	}

	void redo() const
	{
		TVectorImageP image = m_level->getFrame(m_frameId, true);
		if (image)
			groupWithoutUndo(image.getPointer(), m_selection.get());
	}

	int getSize() const
	{
		return sizeof(*this);
	}

	QString getToolName()
	{
		return QObject::tr("Group");
	}
};

//=============================================================================
// UngroupUndo
//-----------------------------------------------------------------------------

class UngroupUndo : public ToolUtils::TToolUndo
{

	std::auto_ptr<StrokeSelection> m_selection;

public:
	UngroupUndo(TXshSimpleLevel *level, const TFrameId &frameId, StrokeSelection *selection)
		: ToolUtils::TToolUndo(level, frameId), m_selection(selection) {}

	void undo() const
	{
		TVectorImageP image = m_level->getFrame(m_frameId, true);
		if (image)
			groupWithoutUndo(image.getPointer(), m_selection.get());
	}

	void redo() const
	{
		TVectorImageP image = m_level->getFrame(m_frameId, true);
		if (image)
			ungroupWithoutUndo(image.getPointer(), m_selection.get());
	}

	int getSize() const
	{
		return sizeof(*this);
	}

	QString getToolName()
	{
		return QObject::tr("Ungroup");
	}
};

//=============================================================================
// MoveGroupUndo
//-----------------------------------------------------------------------------

class MoveGroupUndo : public ToolUtils::TToolUndo
{

	UCHAR m_moveType;
	int m_refStroke, m_count, m_moveBefore;
	std::vector<std::pair<TStroke *, int>> m_selectedGroups;

public:
	MoveGroupUndo(TXshSimpleLevel *level, const TFrameId &frameId, UCHAR moveType,
				  int refStroke, int count, int moveBefore, const std::vector<std::pair<TStroke *, int>> &selectedGroups)
		: ToolUtils::TToolUndo(level, frameId), m_moveType(moveType), m_refStroke(refStroke), m_count(count), m_moveBefore(moveBefore), m_selectedGroups(selectedGroups)
	{
	}

	~MoveGroupUndo()
	{
	}

	void undo() const
	{
		int refStroke;
		int moveBefore;
		switch (m_moveType) {
		case TGroupCommand::FRONT:
			refStroke = m_moveBefore - m_count;
			moveBefore = m_refStroke;
			break;
		case TGroupCommand::FORWARD:
			refStroke = m_moveBefore - m_count;
			moveBefore = m_refStroke;
			break;
		case TGroupCommand::BACK:
			refStroke = m_moveBefore;
			moveBefore = m_refStroke + m_count;
			break;
		case TGroupCommand::BACKWARD:
			refStroke = m_moveBefore;
			moveBefore = m_refStroke + m_count;
			break;
		default:
			assert(!"group move not defined!");
			break;
		}
		TVectorImageP image = m_level->getFrame(m_frameId, true);
		if (!image)
			return;
		QMutexLocker lock(image->getMutex());
		image->moveStrokes(refStroke, m_count, moveBefore);
		StrokeSelection *selection = dynamic_cast<StrokeSelection *>(TTool::getApplication()->getCurrentSelection()->getSelection());
		if (selection) { //Se la selezione corrente e' la StrokeSelection
			// seleziono gli stroke che ho modificato con l'undo
			selection->selectNone();

			for (int i = 0; i < (int)m_selectedGroups.size(); i++) {
				int index = image->getStrokeIndex(m_selectedGroups[i].first);
				if (index == -1)
					continue;
				for (int j = index; j < index + m_selectedGroups[i].second; j++)
					selection->select(j, true);
			}
		}
		TTool::getApplication()->getCurrentScene()->notifySceneChanged();
		notifyImageChanged();
	}

	void redo() const
	{
		TVectorImageP image = m_level->getFrame(m_frameId, true);
		if (!image)
			return;
		QMutexLocker lock(image->getMutex());
		image->moveStrokes(m_refStroke, m_count, m_moveBefore);
		StrokeSelection *selection = dynamic_cast<StrokeSelection *>(TTool::getApplication()->getCurrentSelection()->getSelection());
		if (selection) { //Se la selezione corrente e' la StrokeSelection
			// seleziono gli stroke che ho modificato con il redo
			selection->selectNone();

			for (int i = 0; i < (int)m_selectedGroups.size(); i++) {
				int index = image->getStrokeIndex(m_selectedGroups[i].first);
				if (index == -1)
					continue;
				for (int j = index; j < index + m_selectedGroups[i].second; j++)
					selection->select(j, true);
			}
		}
		TTool::getApplication()->getCurrentScene()->notifySceneChanged();
		notifyImageChanged();
	}

	int getSize() const
	{
		return sizeof(*this);
	}

	QString getToolName()
	{
		return QObject::tr("Move Group");
	}
};

//-----------------------------------------------------------------------------
} // namespace
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// TGroupCommand
//-----------------------------------------------------------------------------

namespace
{

std::vector<std::pair<TStroke *, int>> getSelectedGroups(TVectorImage *vimg, StrokeSelection *sel)
{
	UINT i, j;
	std::vector<std::pair<TStroke *, int>> ret;

	for (i = 0; i < vimg->getStrokeCount(); i++)
		if (sel->isSelected(i)) {
			if (vimg->isStrokeGrouped(i)) {
				for (j = i + 1; j < vimg->getStrokeCount() && vimg->sameSubGroup(i, j); j++)
					if (!sel->isSelected(j))
						return std::vector<std::pair<TStroke *, int>>();
				ret.push_back(std::pair<TStroke *, int>(vimg->getStroke(i), j - i));
				i = j - 1;
			} else
				ret.push_back(std::pair<TStroke *, int>(vimg->getStroke(i), 1));
		}
	return ret;
}

} //namepsace

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

UCHAR TGroupCommand::getGroupingOptions()
{
	TTool *tool = TTool::getApplication()->getCurrentTool()->getTool();
	if (!tool)
		return 0;
	TVectorImage *vimg = dynamic_cast<TVectorImage *>(tool->getImage(false));
	if (!vimg)
		return 0;

	UCHAR mask = 0;
	int count = 0;
	UINT i, j;

	//spostamento: si possono  spostare solo gruppi interi  oppure  stroke   non gruppate

	std::vector<std::pair<TStroke *, int>> strokeIndexes = getSelectedGroups(vimg, m_sel);

	/*
	//spostamento: si puo' spostare solo un gruppo(e uno solo per volta) oppure una stroke singola  non gruppata
for (i=0; i<vimg->getStrokeCount(); i++)
	if (m_sel->isSelected(i))
  	{
		if (strokeIndex != -1)
			{
			if (!vimg->isStrokeGrouped(i) || !vimg->sameSubGroup(strokeIndex, i)) 
				break;  
			}
		else 
			{
			strokeIndex = i;
			if (vimg->isStrokeGrouped(i))
				{
				for (j=0; j<vimg->getStrokeCount(); j++)
					if (!m_sel->isSelected(j) && vimg->sameSubGroup(i, j))
						break;//non tutto il gruppo e' selezionato
				if (j<vimg->getStrokeCount())
					break;
				}
			}
		count++;
		}
	 */

	if (strokeIndexes.empty())
		return 0; //no stroke selected

	int strokeIndex = vimg->getStrokeIndex(strokeIndexes[0].first);

	if (strokeIndexes.size() > 1 || strokeIndex > 0) {
		mask |= BACK;
		mask |= BACKWARD;
	}
	if (strokeIndexes.size() > 1 || strokeIndex + strokeIndexes[0].second - 1 < (int)vimg->getStrokeCount() - 1) {
		mask |= FRONT;
		mask |= FORWARD;
	}

	/*
if (i == vimg->getStrokeCount())
  {
	if (strokeIndex+count<(int)vimg->getStrokeCount())
	  {
		mask |= FRONT;
		mask |= FORWARD;
	  }
	if (strokeIndex>0)
	  {
		mask |= BACK;
		mask |= BACKWARD;
	  }  
  }
*/

	//PER l'UNGROUP: si ungruppa solo se tutti gli stroke selezionati stanno nel gruppo (anche piu' gruppi insieme)

	for (i = 0; i < vimg->getStrokeCount(); i++) {
		if (m_sel->isSelected(i)) {
			if (!vimg->isStrokeGrouped(i))
				break;
			for (j = 0; j < vimg->getStrokeCount(); j++)
				if (!m_sel->isSelected(j) && vimg->sameSubGroup(i, j))
					break;
			if (j < vimg->getStrokeCount())
				break;
		}
	}
	if (i == vimg->getStrokeCount())
		mask |= UNGROUP;

	//PER il GROUP: si raggruppa solo  se:
	// //almeno una delle  stroke selezionate non fa parte di gruppi o e' di un gruppo diverso
	// e se c'e' una stroke di un gruppo, allora tutto il gruppo della stroke e' selezionato

	bool groupingMakesSense = false;
	int refStroke = -1;
	for (i = 0; i < vimg->getStrokeCount(); i++)
		if (m_sel->isSelected(i)) {
			if (vimg->isStrokeGrouped(i)) {
				if (refStroke == -1)
					refStroke = i;
				else if (!vimg->sameSubGroup(refStroke, i))
					groupingMakesSense = true; //gli storke selezionati non sono gia' tutti dello stesso gruppo
				for (j = 0; j < vimg->getStrokeCount(); j++)
					if (!m_sel->isSelected(j) && vimg->sameGroup(i, j))
						return mask;
			} else
				groupingMakesSense = true;
		}

	if (groupingMakesSense)
		mask |= GROUP;

	return mask;
}

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

#ifdef NUOVO

UCHAR TGroupCommand::getGroupingOptions()
{
	TTool *tool = TTool::getApplication()->getCurrentTool()->getTool();
	if (!tool)
		return 0;
	TVectorImage *vimg = dynamic_cast<TVectorImage *>(tool->getImage(false));
	if (!vimg)
		return 0;

	int strokeIndex = -1;
	UCHAR mask = 0;
	int count = 0;
	UINT i, j;
	bool valid = true;
	//spostamento: si puo' spostare solo un gruppo(e uno solo per volta) oppure una stroke singola  non gruppata
	std::vector<pair<int, int>> groups;

	for (i = 0; i < vimg->getStrokeCount() && valid;)
		if (m_sel->isSelected(i)) {
			strokeIndex = i;
			count = 1;
			if (vimg->isStrokeGrouped(i)) {
				for (j = i + 1; j < vimg->getStrokeCount() && valid; j++)
					if (m_sel->isSelected(j) && vimg->sameSubGroup(i, j))
						count++;
					else if (!m_sel->isSelected(j) && vimg->sameSubGroup(i, j))
						valid = false; //non tutto il gruppo e' selezionato
			}
			groups.push_back(pair<int, int>(strokeIndex, count));

			i = i + count;
		}

	if (groups.empty())
		return 0; //no stroke selected

	if (!valid)
		return 0;

	if (strokeIndex + count < (int)vimg->getStrokeCount()) {
		mask |= FRONT;
		mask |= FORWARD;
	}
	if (strokeIndex > 0) {
		mask |= BACK;
		mask |= BACKWARD;
	}

	//PER l'UNGROUP: si ungruppa solo se tutti gli stroke selezionati stanno nel gruppo (anche piu' gruppi insieme)

	for (i = 0; i < vimg->getStrokeCount(); i++) {
		if (m_sel->isSelected(i)) {
			if (!vimg->isStrokeGrouped(i))
				break;
			for (j = 0; j < vimg->getStrokeCount(); j++)
				if (!m_sel->isSelected(j) && vimg->sameSubGroup(i, j))
					break;
			if (j < vimg->getStrokeCount())
				break;
		}
	}
	if (i == vimg->getStrokeCount())
		mask |= UNGROUP;

	//PER il GROUP: si raggruppa solo  se:
	// //almeno una delle  stroke selezionate non fa parte di gruppi o e' di un gruppo diverso
	// e se c'e' una stroke di un gruppo, allora tutto il gruppo della stroke e' selezionato

	bool groupingMakesSense = false;
	int refStroke = -1;
	for (i = 0; i < vimg->getStrokeCount(); i++)
		if (m_sel->isSelected(i)) {
			if (vimg->isStrokeGrouped(i)) {
				if (refStroke == -1)
					refStroke = i;
				else if (!vimg->sameSubGroup(refStroke, i))
					groupingMakesSense = true; //gli storke selezionati non sono gia' tutti dello stesso gruppo
				for (j = 0; j < vimg->getStrokeCount(); j++)
					if (!m_sel->isSelected(j) && vimg->sameGroup(i, j))
						return mask;
			} else
				groupingMakesSense = true;
		}

	if (groupingMakesSense)
		mask |= GROUP;

	return mask;
}

#endif

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

void TGroupCommand::group()
{
	if (!(getGroupingOptions() & GROUP))
		return;

	TTool *tool = TTool::getApplication()->getCurrentTool()->getTool();
	if (!tool)
		return;
	TVectorImage *vimg = (TVectorImage *)tool->getImage(true);

	if (!vimg)
		return;
	QMutexLocker lock(vimg->getMutex());
	groupWithoutUndo(vimg, m_sel);
	TXshSimpleLevel *level = TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
	TUndoManager::manager()->add(new GroupUndo(level, tool->getCurrentFid(), new StrokeSelection(*m_sel)));
}

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

void TGroupCommand::enterGroup()
{
	TTool *tool = TTool::getApplication()->getCurrentTool()->getTool();
	if (!tool)
		return;
	TVectorImage *vimg = (TVectorImage *)tool->getImage(true);

	if (!vimg)
		return;
	int index = -1;

	for (int i = 0; i < (int)vimg->getStrokeCount(); i++)
		if (m_sel->isSelected(i)) {
			index = i;
			break;
		}

	if (index == -1)
		return;

	if (!vimg->canEnterGroup(index))
		return;
	vimg->enterGroup(index);
	TSelection *selection = TSelection::getCurrent();
	if (selection)
		selection->selectNone();

	TTool::getApplication()->getCurrentScene()->notifySceneChanged();
}

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

void TGroupCommand::exitGroup()
{
	TTool *tool = TTool::getApplication()->getCurrentTool()->getTool();
	if (!tool)
		return;
	TVectorImage *vimg = (TVectorImage *)tool->getImage(true);

	if (!vimg)
		return;
	vimg->exitGroup();
	TTool::getApplication()->getCurrentScene()->notifySceneChanged();
}

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

void TGroupCommand::ungroup()
{
	if (!(getGroupingOptions() & UNGROUP))
		return;
	TTool *tool = TTool::getApplication()->getCurrentTool()->getTool();
	if (!tool)
		return;
	TVectorImage *vimg = (TVectorImage *)tool->getImage(true);
	if (!vimg)
		return;
	QMutexLocker lock(vimg->getMutex());
	ungroupWithoutUndo(vimg, m_sel);
	TXshSimpleLevel *level = TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
	TUndoManager::manager()->add(new UngroupUndo(level, tool->getCurrentFid(), new StrokeSelection(*m_sel)));
}

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

/*
void computeMovingBounds(TVectorImage*vimg, int fromIndex, int toIndex, int&lower, int& upper)
{
lower = 0;
upper = vimg->getStrokeCount();

int refDepth = vimg->getGroupDepth(fromIndex)-1;  

if (refDepth==0)
  return;
  
int i;  
for (i=fromIndex-1; i>=0; i--)
  if (vimg->getCommonGroupDepth(fromIndex, i)<refDepth)
    {
    lower = i+1;
    break;
    }

for (i=fromIndex+1; i<vimg->getStrokeCount(); i++)
  if (vimg->getCommonGroupDepth(fromIndex, i)<refDepth)
    {
    upper = i;
    break;
    }    
}
*/
//-----------------------------------------------------------------------------
namespace
{

int commonDepth(TVectorImage *vimg, int index1, int count, int index2)
{
	int i, ret = 1000;
	for (i = index1; i < index1 + count; i++)
		ret = std::min(ret, vimg->getCommonGroupDepth(i, index2));
	return ret;
}

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

/*
bool cantMove1(TVectorImage* vimg, int refStroke, int count, int moveBefore, bool rev)
  {   
  if (moveBefore<(int)vimg->getStrokeCount() && moveBefore>0 &&
             vimg->getCommonGroupDepth(moveBefore-1, moveBefore)>commonDepth(vimg, refStroke, count, moveBefore) && 
             vimg->getCommonGroupDepth(moveBefore-1, moveBefore)>commonDepth(vimg, refStroke, count, moveBefore-1))
    return true;
  
  int prev = (rev)?moveBefore:moveBefore-1;
  
  if (refStroke>0 && commonDepth(vimg, refStroke, count, refStroke-1)>commonDepth(vimg, refStroke, count, prev))
    return true;
  if (refStroke+count<(int)vimg->getStrokeCount() && commonDepth(vimg, refStroke, count, refStroke+count)>commonDepth(vimg, refStroke, count, prev))
    return true;

  return false;
  }
*/

} //namespace

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

/*
  int i, refStroke=-1, count=0;
  for (i=0; i<(int)vimg->getStrokeCount(); i++)
    if(m_sel->isSelected(i))
      {
      assert(refStroke==-1 || i==0  || m_sel->isSelected(i-1));
      if (refStroke==-1)
        refStroke = i;
      count++;
      }
      
  if(count==0) return;
  */

int doMoveGroup(UCHAR moveType, TVectorImage *vimg, const std::vector<std::pair<TStroke *, int>> &selectedGroups, int index)
{
	int refStroke = vimg->getStrokeIndex(selectedGroups[index].first);
	int count = selectedGroups[index].second;

	int moveBefore;
	switch (moveType) {
	case TGroupCommand::FRONT:
		moveBefore = vimg->getStrokeCount();

		while (moveBefore >= refStroke + count + 1 && !vimg->canMoveStrokes(refStroke, count, moveBefore))
			moveBefore--;
		if (moveBefore == refStroke + count)
			return -1;

		break;
	case TGroupCommand::FORWARD:
		moveBefore = refStroke + count + 1;
		while (moveBefore <= (int)vimg->getStrokeCount() && !vimg->canMoveStrokes(refStroke, count, moveBefore))
			moveBefore++;
		if (moveBefore == vimg->getStrokeCount() + 1)
			return -1;
		break;
	case TGroupCommand::BACK:
		moveBefore = 0;
		while (moveBefore <= refStroke - 1 && !vimg->canMoveStrokes(refStroke, count, moveBefore))
			moveBefore++;
		if (moveBefore == refStroke)
			return -1;
		break;
	case TGroupCommand::BACKWARD:
		moveBefore = refStroke - 1;
		while (moveBefore >= 0 && !vimg->canMoveStrokes(refStroke, count, moveBefore))
			moveBefore--;
		if (moveBefore == -1)
			return -1;
		break;
	default:
		assert(false);
	}

	vimg->moveStrokes(refStroke, count, moveBefore);

	TXshSimpleLevel *level = TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
	TTool *tool = TTool::getApplication()->getCurrentTool()->getTool();
	TUndoManager::manager()->add(new MoveGroupUndo(level, tool->getCurrentFid(), moveType,
												   refStroke, count, moveBefore, selectedGroups));

	return moveBefore;
}

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

void TGroupCommand::moveGroup(UCHAR moveType)
{

	TTool *tool = TTool::getApplication()->getCurrentTool()->getTool();
	if (!tool)
		return;

	TVectorImage *vimg = (TVectorImage *)tool->getImage(true);
	if (!vimg)
		return;

	std::vector<std::pair<TStroke *, int>> selectedGroups = getSelectedGroups(vimg, m_sel);
	if (selectedGroups.empty())
		return;

	int i, j;

	TUndoManager::manager()->beginBlock();

	switch (moveType) {
	case TGroupCommand::FRONT:
	case TGroupCommand::BACKWARD:
		i = 0;
		if (moveType == TGroupCommand::BACKWARD && vimg->getStrokeIndex(selectedGroups[i].first) == 0) //tutti i gruppi adiacenti gia in fondo non possono essere backwardati
		{
			i++;
			while (i < (int)selectedGroups.size() &&
				   vimg->getStrokeIndex(selectedGroups[i - 1].first) + selectedGroups[i - 1].second - 1 == vimg->getStrokeIndex(selectedGroups[i].first) - 1)
				i++;
		}
		for (; i <= (int)selectedGroups.size() - 1; i++)
			doMoveGroup(moveType, vimg, selectedGroups, i); //vimg->getStrokeIndex(selectedGroups[i].first), selectedGroups[i].second);
		break;

	case TGroupCommand::BACK:
	case TGroupCommand::FORWARD:
		i = selectedGroups.size() - 1;
		if (moveType == TGroupCommand::FORWARD && vimg->getStrokeIndex(selectedGroups[i].first) + selectedGroups[i].second - 1 == vimg->getStrokeCount() - 1) //tutti i gruppi adiacenti gia in cime non possono essere forwardati
		{
			i--;
			while (i >= 0 &&
				   vimg->getStrokeIndex(selectedGroups[i + 1].first) - 1 == vimg->getStrokeIndex(selectedGroups[i].first) + selectedGroups[i].second - 1)
				i--;
		}
		for (; i >= 0; i--)
			doMoveGroup(moveType, vimg, selectedGroups, i);
		break;

	default:
		assert(false);
	}

	TUndoManager::manager()->endBlock();

	m_sel->selectNone();

	for (i = 0; i < (int)selectedGroups.size(); i++) {
		int index = vimg->getStrokeIndex(selectedGroups[i].first);
		for (j = index; j < index + selectedGroups[i].second; j++)
			m_sel->select(j, true);
	}

	tool->notifyImageChanged();
}

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

void TGroupCommand::addMenuItems(QMenu *menu)
{
	UCHAR optionMask = getGroupingOptions();
	if (optionMask == 0)
		return;

	CommandManager *cm = CommandManager::instance();

	if (optionMask & TGroupCommand::GROUP)
		menu->addAction(cm->getAction(MI_Group));

	if (optionMask & TGroupCommand::UNGROUP)
		menu->addAction(cm->getAction(MI_Ungroup));

	if (optionMask & (TGroupCommand::GROUP | TGroupCommand::UNGROUP) &&
		optionMask & (TGroupCommand::FORWARD | TGroupCommand::BACKWARD))
		menu->addSeparator();

	if (optionMask & TGroupCommand::FORWARD) {
		menu->addAction(cm->getAction(MI_BringToFront));
		menu->addAction(cm->getAction(MI_BringForward));
	}
	if (optionMask & TGroupCommand::BACKWARD) {
		menu->addAction(cm->getAction(MI_SendBack));
		menu->addAction(cm->getAction(MI_SendBackward));
	}
	menu->addSeparator();
}