Example #1
0
 void undo() const override {
   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
   for (int i = 0; i < (int)m_oldFrames.size(); i++) {
     xsh->setCell(m_r0 + i, m_col, m_oldFrames[i]);
   }
   xsh->updateFrameCount();
   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
 }
Example #2
0
 void redo() const override {
   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
   xsh->insertColumn(m_columnIndex);
   int frameCount                 = m_childLevel->getXsheet()->getFrameCount();
   if (frameCount < 1) frameCount = 1;
   for (int r = 0; r < frameCount; r++)
     xsh->setCell(r, m_columnIndex,
                  TXshCell(m_childLevel.getPointer(), TFrameId(r + 1)));
   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
 }
Example #3
0
 void redo() const override {
   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
   int r0, r1;
   xsh->getCellRange(m_index, r0, r1);
   assert(r0 <= r1);
   if (r0 > r1) return;
   TXshCell cell = xsh->getCell(r0, m_index);
   assert(!cell.isEmpty());
   assert(cell.m_level->getChildLevel());
   xsh->clearCells(r0, m_index, r1 - r0 + 1);
   for (int i = 0; i < m_newFramesCount; i++) {
     cell.m_frameId = TFrameId(i + 1);
     xsh->setCell(m_r0 + i, m_index, cell);
   }
   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
 }
Example #4
0
 void undo() const override {
   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
   int r0, r1;
   xsh->getCellRange(m_index, r0, r1);
   assert(r0 <= r1);
   if (r0 > r1) return;
   TXshCell cell = xsh->getCell(r0, m_index);
   assert(!cell.isEmpty());
   assert(cell.m_level->getChildLevel());
   xsh->clearCells(r0, m_index, r1 - r0 + 1);
   for (int i = 0; i < (int)m_oldFrames.size(); i++) {
     TFrameId fid = m_oldFrames[i];
     if (fid != TFrameId::EMPTY_FRAME) {
       cell.m_frameId = fid;
       xsh->setCell(m_r0 + i, m_index, cell);
     }
   }
   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
 }
Example #5
0
void ColumnCmd::cloneChild(int index) {
  if (!canResequence(index)) return;

  /*-- カラムを取得 --*/
  TXsheet *xsh       = TApp::instance()->getCurrentXsheet()->getXsheet();
  TXshColumn *column = xsh->getColumn(index);
  assert(column);

  // get the subxsheet to clone (childLevel, childXsh)
  /*-- SubXsheetレベルを取得 --*/
  TXshLevelColumn *lcolumn = column->getLevelColumn();
  assert(lcolumn);
  int r0 = 0, r1 = -1;
  lcolumn->getRange(r0, r1);
  assert(r0 <= r1);
  /*-- SubXsheetの一番頭のセル --*/
  TXshCell cell = lcolumn->getCell(r0);
  assert(!cell.isEmpty());
  /*- cell内に格納されているLevelを取得 -*/
  TXshChildLevel *childLevel = cell.m_level->getChildLevel();
  assert(childLevel);
  /*- SubXsheetのXsheetを取得 -*/
  TXsheet *childXsh = childLevel->getXsheet();

  // insert a new empty column
  /*- 隣に空きColumnをInsertしてCloneに備える -*/
  int newColumnIndex = index + 1;
  xsh->insertColumn(newColumnIndex);

  // create a subxsheet (newChildLevel, newChildXsh)
  ToonzScene *scene      = TApp::instance()->getCurrentScene()->getScene();
  ChildStack *childStack = scene->getChildStack();
  TXshChildLevel *newChildLevel = childStack->createChild(0, newColumnIndex);
  TXsheet *newChildXsh          = newChildLevel->getXsheet();

  // copy columns.
  std::set<int> indices;
  for (int i = 0; i < childXsh->getColumnCount(); i++) indices.insert(i);
  StageObjectsData *data = new StageObjectsData();
  data->storeColumns(indices, childXsh, 0);
  data->storeColumnFxs(indices, childXsh, 0);
  std::list<int> restoredSplineIds;
  data->restoreObjects(indices, restoredSplineIds, newChildXsh,
                       StageObjectsData::eDoClone);
  delete data;

  cloneNotColumnLinkedFxsAndOutputsFx(childXsh, newChildXsh);
  cloneXsheetTStageObjectTree(childXsh, newChildXsh);
  /*--以下は、Clone SubXsheet
  するときに、SubXsheet内にある子SubXsheetをクローンする関数
  クローンされた中にある子SubXsheetは、同じもので良いので、スキップする --*/
  // cloneSubXsheets(newChildXsh);

  /*-- XSheetノードのFxSchematicでのDagNodePosを再現
  FxやColumnノードの位置の再現は上のsetColumnで行っている
--*/
  newChildXsh->getFxDag()->getXsheetFx()->getAttributes()->setDagNodePos(
      childXsh->getFxDag()->getXsheetFx()->getAttributes()->getDagNodePos());

  newChildXsh->updateFrameCount();

  /*-- TXshChildLevel作成時にsetCellした1つ目のセルを消去 --*/
  xsh->removeCells(0, newColumnIndex);
  /*-- CloneしたColumnのセル番号順を再現 --*/
  for (int r = r0; r <= r1; r++) {
    TXshCell cell = lcolumn->getCell(r);
    if (cell.isEmpty()) continue;

    cell.m_level = newChildLevel;
    xsh->setCell(r, newColumnIndex, cell);
  }

  TStageObjectId currentObjectId =
      TApp::instance()->getCurrentObject()->getObjectId();
  xsh->getStageObject(TStageObjectId::ColumnId(newColumnIndex))
      ->setParent(xsh->getStageObjectParent(currentObjectId));

  xsh->updateFrameCount();
  TUndoManager::manager()->add(
      new CloneChildUndo(newChildLevel, newColumnIndex));

  // notify changes
  TApp::instance()->getCurrentScene()->setDirtyFlag(true);
  TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
bool LevelCreatePopup::apply() {
  TApp *app = TApp::instance();
  int row   = app->getCurrentFrame()->getFrame();
  int col   = app->getCurrentColumn()->getColumnIndex();
  int i, j;

  ToonzScene *scene = app->getCurrentScene()->getScene();
  TXsheet *xsh      = scene->getXsheet();

  bool validColumn = true;
  if (xsh->getColumn(col))
    validColumn =
        xsh->getColumn(col)->getColumnType() == TXshColumn::eLevelType;

  int from   = (int)m_fromFld->getValue();
  int to     = (int)m_toFld->getValue();
  int inc    = (int)m_incFld->getValue();
  int step   = (int)m_stepFld->getValue();
  double w   = m_widthFld->getValue();
  double h   = m_heightFld->getValue();
  double dpi = m_dpiFld->getValue();
  int xres   = std::max(tround(w * dpi), 1);
  int yres   = std::max(tround(h * dpi), 1);
  int lType  = getLevelType();

  std::wstring levelName = m_nameFld->text().toStdWString();
  // tolgo i blanks prima e dopo

  i = levelName.find_first_not_of(L' ');
  if (i == (int)std::wstring::npos)
    levelName = L"";
  else {
    int j = levelName.find_last_not_of(L' ');
    assert(j != (int)std::wstring::npos);
    levelName = levelName.substr(i, j - i + 1);
  }
  if (levelName.empty()) {
    error(tr("No level name specified: please choose a valid level name"));
    return false;
  }

  if (from > to) {
    error(tr("Invalid frame range"));
    return false;
  }
  if (inc <= 0) {
    error(tr("Invalid increment value"));
    return false;
  }
  if (step <= 0) {
    error(tr("Invalid step value"));
    return false;
  }

  int numFrames = step * (((to - from) / inc) + 1);

  if (scene->getLevelSet()->getLevel(levelName)) {
    error(
        tr("The level name specified is already used: please choose a "
           "different level name"));
    m_nameFld->selectAll();
    return false;
  }

  TFilePath parentDir(m_pathFld->getPath().toStdWString());
  TFilePath fp =
      scene->getDefaultLevelPath(lType, levelName).withParentDir(parentDir);

  TFilePath actualFp = scene->decodeFilePath(fp);
  if (TSystem::doesExistFileOrLevel(actualFp)) {
    error(
        tr("The level name specified is already used: please choose a "
           "different level name"));
    m_nameFld->selectAll();
    return false;
  }
  parentDir = scene->decodeFilePath(parentDir);
  if (!TFileStatus(parentDir).doesExist()) {
    QString question;
    /*question = "Folder " +toQString(parentDir) +
                                                     " doesn't exist.\nDo you
       want to create it?";*/
    question = tr("Folder %1 doesn't exist.\nDo you want to create it?")
                   .arg(toQString(parentDir));
    int ret = DVGui::MsgBox(question, QObject::tr("Yes"), QObject::tr("No"));
    if (ret == 0 || ret == 2) return false;
    try {
      TSystem::mkDir(parentDir);
      DvDirModel::instance()->refreshFolder(parentDir.getParentDir());
    } catch (...) {
      error(tr("Unable to create") + toQString(parentDir));
      return false;
    }
  }

  TXshLevel *level =
      scene->createNewLevel(lType, levelName, TDimension(), 0, fp);
  TXshSimpleLevel *sl = dynamic_cast<TXshSimpleLevel *>(level);
  assert(sl);
  sl->setPath(fp, true);
  if (lType == TZP_XSHLEVEL || lType == OVL_XSHLEVEL) {
    sl->getProperties()->setDpiPolicy(LevelProperties::DP_ImageDpi);
    sl->getProperties()->setDpi(dpi);
    sl->getProperties()->setImageDpi(TPointD(dpi, dpi));
    sl->getProperties()->setImageRes(TDimension(xres, yres));
  }

  /*-- これからLevelを配置しようとしているセルが空いているかどうかのチェック
   * --*/
  bool areColumnsShifted = false;
  TXshCell cell          = xsh->getCell(row, col);
  bool isInRange         = true;
  for (i = row; i < row + numFrames; i++) {
    if (!cell.isEmpty()) {
      isInRange = false;
      break;
    }
    cell = xsh->getCell(i, col);
  }
  if (!validColumn) {
    isInRange = false;
  }

  /*-- 別のLevelに占有されていた場合、Columnを1つ右に移動 --*/
  if (!isInRange) {
    col += 1;
    TApp::instance()->getCurrentColumn()->setColumnIndex(col);
    areColumnsShifted = true;
    xsh->insertColumn(col);
  }

  CreateLevelUndo *undo =
      new CreateLevelUndo(row, col, numFrames, step, areColumnsShifted);
  TUndoManager::manager()->add(undo);

  for (i = from; i <= to; i += inc) {
    TFrameId fid(i);
    TXshCell cell(sl, fid);
    if (lType == PLI_XSHLEVEL)
      sl->setFrame(fid, new TVectorImage());
    else if (lType == TZP_XSHLEVEL) {
      TRasterCM32P raster(xres, yres);
      raster->fill(TPixelCM32());
      TToonzImageP ti(raster, TRect());
      ti->setDpi(dpi, dpi);
      sl->setFrame(fid, ti);
      ti->setSavebox(TRect(0, 0, xres - 1, yres - 1));
    } else if (lType == OVL_XSHLEVEL) {
      TRaster32P raster(xres, yres);
      raster->clear();
      TRasterImageP ri(raster);
      ri->setDpi(dpi, dpi);
      sl->setFrame(fid, ri);
    }
    for (j = 0; j < step; j++) xsh->setCell(row++, col, cell);
  }

  if (lType == TZP_XSHLEVEL || lType == OVL_XSHLEVEL) {
    sl->save(fp);
    DvDirModel::instance()->refreshFolder(fp.getParentDir());
  }

  undo->onAdd(sl);

  app->getCurrentScene()->notifySceneChanged();
  app->getCurrentScene()->notifyCastChange();
  app->getCurrentXsheet()->notifyXsheetChanged();

  // Cambia l'immagine corrente ma non cambiano ne' il frame ne' la colonna
  // corrente
  // (entrambi notificano il cambiamento dell'immagine al tool).
  // devo verfificare che sia settato il tool giusto.
  app->getCurrentTool()->onImageChanged(
      (TImage::Type)app->getCurrentImageType());
  return true;
}
bool VectorizerPopup::apply()
{
	std::set<TXshLevel *> levels;

	ToonzScene *scene = m_sceneHandle->getScene();
	if (!scene) {
		assert(scene);
		return false;
	}

	TSceneProperties *sceneProp = scene->getProperties();
	if (!sceneProp)
		return false;

	VectorizerParameters *vectorizerParameters = sceneProp->getVectorizerParameters();
	if (!vectorizerParameters)
		return false;

	int r0 = 0;
	int c0 = 0;
	int r1 = 0;
	int c1 = 0;
	bool isCellSelection = getSelectedLevels(levels, r0, c0, r1, c1);
	if (levels.empty()) {
		error(tr("The current selection is invalid."));
		return false;
	}

	//Initialize Progress bar
	m_progressDialog = new DVGui::ProgressDialog("", "Cancel", 0, 1);
	m_progressDialog->setWindowFlags(Qt::Dialog | Qt::WindowTitleHint); //Don't show ? and X buttons
	m_progressDialog->setWindowTitle(QString("Convert To Vector..."));
	m_progressDialog->setAttribute(Qt::WA_DeleteOnClose);
	m_progressDialog->setWindowModality(Qt::WindowModal); //No user interaction is allowed during vectorization
	m_progressDialog->setFixedSize(200, 100);

	//Initialize vectorizer
	m_vectorizer = new Vectorizer;

	m_vectorizer->setParameters(*vectorizerParameters);

	connect(m_vectorizer, SIGNAL(frameName(QString)), this, SLOT(onFrameName(QString)), Qt::QueuedConnection);
	connect(m_vectorizer, SIGNAL(frameDone(int)), this, SLOT(onFrameDone(int)), Qt::QueuedConnection);
	connect(m_vectorizer, SIGNAL(partialDone(int, int)), this, SLOT(onPartialDone(int, int)), Qt::QueuedConnection);
	//We DON'T want the progress bar to be hidden at cancel press - since its modal
	//behavior prevents the user to interfere with a possibly still active vectorization.
	disconnect(m_progressDialog, SIGNAL(canceled()), m_progressDialog, SLOT(onCancel()));
	//We first inform the vectorizer of a cancel press;
	bool ret = connect(m_progressDialog, SIGNAL(canceled()), m_vectorizer, SLOT(cancel()));
	//which eventually transmits the command to vectorization core, allowing full-time cancels
	ret = ret && connect(m_progressDialog, SIGNAL(canceled()), m_vectorizer, SIGNAL(transmitCancel()));
	//Only after the vectorizer has terminated its process - or got cancelled, we are allowed
	//to proceed here.
	ret = ret && connect(m_vectorizer, SIGNAL(finished()), this, SLOT(onFinished()), Qt::QueuedConnection);
	assert(ret);

	int newIndexColumn = c1 + 1;
	std::set<TXshLevel *>::iterator it = levels.begin();
	for (it; it != levels.end(); it++) {
		TXshSimpleLevel *sl = dynamic_cast<TXshSimpleLevel *>(*it);
		if (!sl || !sl->getSimpleLevel() || !isLevelToConvert(sl)) {
			QString levelName = tr(toString(sl->getName()).c_str());
			QString errorMsg = tr("Cannot convert to vector the current selection.") + levelName;
			error(errorMsg);
			continue;
		}

		std::vector<TFrameId> fids;

		if (isCellSelection)
			getSelectedFids(fids, sl, r0, c0, r1, c1);
		else
			sl->getFids(fids);
		assert(fids.size() > 0);

		close();

		// Re-initialize progress Bar
		m_progressDialog->setMaximum(fids.size() * 100);
		m_progressDialog->setValue(0);
		m_currFrame = 0;

		// Re-initialize vectorizer
		m_vectorizer->setLevel(sl);
		m_vectorizer->setFids(fids);

		// Start vectorizing
		m_vectorizer->start();
		m_progressDialog->show();

		// Wait the vectorizer...
		while (!l_quitLoop)
			QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents);

		l_quitLoop = false;

		// Assign output X-sheet cells
		TXshSimpleLevel *vl = m_vectorizer->getVectorizedLevel();
		if (isCellSelection && vl) {
			TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
			xsheet->insertColumn(newIndexColumn);

			int r, c;
			for (c = c0; c <= c1; c++) {
				for (r = r0; r <= r1; r++) {
					TXshCell cell = xsheet->getCell(r, c);
					TXshSimpleLevel *level = (!cell.isEmpty()) ? cell.getSimpleLevel()
															   : 0;
					if (level != sl)
						continue;
					TFrameId curFid = cell.getFrameId();
					std::vector<TFrameId> newFids;
					vl->getFids(newFids);
					std::vector<TFrameId>::iterator it1 = newFids.begin();
					for (it1; it1 != newFids.end(); it1++) {
						TFrameId id = *it1;
						if (id.getNumber() == curFid.getNumber() ||			   // Hanno stesso numero di frame
							(id.getNumber() == 1 && curFid.getNumber() == -2)) // La vecchia cella non ha numero di frame
							xsheet->setCell(r, newIndexColumn, TXshCell(vl, id));
					}
				}
			}
			newIndexColumn += 1;
		} else if (vl) {
			std::vector<TFrameId> gomi;
			scene->getXsheet()->exposeLevel(0, scene->getXsheet()->getFirstFreeColumnIndex(), vl, gomi);
		}

		if (m_vectorizer->isCanceled())
			break;
	}

	m_progressDialog->close();
	delete m_vectorizer;

	TApp::instance()->getCurrentScene()->notifyCastChange();
	TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();

	return true;
}
Example #8
0
void mergeCmapped(int column, int mColumn, const QString &fullpath, bool isRedo)
{
	static int MergeCmappedSessionId = 0;
	MergeCmappedSessionId++;

	TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
	int start, end;
	int mStart, mEnd;
	xsh->getCellRange(column, start, end);
	xsh->getCellRange(mColumn, mStart, mEnd);

	if (start > end)
		return;
	vector<TXshCell> cell(max(end, mEnd) - min(start, mStart) + 1);
	vector<TXshCell> mCell(cell.size());

	xsh->getCells(min(start, mStart), column, cell.size(), &(cell[0]));

	if (mColumn != -1)
		xsh->getCells(min(start, mStart), mColumn, cell.size(), &(mCell[0]));

	TXshColumn *col = xsh->getColumn(column);
	TXshColumn *mcol = xsh->getColumn(mColumn);

	vector<MergeCmappedPair> matchingLevels;

	std::map<MergedPair, TFrameId> computedMergedMap;

	TXshSimpleLevel *level = 0, *mLevel = 0;
	TXshLevel *xl;

	std::map<TFrameId, QString> images;
	double dpix = 0, dpiy = 0;
	for (int i = 0; i < (int)cell.size(); i++) {
		if (!cell[i].isEmpty() && dpix == 0)
			((TToonzImageP)(cell[i].getImage(false)))->getDpi(dpix, dpiy);

		if (!level) {
			level = cell[i].getSimpleLevel();
			xl = cell[i].m_level.getPointer();
		}
		if (!mLevel)
			mLevel = mCell[i].getSimpleLevel();
	}

	if (!level || !mLevel)
		return;

	TFilePath fp(fullpath.toStdString());

	TXshLevel *txl = level->getScene()->createNewLevel(level->getType(), fp.getWideName(), level->getResolution());
	TXshSimpleLevel *newLevel = txl->getSimpleLevel();
	newLevel->setPalette(level->getPalette());
	newLevel->clonePropertiesFrom(level);
	newLevel->setPath(fp);

	TApp::instance()->getCurrentScene()->notifySceneChanged();
	TApp::instance()->getCurrentScene()->notifyCastChange();
	TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();

	int count = 0;
	for (int i = 0; i < (int)cell.size(); i++) {
		if (cell[i].isEmpty() && mCell[i].isEmpty())
			continue;

		TAffine imgAff, matchAff;

		getColumnPlacement(imgAff, xsh, min(start, mStart) + i, column, false);
		getColumnPlacement(matchAff, xsh, min(start, mStart) + i, mColumn, false);

		//std::map<TFrameId, TFrameId>::iterator it;
		MergedPair mp(cell[i].isEmpty() ? TFrameId() : cell[i].getFrameId(),
					  mCell[i].isEmpty() ? TFrameId() : mCell[i].getFrameId(),
					  imgAff.inv() * matchAff);

		std::map<MergedPair, TFrameId>::iterator computedMergedIt = computedMergedMap.find(mp);

		if (computedMergedIt != computedMergedMap.end()) {
			TXshCell newCell(newLevel, computedMergedIt->second);
			xsh->setCell(i, column, newCell);
			cell[i] = newCell;
			continue;
		}

		TFrameId newFid(++count); //level->getLastFid().getNumber()+1);
		TDimension dim = level->getResolution();
		TToonzImageP newImage;
		if (cell[i].isEmpty()) {
			newImage = TToonzImageP(TRasterCM32P(dim), TRect(0, 0, dim.lx - 1, dim.ly - 1));
			newImage->setDpi(dpix, dpiy);
		} else
			newImage = (TToonzImageP)(cell[i].getImage(false)->cloneImage());

		newImage->setPalette(level->getPalette());

		newLevel->setFrame(newFid, newImage);
		TXshCell newCell(newLevel, newFid);
		xsh->setCell(i, column, newCell);
		computedMergedMap[mp] = newCell.getFrameId();

		cell[i] = newCell;

		TImageP img = cell[i].getImage(true);
		TImageP match = mCell[i].getImage(true);
		TFrameId fid = cell[i].m_frameId;
		TFrameId mFid = mCell[i].m_frameId;

		if (!img || !match)
			continue;

		TToonzImageP timg = (TToonzImageP)img;
		TToonzImageP tmatch = (TToonzImageP)match;

		QString id = "MergeCmappedUndo" + QString::number(MergeCmappedSessionId) + "-" + QString::number(fid.getNumber());
		TImageCache::instance()->add(id, timg->clone());
		images[fid] = id;

		TAffine dpiAff = getDpiAffine(level, fid);
		TAffine mdpiAff = getDpiAffine(mLevel, mFid);
		matchingLevels.push_back(MergeCmappedPair(cell[i], imgAff * dpiAff, mCell[i], matchAff * mdpiAff));
	}

	if (!isRedo) {
		TPalette *plt = level->getPalette();

		TPaletteHandle *pltHandle = new TPaletteHandle();
		pltHandle->setPalette(plt);
		int styleCount = plt->getStyleCount();

		TUndoManager::manager()->add(new MergeCmappedUndo(txl, MergeCmappedSessionId,
														  column,
														  level, images,
														  mColumn,
														  plt));
	}

	removeLevel(xl);
	QApplication::setOverrideCursor(Qt::WaitCursor);
	mergeCmapped(matchingLevels);
	QApplication::restoreOverrideCursor();

	for (int i = 0; i < (int)cell.size(); i++) //the saveboxes must be updated
	{
		if (cell[i].isEmpty() || mCell[i].isEmpty())
			continue;

		if (!cell[i].getImage(false) || !mCell[i].getImage(false))
			continue;

		TXshSimpleLevel *sl = cell[i].getSimpleLevel();
		const TFrameId &fid = cell[i].m_frameId;

		ToolUtils::updateSaveBox(sl, fid);
		IconGenerator::instance()->invalidate(sl, fid);
		sl->setDirtyFlag(true);
	}

	newLevel->setDirtyFlag(true);
	TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
Example #9
0
TImage *TTool::touchImage() {
  if (!m_application) return 0;

  m_cellsData.clear();

  m_isLevelCreated  = false;
  m_isFrameCreated  = false;
  Preferences *pref = Preferences::instance();

  bool isAutoCreateEnabled   = pref->isAutoCreateEnabled();
  bool animationSheetEnabled = pref->isAnimationSheetEnabled();
  bool isAutoStretchEnabled  = pref->isAutoStretchEnabled();

  TFrameHandle *currentFrame    = m_application->getCurrentFrame();
  TXshLevelHandle *currentLevel = m_application->getCurrentLevel();

  if (currentFrame->isEditingLevel()) {
    // Editing level

    // no level => return 0
    TXshLevel *xl = currentLevel->getLevel();
    if (!xl) return 0;
    TXshSimpleLevel *sl = xl->getSimpleLevel();
    if (!sl || sl->isEmpty()) return 0;

    TFrameId fid = currentFrame->getFid();
    TImageP img  = sl->getFrame(fid, true);
    if (!img) {
      // no drawing found
      if (sl->isSubsequence() || sl->isReadOnly() || !isAutoCreateEnabled)
        return 0;

      // create a new drawing
      img = sl->createEmptyFrame();
      sl->setFrame(fid, img);
      currentLevel->notifyLevelChange();
      m_isFrameCreated = true;
    }
    return img.getPointer();
  } else {
    // editing xsheet
    if (m_application->getCurrentObject()->isSpline()) return 0;

    TSceneHandle *currentScene = m_application->getCurrentScene();
    ToonzScene *scene          = currentScene->getScene();
    int row                    = currentFrame->getFrame();
    int col = m_application->getCurrentColumn()->getColumnIndex();
    if (col < 0) return 0;

    TXsheetHandle *currentXsheet = m_application->getCurrentXsheet();
    TXsheet *xsh                 = currentXsheet->getXsheet();
    if (!xsh) return 0;

    TXshCell cell       = xsh->getCell(row, col);
    TXshSimpleLevel *sl = cell.getSimpleLevel();

    if (sl != 0) {
      // current cell is not empty
      if (isAutoCreateEnabled && animationSheetEnabled && row > 0 &&
          xsh->getCell(row - 1, col) == xsh->getCell(row, col)) {
        // animationSheet is enabled and the current cell is a "hold". We must
        // create a new drawing.
        // measure the hold length (starting from the current row) : r0-r1
        int r0 = row, r1 = row;
        if (isAutoStretchEnabled)
          while (xsh->getCell(r1 + 1, col) == cell) r1++;
        // find the proper frameid (possibly addisng suffix, in order to avoid a
        // fid already used)
        TFrameId fid = getNewFrameId(sl, row);
        // create the new drawing
        TImageP img      = sl->createEmptyFrame();
        m_isFrameCreated = true;
        // insert the drawing in the level
        sl->setFrame(fid, img);
        // update the cell
        cell = TXshCell(sl, fid);
        // update the xsheet (change the current cell and possibly all the
        // following "hold")
        for (int r = r0; r <= r1; r++) xsh->setCell(r, col, cell);
        // notify
        currentXsheet->notifyXsheetChanged();
        currentScene->notifyCastChange();
        currentLevel->notifyLevelChange();
        m_cellsData.push_back(r0);
        m_cellsData.push_back(r1);
        m_cellsData.push_back(0);
      }
      // we've found the image. return it.
      return cell.getImage(true).getPointer();
    }

    // current cell is empty.
    if (!isAutoCreateEnabled) return 0;

    // get the column range
    int r0, r1;
    xsh->getCellRange(col, r0, r1);

    if (animationSheetEnabled && r0 <= r1) {
      // animation sheet enabled and not empty column. We must create a new
      // drawing in the column level and possibly add "holds"

      // find the last not-empty cell before the current one (a) and the first
      // after (b)
      int a = row - 1, b = row + 1;
      while (a >= r0 && xsh->getCell(a, col).isEmpty()) a--;
      while (b <= r1 && xsh->getCell(b, col).isEmpty()) b++;

      // find the level we must attach to
      if (a >= r0) {
        // there is a not-emtpy cell before the current one
        sl = xsh->getCell(a, col).getSimpleLevel();
      } else if (b <= r1) {
        sl = xsh->getCell(b, col).getSimpleLevel();
      }
      if (sl) {
        // note: sl should be always !=0 (the column is not empty)
        // if - for some reason - it is ==0 then we skip to the standard (i.e.
        // !animationSheetEnabled) beahviour

        // create the drawing
        // find the proper frameid (possibly addisng suffix, in order to avoid a
        // fid already used)
        TFrameId fid = getNewFrameId(sl, row);
        // create the new drawing
        TImageP img      = sl->createEmptyFrame();
        m_isFrameCreated = true;
        // insert the drawing in the level
        sl->setFrame(fid, img);
        // update the cell
        cell = TXshCell(sl, fid);
        xsh->setCell(row, col, cell);

        // create holds
        if (!isAutoStretchEnabled) {
          m_cellsData.push_back(row);
          m_cellsData.push_back(row);
          m_cellsData.push_back(2);  // vuoto => nuovo
        } else {
          if (a >= r0) {
            // create a hold before : [a+1, row-1]
            TXshCell aCell = xsh->getCell(a, col);
            for (int i = a + 1; i < row; i++) xsh->setCell(i, col, aCell);
            m_cellsData.push_back(a + 1);
            m_cellsData.push_back(row - 1);
            m_cellsData.push_back(1);  // vuoto => vecchio

            if (b <= r1 && xsh->getCell(b, col).getSimpleLevel() == sl) {
              // create also a hold after
              for (int i = row + 1; i < b; i++) xsh->setCell(i, col, cell);
              m_cellsData.push_back(row);
              m_cellsData.push_back(b - 1);
              m_cellsData.push_back(2);  // vuoto => nuovo
            } else {
              m_cellsData.push_back(row);
              m_cellsData.push_back(row);
              m_cellsData.push_back(2);  // vuoto => nuovo
            }
          } else if (b <= r1) {
            // create a hold after
            for (int i = row + 1; i < b; i++) xsh->setCell(i, col, cell);
            m_cellsData.push_back(row);
            m_cellsData.push_back(b - 1);
            m_cellsData.push_back(2);  // vuoto => nuovo
          }
        }
      }
      // notify & return
      currentXsheet->notifyXsheetChanged();
      currentScene->notifyCastChange();
      currentLevel->notifyLevelChange();
      return cell.getImage(true).getPointer();
    }

    if (row > 0 && xsh->getCell(row - 1, col).getSimpleLevel() != 0 &&
        !animationSheetEnabled) {
      sl = xsh->getCell(row - 1, col).getSimpleLevel();
      if (sl->getType() != OVL_XSHLEVEL ||
          sl->getPath().getFrame() != TFrameId::NO_FRAME) {
        // la cella precedente contiene un drawing di un livello. animationSheet
        // e' disabilitato
        // creo un nuovo frame
        currentLevel->setLevel(sl);
        if (sl->isSubsequence() || sl->isReadOnly()) return 0;
        TFrameId fid     = sl->index2fid(sl->getFrameCount());
        TImageP img      = sl->createEmptyFrame();
        m_isFrameCreated = true;
        sl->setFrame(fid, img);
        cell = TXshCell(sl, fid);
        xsh->setCell(row, col, cell);
        currentXsheet->notifyXsheetChanged();
        currentScene->notifyCastChange();
        currentLevel->notifyLevelChange();
        return img.getPointer();
      }
    }

    // animation sheet disabled or empty column. autoCreate is enabled: we must
    // create a new level
    int levelType    = pref->getDefLevelType();
    TXshLevel *xl    = scene->createNewLevel(levelType);
    sl               = xl->getSimpleLevel();
    m_isLevelCreated = true;

    // create the drawing
    TFrameId fid = animationSheetEnabled ? getNewFrameId(sl, row) : TFrameId(1);
    TImageP img  = sl->createEmptyFrame();
    m_isFrameCreated = true;
    sl->setFrame(fid, img);
    cell = TXshCell(sl, fid);
    xsh->setCell(row, col, cell);
    if (animationSheetEnabled) {
      m_cellsData.push_back(row);
      m_cellsData.push_back(row);
      m_cellsData.push_back(2);  // vuoto => nuovo
    }

    currentXsheet->notifyXsheetChanged();
    currentScene->notifyCastChange();
    currentLevel->notifyLevelChange();
    return img.getPointer();
  }
}